Generated Projection Drift Runtime
An owner-routed gate that fingerprints generated artifacts against their sources, uses a prior-clean skip cache, and runs each owner's no-write check to flag projection drift by recomputation.
The write-up
A generated file should be reproducible from its source, drift is when that stops being true through a hand-edit or an out-of-step builder, and this component is the gate that catches it. A generated file is one that should be reproducible from its source: run the builder, get the same output. Drift is when that stops being true, usually because someone hand-edited the output or a builder quietly fell out of step with what it builds from. This component is the gate that catches drift.
It models each generated file as an owner with artifacts, sources, and a no-write check command, hashing both sides and marking an owner clean only when its check returns zero and its artifact exists. It models each generated file as an owner: a set of artifact paths, the source paths they derive from, and a no-write check command that recomputes the output and compares. For each owner it hashes the source and the artifact, decides whether a recent clean record lets it skip re-running the check, confirms the required artifact is present, and otherwise runs the owner's own check. An owner is clean only when its check returns zero and its artifact exists. The gate reports drift. It does not repair the file.
Purpose
Recomputation is the honest unit
Timestamp comparison and a builder's own success message both mislead, so the honest unit is recomputation: hash the source, hash the artifact, and run the regeneration check so drift is observed, not assumed. Loose drift detection fails in a predictable way. If you compare a timestamp, a touched-but-identical file looks dirty and a stale-but-recently-written file looks clean. If you trust a builder's own success message, a builder that stopped matching its source still reports success. Drift is then observed, not assumed.
The declared boundary per owner
The declared boundary matters as much as the check: this gate proves nothing about the larger source registry or software launch, answering only whether each artifact still passes its own no-write check. The declared boundary matters as much as the check. This gate does not establish that every builder in the larger source registry uses true content-diff semantics, and it does not decide anything about releasing software. It answers one question per owner: does this artifact still pass its own no-write check.
How it works
Bundle authority, component wrapper
The engine-room bundle is the drift authority and the component wrapper runs it over a bounded set of fixture cases, turning the outcome into a pass or fail. Two files carry the mechanism. The engine-room bundle src/microcosm_core/engine_room/generated_projection_drift_gate.py is the drift authority. The component wrapper src/microcosm_core/organs/generated_projection_drift_runtime.py runs it over a bounded set of fixture cases and turns the outcome into a pass or fail.
An owner is a ProjectionOwner
A ProjectionOwner names its owner_id, artifacts, source_authorities, and check_command, and select_projection_owners narrows the set by ids or changed paths so a run checks only the owners a change touches. An owner is a ProjectionOwner: owner_id, artifacts, source_authorities, and a check_command, plus optional fields for repair hints and fact-authority lineage. select_projection_owners narrows the owner set by explicit owner_ids or by changed_paths, so a run can check only the owners a change actually touches. With no filter it selects all owners.
_owner_fingerprint recomputes hashes
_owner_fingerprint expands each pattern to concrete files, SHA-256-hashes every body, and folds them into one source_hash and one artifact_hash, recording an unmatched pattern in missing. _owner_fingerprint is the recomputation step. It calls _fingerprint_patterns once for the source paths and once for the artifact paths. That helper expands each pattern to concrete files, SHA-256-hashes every file body, sorts the entries by path, and folds them into one source_hash and one artifact_hash. A pattern that matches nothing is recorded in missing, which is how a missing artifact becomes visible later.
_source_hash_cache_hit is the skip cache
_source_hash_cache_hit reuses a prior clean record only when its status, zero missing artifacts, both hashes, and check command all match, so it skips settled work but never turns a failing check into a pass. _source_hash_cache_hit is the skip cache. It reads a prior clean record for this owner and returns it only when every guard holds: the record status is clean, its artifact-missing count is zero, its stored source_hash and artifact_hash both equal the freshly recomputed hashes, and its recorded check_command matches the owner's current command. If any guard fails it returns nothing and the check runs for real. The cache never turns a failing check into a pass; it only skips work that a byte-for-byte match has already settled.
_check_owner assigns drift reasons
_check_owner fingerprints, evaluates lineage, then takes a cache hit or runs the command, collecting check_command_failed, artifact_missing, or lineage reasons, and any reason marks the owner drift. _check_owner composes these. It fingerprints the owner, evaluates lineage, then either takes a cache hit and builds a cached clean result or runs the command through _run_command. It collects status_reasons: check_command_failed when the return code is non-zero, artifact_missing when the artifact fingerprint has any missing pattern, and a fact-authority-lineage reason when a required lineage block is missing or malformed. Any reason makes the owner drift; none makes it clean. _run_command first tries the small builtin commands (builtin:pass, builtin:fail, builtin:assert-file-equals) and otherwise runs a real subprocess under a timeout. check_projection_drift aggregates the per-owner rows into one status: drift if any owner drifted, clean if none did.
The component wrapper drives fixtures
_evaluate_case materialises each fixture in a scratch tree via the real bundle, and build_result passes only with at least one positive and one negative case, all observed as expected and both negative ids present. The component wrapper drives this over fixtures. _evaluate_case materialises each fixture's declared file tree into its own TemporaryDirectory and calls the real bundle evaluate_case. It does not re-implement the gate and it bakes no answers. For a negative case it requires the observed status to be not clean, the case's own expectation to be met, and the expected drift markers named in EXPECTED_NEGATIVE_CASES to appear in the recomputed reasons. For a positive case it requires clean plus expectation met. build_result runs every case and returns status == "pass" only when there is at least one positive and one negative case, all positives observed clean, all negatives observed as drift, and both expected negative ids are present. run writes the result, board, and validation records, and run_generated_projection_drift_runtime_bundle forwards to it.
| Function | Role |
|---|---|
select_projection_owners | picks owners by id or by changed path |
_owner_fingerprint | SHA-256 hashes source and artifact files |
_source_hash_cache_hit | allows a skip only on a guarded exact hash match |
_check_owner | runs the check and assigns drift reasons |
check_projection_drift | aggregates owners into one clean or drift status |
_evaluate_case | runs one fixture in a scratch tree via the real bundle |
build_result | passes only when positives, negatives, and markers all hold |
Diagram source & refs
flowchart TD Owner["Owner row source + artifact + check"] Fingerprint["SHA-256 fingerprint source + artifact"] Cache["Prior-clean skip cache guarded hash match"] Check["No-write check return code + artifact present"] Verdict["clean or drift with status reasons"] Owner --> Fingerprint Fingerprint --> Cache Cache --> Check Check --> VerdictNegative cases
Two drift plants and two clean cases
planted_byte_drift and missing_artifact_drift must be recomputed to drift carrying their markers, while clean_command_owner and clean_source_hash_cache_hit fix the positive side, including a genuine skip of a would-fail check. The fixtures plant two failures and require each to be caught by recomputation, not by a label. planted_byte_drift changes an artifact byte so the owner's check returns non-zero; the run demands the marker check_command_failed. missing_artifact_drift removes a required artifact so its fingerprint reports a missing pattern; the run demands the marker artifact_missing. Both must land as drift for the group to pass. The two positive cases fix the other side: clean_command_owner passes a clean check, and clean_source_hash_cache_hit exercises a genuine skip where the baked hashes match the recomputed ones, so a deliberately failing check is skipped rather than passed by accident.
Prior Art Grounding
Build-system and provenance practice
This follows ordinary build-system and provenance practice, hashing inputs and outputs and trusting a regeneration check's exit code over a timestamp, refactored publicly under the Plectis coverage contract. This follows ordinary build-system and provenance practice: hash inputs and outputs, skip work when a content hash already settled it, and treat a regeneration check's exit code as the authority rather than a timestamp. The engine-room bundle is a public refactor of the private repository's own drift tooling. The local prior art is the Plectis paper-module coverage contract: each reader page states what source and public fixtures can be rerun, which generated views are navigation aids, and which claims stay outside the evidence boundary. No external citation is claimed for the mechanism beyond that engineering lineage.
Validation Result record Path
Run the component over its public fixtures:
Coverage and corpus-parity validation
From the repository root, rerun the coverage contract and the corpus parity check: From the repository root, rerun the coverage contract and the corpus parity check:
PYTHONPATH=src ./repo-pytest tests/test_plectis_paper_module_coverage_contract.py -q --tb=short
PYTHONPATH=src ./repo-python scripts/build_doctrine_projection.py --check-paper-module-corpus
What a pass means here
A pass means the two positive cases observed clean, the two negatives recomputed to drift with their markers, and both expected negative ids were present, bounded fixture-scoped evidence. A pass means the two positive cases observed clean, the two negatives were recomputed to drift carrying their expected markers, and both expected negative ids were present. It is fixture-scoped evidence over bounded inputs.
Scope boundary
Scope limit
The proof boundary and scope limit
Over these public fixtures the gate fingerprints source and artifact, honours a skip cache only on a guarded exact match, runs each no-write check, and reports drift by recomputation, repairing nothing. The strongest claim the evidence supports: over these public fixtures the gate fingerprints source and artifact, honours a skip cache only on a guarded exact hash match, runs each owner's no-write check, and reports a drifted owner by recomputation rather than by trust. That is the proof boundary and the scope limit. It does not establish content-diff equivalence for every builder in the larger source registry, and it does not validate that whole registry. It grants no launch, public sharing, provider-call, or source-file changes, and it makes no claim of whole-system equivalence or production correctness beyond what the fixtures exercise.
Context & evidence
In short Generated Projection Drift Runtime surfaces a drift gate over generated build artifacts. Each owner row pairs an artifact, its source authorities, and a no-write check command. The gate SHA-256-fingerprints source and artifact files, consults a prior-clean source-hash skip cache, requires declared artifacts to be present, and runs the owner check, marking an owner clean only on a zero return with present artifacts. Four bounded public fixtures exercise it: a passing no-write check, a genuine source-hash cache hit (the check is skipped), a planted artifact byte rejected as check_command_failed, and a missing required artifact rejected as artifact_missing. It flags drift; it does not repair files, prove content-diff equivalence for every source builder, validate the whole registry, or include launch operations.
Scope limit A clean result means the selected owner's declared no-write check passed and its required artifacts were present for the supplied root over bounded public fixtures. It is command-result record-style evidence, not a semantic proof, not a file repairer, not full-registry validation, and not launch/public sharing/source-file changes.
Covers Generated Projection Drift Runtime
Source
Source Source module: src/microcosm_core/organs/generated_projection_drift_runtime.py · Source module: src/microcosm_core/engine_room/generated_projection_drift_gate.py · Design note · Source registry