Repository navigation
Typed-module materialization across runs: the host-persisted tier - #11793
Merged
Merged
Conversation
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>
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
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.
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 valuelives 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— thefloor_typecheck_store_persist_*rows.v1_compiler.shared_typecheck_store—PersistentTypedStore, appended to the modulethat 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
SharedStateFramebecause processes share adirectory, 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 onlya store tier can discharge. And
tier_axesassignsCasTiertheRemoteNetworkplacement; a host-local directory is
ArtifactTier { keying: ContentKeyed }— the samereading 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_keyalready 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, andbuild.rsembedsGUNBC_BUILD_IDENTITY— the checkout's commit — into that binary viacargo:rustc-env. A rebuild at any new commit, including a corpus-only.dagone,therefore re-keys every entry. The design's "a
.dagcorpus change gets nearly all ofit" 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_envsand thetypecheck 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
ReleasedNeverwithCapacityUnboundedand withCapacityUnobserved, so a byte ceiling and an observedoccupancy are obligations. The existing
SharedTypecheckStoreCountersare cumulativebytes 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
RefuseNewStoreand the run is merely cold. Two arms stop theline 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.
persistent_typed_store_testspass (cargo test --release -p v1-compiler --lib persistent_typed_store, 9 passed / 0 failed): roundtrip across two store objects over onedirectory, 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.
floorlane is green on this head, and itstouched-entry-compile-subjectphase namesgunbc.floor_materializationandtest.claim.floor_materialization_witness— so the rows andtheir 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:
dag/test/claim/roadmap/roadmap_review_function_witness_test.dagare error diagnostics that abortnormalize before typecheck, and the corpus-wide bare-name census pulls that file into every
closure. (The fold's path is unaffected — the
floorlane is green — so this is a property of thecompile-clean entry route, not a broken corpus.)
The real consumer is
claim_executor --required-ci, and thefloorlane already runs fold thendrift 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-artifactsfails on this PR and on main for two reasons in files this diffdoes not touch: a trailing module-scope comment in
src/v2/workflow/floor_naming_hygiene.dagthat the 4c annotation channel (#11573) now refuses, and a non-exhaustive match in
dag/gunbc/fleet/fleet_converge_workflow.dagmissingApprovalKeyringConvergeandMtCollins1Boot(#11484). The three required lanes pass.🤖 Generated with Claude Code