cp-j0gw.14: R19 suppressed-earnings annotation — fourth case - #4
Open
AJBcoding wants to merge 1 commit into
Open
cp-j0gw.14: R19 suppressed-earnings annotation — fourth case#4AJBcoding wants to merge 1 commit into
AJBcoding wants to merge 1 commit into
Conversation
Per SPEC-DELTA §2.2 + §2.3 four-layer suppression model: cells where the
PPD-published OBBBA verdict surfaces but the underlying earnings are
unavailable need TWO distinct annotations, not one. The prior 3-case enum
conflated:
- earn_suppressed=true (privacy-rule cell suppression)
- median_earn_p4 IS NULL with earn_suppressed=false (cohort-level
reason — typically cohort below floor or pool-inherited verdict)
into a single 'earnings_suppressed_ppd_published' bucket with a
privacy-rule-flavored message. The two cases are structurally distinct
(privacy rule vs cohort floor) and the user-facing explanation should
match the actual cause.
Adds fourth annotation provenance value 'cohort_suppressed_ppd_published':
src/types.ts NoiseBandAnnotation.provenance union
(3 → 4 values; doc comments per case)
src/engine.ts buildNoiseBandAnnotation() splits the conflated
branch into earn_suppressed first (privacy-rule
message), then median_earn_p4===null (cohort-level
message). When both flags fire, earn_suppressed
wins per the spec's user-relevance ordering.
src/api/schema.ts NoiseBandAnnotation provenance enum extended
Tests: tests/engine.test.ts gains a 'fourth annotation case' describe
block with 4 tests:
- new case fires on cohort_suppressed=true / earn_suppressed=false
- earn_suppressed precedence preserved when both flags set
- NOT MEASURED still produces null (no leakage into the new branch)
- measurable-with-noise-band path (gap_tool_derived) preserved
The pre-existing earnings_suppressed_ppd_published test continues to pass
unchanged because earn_suppressed=true still hits its dedicated branch.
Suite: 197 → 201 tests, all green. Typecheck clean.
Out of scope (chair args were strict on file list):
- rules.ts R19 rule fire still treats both cases as one rule output
(annotation refinement does not propagate to RuleFire.note text).
File a follow-on bead if downstream UI / verdict-card surface needs
the cohort/privacy distinction in the rules_fired array too.
- No senate-distribution packet alteration (per chair args).
Closes cp-j0gw.14.
AJBcoding
added a commit
that referenced
this pull request
May 5, 2026
Adds three new sections to the changelog reflecting the 2026-05-05 evening polecat-return wave and chair-review track: 1. "Stacked PR record" — table of all 5 PRs (#1-#5) with branches, beads, authors, and post-merge test counts. Suggested chair merge order: #1 -> #3 -> #2 -> #4 -> #5. 2. Sub-bead ledger refreshed: cp-j0gw.8, .11, .13, .14, .15 all marked done with PR pointers; cp-j0gw.16 deferred until preview URL is live. 3. "Chair-review track applied" — the 4 reviews iterated inline this evening (#1 disclaimers approved, #2 systemwide refined, #3 groupings applied, #4 prose skipped + bug fix shipped). #5 comment-outline review not yet run. 4. "Smoke-stack record" — verifies all 5 PRs merge cleanly in order with one trivial conflict (web/index.html persona-card stack). Final stacked test count: 249/249. Production lint scan caught 3 dean-facing literal violations (OPEID6 + cascade) in PR #1's surfaces; fixed in commit 0993045. PR #2 (obsidian widget) has one analogous "cascade" literal in link text — surfaced as follow-up note. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
14 tasks
AJBcoding
added a commit
that referenced
this pull request
May 5, 2026
Stacked PR record updated: - 6 PRs now open (added PR #6, quartz batch-2 explainers M04/M07/M14) - PR #2 carries obsidian's cp-j0gw.17 lint fix (commit 38e236c) - Final stacked test count: 270/270 - Suggested chair merge order: #1 -> #3 -> #2 -> #4 -> #5 -> #6 - Broader lint scan: 0 OPEID6/cascade violations remaining; only formal cite names (STATS NPRM, RIA Table 3.19) kept per M-doc convention. Sub-bead ledger: cp-j0gw.17 + cp-j0gw.18 marked done with PR pointers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
NoiseBandAnnotation.provenancevaluecohort_suppressed_ppd_published, splitting the prior conflated branch inbuildNoiseBandAnnotation()into two cases per SPEC-DELTA §2.2 + §2.3 four-layer suppression model.earn_suppressed=true) keepsearnings_suppressed_ppd_published; cohort-level suppression (median_earn_p4 IS NULLwhileearn_suppressed=false) gets the new value with a cohort-flavored message.src/api/schema.ts) extended in lockstep so OpenAPI consumers see the new value.Why
Prior 3-case enum lumped two structurally distinct suppression causes under one privacy-rule-flavored message. Per SPEC-DELTA §2.3, suppression has four layers (
out_of_scope,cohort_suppressed,missing_test,earn_suppressed); the noise-band annotation should distinguish privacy-rule causes from cohort-level causes so the UI can explain WHY the noise band did not run.When both flags fire,
earn_suppressedwins (privacy-rule message is the more user-relevant explanation).Test plan
npm test— 197 → 201 tests, all greennpm run typecheck— cleanearnings_suppressed_ppd_publishedtest (earn_suppressed: true) passes unchangedfourth annotation casedescribe block (4 tests): cohort case fires, earn_suppressed precedence, NOT MEASURED leakage check, gap_tool_derived backward compatOut of scope
src/rules.tsR19 rule fire still treats both suppression sub-cases as one rule output (only the structurednoise_bandannotation field distinguishes;rules_fired[].notetext is unchanged). Per chair args strict file list — file a follow-on bead if the verdict-card surface needs the distinction propagated.Closes cp-j0gw.14.