Skip to content

feat(prompts): the merits stage contract, and the merits fan-out - #1027

Merged
modelmirror merged 1 commit into
stagingfrom
feat/stage-prompts-merits
Aug 4, 2026
Merged

feat(prompts): the merits stage contract, and the merits fan-out#1027
modelmirror merged 1 commit into
stagingfrom
feat/stage-prompts-merits

Conversation

@modelmirror

Copy link
Copy Markdown
Collaborator

⚠️ Maintainer merge — not self-merged. Touches .github/workflows/run-predict.yml (one line + comment), which is the permission surface AGENTS.md holds for the maintainer even into staging. Everything else is green and reviewed; the workflow line is described in full below.

What

The merits stage becomes runnable: prompt sections in both prompts, the fan-out widened to the merits event, and an offline merits cell proving the path.

  • predict.md### Stage: meritsjudgment's six values, probability = P(disturbed) (with granted as the same binary and predicted_disposition = other), the mandatory intersection-only-scored vote block, what the record does and does not show, why the cert signals and salience band are spent rather than wrong, the statpack merits section with its cautions, and the single merits-v1 claim.
  • evaluate.md — a merits block mirroring the interim one, a judgment_correct field bullet, and a stage-aware correct.
  • Fan-out: _merits_forecastable (merits stage + order kind + no latched judgment + opens_merits_proceeding + row scope). The deliberate hold-back test is replaced by five, one per arm.
  • Smoke: test_stub_cascade_merits_smoke drives provision → stub predict → judgment outcome → evaluate → validate, asserting the cell lands in the leaderboard's merits stage block with the cert board still empty.

Two findings that changed the design

1. Provisioning would have refused every merits cell. provision-snapshot --refuse-terminal matched "Petition GRANTED." through match_disposition_signal — so the very entry that mints a merits event would have refused its own snapshot. Every production merits cell would have run snapshot-less and context-less: no frozen conditioning, therefore no claim block. The guard is now keyed on the event (--event), because one docket carries several events' outcomes at once: on the merits event the outcome is the judgment and the cert grant is legitimate record; on every other event the existing rule is unchanged. This is the workflow line — passing matrix.event_id through to the provisioner. zizmor and actionlint both clean; workflow-reviewer found nothing.

2. Merits skill scores are suppressed, not computed — a deliberate deviation. docs/decision-model.md pre-registers that no merits skill number may be published against a pool with unlabelled GVRs, and that the fan-out owes a label-independent guard first. That guard is not built, and a per-cell heuristic cannot substitute: the escapees surface as partly-labelled Terms (OT2023/OT2024 render gvr at 0.4%/0.8%, not zero), and the agent-readable table is keyed on the docket-number Term. So brier_skill_score is omitted on every merits cell, enforced by a new validate check. segment_base_rate is still recorded, so the number is there to audit when the guard lands. This honours the pre-registration ordering rather than publishing a number the record says we may not.

Review

  • code-reviewer — 1 blocker: the merits branch had swapped the high-recall scan for the conservative parser, letting "Opinion Issued" / "Judgment Issued" / termination and dismissal shapes provision a forward cell. Fixed, with tests pinning both directions.
  • stats-reviewer — 5 blockers, all fixed: the provisioning chain; an "exact baseline" overclaim (ten-Term bound and 30-parsed floor now stated in both prompts); the untruncated merits table against cert's "the shown window is your window"; an inert GVR check; and the pre-registration ordering, resolved by suppressing the number per the reviewer's own preferred option.
  • docs-reviewer — 1 blocker (the provisioning refusal) plus stale scope prose in outcome-decomposition.md, README.md, budget.md (the merits stream is now priced, and its lack of a quota justified) and data-pipeline.md.
  • workflow-reviewer — clean.

Stated at the seam rather than bounded away: predict scope excludes IFP grants while the statpack merits population does not — ~13% of the pack's plain grants (86/679, verified).

Digest: YES (both prompts). Expected pre-freeze; the paste happens once, post-promotion.

Gate: lint / types / test (1912 passed) / schemas / data (11,212 artifacts, 5,509 references) — green, plus zizmor@1.26.1 and actionlint 1.7.12 clean.

Closes #1009.

… proof

The artifact is the first runnable merits cell: the prompt contract that tells
a predictor and an evaluator what a merits event is, the fan-out that mints
one, the provisioning guard that lets it keep its snapshot, and an offline
cascade that drives it end to end.

Prompts. `predict.md` gains `### Stage: merits`: the judgment vocabulary,
`probability` read as P(disturbed) with DIG and an equally divided affirmance
declared undisturbed, the mandatory vote block and why naming a Justice costs
nothing, what the granted docket does and does not disclose (the merits briefs
and the argument are recorded as events, never as text, and the provisioned
documents are cert-stage), the cert signals and the frozen salience band being
spent rather than wrong, the statpack merits section on its stated terms — the
ten-Term window counted rather than read off an untruncated table, the
30-parsed floor, the grant-Term axis, the censoring direction — and the one
declared `merits-v1` claim. `evaluate.md` gains the matching rules: `correct`
routed on the outcome's judgment, `judgment_correct` beside it, the merits
baseline in `segment_base_rate`, and `base_rate_basis` left null — which is
what makes the harness stamp `base_rate_salience_version` null, correct because
the merits pool is no salience-band product.

No merits cell publishes a skill score. `docs/decision-model.md` pre-registers
that no merits skill number may be published against a pool whose grant Terms
carry unlabelled GVRs, and that the fan-out owes a label-independent guard
first. That guard is not built. Detecting the contaminated Terms per cell was
considered and rejected: the escapees show up as a partly-labelled Term rather
than an empty one, and the table an evaluator can read is keyed on the
docket-number Term, so the test would pass exactly the Terms the pack's own
caveat names. So `brier_skill_score` is omitted on every merits cell, stated in
the prompt and enforced by a new `validate` check rather than left to prose.
`segment_base_rate` is still recorded — the pool the cell faced is a fact about
the run, and recording it is not a skill claim over it.

Fan-out. `store.forecastable_events` gains the merits admission, keyed on the
merits stage, an unlatched judgment, and `opens_merits_proceeding` on the row —
the same predicate that mints the event and that the statpack merits section
measures its rate over, re-checked so a docket re-resolved to `gvr` sheds its
cell. The guard test it replaces is now five, one per arm. The residue the
predicate cannot close — predict scope excludes IFP grants, the merits
population does not — is stated at the seam with its size rather than bounded
away.

Provisioning. The forward-cell leakage guard is keyed on the event, because one
docket carries several events' outcomes at once: a granted docket's grant order
is a disclosed cert outcome and is also what opens the merits proceeding, so
without the key every merits cell would be refused by the entry that minted it
and would run snapshot-less and context-less. On the merits event the test is a
parsed judgment plus the cert scan's own recall minus its one cert-before-
judgment grant branch — the conservative parser alone would let "Opinion
Issued" through, and the two failure costs point in opposite directions.

Proof. The offline cascade smoke drives a merits cell provision -> stub predict
-> judgment outcome -> evaluate -> validate over the fixture's granted docket,
and asserts it lands in the leaderboard's unranked `merits` stages block with
nothing entering the cert board.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@modelmirror
modelmirror merged commit 0003247 into staging Aug 4, 2026
7 checks passed
@modelmirror
modelmirror deleted the feat/stage-prompts-merits branch August 4, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant