Skip to content

Promotion: pre-freeze batch — stage enablement, claim scoring, sal-v2 groundwork - #1019

Draft
modelmirror wants to merge 46 commits into
mainfrom
staging
Draft

Promotion: pre-freeze batch — stage enablement, claim scoring, sal-v2 groundwork#1019
modelmirror wants to merge 46 commits into
mainfrom
staging

Conversation

@modelmirror

@modelmirror modelmirror commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Promotion batch (draft — held until the batch completes)

Do not merge yet. This drafts the next staging→main promotion so the batch is visible as it assembles. The maintainer-dispatched promote workflow re-runs the gate, sync and freshness checks at merge time regardless. Merges as a merge commit (promotion ancestry rule).

Landed on staging

Stage enablement — cert, interim and merits are one pipeline now

Claim scoring and its surface

Scoring the predictors, not just the predictions

Security

Workflows (maintainer-merged)

Ledger hygiene

Both change no data by merging; their --apply + data/ commit is a maintainer step, because neither collect jail admits deleting an outcome.json.

Docs

Earlier in the window: #1000#1006 (corpus fixes, version-pinned band pool, pre-registration docs, lookback pin, forecastable-events seam, sal-v2 intent).

Still to join

Issues this batch closes

#903, #904, #1007, #1008, #1009 already hand-closed on staging merge; #864, #907 and #963 close after promotion. #1010 and #1033 close when the two ledger applies land as a data/ commit on main — the tools are merged; the records are not yet removed.

Maintainer actions

  1. The two corpus-lane sweeps now run themselves. chore(workflows): wire blind grading, and the two corpus convergence sweeps #1031 put the application relabel and the merits judgment backfill in run-seed's writer lane, so neither needs a dispatch — they converge on the schedule against the pointer the writers own (corpus-push is last-writer-wins, so a dev-checkout run would silently discard concurrent writes).
  2. The two ledger applies (data: scotus/72460453's stay-motion event carries the petition's GVR as its outcome #1010, data: scotus/63195851's spurious appeal event duplicates the petition's cert grant #1033), as one data/ commit, in this order:
    uv run fedcourts remove-unmintable-events --apply       # scotus/63195851
    uv run fedcourts reopen-misattributed-outcomes --apply  # scotus/72460453
    # commit data/ to main, then corpus-push
    
    Removal first: it drops that case's whole event directory, which also clears the two triage lines the reopen sweep otherwise prints for it. Not lane-able — the change set deletes an outcome.json and rewrites an event.yaml, which assert_within_jail (adds only) and assert_cleanup_within_jail (deletes under predictions/ only) both refuse. Independent of the promotion; touches only data/.
  3. After promotion: flip the evaluate handoff variable back on (held at 0 so the owed gradings do not re-run against a main that lacks the redaction fix), then fresh fedcourts process-digest --all on the promoted tree → paste into chore(metrics): freeze the proc-v1 process digests #991 → merge-commit chore(metrics): freeze the proc-v1 process digests #991 → bless (metrics: bless the settled process digest into the frozen set and record the freeze commit #907).

dependabot Bot and others added 28 commits August 2, 2026 02:21
Bumps the python group with 4 updates: [apsw](https://github.com/rogerbinns/apsw), [boto3](https://github.com/boto/boto3), [ruff](https://github.com/astral-sh/ruff) and [types-pyyaml](https://github.com/python/typeshed).


Updates `apsw` from 3.53.3.1 to 3.53.4.0
- [Release notes](https://github.com/rogerbinns/apsw/releases)
- [Changelog](https://github.com/rogerbinns/apsw/blob/master/doc/changes.rst)
- [Commits](rogerbinns/apsw@3.53.3.1...3.53.4.0)

Updates `boto3` from 1.43.53 to 1.43.59
- [Release notes](https://github.com/boto/boto3/releases)
- [Commits](boto/boto3@1.43.53...1.43.59)

Updates `ruff` from 0.15.22 to 0.16.0
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ruff@0.15.22...0.16.0)

Updates `types-pyyaml` from 6.0.12.20260518 to 6.0.12.20260724
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: apsw
  dependency-version: 3.53.4.0
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: python
- dependency-name: boto3
  dependency-version: 1.43.59
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: python
- dependency-name: ruff
  dependency-version: 0.16.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: python
- dependency-name: types-pyyaml
  dependency-version: 6.0.12.20260724
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: python
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps the actions group with 4 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/analyze](https://github.com/github/codeql-action), [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) and [anthropics/claude-code-action](https://github.com/anthropics/claude-code-action).


Updates `github/codeql-action/init` from 4.37.3 to 4.37.4
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@e4fba86...f205ea1)

Updates `github/codeql-action/analyze` from 4.37.3 to 4.37.4
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@e4fba86...f205ea1)

Updates `aws-actions/configure-aws-credentials` from 6.2.2 to 6.2.3
- [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases)
- [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md)
- [Commits](aws-actions/configure-aws-credentials@517a711...e6de054)

Updates `anthropics/claude-code-action` from 1.0.180 to 1.0.183
- [Release notes](https://github.com/anthropics/claude-code-action/releases)
- [Commits](anthropics/claude-code-action@fa7e2f0...be7b93b)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.37.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: aws-actions/configure-aws-credentials
  dependency-version: 6.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: anthropics/claude-code-action
  dependency-version: 1.0.183
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
…on-6d89d027fe

chore(deps): bump the python group with 4 updates
…staging/actions-d662d045fd

chore(deps): bump the actions group with 4 updates
)

Sweeps the committed docs surfaces for statements written before the first
staging-to-main promotion that no longer describe the current design:
main-base requireability, the sync-staging bootstrap caveat, the auto
deploy-environment resolution, the run-seed refresh/dedupe tail, the counsel
column's legacy rows, denial-sampling present-tense leftovers, the sal-v1
feature list, the inert interim reserve, and CLI/backtest dispatch surfaces.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
chore: sync main into staging
The main-base merge-routing job joins main's required status checks
(ruleset 'main: require PR'), closing the gap where a mis-routed feature
PR went red without being blocked. Updates every surface that records
the required set: the pinned list in tests/test_required_checks.py, the
ci.yml comments, the promotion section and one-time-setup checklist in
docs/pipeline.md, the ruleset inventories in docs/security.md, and the
public copy in SECURITY.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
chore: sync main into staging
… procedure example

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
chore: record main-base as a required check on main
* fix(corpus): drop cluster-joined fields from bulk circuit rows

The bulk export's docket-to-opinion-cluster join is misjoined on the
circuit slices (19th-century cluster text and OCR-garbled judge names on
2018-19 dockets — an id-space collision in the staged join), so the
storage projection drops summary, precedential_status, judges, and panel
for a bulk-sourced non-SCOTUS row instead of storing text the join
cannot vouch for. Those columns take the incoming value on upsert, so a
re-served bulk row also clears any stored misjoin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: record the bulk-circuit cluster-field carve-out per review

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(corpus): extend the bulk-circuit carve-out to the citation fields

citation_count is populated on 1.36M of 1.56M non-SCOTUS rows — the same
row count as precedential_status — so the misjoined cluster join supplied
it too, and it feeds the outcome-evidence predicates. citations rides
along for provenance symmetry (empty on every circuit row today). Docs in
corpus/README.md and the replica section of docs/data-pipeline.md record
the carve-out where a pivot implementer will read it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(corpus): max-latch has_opinion across upserts

The presence bit is monotone — an opinion once linked is never unlinked
— and every writer asserts it (NOT NULL, default False), so without a
latch a docket-only re-ingest flips a stored True back to False and the
case silently drops out of query --full and every presence check. Joins
the distribution_count / escalation-signal max-latch family; the model
validator already keeps the bit monotonic in memory.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: tighten the latch comment per review

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…rompt) (#1002)

* fix(metrics): version-pin the band base-rate pool

A band name is meaningful only under the salience version that assigned
it, and the nested risk sets compound a mismatch: moving one cutpoint
moves every rate below it. _pooled_band_rate now pools only statpack
Terms whose salience_version matches the version that produced the band
(the frozen context's version on the risk-set path, the live scorer's
on the terminal path); no matching Term yields the contracted None, so
brier_skill_score is omitted rather than computed against a blend no
version ever defined. All committed Terms are sal-v1, so no published
number moves. Rule recorded in docs/salience.md beside the base-rate
contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: correct the version-pin rationale per review

The cutpoint-move mechanism sentence was wrong — a moved cutpoint
changes membership of exactly one risk set, since the moved rows sit
inside every weaker band's risk set either way — so the rationale rests
on the populations-sharing-a-label argument alone. The bias/variance
paragraph's bounded-window clause is reconciled with the pin (the
window carries no versioning duty), the fallback chain's terminal-basis
mirror case is named, and the doc anchors on the public entry points.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* prompt(evaluate): carry the version pin to the agent-side baseline

The evaluator agent, not the code path, publishes forward skill — and
its pooling recipe read the band table with no version check, so a
transition-time cell would compute exactly the cross-version number the
pin forbids. The table's heading names its salience version; on a
mismatch with the prediction's frozen version the agent omits the
baseline and flags it. docs/salience.md names the agent-side half
beside the code rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: object sentinel for the context fixture per review

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* docs(outcome-decomposition): pre-register the pre-freeze scope

Adds the eighth forecastability test (a baseline within rounding of 0
lets shotgunning back in through the level tests 1-7 never check);
records that Prediction.context closes the as-at-prediction end of an
increment claim, replacing the one-field-away text; states the scope
decision that claim scoring sits outside the frozen process (the
evaluator prompt's do-not-score rule is itself digest-hashed), so the
first declared set arrives as a deliberate new process version; and
fixes the mechanical-semantic agreement definition (Kendall tau-b,
intersection-only with printed n and a suppression floor of 10, a
record-not-predictor availability mask, never pooled or ranked) so
defining it after grades exist cannot be a judgment call.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: reproducible arithmetic and selection guards per stats review

The worked example states its p (0.74 is exact for p=0.5; p=1 reads
~0.99); the eighth test grounds its failure mode in the realized total
collapsing to a Bernoulli draw rather than in the rule's expectation,
and marks itself the stated exception to the fineness preference; the
agreement intersection counts operational absences beside its n; the
context-block coverage boundary is named time-skewed; the noting rate
carries its public-record provenance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs+schema: finish the premise flip per review

The correction that Prediction.context closes the prediction-time end
left four passages asserting the old premise — the failure narrative,
the scoreable-today intro, the withdrawn-claim table row, and the
signals-block presence paragraph (whose antecedent the rewrite had
stolen; it now names Outcome.signals and contrasts the context block's
signals_observable line). The ResolutionSignals docstring and the
Outcome.signals description said the opposite of the doc they cite, so
they are corrected with the schema regenerated. The coverage sentence
states plainly that every committed prediction predates the block.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ntity (#1006)

* docs(salience): pre-register the sal-v2 intent; pin the carve-out identity

Records the two-cohort sal-v2 design before the freeze fixes sal-v1:
an arrival cohort with a load-bearing random-sample component (the only
cohort whose skill transfers to live prospective use) and an escalation
cohort on its own risk-set baselines, never pooled; a new frozen
version, never an in-place edit; fitting deferred to the denial-complete
re-walk with the replay bar recomputed there. The emergent identity
between the carve-out set and the high band (floor in config, cutpoint
in code) is now pinned by an exhaustive lattice test plus a
public-predicate check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: drive the lattice through the public scorer per review

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* chore(metrics): bound the base-rate lookback at ten Terms

salience.base_rate_lookback_terms moves 0 -> 10, matching
statpack.markdown_terms, so the scored baseline and the band table the
agents anchor on share one window by construction and cannot silently
diverge as walked Terms accumulate. With nine Terms walked the bound
excludes nothing: every published skill number is unchanged. Docs and
the shipped-value pin updated in step.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: bring every shipped-window statement to the new value per review

metrics/README.md, the segment_base_rate docstring, the
outcome-decomposition free-variance example, the SalienceConfig field
comment, the StatpackConfig docstring, and the cert back-test's
None-fallback docstring all stated the old shipped value as fact; each
now states the ten-Term window (or names the field default as the
absent-file fallback it is). The between-Terms timing clause returns to
the config comment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(pipeline): only case-baseline events earn forecast cells

Two halves of one leak. (a) A substantive motion event minted on a cert
docket rode the case into every predict queue — live re-poll, selection
sweep, route_result, pull, and the matrix's default-event resolution —
where a cell would be forecast by the cert-shaped prompt and scored
against cert band base rates. The new store.forecastable_events seam
filters prediction targeting to petition/appeal kinds; open_events stays
unfiltered for evaluate, outcome detection, rotation, and the corpus
service, because a motion's ground truth is still tracked. (b) A decided
docket whose one open event is a motion inherited the case-level cert
disposition in detect_resolution; the committed ledger carries exactly
that shape. Attribution now requires a case-baseline (petition/appeal)
event id; anything else lands unrecorded with its own reason.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test+docs: pin the two case-baseline encodings together per review

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs+test: seam coverage and stale open-events prose per review

The pull-queue payload test pins that a changed case with an open stay
motion queues predict with the baseline event only; every surface that
described the predict default as 'all open events' (the --event help,
the matrix docstrings, pipeline.md's trigger diagram, data-pipeline.md,
open_events' own docstring, the salience sweep prose) now says
case-baseline events; the UnrecordedOutcome contract acknowledges the
slugified event-id interpolation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: rewrap the open_events docstring

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(events): carry the decision stage on the event surface

Every event definition — corpus row and materialized event.yaml — now names
the decision standard that governs it (cert / interim / merits), stamped at
minting: a SCOTUS petition's baseline event is cert-stage, a circuit appeal
declares none. Nullable and additive, so every committed record and every
older corpus opens and validates unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs+schemas: null stage means unrecorded, not standard-free; stale neighbors

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(corpus): tolerate pre-stage remote blobs; stamp interim on SCOTUS applications

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: hoist the corpus_ranged import to module level

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Serves metrics/statpack.{json,md}: beside the sal-v1 salience-band axis the
pack gains a stage section for the interim application docket (strict YYAnnn
form), pack-level and per application-Term year — counts by parsed ask
(extension / substantive / unknown, never-parsed kept apart), and over the
substantive slice only the resolved/granted counts, a raw descriptive grant
rate, and the escalation-signal counts (response requested, referred to the
Court, amicus on file). This is the accumulating cohort that will eventually
ground an interim segment base rate; until one is specified the published cut
is descriptive only, and extensions never pool into any rate.

Modeled as its own section type (StatPackInterim, no salience_version), fed by
the same single corpus pass, and omitted from the serialized pack — not
emitted as null — while the corpus holds no application rows, so a pack built
from an application-free corpus serializes with no interim key at all (pinned
by a committed-pack round-trip regression test). Resolved counts gate on
machine-readable interim dispositions and out-of-vocabulary kind values fold
visibly into the unknown bucket, so corrupt external input can neither deflate
the rate nor break the kind-split identity. The cert sections, Term entries,
and band tables are untouched; a merits sibling joins the same way once a
merits judgment column exists to feed it.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…s scoring (#1013)

The declared cert-stage set of three (disposition, relist-increment,
cvsg-increment) under pipeline.claims: the declaration table, the resolvers
with the availability mask, strictly-prior baselines (disposition reuses
prediction_base_rate; the increments are honestly baseline-less until the
statpack carries per-Term relist/CVSG cuts), and the score_claims orchestrator
wiring the pre-registered claim_score rule. Prediction.claims and
Evaluation.claim_scores carry the blocks (optional, so committed records
validate); both prompts carry the claim contract; the outcome-decomposition
doc states the declared set as current design. Issue #903 (mechanical family
only — the semantic family stays pre-registered).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
#1014)

Artifact: metrics/leaderboard.json grows a stage axis — the ranked board is
explicitly the cert stage, any non-cert stage reports its own unranked
per-predictor block under `stages` (omitted while empty), and every stratum
block now carries `skill_scored`, the visible denominator behind
`mean_brier_skill_score`. Verified a no-op on the committed artifact: all 9
committed cells stratify as cert and the rebuilt board is byte-identical to
the committed metrics/leaderboard.json, so the file is deliberately not
regenerated here (the metrics refresh owns it).

Upstream of the board, outcome attribution is stage-routed: the case-level
cert disposition attributes to the open event whose stage is cert — an open
interim motion beside it stays open instead of forcing a refusal — with the
case-baseline id-prefix rule kept as the stage-less fallback and every
refusal reason preserved verbatim. The stratification join
(store.iter_stratified_evaluations) reads each event's stage off its
event.yaml, normalizing a null stage on petition/appeal kinds to cert.

Reviewed by code-reviewer, stats-reviewer, and docs-reviewer; their blockers
(a committed-data byte test that would redden path-jailed collect PRs, and a
false universal claim about pre-vocabulary events) are fixed — the byte test
now round-trips the committed artifact against its own schema instead of
rebuilding from data/.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…e section (#1015)

Groundwork for the merits base rate (#1009): granted SCOTUS cases' stored
terminal docket entries now feed a deterministic merits axis.

- pipeline/judgment.py: sentence-anchored parser from a disposition entry onto
  the Judgment vocabulary (plain verbs with the lower-court prose gap, in-part
  variants in either order, DIG, equally divided, and the canonical GVR order
  behind its cert recital), the disturbed projection (DIG / equally divided
  leave the judgment below standing), a best-effort authorship reader, and the
  backfill pass over granted rows reading the same stored-snapshot path the
  salience replay uses.
- corpus: additive merits_judgment / merits_decided columns owned by the
  backfill pass (keep-stored upsert latch, set_merits_judgment sole writer);
  the decided_before retrieval mask now strips the pair fail-closed unless
  merits_decided provably precedes the clock, so query priors carry no
  post-clock outcome facts into replay cells.
- CLI: backfill-merits-judgments (dry-run default, --apply), idempotent, with
  degradation counted (no_snapshot / no_match / stale), runnable standalone or
  from the run-seed corpus-writer lane.
- statpack: the merits stage section (per-grant-Term counts, six-way judgment
  distribution, descriptive disturbed rate with its coverage beside it),
  omitted byte-identically while no row carries a judgment; claim contract in
  metrics/README.md.

Artifact served: the statpack's merits stage section, and downstream the
merits Brier baseline feed.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…1016)

Artifact: a truthfully-labeled interim event surface — the precondition for
interim prediction. A SCOTUS YYAnnn application docket is a stay/injunction
motion governed by the interim standard, not a cert petition, but its baseline
event was minted cert-shaped (evt-petition-disposition / kind=petition /
stage=cert). This PR makes the event surface say what the docket is:

- default_event mints kind=motion / evt-motion-disposition / stage=interim for
  application dockets (strict YYAnnn key, the same identity the live channel
  addresses and the migration converges on); cert dockets and circuit appeals
  unchanged.
- corpus.rename_event: atomic upsert-new + delete-old primitive; the resolved
  latch is MAX across the old row, the incoming event, and any pre-existing
  row under the new id; casestore events mirror re-mirrored; same-identity and
  missing-subject calls fail loud.
- application_migration.relabel_application_baseline_events + CLI
  relabel-application-events (dry-run default): deterministic, offline,
  idempotent corpus migration renaming the cert-shaped baselines. Skips and
  reports (never folds) a case with committed ledger artifacts under the old
  identity or an entry-pinned evt-motion-disposition row.
- pipeline.outcome: form-keyed application guard (tolerant recognizer) — an
  application docket never takes the cert rule regardless of its baseline's
  current shape, closing the deploy-to-migration window; its resolution routes
  to the unrecorded queue with a reason naming the interim standard. Interim
  outcome recording is stage-keyed and ships with the interim predict path,
  backfilling from the row's disposition.

Migration execution (corpus-writer lane; interactive sessions cannot dispatch
workflows — run where the corpus is pulled, serialized with the corpus-write
concurrency group):
  uv run fedcourts corpus-pull
  uv run fedcourts relabel-application-events        # dry-run; review counts + skips
  uv run fedcourts relabel-application-events --apply
  uv run fedcourts corpus-push
  git add corpus/corpus.db.ref && git commit -m "corpus: relabel application-docket baseline events to motion/interim"
  # push via the data lane, as run-seed's maintenance sweeps do

Reviewed by code-reviewer and stats-reviewer subagents; both blockers fixed in
their suggested direction (form-keyed outcome guard + migration skip guards,
conditioned application-rotation visibility prose with a scope-latched test).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…1017)

* feat(metrics): add the claim-score surface and its judge validation

The artifact is metrics/claim-scores.json — the #904 predicted-reasoning
analytics surface: per predictor x stratum mechanical claim-total means with
the floor and lift beside them, per-claim mean scores, the largest
single-claim contribution, and the pre-registered per-stratum Kendall tau-b
judge validation (mechanical claim total vs reasoning_quality) over the
intersection population, suppressed below 10 pairs with the counts still
published. Committed in its honest fully-suppressed state: no committed
evaluation carries a claim_scores block yet.

`fedcourts claim-scores` produces it, sharing the leaderboard's stratified
join and frozen-scope default; the refresh plan lists it and the
corpus-status gate tracks it. The run-analytics workflow's "Regenerate the
metrics artifacts" step still needs the one-line addition
`uv run fedcourts claim-scores` after `uv run fedcourts leaderboard`
(workflows are maintainer-lane, so it is not part of this change).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(claim-metrics): resolve reviewer findings on the claim-score surface

The event is the reporting unit: blocks deduplicate to one per (case, event)
before averaging (the newest evaluation's block wins where copies could ever
differ), with `cells` beside `events` as the raw census and `scored_events`
as the one denominator of the three means. The judge validation publishes
`pair_events` beside the per-cell `pairs` its suppression keys on, so
evaluator multiplicity is visible where it matters most. The contract now
also states the process-version comparability rule and that the absence
counts cover committed cells only; the inverted `cells_with_claims`
description is fixed; `kendall_tau_b` is public now that two modules call it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…nted dockets in the live rotation (#1018)

The artifact is the open merits event (evt-order-judgment, kind order,
stage merits, opened on the grant date) plus a granted docket that keeps
polling toward its judgment: previously a cert grant resolved the
petition event and the docket exited corpus.live_rotation (disposition
latched), so no channel would ever observe the merits decision.

- resolve_case mints the merits event after outcome attribution (the
  detection pass never sees it among the open set), SCOTUS-only, for
  granted / granted-in-part; gvr and summary-reversal terminate the case
  at the cert order and mint nothing. Idempotent via the events upsert's
  (case_id, event_id) key and resolved MAX-latch; the ledger event.yaml
  is written from the post-upsert state so it honours the same latch.
- live_rotation admits a granted-set docket carrying an open merits-stage
  event until date_decided lands; the granted-set SQL literal is built
  from schemas.GRANTED_DISPOSITIONS (moved from outcome._GRANTED as the
  single source). A retained granted docket's stale conference date is
  masked out of the distributed-leads ordering.
- Re-polls of a retained granted docket are a clean no-op: when a
  resolved cert-home already carries the (still granted-set, still
  undated-decided) disposition, nothing is recorded or triaged; a
  latched date_decided or a mutated disposition falls through to the
  conservative unrecorded surface.
- The merits event is ground truth tracking only: kind order is not
  forecastable, so no predict cell fans out and no judgment detection
  resolves it yet.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
)

A GVR and a summary reversal grant the petition and dispose of the judgment
in the same order, so neither is followed by briefing, argument, or a separate
judgment to forecast. The minting rule already excluded them; the judgment
backfill and the statpack's merits section did not, admitting every row with a
cert grant date. That put a near-certain vacatur into the rate meant to
describe argued cases — on the real corpus, 555 of 1,235 admitted rows were
GVRs, and 98 of 244 parsed judgments came from them, inflating the disturbed
rate from 0.740 to 0.844 with cases no one was asked to predict.

One definition now governs every merits-population question — which grants
mint the event, which rows the backfill parses, which rows the statpack
describes — so the population that is predicted is the population the base
rate is measured over.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Artifact: retrieval logs that cannot carry a credential into a committed
artifact.

`retrieval_log.json` is harvested from the engine's own transcript, which
records whatever a tool call carried — not what the agent chose to write. A
codex payload keyed `message` put a Fernet-encrypted token into a query slice
verbatim, and the collect job's secret scan withheld the whole run's PR: the
gate did its job, but the capture path had no redaction at all.

`fedcourtsai.secretscan` now owns `redact_credentials()`, a pure rewrite of
credential-shaped runs to a `[redacted:<rule>]` marker: the scan's own
structured patterns plus Fernet, JWT, `sk-`, `AIza`/`ya29.`; a
credential-keyword/auth-header assignment rule; and, as a last resort, the
scan's calibrated entropy discriminator at a 64-char floor rather than 40,
because a redaction false positive silently eats evidence the leakage grading
reads while a scan false positive costs one human look. `fedcourtsai.retrieval`
applies it to every string it harvests, ahead of the 500-char cut, over a fixed
window so an agent cannot choose how much work capture does.

Redaction is not a gate — it lets through a run the scan would have withheld —
so `record-retrieval` reports how many calls it rewrote and warns when the
count is non-zero, and the shapes it cannot name still meet the scan. Measured
over the committed ledger: 0 of 32,880 transcript strings in 413 retrieval logs
are altered, and the widened keyword list flags no new line in 7,239 data files.

Touches SECURITY.md and docs/security.md, which describe the outcome this
changes for one artifact — maintainer lane, not self-merge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ial-redaction

fix(retrieval): redact credential-shaped values at capture
Comment thread tests/test_secretscan.py Fixed
Comment thread tests/test_secretscan.py Fixed
Comment thread tests/test_secretscan.py Fixed
Comment thread tests/test_secretscan.py Fixed
…e reserve (#1020)

* feat(interim): open the interim predict gates, quota'd by the salience reserve

The artifact is the first fundable interim predict cell — gates open, quota
enforced, outcomes recordable. A substantive SCOTUS application (stay,
injunction, vacatur) now enters predict scope, its motion/interim baseline
becomes forecastable, the live application rotation queues it forward on a
docket change (daily-debounced, leakage-guarded by a form-keyed interim
disposal scan), the selection pass funds up to interim_reserve_slots (5)
concurrent applications by displacing rank fill inside the current
conference's N, and a machine-matched interim resolution records the
stage-keyed outcome.json (interim vocabulary, disposing-entry date, no cert
signals block). Skill over the interim stage stays unscored: the segment base
rate and its estimator publish only at the pre-registered 25-substantive-
resolved floor, and an application cell's frozen context carries no cert band
so no cert-population baseline can reach it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(interim): gate the interim fan-out on the application docket form

A cert docket carries interim-stage events too — an entry-pinned stay or
injunction motion on the petition's own docket — and a cert docket is in
scope, so the stage and scope rules alone admitted one. Its cell would then
freeze the petition's salience band as its conditioning, scoring an interim
forecast against a cert population. The docket form is what the admission's
own contract already described; now the code implements it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(interim): refuse an application docket's cert-shaped baseline

Narrowing the scope rules to admit substantive applications releases their
predict_excluded latch, and an application whose baseline still reads
petition-kind then satisfied the case-baseline rule on the strength of the
kind alone — forecasting a stay application under the cert contract, against
a cert population. Discovery mints an application's baseline as the
interim-stage motion, but a row minted before that rule keeps the cert-shaped
id, so forecastability must be correct on its own terms: the migration
changes which event is forecast, never whether a mislabeled one is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
modelmirror and others added 10 commits August 4, 2026 13:30
…im cells (#1022)

The artifact is the first runnable interim cell — its prompt contract and its
offline proof. The interim predict gates are open, so a stay/injunction
application now fans out to the same predict/evaluate cells cert petitions do,
against prompts that described only the cert stage.

Both prompts now select on the `stage` the provisioned `event.yaml` records.
The cert content is framed as the cert path and otherwise unchanged; an interim
section states what the event resolves to, what the record carries in place of
the cert signals (the escalation ladder), and what a predictor may not treat as
a yardstick.

The interim rules, keyed to what the code does:

- The resolver emits four labels only (`granted`/`denied`/`withdrawn`/
  `dismissed`) and matches denial language first, so `probability` is P(an
  unqualified grant) — scoring partial relief as a grant would over-state the
  forecast on the mixed shadow-docket shape.
- No `claims` block: only petition-kind events declare a set.
- The evaluator computes `correct`/`brier_score` identically and omits
  `segment_base_rate` and `brier_skill_score` with `base_rate_basis` null, no
  flag — a standing stage rule, not a per-cell anomaly. No interim segment base
  rate is published until the pre-registered 25-resolved floor.
- The band rules key on the frozen conditioning rather than the docket's shape,
  so an interim cell that carries a cert band declines it and flags the
  mismatch instead of anchoring on a population it does not belong to.

The offline proof: the fixture corpus gains a resolved substantive stay
application (`26A11`), whose motion baseline carries `Stage.interim`, and the
cascade smoke drives it provision -> predict -> interim outcome -> evaluate ->
validate, landing it in the leaderboard's unranked `interim` block. The stub
now answers the declared cert claim set, so the cert path exercises that
contract too. The rules the smoke composes are pinned at their own seams: the
absent cert baseline, the interim `signals` guard, and the fixture's agreement
with the production event mint.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…aseline (#1025)

The artifact is the scoreable merits cell: a judgment, P(disturbed) as the
Brier axis, a mandatory per-Justice vote block, and the baseline that makes a
skill claim meaningful.

Schema. `Prediction.judgment` mirrors `Outcome.judgment`, with a model
validator holding "judgment set => votes non-empty" — the half a prediction
can enforce alone, since it does not carry its event's stage. The other half
is a `validate` check reading the committed `event.yaml`: a merits-stage
event's latest-per-predictor prediction must carry a judgment.
`Evaluation.judgment_correct` records the exact-match on the full vocabulary,
and `Evaluation.base_rate_salience_version` records which salience version the
segment base rate was read under — harness-stamped in `stamp-cell --role
evaluator` from the recorded basis, never the evaluator's word.

Detection. The live poll latches `merits_judgment` / `merits_decided` at
ingest through the shared parser on a granted cert docket, the corpus upsert
latches the pair atomically, and outcome detection resolves the open
merits-stage event from those columns alone — one deterministic seam rather
than two. A merits outcome records `other` for the cert vocabulary it has no
member of, carries the result on the judgment axis, and declares
judgment-disturbed as `actual_granted`, so one Brier formula serves every
stage. Resolving the event closes the case's last open event and the docket
leaves the rotation.

Scoring. `merits_base_rate` pools the statpack merits section's per-grant-Term
`disturbed` over `parsed`, strictly-prior. That section's population is
already the scored population — the grants that open a merits proceeding, the
same rule that mints the event — so the baseline is measured over exactly the
cases a merits cell is drawn from, and a GVR's near-certain vacatur (a
cert-stage disposition the cert sections already carry) never anchors a merits
forecast. The pool is keyed on the grant Term on both sides (read from the
merits event's `opened_at`), because the docket-number Term runs a Term later
for a summer-docketed pre-October grant and would admit the case's own cohort;
it must also clear a stated minimum sample, since the section exists from its
first parsed judgment and `brier_skill` masks exactly the cells a degenerate
baseline got right. On a merits cell `correct` is the judgment match, so the
stage block's accuracy is a real number rather than a constant the predictor
sets.

`merits-v1` declares one claim, `judgment-disturbed`. The per-Justice vote,
split, and authorship claims were tested against the eight forecastability
tests and dropped: no committed resolution channel (docket text discloses no
provenance denominator), no strictly-prior committed baseline, and a
nine-to-one re-encoding of one correlated insight. The vote block stays
mandatory as elicitation, scored intersection-only when a real vote source
lands.

Merits cells still do not fan out — the merits event is not a forecastable
kind until its prompt contract ships, so a cell can never run ahead of the
prompt that defines it.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Add docs/predicted-artifacts.md, a per-artifact walk of everything one
predict cell writes: prediction.json with its per-stage differences (cert,
interim, merits), the two prose documents, retrieval.md, flags.json, and
tooling.json — plus the harness-written files and stamps a cell does not
author, and a short cross-reference to what the evaluate side does with a
prediction.

The purpose is to make what a prediction constitutes legible without
reading the pydantic models. Every JSON example is invented data and
validates against its model. README's data-model section and AGENTS.md's
doc table point at it.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
… 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>
feat(prompts): the merits stage contract, and the merits fan-out
…ing seam (#1028)

The artifact: a semantic reasoning-scoring seam that exists, is exercised end
to end against synthetic graded fixtures, and produces nothing. It serves no
published number today by design — it exists so that when opinion bodies land,
iterating on the methodology is a declaration plus a prompt rather than a new
shape.

`semantic-v0` is **alpha**: provisional, never tested against a real opinion,
and explicitly not a pre-registered commitment of the kind `cert-v1` and
`merits-v1` are. What makes that honest rather than a loophole is that nothing
depends on it — the process freeze governs the digest (prompt bytes plus the
resolved actor config), no prompt asks a cell for a semantic claim, so no
digest moves, no cell produces a grade, and no committed artifact carries one.
The mechanical family's contract is untouched, and the new section states that
where the two appear to conflict the pre-registered rule governs.

The load-bearing design decision: a semantic grade is **never** run through
`claim_score`. That rule needs a harness-computed prior from strictly-prior
history, a proposition like "the majority rests on textualist grounds" has no
such frequency, and every route to manufacturing one fails against a rule this
document already carries. So a semantic claim earns an ordinal grade —
supported / partially-supported / unsupported, plus `not-addressed` as an
availability mask that is a property of the record — reported descriptively
with leave-one-out inter-grader agreement beside it. Whether any baseline is
ever derivable is left open as an empirical question for when text exists.

- `docs/outcome-decomposition.md`: the methodology, quarantined from the
  pre-registered body — the claim vocabulary sketched with the eight tests
  applied candidate by candidate (and one alpha test the eight cannot supply),
  the grading protocol, the agreement requirement, the supersession rule, and
  what remains unbuilt.
- `pipeline/semantic.py`: declaration tables that are empty on purpose and
  authoritative over any grader's block, plus the descriptive roll-up.
- Schema blocks optional everywhere, so every committed artifact still
  validates; `Stratum` moves to `schemas` so a census can type its population
  on the closed vocabulary.
- `metrics/README.md`: what may and may not be read off a grade, written
  before the surface exists so a first publication has a contract to meet.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
The artifact: an evaluate cell whose grader forms `reasoning_quality` — the
semantic side of the pre-registered judge validation — without the predictor's
identity in view. `docs/outcome-decomposition.md` registered this as the
precondition on scoring semantic claims and recorded that the harness could not
deliver it, because the evaluator read `predictions/<predictor_id>/<run_id>/` and
wrote under a path keyed on the same id.

`fedcourtsai.blinding` supplies both halves. `provision_blinded_predictions`
stages each predictor's latest prediction into the gitignored
`record/blinded/<alias>/` under an opaque alias assigned by a keyed shuffle over
the run, case, and event — never predictor-id sort order, which would leave the
alias a lookup performed by inspection. Identity fields are masked, the prose is
staged under harness-chosen names, and predictor ids, evaluator ids, and
engine/model names are scrubbed from every staged byte; `usage.json`,
`tooling.json`, and `flags.json` are not staged at all. `unblind_evaluations`
renames the alias-keyed output onto the real predictor ids, rewrites the
`predictor_id` field, and resolves aliases in the evaluator's prose and flags so
a leakage note reaching a maintainer names a predictor.

Ordering is load-bearing and documented in four places: un-aliasing runs before
`stamp-cell --role evaluator`, whose prediction join misses under an alias and
drops `claim_scores` and `base_rate_salience_version` silently rather than
failing. `validate`'s `check_evaluation_targets` resolves the same join and is
the loud backstop.

What this buys is stated where the numbers are read rather than claimed
broadly: it removes the predictor's *name*, and the engine stays legible from
the staged transcript's tool vocabulary, which the leakage grading needs intact.
`metrics/README.md` now carries the rule that blinded and unblinded grades are
two populations.

The workflow wiring ships separately as a maintainer-lane PR
(`.github/workflows/` is not this lane), so this half is inert until that lands.
The two must reach `main` in the same promotion batch: the prompt change moves
all three evaluator digests, and a digest that moves without the information-set
change it advertises is worse than no digest move.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Artifact: `metrics/leaderboard.json`. Each `LeaderboardStratum` (and each
stage block) gains `population_realized_term_skill_score` with its own
`realized_term_skill_scored` denominator — the same salience band and the
same risk-set basis as the existing skill column, scored against the grant
rate the case's OWN October Term realized rather than the strictly-prior
pool.

The two decompose skill per cell. Prior-Term skill rewards knowing the level
AND discriminating within it, so a Term that ran hot or cold credits or
penalises every predictor for the shift. Realized-Term skill holds the level
at what obtained and leaves discrimination: a predictor with the Term's level
right but no ability to separate its cases reads positive on the first and ~0
on the second, and one that beat history while losing to the Term reads
positive then negative.

The baseline is leave-one-out — the scored case sits inside its own Term's
rate, and one case moves a seventy-row band by well over a point. The pack
publishes no grant count, so the numerator is reconstructed as
`round(rate * weighted_resolved)` (a ratio of integer weighted counts) and
the case comes off it on the pack's own grant-family definition, promoted to
`schemas.GRANT_FAMILY_DISPOSITIONS` so `analytics` and the subtraction cannot
diverge — `granted-in-part` is a granted binary outcome the published rate's
numerator never counted. `REALIZED_BAND_RATE_MIN_RESOLVED` (30) binds on both
the leave-one-out weighted denominator and the observed row count behind it,
since a reweighted Term can clear a weighted 31 on five real petitions.

It is ex post: no predictor could have known its Term's realized rate, so it
never ranks (the rank key is untouched), is never pooled or averaged with the
prior-Term column, and carries its scope rules in the claim contract — cert
stage only, version-pinned, statpack-vintage, and mid-Term grant-depleted in
a direction that does not average out. Computed at board build from the
committed pack rather than carried on a cell, because a Term's own rate keeps
moving until the Term closes; `run-analytics` therefore regenerates the board
after the pack, and `corpus-status`'s hand-regeneration message names the
same order.

Both columns now aggregate as a population ratio, `1 - sum(brier) /
sum(baseline_brier)`, rather than a mean of per-cell ratios, and are renamed
`population_*` so the estimator travels with the number rather than only with
its description (the `mean_*` fields beside them really are per-cell means).
The per-cell ratio caps at +1 but is unbounded below, so under cert's class
imbalance a mean of ratios is dominated by low-baseline denial cells and pays
a predictor to under-forecast the rare event: on the committed pack's OT2025
segments an always-deny forecaster means to +0.94 / +0.75 / -0.40 across the
bands against +0.002 to +0.03 for the honest level-only forecaster, an
inverted ordering that also swings on band mix. The ratio of sums prices the
same forecaster at -0.05 / -0.20 / -0.75.

Because the prior column's value is now derived from the cell's recorded
`segment_base_rate` and outcome rather than read off its recorded ratio — the
aggregate needs the baseline term, which a ratio cannot yield back on a cell
whose Brier is zero — and `Evaluation` constrains no relation between its own
numbers, the derivation is checked against the record: a cell whose recorded
skill does not reproduce from its own inputs is omitted, visibly, from
`skill_scored`, rather than published on a baseline it was never graded
against.

The board is empty and no digest is frozen, so no published number moves, and
the committed `metrics/leaderboard.json` rebuilds byte-identically.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat(metrics): realized-Term skill beside prior-Term skill, and a corrected aggregation
…sweeps

Three steps, all maintainer-lane, all inert until their code is on main.

run-evaluate gains the blinding bracket: stage each predictor's latest
prediction under an opaque alias before the agent runs, un-alias before the
stamp. The second position is load-bearing — stamp-cell joins an evaluation to
its prediction on predictor_id, and under an alias that join misses and drops
claim_scores and base_rate_salience_version silently rather than failing.

run-seed gains two convergence sweeps beside the scope reconcile it mirrors:
the application-baseline relabel and the merits-judgment backfill. Both belong
in the lane rather than a dev checkout because the corpus pointer is a git file
the writers commit to main and corpus-push is last-writer-wins, so a stale
local copy republishes over concurrent walks; the corpus-write concurrency
group is what serializes them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
modelmirror and others added 3 commits August 4, 2026 14:35
chore(workflows): wire blind grading, and the two corpus convergence sweeps
…rics

The artifact is committed and the CLI is tested, but nothing regenerated it —
the refresh rolled the leaderboard, backtest, statpack and scope manifest and
stopped there, so the claim-score board would have gone stale the moment the
first evaluation carried a claim block.

Placed beside the leaderboard, outside the corpus gate: it reads only the
committed ledger, and pairing the two evaluation-derived artifacts means they
refresh from one tree state rather than two.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…efresh

chore(workflows): regenerate the claim-score board with the other metrics
modelmirror and others added 4 commits August 4, 2026 21:33
An earlier single-open-event attribution shortcut duplicated a petition's cert
disposition onto sibling events; such records still sit in the committed ledger
and would otherwise be frozen into the pre-registration record.

A copy is identified by duplication — the outcome repeats a case-baseline
sibling's (actual_disposition, resolved_at, actual_granted) exactly — not by
date ordering, which is a faithful upstream shape on this docket source rather
than evidence of fabrication. Repair deletes the outcome and reopens the event
in both stores, corpus first so an interrupted run stays detectable.

Only non-baseline events are repaired: reopening a case-baseline event makes it
the stage-less fallback's target, so the resolution pass would rewrite what was
deleted. A duplication between two case-baseline events is reported for triage,
as are an event the corpus does not know and one carrying committed agent
output.

Closes #1010.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…outcomes

fix(ledger): reopen outcomes copied from a sibling case-baseline event
At SCOTUS the petition is the case, and nothing separately predictable
arrives by appeal — the "notice of appeal" wording reaches a SCOTUS docket
inside a record-transmittal line from the court below. Minting on it produces
a second case-baseline id, the one shape _cert_disposition_target cannot
disambiguate, so the case-level disposition attaches to whichever of the two
it lands on rather than to the petition it decides. The existing petition
collapse now covers both kinds.

Adds remove-unmintable-events for the rows already written that way: an
entry-pinned SCOTUS event carrying a case-baseline id, dropped from the ledger
directory then the corpus row — the convergent order for a corpus-driven scan,
since the row is the sweep's detection handle. It refuses an event carrying
committed agent output, one whose outcome copies no case-baseline sibling's,
and an unrecognized directory shape.

Closes #1033.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…-guard

fix(events): a SCOTUS appeal entry collapses into the case baseline
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.

2 participants