Skip to content
Merged
1 change: 1 addition & 0 deletions changelog.d/1291-orrery-reconstruction-marker.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `microcosm.graph.reconstruction`, which rebuilds a declaration-only graph schema from a build's recorded `graph.json` and marks it with the `microcosm.reconstruction` extension (protocol `microcosm.graph.reconstruction.v1`). The marker names the revision that declared the graph (`source_revision`), the revision of the code that performed the reconstruction (`reconstruction_revision`, read by `code_revision` from the checkout that tracks that code, ignoring inherited repository-selecting git variables such as `GIT_DIR`), and every input file by digest. Only the declaration's bytes come from the build: the schema's `compiled`, `fields` and `input_bindings` sections and the Orrery projection come from the reconstructing code. The marker travels in `graph.schema.json` into the Orrery document's metadata. A declaration that the current serializer would not reproduce byte for byte is refused, and no operation is presented as executed.
1 change: 1 addition & 0 deletions changelog.d/1291-uk-orrery-reconstruction.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `python -m microcosm.build.uk_runtime.orrery_reconstruction`, which turns the output directory of a UK dense or national build from before #1144 into a declaration-only Orrery publication. It writes a new directory with the normal publication layout (`graph.schema.json`, `graph.orrery.json`, `orrery.upload.json`) and a summary report. The declaration comes from the build's recorded `graph.json` and the revision from its recorded code pin. The schema's compiled sections and the Orrery document are computed by the code running the command, whose revision the marker records beside the build's. Every input is checked against the digests the build recorded. The one exception is the candidate manifest digest that dense builds closed by #901's driver left in `build.json` beside the note that staging receipts follow: it is skipped and reported, and the manifest is checked against `sha256sums.txt` instead. Builds that already record Orrery evidence are refused, as is an output directory inside the build directory, which is only read. Nothing is uploaded.
4 changes: 3 additions & 1 deletion docs/agent-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ engine-free runner installs the supported public Orrery range and locked Node
dependency set under `tools/orrery-contract/`; the pytest test generates a
document through Microcosm's public Python API and requires Orrery's public
parser to accept it. The recorded-evidence assertion in `test_graph_evidence.py`
uses the same pinned parser for groups, statuses, activities and artifacts.
uses the same pinned parser for groups, statuses, activities and artifacts, and
the one in `test_graph_reconstruction.py` uses it for the reconstruction marker
of a declaration-only document.
These CI tests perform no browser rendering.

Workspace tests use an in-memory telemetry emitter by default. The shared
Expand Down
184 changes: 184 additions & 0 deletions docs/orrery-adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,190 @@ mismatches, missing required bytes and corrupt files refuse export. Unavailable
gate-blocked outputs, omitted operations and unconfigured summary providers are
explicit. Earlier phases not supplied to the exporter remain unknown.

## Reconstructed declarations

A build from before #1144 recorded its graph declaration, but not the run
binding that execution export validates, so its execution cannot be shown.
`microcosm.graph.reconstruction` rebuilds that build's schema from the recorded
declaration alone and marks it as reconstructed:

```python
from microcosm.graph.reconstruction import (
code_revision,
reconstructed_schema,
reconstruction_marker,
)

marker = reconstruction_marker(
git_commit=recorded_commit,
git_dirty=False,
revision_origin="recorded",
reconstruction_revision=code_revision(__file__),
inputs=inputs, # one {name, role, sha256, bytes, verification} per file read
)
schema = reconstructed_schema(declaration_bytes, marker)
```

Only the declaration comes from the build. The schema's `graph` section and
`graph_sha256` reproduce the recorded file byte for byte. Its `compiled`,
`fields` and `input_bindings` sections, and the Orrery document projected from
it, are computed by the code running the reconstruction. A reconstruction made
at another revision can therefore differ in those parts, which is why the
marker records that revision separately from the build's.

The marker is the producer extension `microcosm.reconstruction`:

```json
{
"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": "graph.json", "role": "declaration", "sha256": "<hex>",
"bytes": 4096, "verification": "no_recorded_digest"},
{"name": "rowwise_candidate_manifest.json", "role": "code_pin",
"sha256": "<hex>", "bytes": 2048, "verification": "recorded_digest_matched"}
],
"scope": "Rebuilt after the build from its recorded graph declaration, the only part taken from the build: ..."
}
```

- **`source_revision`:** the code that declared the graph. `origin` is
`recorded` when the build wrote the revision itself, and `caller_asserted`
when the build recorded none and the caller supplied one. `git_dirty` is
`null` when the build did not record it.
- **`reconstruction_revision`:** the code that performed the reconstruction,
and so computed every section except the declaration. `code_revision(path)`
reads it from the git checkout that tracks `path` (the reconstructing
module's own file), not from the current directory, and runs git without
the repository-selecting variables of `git rev-parse --local-env-vars`
(`GIT_DIR`, `GIT_WORK_TREE` and the rest), so an inherited one cannot point
it at another checkout. Both fields are `null`
when no checkout tracks that file, as for an installed wheel. `git_dirty` is
`null` alone when git could not check the working tree.
- **`inputs`:** every file the reconstruction read, in name order, by digest
and size. Exactly one input is the declaration, and its digest equals the
schema's `graph_sha256`. `verification` compares the file with the digests
the build itself recorded for it:

| Value | Meaning |
| --- | --- |
| `recorded_digest_matched` | Every recorded digest matched. |
| `fallback_digest_matched` | The build marked one recorded digest as taken before it rewrote the file. That digest was skipped, and another recorded digest matched. |
| `only_stale_digest_recorded` | Every recorded digest is marked as taken before the rewrite, so nothing the build recorded checks the file. |
| `no_recorded_digest` | The build recorded no digest for the file. |

In the last two cases the file is bound only by the digest in the marker.
- **Exact bytes:** `reconstructed_schema` refuses a declaration that the
current serializer would not write byte for byte. The schema's `graph`
section is therefore the recorded file, not a reinterpretation of it. A file
that is not a graph declaration at all, such as a JSON array, is also refused
with a `ValueError`.
- **Where it travels:** the marker is saved in `graph.schema.json`, so any
later projection of the saved schema keeps it. Orrery documents show it at
`metadata.microcosm.extensions["microcosm.reconstruction"]`.
`validate_reconstruction(schema)` returns the marker, or `None` for a schema
that was not reconstructed.
- **No execution claims:** a reconstructed document has no activities,
artifacts, statuses or `metadata.execution`. No operation appears as
executed, cached or gated. CI parses one with Orrery's public parser
(`tools/orrery-contract/verify-reconstruction.mjs`).

### UK builds before #1144

UK dense and national builds from before #1144 wrote `graph.json` beside their
outputs, and recorded their code revision in `rowwise_candidate_manifest.json`.
This command turns such a build's output directory into a reconstructed
publication:

```sh
uv run python -m microcosm.build.uk_runtime.orrery_reconstruction \
--build path/to/past-build \
--output path/to/reconstructed-publication
```

The declaration is the build's own `graph.json`. It is not rebuilt by running
the UK graph builders at the current revision. The declaration format has not
changed since the UK graph driver, so a recorded file reproduces byte for byte;
one that does not is refused. Everything derived from it (the schema's
compiled sections and the Orrery document) is computed by the code running the
command, and the marker records that code's revision as
`reconstruction_revision`.

The build directory is only read. The command:

- **Reads:** `graph.json`, which is required, and also `build.json`,
`rowwise_candidate_manifest.json` and `sha256sums.txt` where present. Links,
devices and inputs over 32 MiB are refused.
- **Checks recorded digests:** a file whose bytes disagree with a digest the
build recorded for it is refused. `build.json` binds the candidate manifest,
and `sha256sums.txt` binds the files it lists. One exception covers the dense
driver merged with #901 (2026-09-29). It recorded the manifest's digest in
`build.json` with the note `staging receipts are appended after
publication`, then appended `staging_delivery` and `staged_dataset` to the
manifest and refreshed only `sha256sums.txt`. `build.json` was reissued over
the final bytes, without the note, only from #1057 (2026-09-30). A
`build.json` digest with that exact note that no longer matches is skipped
and reported, and the manifest must then match `sha256sums.txt`
(`fallback_digest_matched`). A build run with `--no-staging` or
`--no-staged-dataset` wrote no digest list, so its manifest is reported as
`only_stale_digest_recorded`, with a warning.
- **Resolves the revision:** it takes the candidate manifest's `git_commit`
and `git_dirty`, which must agree with the copy under `identity.code`.
`--revision` must match that commit, or supplies one when the build recorded
none; that revision is marked `caller_asserted`. With neither, it refuses.
- **Refuses later builds:** a build that records Orrery evidence
(`graph.schema.json` or `execution.evidence.json`) is exported with
`python -m microcosm.graph.orrery` instead.
- **Refuses missing declarations:** a directory without `graph.json` is refused,
and the refusal names the build records that were found. Staged bundles never
carried `graph.json`, and builds from before the UK graph driver never wrote
it.

It writes a new directory with the layout of a normal publication, and moves the
directory into place only after every file is written. The output must not
exist, either as given (a symbolic link counts, even one whose target does not
exist) or once resolved. It must not be the build directory or lie inside it
once `..` and links are resolved. The command writes to that resolved path, so
a path that passes through a missing folder inside the build creates nothing
there, and a read-only build can be reconstructed. It prints the resolved path,
because the path as given need not reach it:

| File | Contents |
| --- | --- |
| `graph.schema.json` | Compiler schema of the recorded declaration, with the reconstruction marker |
| `graph.orrery.json` | Orrery document, titled `UK · reconstructed declaration (<commit>)` |
| `orrery.upload.json` | Publication inventory (roles `graph` and `schema`); its publication ID is derived from the files' digests |
| `orrery.reconstruction.json` | Summary report; not in the inventory |

The summary report, of which the command prints a short version, lists:

- the inputs and how each was verified;
- the source revision and the reconstruction revision;
- the recorded digests skipped as stale, each with its note and the records the
file was checked against instead;
- the expected files that are missing;
- evidence found but not bound to operations: run manifests,
`evidence-index.json`, `operations.json` and the content store;
- warnings, for a dirty or unrecorded working tree in the build or the
reconstruction, an unknown reconstruction revision, a caller-asserted
revision, a skipped stale digest, or a declaration the build recorded no
digest for. Before #1144, the last one applies to every build.

Output is deterministic: reconstructing the same build twice from the same
code revision gives identical bytes and the same publication ID. The command
uploads nothing and starts no emitter service, so publishing the directory
remains a separate step.

## Orrery evidence panels

| Surface | Exported evidence |
Expand Down
Loading
Loading