Skip to content

Deny the sealed candidate-26 pool in every readiness and release loader - #857

Merged
MaxGhenis merged 9 commits into
mainfrom
deny-c26-pool
Sep 3, 2026
Merged

Deny the sealed candidate-26 pool in every readiness and release loader#857
MaxGhenis merged 9 commits into
mainfrom
deny-c26-pool

Conversation

@MaxGhenis

Copy link
Copy Markdown
Contributor

The readiness loaders already refuse a gate_failed pool, but load_authenticated_us_multispine_pool_for_release(allow_terminal_gate_failure=True) admits the same status pair into a release build or release preflight on an operator opt-in (--allow-gate-failed-base-pool), and the preflight records that carried red verdict with affects_exit_code: False. The exclusion of the sealed candidate-26 stacked pool from the certifiable dense line (#856; plan gate 20260902-220844-plan-532dab66) is therefore a decision without a mechanism. This PR adds the mechanism.

What it does

  • DENIED_POOL_PUBLICATIONS: a sealed, source-coded deny-list in us_runtime/h5_io.py, keyed by publication_run_id, each entry carrying the manifest sha256, the sealed pool-H5 sha256, the release id, a reason, and a reference. Seeded with candidate-26 (2ab3f5a1…, manifest 2a06fc2b…, H5 45f40173…).
  • Enforced centrally in _load_authenticated_us_multispine_pool_manifest, which every loader routes through, on any of three identities: the publication run id, the manifest digest, or the pool-H5 digest. A renamed, re-serialized, or repackaged publication is still refused, regardless of allow_terminal_gate_failure.
  • The only exception is load_authenticated_us_multispine_pool_for_scoring (scoring_only=True), which never labels a pool ready and which the diagnosis of c-26 stacked pool: emergent undocumented population 18.9M vs the 11.0M anchor (1.72x; gate band [0.5, 1.6]) #856 needs. A source-scan test asserts that no file outside h5_io.py and the head-to-head scorer references it, so the exception cannot become a release ingress.
  • Tests: the gate-failed fixture is available only for scoring and refused by the manifest-only, simulation-ready, and both release loaders; matching by manifest digest alone and by H5 digest alone; the candidate-26 identities are present verbatim; preflight refuses a denied pool even with --allow-gate-failed-base-pool.
  • CLAUDE.md boundary paragraph and a towncrier fragment.

Implemented by a sol build lane under a written brief; reviewed and extended (H5-digest rule, scoring-to-release scan test, reason text) by the dispatching session.

Refs #856.

🤖 Generated with Claude Code

MaxGhenis and others added 8 commits September 2, 2026 23:25
…er (#856)

The readiness loaders already refuse a gate_failed pool, but
load_authenticated_us_multispine_pool_for_release(allow_terminal_gate_failure=True)
admits the same status pair into a release build or preflight on an
operator opt-in, and the preflight records that with affects_exit_code
false. The exclusion of candidate-26 from the certifiable dense line is
a decision (plan gate 20260902-220844-plan-532dab66), so it needs a
mechanism: a sealed, source-coded deny-list checked centrally in the
manifest authenticator every loader routes through, matching on the
publication run id, the manifest digest, or the sealed pool H5 digest,
so a renamed, re-serialized, or repackaged publication is still refused.
The only exception is the scoring-only loader, which never labels a pool
ready and which the diagnosis of #856 needs; a source-scan test keeps it
unreachable from release paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A pool whose sidecar manifest and artifact-metadata row are stripped no
longer identifies as a pool, so the release builder and the release-gate
preflight fall through to generic H5 loading and never consult the
deny-list. The bytes are the identity that survives repackaging: both
generic paths now hash the file and refuse a denied pool's H5 digest
before loading it (refuse_denied_pool_h5_digest), with a unit test for
the helper and a preflight test on a stripped fixture.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ase-receipt boundary

Sol's gate review of #857 found two laundering paths: release-producing
generic ingresses (export_us_l0_refit_h5 through load_us_frame; the
legacy calibrated loader) never consult the manifest loader, and the
scoring-only result could be handed to us_multispine_pool_release_receipt
with the gate-failed opt-in. The byte-identity check now lives in
load_us_frame and load_legacy_calibrated_us_h5 themselves (so every
caller inherits it, and preflight's explicit check is dropped as
redundant), and the release-receipt builder rechecks the deny-list on the
authenticated identity it is handed, so scoring evidence of a denied
publication cannot become release evidence. Tests cover each ingress
end to end and the scoring-to-receipt transition.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oundary for every generic ingress

Sol's second review of #857 showed that the whole-file digest does not
survive what it claimed to cover: dropping the artifact-metadata table or
re-serializing the pool changes the SHA-256 and removes every pool marker,
after which the generic loaders accept the file. The deny-list entry now
also carries a content identity (SHA-256 of the household table's id,
weight, channel, and clone-index columns sorted by id; 674b6e69... for
candidate-26), and refuse_denied_pool_h5 checks both the bytes and that
identity. Every generic ingress routes through it: load_us_frame, the
legacy calibrated loader, the fiscal builder's and PUF support base's
_load_frame, and the legacy ACS base's _load_base_frame, each verifying
after its read that the file did not change under it. A regression
rewrites a valid gate-failed fixture without its metadata table and
without a sidecar and shows all five ingresses still refuse it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s to what it actually loaded

Sol's third review of #857: the column-projection identity could be
evaded by dropping optional provenance columns or renumbering ids; the
authenticated paths never compared the content identity, so a reserialized
publication with regenerated metadata passed the gate-failed opt-in; the
selection-source manifest builder and loaders were an unguarded ingress;
and every loader reopened the pathname between hashing and reading.

- The content identity is now version 2: SHA-256 of the sorted multiset
  of household weights with the count (f5a5023b... for candidate-26,
  computed from the sealed pool). No ids, no optional columns, no order.
- Authentication computes it, carries it on AuthenticatedPoolH5, refuses
  it on every non-scoring path, and the release-receipt boundary rechecks
  it; the authenticated loader also refuses by the identity of the frame
  it actually loaded.
- Every generic ingress computes the identity from the loaded frame in
  memory after its read (refuse_denied_frame), so a file swapped between
  the on-disk checks and the read is caught by what was loaded (ABA
  regression on the legacy loader); the fiscal builder binds its late
  base load to the digest it recorded for the release id.
- Selection-source manifests: the builder routes through the boundary and
  records byte and content provenance; consumers refuse a manifest with
  no source digest or with denied provenance.
- The rewrite regression now also drops the provenance columns and
  relabels the ids; identity, selection-manifest, and digest-binding
  tests added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… receipts, and version selection manifests

The final review round of #857 found that the publisher never consulted
the deny-list (publish_release validates the release directory without
looking at base_pool), that the release receipt carried no content
identity for a last check, and that the selection-manifest loader's new
provenance requirement rejected existing schema-1 manifests while the
public writer could emit unloadable ones.

- The deny-list moves to microcosm-data (microcosm.data.denied_pools),
  which the publisher can import; h5_io re-exports it. The release
  contract refuses a build manifest whose base_pool matches a denied
  publication by any of its four identities.
- us_multispine_pool_release_receipt persists content_identity_sha256.
- Selection manifests are schema 2 with mandatory canonical provenance
  (lowercase sha256 and content identity); schema-1 manifests still load
  and are refused only when what they record is denied; the writer
  refuses to emit a schema-2 manifest without provenance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…test canonical provenance

The engine-free CI lane failed because load_us_frame and its siblings
imported policyengine_us before consulting the deny-list, so a denied
file raised ModuleNotFoundError there instead of the refusal; the check
now precedes the import in every generic loader. The selection-manifest
round-trip test wrote a manifest with placeholder provenance, which the
schema-2 writer rightly refuses; it now records a canonical digest and
the frame's content identity.

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

Copy link
Copy Markdown
Contributor Author

Merging at c8ed76f on the owner's authorization (Max, 2026-09-03: "merge once every check is green and the PR is mergeable"). The sol gate 20260902-232600-pr-b4d885d0 closed at its four-round cap; its residual findings are filed as #859, #860, #861. Every check passed at merge time.

@MaxGhenis
MaxGhenis merged commit 42ee2c6 into main Sep 3, 2026
23 checks passed
juaristi22 added a commit that referenced this pull request Sep 3, 2026
…ain (#762 PR A)

main merged #842 (age reorder) and #850/#857 under this branch; the merged
gates.json and spec move the policy, manifest, fingerprint and spec digests.
Recomputed from the live tree on the rebased base.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
juaristi22 added a commit that referenced this pull request Sep 3, 2026
…t UK gate-battery pins (#762 PR A)

The rebase conflict on the data contract was resolved on this branch's
side, which dropped main's newer content (#850's UC-deduction entries,
#842's age_tail phase move, #857's denied-pool check, the certification
part digests). This branch's only legitimate delta there is the three
gate-battery digests re-cut on the merged gates spec; everything else is
main's.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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