Skip to content

Typed-module materialization across runs: the host-persisted tier - #11793

Merged
briansrls merged 7 commits into
mainfrom
session/valiant-raven-176
Sep 20, 2026
Merged

briansrls merged 7 commits into
mainfrom
session/valiant-raven-176

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Implements the typed-module cross-run materialization design (#11779), with three
corrections the design got wrong. #11779 has since merged, so this PR also replaces its
document in place
— which is what that document's own status line prescribes on acceptance.
Its projected figures (8.3 min → ~1 min, 32 → ~20 min per push) were never measurements and
are withdrawn rather than quietly dropped; see Evidence.

What landed

One ladder rung, not a cache. A second provider over the identity the in-process
provider already covers — union-typecheck-store(dag,src/v2) — saying where the value
lives when the process that computed it is gone. The byte transport, the content key and
the seam are the ones that were already there; only the backing changed.

  • gunbc.floor_materialization — the floor_typecheck_store_persist_* rows.
  • v1_compiler.shared_typecheck_store — PersistentTypedStore, appended to the module
    that already owns this transport rather than minted beside it.
  • test.claim.floor_materialization_witness — the discharge cell and four reds.
  • docs/plans/typed-module-cross-run-materialization.md — rewritten to the landed state:
    the operating procedure, the four decisions and their reasons, the deletion trigger.

Three corrections to the design

1. Frame and tier. The design proposed SharedStateFrame because processes share a
directory, and CasTier. Both are wrong against the vocabulary that already exists.
Shared-state plurality is the ladder's rewire case — declaring it there would have made
the ladder call this authored duplication. Recurring sibling processes are
UnboundedSiblingsFrame, which is what makes the repetition an isolation obligation only
a store tier can discharge. And tier_axes assigns CasTier the RemoteNetwork
placement; a host-local directory is ArtifactTier { keying: ContentKeyed } — the same
reading that already puts extdeps.realization.resolved_graph's per-host store there.

2. The key already existed, and the cross-push claim does not follow from it. The
design said "what does not exist: any content key". std.interface_summary.typed_module_key
already is one, including a compiler-identity term, and this change reuses it rather than
authoring a second.

But that term is transform_content_digest(), the content hash of the running binary, and
build.rs embeds GUNBC_BUILD_IDENTITY — the checkout's commit — into that binary via
cargo:rustc-env. A rebuild at any new commit, including a corpus-only .dag one,
therefore re-keys every entry. The design's "a .dag corpus change gets nearly all of
it" does not follow from the key it proposed, and is withdrawn.
What this delivers is
same-build, cross-process reuse: the fold and the drift gate are two processes over one
corpus under one binary, and the second pays typecheck preparation again today. Cross-push
reuse needs a compiler identity invariant under a corpus-only commit — a change to that
derivation, not to this store, and not claimed here.

The bound is also narrower than "preparation": a hit skips collect_parent_envs and the
typecheck compute, which is where the wall is spent, but the module is still parsed and
resolved because that is what derives the key. Parse is not relieved.

3. Occupancy is residency, not throughput. The ladder refuses ReleasedNever with
CapacityUnbounded and with CapacityUnobserved, so a byte ceiling and an observed
occupancy are obligations. The existing SharedTypecheckStoreCounters are cumulative
bytes encoded/decoded, which cannot discharge a residency obligation, so the store carries
live bytes and live entries beside them and a unit test asserts the two move differently.

Fail-closed arms

Every failure is a counted miss or a counted refusal, never a fabricated result. Absence,
verification rejection (magic, format version, declared length, stored key) and IO failure
are separately counted; a rejected entry is deleted rather than left to be re-read; at the
ceiling the disposition is RefuseNewStore and the run is merely cold. Two arms stop the
line rather than widening: an entry that passes header verification but whose payload will
not decode is codec drift, and a directory that was named and cannot be opened is an
operator asking for a store they do not have. Unarmed and armed-but-unusable are different
states — only the first runs cold.

Evidence

Established by execution.

  • All nine persistent_typed_store_tests pass (cargo test --release -p v1-compiler --lib persistent_typed_store, 9 passed / 0 failed): roundtrip across two store objects over one
    directory, key miss, namespace isolation, truncation rejection, key-mismatch rejection, stale
    format version, ceiling refusal, residency-vs-throughput, and the lockstep between the
    realization's default ceiling and the modeled row.
  • The floor lane is green on this head, and its touched-entry-compile-subject phase names
    gunbc.floor_materialization and test.claim.floor_materialization_witness — so the rows and
    their four red controls execute rather than passing by absence. Each red withdraws one obligation
    from the same row: unbounded → RefusedRetentionUnbounded, unobserved →
    RefusedRetentionUnobserved, process-scoped → RefusedScopeTooNarrow, none →
    RefusedNoProvider.

NOT established: the production-path cold/warm receipt. This PR has an implemented,
CI-validated tier and no measured saving. The design's 8.3 min → ~1 min and 32 → 20 min are
its projections, not results, and are not repeated here as though they were.

Three vehicles were tried and none can carry it:

  • BuildBuddy cannot host the fold. Runners are 6 cores / 7 GiB; the fold peaks at 20.2 GiB.
  • The compile CLI does not reach the seam. In-body annotation comments in
    dag/test/claim/roadmap/roadmap_review_function_witness_test.dag are error diagnostics that abort
    normalize before typecheck, and the corpus-wide bare-name census pulls that file into every
    closure. (The fold's path is unaffected — the floor lane is green — so this is a property of the
    compile-clean entry route, not a broken corpus.)
  • A session container is the wrong place for two 20 GiB folds under a shared slice.

The real consumer is claim_executor --required-ci, and the floor lane already runs fold then
drift gate in one job under one binary — exactly the two sibling processes this provider row names.
Arming the store there for one run is the measurement; doing so puts an unmeasured cache on a
required lane and inverts the design's own rollout order, so it is an operator call rather than
something to slip in. Escalated.

Not mine

heal-generated-artifacts fails on this PR and on main for two reasons in files this diff
does not touch: a trailing module-scope comment in src/v2/workflow/floor_naming_hygiene.dag
that the 4c annotation channel (#11573) now refuses, and a non-exhaustive match in
dag/gunbc/fleet/fleet_converge_workflow.dag missing ApprovalKeyringConverge and
MtCollins1Boot (#11484). The three required lanes pass.

🤖 Generated with Claude Code

gunbc-ci-auto-heal and others added 3 commits September 20, 2026 04:21
Implements the C3 rung of gunbc.floor_materialization: a host-local,
content-addressed backing for the typed-module snapshots the in-process
tiers already produce, so the value survives the process that computed it.

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

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

clippy::disallowed_macros forbids eprintln in this library module, and the note
names the right repair rather than an allow: a structured return. That forced
the better arm too. UNARMED and ARMED-BUT-UNUSABLE were being collapsed into one
silent cold path; they are different states. No environment variable means the
operator asked for no store and cold is correct. A directory that was NAMED and
cannot be opened is an operator asking for a store they do not have, so it now
stops the line with a located diagnostic instead of running cold under a name
that promised otherwise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot gunbai-bot Bot changed the title CI Parallelism Typed-module materialization across runs: the host-persisted tier Sep 20, 2026
gunbc-ci-auto-heal and others added 4 commits September 20, 2026 06:27
Both are per-host content-addressed stores, so a reviewer is right to ask why
there are two. They key different computations at different grains, and they
share one compiler-identity authority rather than each deriving its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The clippy repair changed an armed-but-unopenable store from a silent cold run
into a located refusal. That is the arm that carries the promise made by naming
a directory, and it had no discriminating test.

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

# Conflicts:
#	docs/plans/typed-module-cross-run-materialization.md
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review September 20, 2026 12:39
@briansrls
briansrls added this pull request to the merge queue Sep 20, 2026
Merged via the queue into main with commit 7f9e624 Sep 20, 2026
4 checks passed
@briansrls
briansrls deleted the session/valiant-raven-176 branch September 20, 2026 15:12
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