Repository navigation
Reconstruct declaration-only Orrery publications for UK builds before #1144 - #1297
Merged
Merged
Conversation
This was referenced Oct 19, 2026
anth-volk
changed the base branch from
main
to
orrery-projection-without-store
October 19, 2026 21:09
anth-volk
added this pull request to stack #1323
October 19, 2026 21:09
anth-volk
removed this pull request from stack #1323
October 20, 2026 18:01
anth-volk
added this pull request to stack #1328
October 20, 2026 18:03
Builds from before #1144 recorded their graph declaration but no run binding. Their execution therefore cannot be validated, and they have no Orrery artifact. Their declared graph is still exact, and is worth showing, provided nothing presents it as recorded execution. microcosm.graph.reconstruction projects the recorded declaration bytes alone. It stores a microcosm.reconstruction marker (protocol microcosm.graph.reconstruction.v1) in the schema's producer extensions. The marker names the source revision and where it came from, and binds every input file by digest, with whether a digest the build itself recorded was matched. The marker lives in graph.schema.json, so any later projection of the saved schema keeps it, and Orrery documents carry it under metadata.microcosm.extensions. The declaration must reproduce its recorded bytes through the current serializer, so the schema always describes the file that was recorded, not a reinterpretation of it. The module adds new files only. It leaves orrery.py and orrery_evidence.py untouched and imports only the declaration, schema and serializer modules. A new Orrery contract script checks that Orrery's public parser accepts the result and finds no execution claims. Part of #1291. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UK dense and national builds from before #1144 can only get an Orrery artifact by being rerun, and their inputs often no longer allow that. They did write graph.json beside their outputs, and a code pin in rowwise_candidate_manifest.json. python -m microcosm.build.uk_runtime.orrery_reconstruction reads a past build's output directory, which it only reads. It writes a new directory with the normal publication layout (graph.schema.json, graph.orrery.json, orrery.upload.json) plus a summary report. The declaration is the build's own graph.json, never one re-derived from current code. The revision is the recorded pin; --revision must match it, or supplies one, marked caller-asserted, when the build recorded none. Inputs are checked against the digests build.json and sha256sums.txt recorded. A build that already records Orrery evidence is refused and pointed at python -m microcosm.graph.orrery. The report lists what was used, what is missing, the evidence found but not bound (run manifests, the artifact index, the content store) and warnings. Output is deterministic and is moved into place only once complete. Nothing is uploaded and no emitter service starts, so publication stays a separate step. Fixes #1291. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dense driver merged with #901 (2026-09-29) recorded the candidate manifest's digest in build.json with the note "staging receipts are appended after publication". Its close step then appended staging_delivery and staged_dataset to the manifest and refreshed only sha256sums.txt. build.json was not reissued over the final bytes until _refresh_completion arrived with #1057 (cb0d390, 2026-09-30). Treating every digest mismatch as fatal therefore refused every completed dense build from that window, even with --revision. A build.json digest carrying that exact note is now skipped when it no longer matches, and the manifest must match sha256sums.txt instead. The marker records this as fallback_digest_matched. With no digest list (staging disabled), nothing the build recorded checks the manifest, so the marker records only_stale_digest_recorded. The report lists each skipped digest with its note and what the file was checked against, and warns. Any other mismatch is still refused. The fixture now replays the drivers' write sequence instead of pairing the note with a digest of the final manifest, which no real build wrote. It has three layouts: the #901 window (note and stale digest), the refreshed dense layout, and the national completion record (uk_national_build_completion with build_record), which arrived in #1057 together with the refresh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The serializer reports JSON of the wrong shape as a TypeError: a top-level array or string, or a graph object whose node list is not an array. reconstructed_schema passed that error through, and the UK command's main turns only OSError, ValueError and RecursionError into a refusal. So a graph.json of [] reconstructed with --revision ended in an uncaught traceback and exit status 1, instead of a refusal with status 2. reconstructed_schema now converts the serializer's TypeError into a ValueError that says the recorded declaration is not a graph. A recorded file is data, so a malformed one is refused like any other input that fails a check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CLI help and the docs say the build directory is only read, but an --output inside it was accepted: the command created its hidden staging directory and the publication inside the build. Output equal to the build was refused only because it already exists, with a message that did not name the problem. The output path is now resolved, and each of its existing ancestors is compared with the build directory by os.path.samefile. An output equal to the build, or beneath it, is refused before anything is written, so "..", symbolic links and case-insensitive file systems cannot disguise the build directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The docs, the changelog and the PR said nothing in a reconstruction is re-derived from current code. Only the declaration's bytes are the build's own, though: the schema's compiled, fields and input_bindings sections, and the Orrery document projected from it, are computed by the code that runs the reconstruction. The marker named only the source revision, so a reader could not tell which code produced those parts, or that a reconstruction at another revision may differ in them. The marker now has a reconstruction_revision field next to source_revision, with git_commit and git_dirty. code_revision(path) reads them from the git checkout that tracks the reconstructing module's own file, not from the current directory, and returns nulls when no checkout tracks that file, so an installed copy inside some unrelated repository is never attributed to it. The UK command records it, and shows it in the report and the summary. It warns when the revision is unknown, or when the working tree is dirty or could not be checked. The scope text, docs and changelog now say which parts come from the build and which come from the reconstructing code. The protocol stays v1: it has not been released. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
code_revision ran git in the reconstructing file's directory with this process's whole environment. An exported GIT_DIR, GIT_WORK_TREE, GIT_INDEX_FILE or GIT_COMMON_DIR, set by an outer git command, a hook or a shell, overrides git -C. git then reads that other checkout. With a second checkout that tracks a file of the same name, the marker recorded the other checkout's commit as the reconstruction revision, reported as clean. A file only the right checkout tracks was reported as an unknown revision. git now runs without the variables the installed git lists as local to one repository (git rev-parse --local-env-vars), as the Axiom checkout check already does. If git cannot list them, the revision is unknown. The adapter's helper is not imported, because it lives in a heavy module and this one stays light. Found by Codex's review of this pull request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
reconstruct_uk_publication checked the resolved output against the build directory, but wrote to the path as given. build/missing/../../publication names a folder beside the build, so the check accepted it. Creating its parents then made build/missing inside the build, which the command must only read. With a read-only build, that mkdir failed, so such a build could not be reconstructed at all, even into a writable destination. The output is now resolved once, and that path is used both for the check and for every write. An output that already exists under the spelling the caller gave, such as a dangling link, is still refused rather than followed. A new test sends that path for a writable build and for a read-only one. The build's files and folders must be unchanged, and the publication must appear beside the build. Found by Codex's review of this pull request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main passed the --output spelling to summary_text, but the publication is written at the resolved path, which the spelling need not reach. For --output <build>/missing/../../cli_publication the printed "Wrote ... to" and "report:" paths did not exist, because missing cannot be entered. main now resolves the output before the write, as reconstruct_uk_publication does, and prints that path. It still passes the given spelling, so the checks on it run. New tests: - the command prints the resolved path, for both lines, and never the given spelling; - an output whose given name exists as a symbolic link but whose resolved path does not (a dangling link leading outside the build) is refused as already existing, and nothing is written where the link points. The adapter doc says the output must not exist as given or once resolved, and that the command prints the resolved path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
force-pushed
the
orrery-graph-reconstruction
branch
from
October 20, 2026 22:45
94a5bff to
065a434
Compare
anth-volk
pushed a commit
that referenced
this pull request
Oct 21, 2026
Merges origin/main at 37c1684 ("Merge pull request #1297 from PolicyEngine/orrery-graph-reconstruction"), which brings in #1297 as merged (head 065a434) and #1329 (211483f, with the #1278, #1319, #1320 and #1324 work it carried). No file conflicted. #1297's merged head 065a434 has the same tree as the pre-rebase #1297 head 94a5bff that this branch had merged, so every #1295 and #1297 file now equals main's version. docs/uk-full-build-graph.md merged automatically: it keeps #1329's 46-stage spine count and this branch's per-epoch telemetry sentence. After the merge, this branch's diff against main is the same nine-file patch it had against the #1297 head 94a5bff. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
pushed a commit
that referenced
this pull request
Oct 21, 2026
…-schema Merges #1296's branch at 0b766e7, which merged origin/main at 37c1684 (#1297 merged as 065a434, and #1329 at 211483f). No file conflicted. packages/microcosm-build/tests/engine_free/uk/ test_uk_graph_terminal.py merged automatically: it keeps #1329's new tests and this branch's JSON Schema test of the final validation-check kernel's diagnostics. docs/agent-guide.md keeps #1297's version from main plus this branch's JSON Schema section. Nothing was regenerated. main has not changed microcosm-diagnostics, any pyproject.toml or uv.lock since this branch last merged #1296, so generate_calibration_diagnostics_json_schema.py --check reports the committed schema current, uv lock --check passes, and APPROVED_UV_LOCK_SHA256 still equals the SHA-256 of uv.lock (751721b231ab8cc6b3d10164f61ca429f5171661951494503fb0873e3f67b117). After the merge, this branch's diff against #1296's branch is the same 22-file patch it had against b0406fd; only the hunk offsets in test_uk_graph_terminal.py moved, because #1329 added tests above them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Fixes #1291
Stack
main← #1295 ← #1297 ← #1296 ← #1298 (GitHub stack #1328, linked on 2026-10-20 withgh stack link).#1322 taken out of the stack (2026-10-20): CI runs only for pull requests into
mainon purpose, to limit the number of simultaneous runs on the organization's shared runners. #1322, which removed that filter, is closed, and the old stack #1323 was removed.4ced66de0on #1295 restores the four files #1322 changed to their versions onmain, and each branch above merges the one below it: #129794a5bff93, #1296b0406fd76, #12987cb857716. None of those merges had conflicts, andtest_ci_integration_job.pypasses on every branch (9 passed). No commit was rewritten. From now on only the bottom pull request, which targetsmain, gets CI; each one above gets CI once it becomes the bottom.orrery-projection-without-store(Project Orrery artifacts without the content store, and write the execution overlay as its own file #1295).de4606dba(docs/orrery-adapter.mdanddocs/agent-guide.mdmerged cleanly, so the expected doc conflict with Project Orrery artifacts without the content store, and write the execution overlay as its own file #1295 is resolved),17553f299(Run the Tests workflow for pull requests into any base branch #1322's review fixes) andf2af7292e(mainata90eec3cb).f2af7292e. Tests: this PR's two changed test files gave 68 passed atbb908fed6and again atf2af7292e.Why
mainwas merged in (2026-10-20): GitHub builds each stacked pull request's test merge on top of the test merge of the pull request below it, starting frommain:refs/pull/1322/mergehasmainas its first parent, and each later one has the previous one's. Oncemaingained #1305, #1296's change torowwise_staging.pyconflicted with it, so GitHub could not build #1296's test merge. It reported #1296 as conflicting although its branch contained its base, and started no CI for #1296 or #1298.main(a90eec3cb) was merged into #1322's branch, and each branch into the one above, with merge commits; #1296 is now mergeable.Merge order: #1295, #1297, #1296, #1298, each after the one below it, with merge commits (or
gh stack merge). Every branch above #1295 has its parent merged in rather than rebased, so no commit was rewritten. Squash-merging a lower pull request would leave its original commits in the branches above, which would then need rebasing.Summary
Builds from before #1144 have no Orrery artifact, and the existing exporter can't give them one. They recorded no run binding, so the evidence validator refuses them. UK dense and national builds from that era did write their graph declaration (
graph.json) and a code pin. This PR turns such a build's output directory into a declaration-only Orrery publication, marked as reconstructed. It works offline and uploads nothing.This is a defined subset of #1170, tracked as sub-issue #1291: the declaration-only fallback for the build formats that recorded a declaration. Binding recovered receipts, timings and validation-check results to individual operations stays under #1170.
The command writes a new directory, outside the build directory, in the same layout as the preserved publication directory that
finalize_graphwrites:graph.schema.jsongraph.orrery.jsonorrery.upload.jsonorrery.reconstruction.jsonThe command also prints a short summary. The CLI is documented in
docs/orrery-adapter.md("Reconstructed declarations").Commits
Local runs were limited to the test files this branch adds, at the user's request. CI runs the full suite on this PR.
aee565df7). Adds the country-neutralmicrocosm.graph.reconstruction(marker, validation,reconstructed_schema),tools/orrery-contract/verify-reconstruction.mjs, docs and a changelog fragment.NotImplementedError(no import or fixture errors).test_graph_reconstruction.py, including Orrery's public parser through the new script).82ae80d35). Addsmicrocosm.build.uk_runtime.orrery_reconstructionand its CLI, docs and a changelog fragment.NotImplementedError.test_uk_orrery_reconstruction.py).Review fixes:
96abc1ec8). Dense builds closed by Register one UK full-build graph with all-geography calibration #901's driver are no longer refused (details under "Recorded digests"). The fixture now replays the drivers' write sequence in three layouts checked against the history withgit show: the Register one UK full-build graph with all-geography calibration #901 window, the refreshed dense layout, and the national completion record.--revision, exit 2). The national-layout test failed only on the newstale_recorded_digestsreport field: that layout already reconstructed. The guard tests passed before the fix.5d44bc7e4).reconstructed_schematurns the serializer'sTypeError(a top-level array or string, or a nested value of the wrong type) into aValueError. The CLI therefore refuses with exit 2 instead of a traceback and exit 1.TypeError. In the CLI case it escapedmain.fc3e2327e). The output is resolved, and each existing ancestor is compared with the build directory byos.path.samefile. An output equal to the build or beneath it, including through..or a link, is refused before anything is written...and link cases wrote into the build.14445a65e). The marker gainsreconstruction_revisionnext tosource_revision, and the docs, changelog and this description now say which parts come from the build (details under "Declaration resolution").KeyError: 'reconstruction_revision', pluscode_revision'sNotImplementedError) and three in the UK command (no revision lookup, no report field, no summary line).Checks on the final tree:
ruff checkandruff format --checkon the changed files pass, andgit diff --checkis clean.tools/ci_test_plan.py verifyandtools/graph_acceptance_burndown.py --verifypass.Other local checks:
python -mon a synthetic build, and a second run into an existing output is refused with exit status 2.code_revisionreports this checkout's HEAD and its uncommitted state.Before local runs were narrowed to the added test files, I also started the complete engine-free category, and both runs were stopped early:
test_us_release_head_to_head_scorer.py::test_spawned_workers_score_identically_to_sequential_cli. That tool deliberately refuses--worker-max-sliceson Python 3.14.6, which the local environment runs (3.14.7 is required), so the failure is unrelated to this branch.Neither run counts as a result for this PR.
Review fixes (2026-10-20)
Codex's
$review-codereview of14445a65efound two bugs, and a later review of2b020992ffound a third. Each fix is its own commit, with tests written to fail first.5b4da04e0Read the reconstructing code's revision without inherited git variablesmicrocosm.graph.reconstruction.code_revisionran git with the whole process environment. An inheritedGIT_DIR,GIT_WORK_TREE,GIT_INDEX_FILEorGIT_COMMON_DIR, exported by an outer git command, a hook or a shell, overridesgit -C, so git read another checkout. The marker then recorded that checkout's commit asreconstruction_revision, as clean. A file only the right checkout tracks came back as an unknown revision.git rev-parse --local-env-vars), the same listmicrocosm.frame.adapters.axiomstrips for its checkout check. If git can't list them, the revision is unknown. That adapter's private helper isn't imported, because the adapter is a heavy module andreconstructionstays light; the duplicated lines could later move to a shared module.test_code_revision_ignores_git_variables_that_select_another_checkoutsets up two checkouts and selects the other one through all four variables. It checks a file both checkouts track by the same name, and a file only the reconstructing checkout tracks.code_revisiontest passed.test_graph_reconstruction.pygave 32 passed.docs/orrery-adapter.mdand the1291-orrery-reconstruction-markerfragment now say this.2b020992fCheck and write the same resolved output pathreconstruct_uk_publicationchecked the resolved--outputagainst the build directory, but wrote to the path as given.build/missing/../../publicationnames a folder beside the build, so the check accepted it.mkdir(parents=True)then createdbuild/missinginside the build, which the command must only read. With a read-only build directory thatmkdirfailed, so the build couldn't be reconstructed at all.test_output_leaving_a_missing_folder_inside_the_build_creates_nothing_there, for a writable and a read-only build (skipped under root, which ignores directory permissions). Every file and folder in the build must be unchanged, and the publication must appear beside the build.missingfolder appeared inside it; with the read-only build,PermissionErrorwas raised on creatingbuild/missing. The 4 existing tests for outputs inside the build passed.docs/orrery-adapter.mdsays the command writes to the resolved path.bb908fed6Print the resolved output path the reconstruction wrotemainpassed the--outputspelling tosummary_text, but the publication is written at the resolved path, which that spelling need not reach. For--output <build>/missing/../../cli_pub, the printed "Wrote … to" andreport:paths did not exist, becausemissingcannot be entered.mainresolves the output before the write, asreconstruct_uk_publicationdoes, and prints that path. It still passes the given spelling, so the checks on it run.test_cli_prints_the_resolved_output_path_it_wrote: for<build>/missing/../../cli_publication, both printed paths are the resolved ones, the given spelling is never printed, and the report exists at the printed path.test_output_named_by_a_dangling_link_leading_outside_the_build_is_refused: the given name exists as a symbolic link, but the path it resolves to, outside the build, does not. It is refused as already existing, and nothing is written where the link points.…/build/missing/../../cli_publication. The dangling-link test passed, because2b020992falready checks the given spelling as well as the resolved path; it is a coverage test. With that check removed temporarily, it failed withDID NOT RAISE.ruff checkandruff format --checkwere clean on the changed files.docs/orrery-adapter.mdsays the output must not exist as given or once resolved, and that the command prints the resolved path.microcosm.graph.reconstruction._git_environmentduplicates the helper inmicrocosm.frame.adapters.axiom. Sharing it isn't worth it here:microcosm.frame's bundle code into a module that imports only the standard library andmicrocosm.graph;microcosm.frame.adapters.axiomin this PR, and the two helpers handle failure differently: the adapter raisesValueErrorwith a message about its checkout, after a 120-second timeout, whilereconstructionreturnsNoneso that the revision is recorded as unknown;Declaration resolution
Decision: use the declaration file the build itself recorded (
graph.json), with the code revision the build recorded. The declaration is not rebuilt by running the UK graph builders at the current revision. Only its bytes come from the build, though:graphsection andgraph_sha256, which reproduce the recorded file byte for byte.compiled,fieldsandinput_bindingssections, and the Orrery projection. A reconstruction made at another revision can differ in those parts. The marker therefore records that revision asreconstruction_revision, apart from the build'ssource_revision.Details:
serialize.py,decl.py) is unchanged from the UK graph driver (4bf7af4, 2026-09-25) to main. Since then, validation has only tightened (non-blank descriptions, expansion claims) and the schema string bound has loosened. A recorded file therefore reproduces byte for byte, andreconstructed_schemarequires that: the schema'sgraph_sha256must equal the file's SHA-256. A declaration that today's validator rejects, or a file that is not a graph at all, is refused, not reinterpreted.rowwise_candidate_manifest.json(git_commitandgit_dirty, which must agree withidentity.code).build.jsonandsha256sums.txtbind that file by digest, and the command checks it against them.--revisionmust match the recorded commit. When the build recorded none,--revisionsupplies the commit and the marker records it ascaller_asserted. With neither, the command refuses.code_revision(path)reads the commit and working-tree state from the git checkout that tracks the reconstructing module's own file, not from the current directory. It returns nulls when no checkout tracks that file, so an installed copy inside an unrelated repository is never attributed to that repository. The report warns when the revision is unknown, or when the working tree is dirty or could not be checked.graph.schema.jsonorexecution.evidence.json) is refused, with a pointer topython -m microcosm.graph.orrery. A directory withoutgraph.jsonis refused, and the refusal names the build records that were found. An output inside the build directory is refused.Alternatives considered:
graph.jsonbefore Add country-neutral Orrery execution evidence and UK contracts #1144. Rejected for now: it needs that revision's environment and the build's exact arguments (posture, size, geography, spine checkpoint), and any gap would present a declaration that never ran.operations.json: it's an inventory only, and can't be projected.Recorded digests
A file whose bytes disagree with a digest the build recorded for it is refused, with one exception, verified against the history with
git show:build.jsonwith the notestaging receipts are appended after publication. Its close step,_close_attempt, then appendedstaging_deliveryandstaged_datasetto the manifest and refreshed onlysha256sums.txt._refresh_completion(cb0d390), which reissuesbuild.jsonover the final bytes and drops the note. The national graph path arrived in the same PR, so national builds from main always carry the refreshed layout.A
build.jsondigest with that exact note that no longer matches is skipped, and the manifest must matchsha256sums.txtinstead. The marker records this asfallback_digest_matched. A dense build from that window run with--no-stagingor--no-staged-datasetwrote no digest list, so nothing the build recorded checks its manifest. The marker records that asonly_stale_digest_recorded. In both cases the report lists the skipped digest, its note and what the file was checked against, and adds a warning. Any other mismatch is still refused.Marker format
The marker is the graph-schema producer extension
microcosm.reconstruction. It is saved ingraph.schema.json, so any later re-projection of the saved schema keeps it (O4 and B2). Orrery documents show it atmetadata.microcosm.extensions["microcosm.reconstruction"], andvalidate_reconstruction(schema)reads it.{ "protocol": "microcosm.graph.reconstruction.v1", "reconstructed": true, "basis": "declaration_only", "source_revision": {"git_commit": "<full lowercase commit>", "git_dirty": false, "origin": "recorded"}, "reconstruction_revision": {"git_commit": "<full lowercase commit, or null>", "git_dirty": false}, "inputs": [ {"name": "build.json", "role": "completion_record", "sha256": "…", "bytes": 512, "verification": "no_recorded_digest"}, {"name": "graph.json", "role": "declaration", "sha256": "<= graph_sha256>", "bytes": 4096, "verification": "no_recorded_digest"}, {"name": "rowwise_candidate_manifest.json", "role": "code_pin", "sha256": "…", "bytes": 2048, "verification": "recorded_digest_matched"}, {"name": "sha256sums.txt", "role": "digest_list", "sha256": "…", "bytes": 160, "verification": "no_recorded_digest"} ], "scope": "Rebuilt after the build from its recorded graph declaration, the only part taken from the build: …" }source_revision,originisrecordedorcaller_asserted, andgit_dirtymay benull. Inreconstruction_revision, both fields arenullwhen no checkout tracks the reconstructing code, andgit_dirtyalone isnullwhen git could not check the working tree. Inputs are plain file names, listed in name order, and exactly one is the declaration.verification:recorded_digest_matched,fallback_digest_matched,only_stale_digest_recordedorno_recorded_digest(see "Recorded digests").metadata.execution, so no operation appears as executed, cached or as having passed or failed a check.reconstructed-followed by 32 hex characters derived from the published files' digests. Reconstructing the same build twice from the same code revision gives identical bytes and the same ID.v1with the new field, since the protocol hasn't been released.Overlap with O1 (#1294) and #1295
orrery.py,orrery_evidence.pyandmicrocosm/graph/__init__.pyare untouched.reconstruction.pyimports onlydecl,schemaandserialize, which O1 keeps light and eager, so it stays importable without the store.orrery_json_from_schemafrommicrocosm.graph.orreryandINVENTORY_NAMEfromorrery_publication. If O1 moves either, the fix is an import path.Risks and limits
graph.jsonis not digest-bound by the build: no record from before Add country-neutral Orrery execution evidence and UK contracts #1144 binds it. A file overwritten after the build can't be detected. The marker recordsno_recorded_digest, and the report warns.only_stale_digest_recorded, and the report warns.graph.jsonwas written beforeuk.full.national.readbackwas appended. The reconstruction shows exactly the recorded declaration, so that operation is absent.Follow-ups
Out of scope for this PR:
_read_input,_json_object) with Project Orrery artifacts without the content store, and write the execution overlay as its own file #1295's overlay reader.🤖 Generated with Claude Code