Repository navigation
Conversation
Records the lane's base, goal, evidence base and the two local constraints: root out.md belongs to another lane, and the v5 cold run holds the machine. Co-Authored-By: Claude Opus <noreply@anthropic.com>
_update_series boxed every value of a plain float, integer or boolean column into a Python object and framed them one at a time; on pandas 3.0.3 a plain column is a NumpyExtensionArray with no _data/_mask, so the fast masked path never applied to it. _object_stream now builds the identical framed bytes -- the same 'object' dtype header, the same shape header, the same per-value length prefixes and payloads -- with vectorised numpy, and returns None for every column kind it cannot reproduce exactly, including a Categorical whose values are an integer-coded view. No digest value moves. test_graph_executor_series_stream compares the live helper against a verbatim copy of the pre-change body at the byte level over every column kind, the float specials, non-canonical NaN payloads, both int64 endpoints and a 200-frame random sweep. Measured 0.47 s -> 0.09 s on a 6,928 x 240 frame. Co-Authored-By: Claude Opus <noreply@anthropic.com>
docs/us-native-verification-once.md states, per mechanism, the guarantee today, the change, why the guarantee survives and the new cost class -- and the two residuals the epoch's unconditional final re-validation exists to close. Co-Authored-By: Claude Opus <noreply@anthropic.com>
_records read the 2.4 GB of ACS person CSV inside csv_pus.zip one Python byte at a time; with peek and columns.lines it was 57-65% of the admission phase in every measured native run. It now finds terminators with bytes.find, counts quote parity with bytes.count, and keeps each terminator cursor until the scan passes it -- so an absent carriage return costs one search per 1 MiB block rather than one per record, which is where the bulk of the win is. The two ceilings are charged by _fence. A record no longer than the smaller of the live ceilings cannot have violated either, because the token counter resets at every record start; anything longer is replayed byte by byte, so the refusal code and the byte it fires on stay the fence's own -- including the LF that closes a CRLF record, which the fence charges against the record ceiling only. test_us_acs_record_fence_scan compares the shipped fence against a verbatim copy of the byte loop: every boundary case, every block-boundary case, every ceiling on both sides, 4,500 randomised strings, 300 row-shaped inputs, and all 9,331 strings up to five bytes over the branching alphabet under each of five ceiling settings -- 46,655 exhaustive comparisons. Measured on the real staged archive: 10.8 MB/s -> 522.8 MB/s, digests identical. Re-pins acs_native_coverage_binding._ACCEPTED for the changed module: 475aa795c8a5b49a0dd3405a0877866dddd03012a1f2b9743447fab1fe85bcff -> 9ec68721a4cf480ef412c51ab354db9000eb7a7989e35e6e09b1574d88e8e49f (shasum -a 256 .../acs_person_coverage_authentication.py, after formatting) The module's graph_implementation_inventory contract is unchanged: the rewrite adds no _RESOURCE_CALLS name and no non-stdlib import. Co-Authored-By: Claude Opus <noreply@anthropic.com>
executor.py re-derived source_content_key for every source a cold-executed node declared, and keys._directory_identity read_bytes()es every file of the tree: O(executed source-declaring nodes x source bytes), which on the native graph is the 3.48 GiB source tree re-read per node. run_graph now carries a _SourceIdentities cache in its own locals, populated by the run-start pass as it reads. A cached key is reused only while the path's stat signature -- (dev, ino, size, mtime_ns, ctime_ns) for a file, and for a directory its own identity plus the relative name, type and identity of every entry _directory_identity would walk -- is identical to the signature taken both immediately before and immediately after the read that produced the key. A derivation whose two signatures disagree is not cached at all. source_content_key itself stays pure and uncached: three key and codec tests call it twice on one path with the bytes changed in between. Before the manifest is built, every source is re-derived in full with the cache bypassed, written inline rather than through _source_paths_and_keys because a build test counts calls to that code object. That closes the two cases a stat signature cannot decide, and gives the executor a guarantee it never had: a source changed during a node that declares none is now caught. RunManifest gains source_identities, attached exactly like populations and mass_ledgers -- repr=False, compare=False, outside content_addressed, outside to_json, outside every node receipt and cache record. test_graph_executor_source_identity pins the contract that had no test at all: a source rewritten by its own node refuses and writes nothing to the store, a file added to or removed from a directory source refuses, a same-length rewrite with mtime restored refuses, a change during a source-free node refuses at run end, and an unchanged source is read twice per run however many nodes declare it. The graph suite falls from 108 s to 72 s. Also records the real-archive record-fence parity receipt: both ACS person members, 2.4 GB, 3,422,890 records, identical digests, 226.9 s -> 4.6 s. Co-Authored-By: Claude Opus <noreply@anthropic.com>
Every borrow of AuthenticatedSurveyPopulationPreparation re-ran the whole authentication: both source catalogues, the ACS native coverage binding (eight full zip reads), the nested ASEC native population (seven files), the ten-file roster re-hash and every pure seal -- once per executed node through the population observer, and again inside every kernel that borrows it. The ASEC native capsule did the same on every .frame access. survey_population_preparation.verification_epoch() makes that happen once. Inside an epoch each borrow still pays a cheap tier in full -- the live authority and attached owner payloads, _encode(_producer()), and _file_stats over the whole roster -- and the expensive tier is skipped only while a signature is unchanged: the stat identity of every path the four foreign verifications and _source_files re-read, the identity and length of every borrowed payload, and a read-free witness of every live Frame's storage (buffer address, shape, strides, dtype and writeable flag per column, index and weight). A signature that moved is a memo miss, not a refusal: the complete validation runs and raises exactly what it would have raised. Leaving the epoch, at every nesting level, re-validates every memoised capsule in full with the memo bypassed, so a change no signature can see -- an in-place write into a live buffer, or a value written into a frozen plan row -- still makes the run refuse before the caller receives anything. Outside an epoch nothing is memoised and every existing borrow, refusal and trace-based mutation test keeps its exact meaning; _validate and _validate_state are untouched. The three foreign owners gain only a _verified_source_stats() helper naming the paths their own verification re-reads. Their inventory contracts do not move. The two capsule modules' unbound_uses digests do, because the new functions use already-imported names in new scopes; both are re-derived through graph_implementation._dependency_contract and recorded in PROGRESS.md. Co-Authored-By: Claude Opus <noreply@anthropic.com>
run_atomic_survey_population and run_atomic_survey_financial now wrap their whole body in survey_population_preparation.verification_epoch(). Every native capsule the run borrows is authenticated in full once, reused while its sources and live storage are provably unchanged, and re-authenticated in full before the run returns. The financial run's prefix opens a nested epoch; each closes with its own full re-validation. Both commits are an indentation plus one 'with' line. Proven mechanically: the statement lists of both function bodies parse to identical ASTs before and after (50 statements and 102 statements), the argument lists are unchanged, the docstrings stay outside the wrapper, and both files were ruff-format clean at the base so every hunk lies inside the wrapped function. Keeping the body in its own function matters: a build test asserts caller.f_code is runner.run_atomic_survey_population.__code__. The epoch's exit closes the nested capsule's epoch first, so this owner's final validation reaches the nested population's complete file check rather than its memo, and collects both refusals so neither can be lost to the other in a finally block. A foreign owner's error raised by a deferred borrow is translated exactly as _checked translates it, instead of escaping raw. test_us_native_verify_once_epoch: 17 tests. Validated once and reused (one _source_files pass for six borrows, five memo hits); the producer encoding never memoised; every on-disk mutation -- append, truncate, same-length rewrite with mtime restored, a file added to a source directory, a touch -- refusing at the borrow that follows it AND again when the epoch declines to close over it; a signature miss outside the roster re-running without refusing; a change no signature can see refusing at the close; a failing body finalizing nothing; and nested epochs finalizing at every level. Also adopts the change in docs/validation-cost-and-borrow-boundaries.md, which recorded this optimization as proposed rather than adopted, and corrects the codecs docstring that called the executor's check post-run. Co-Authored-By: Claude Opus <noreply@anthropic.com>
Co-Authored-By: Claude Opus <noreply@anthropic.com>
The frame-witness bridge into the preparation owner is module-internal; naming it without the underscore made it look like an owner API. Renaming it moves the module's unbound_uses digest, because that digest keys each use site by its enclosing scope, so the inventory contract is re-derived through the module's own generator: 71463df4f645cd2a98f537a212187b4d1e52bffcc4d2317defe6968d7f287608 -> 7bb20439da26f75b48551e21e304c4bb7f3983ee07ef78ed804411640d58cdf4 All ten stage manifests still build. Co-Authored-By: Claude Opus <noreply@anthropic.com>
Co-Authored-By: Claude Opus <noreply@anthropic.com>
The split branch, the measurement staging and the remaining work, so the journal is accurate while the probes are running. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The measurement staging itself stays out of the tree (.measure/ is ignored): this is the script, parameterised so the identical file measures the base and this branch, with the import-origin assertion that makes that comparison mean something. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tions Finishes experiments/native-verify-once/out.md per the lane brief's report section, on top of 284bc6e (the head at which every number in it was measured). - Status first: no test group failed. `ruff format --check .` exits 1 on 77 pre-existing files; the intersection with this branch's diff is empty, the branch does not touch the ruff config, its own 17 .py files are clean, and CI's lint job runs `ruff check .` only. Stated, not softened. - Per mechanism, a probe-level paragraph beside the unit-level numbers, each saying what the runs can and cannot support. The one chain-level before/after the partial before probe supports is the ACS record fence: rank 1 at 760.23 s (still running at the ceiling) against 10.80 s in the after probe and 11.98 s in the 19-node run. - A consolidated digest-equality section: the two mechanisms that rebuild bytes and are compared against verbatim copies of the pre-change code, the three that rebuild nothing, and the whole-inventory recomputation. - Pins re-derived, each half re-checked against the tree while writing this (shasum on both sides, and the inventory diff is exactly two lines). - Test summaries verbatim: the lane's own runs plus the 2026-09-16 CI-shaped battery, with the 62-file selection, the three expected skips, the engine-free skip delta and the load contention that inflates the wall times. - Measurement: files A-E, run-level table, the 9-node probe's bound (>= 1.25x, not a ratio), the 19-node like-for-like (5,278.61 -> 2,010.07 CPU s, 2.63x; peak RSS 9.31 -> 13.31 GB), the top ten remaining chains, and the caveats, including the second before-probe JSON on disk. - PR URL, base, head sha, and seven open questions for Max as questions with options. Also corrects the experiment README, which named two probe result files that do not exist under those names, and updates the lane's journal section. No claim about dataset quality, calibration or release eligibility. Co-Authored-By: Claude Opus <noreply@anthropic.com>
MaxGhenis
force-pushed
the
native-verify-once
branch
from
September 16, 2026 18:43
e9c1f7e to
41703d5
Compare
…ules Every mutation test the lane had for the preparation capsule targets a roster file or a roster directory, and the cheap tier compares `_file_stats` over exactly those before any memo lookup, so all of them refused with SOURCE_STAT_CHANGED without reaching the memo at all. The branch of the new code the design leans on hardest -- a signature miss running the complete validation and raising whatever it would have raised today -- was untested for that capsule. Both new tests change file content while keeping the length, the inode and the modification time, and both pick a path the cheap tier cannot compare: * the preparation capsule's test writes into an ACS catalogue private snapshot copy, which is in the memo signature and outside `_file_stats`, asserts the roster stats are byte-identical across the mutation, and asserts the borrow refuses with the same code an unmemoised borrow of the same mutation raises (PREPARATION_VERIFICATION_REFUSED, from the ACS catalogue's own SOURCE_CHANGED); * the ASEC capsule's cheap tier compares no file stat at all, so its test counts the file loop to prove the refusing borrow ran the complete `_validate_state` rather than a cheap check, and pins SOURCE_FILE_CHANGED at the borrow and again at the close. The existing cheap-tier tests are unchanged. Co-Authored-By: Claude Opus <noreply@anthropic.com>
The design note promised that a signature mismatch is a memo miss rather than a refusal, so the complete validation runs and raises "whatever refusal it would have raised today ... with the same code, at the same access". The cheap tier broke that promise for one comparison: it compared `_file_stats` before the memo lookup, while `_validate` compares it last, after both catalogues, both native verifications and the roster re-hash. An appended `acs/csv_pus.zip` therefore refused with SOURCE_STAT_CHANGED inside an epoch and with PREPARATION_VERIFICATION_REFUSED outside one, for the same mutation. The fix is behavioural rather than documentary, because it is smaller than the divergence it removes and it deletes a redundant `_file_stats` walk per borrow: the roster's stat identities are read into the signature (where `_verified_source_stats` already reads them) and no longer compared in the cheap tier. A moved stat field is now a miss, `_validate` runs in full and compares the same roster stats it always compared, and the borrow raises the code an unmemoised borrow raises. A signature that cannot be taken at all -- a roster entry removed, or no longer a regular file -- defers to `_validate` first for the same reason, and re-raises only as the fail-closed remainder. `test_an_in_epoch_refusal_carries_the_code_it_carries_today` refuses four mutations twice each, once inside an epoch and once with no memo at all, and asserts the codes are equal and are SOURCE_CHANGED, PREPARATION_VERIFICATION_REFUSED, PREPARATION_VERIFICATION_REFUSED and SOURCE_STAT_CHANGED. The existing cheap-tier tests, including the touch test that pins SOURCE_STAT_CHANGED, are unchanged and still pass. The note's two tier paragraphs are corrected with the same change: Tier A also keeps the live authority and the attached owner payloads (the code was already stricter than the note there), and the signature is named as input, never a verdict. Recomputed after the edit: all 124 inventory contracts through `graph_implementation._dependency_contract` give `moved: 0`, and all ten stage manifests build. Co-Authored-By: Claude Opus <noreply@anthropic.com>
The brief asked for the run-end re-verification to be recorded. The epoch already yielded a record, but both call sites opened it bare, so hits, misses, final validations and the capsule count were dropped the moment the run ended, and nothing anywhere said how many full validations a real run performed. The design note named a `verification_epoch.last_record()` API that does not exist. Both runners now bind the record and hand it to `run_graph`, which attaches it to the manifest as `RunManifest.verification_epoch` -- a live view of the caller's own mapping, kept rather than copied because such a record is finalised when the epoch closes, which happens after `run_graph` returns and before the caller receives the manifest. It carries counts and labels only, and like `source_identities` it is `repr=False`, `compare=False`, outside `content_addressed`, outside `to_json`, and outside every node receipt and cache record. No existing receipt field moves. `test_a_real_run_records_its_epoch_in_the_manifest` runs the nine-node atomic survey population graph over invented sources and reads the record off the manifest: 20 memo hits, 3 signature misses, one unconditional final re-validation, one capsule. The report now quotes those counts and states plainly that "once per run" is the shape rather than the literal count -- that run paid four complete validations for twenty-three borrows. The design note's nonexistent API is replaced by what the code actually exposes. Co-Authored-By: Claude Opus <noreply@anthropic.com>
`_source_stat_signature` lstat()ed every entry of a directory source, while `keys._directory_identity` selects members with `is_file()` and reads them with `read_bytes()` -- both of which follow symlinks. A member link's target was therefore inside the source's content key and outside the signature over it, so rewriting that target left all five of the link's stat fields untouched, the per-node check answered from cache, and the refusal was deferred to the run-end re-derivation, after intervening nodes had written store records. That residual was in neither the design note nor the report. The signature now contributes the resolved identity of every member that is a symlink, so both sides walk the same members. A link that resolves to nothing contributes that fact and raises nothing, matching `_directory_identity` skipping it. `test_a_symlinked_member_refuses_at_the_node_that_changed_its_target` runs a graph whose kernel rewrites the target and asserts the node refuses and the store gained no object; two unit tests pin the signature itself, including the broken-link case. The note and the report state the follow on both sides. Co-Authored-By: Claude Opus <noreply@anthropic.com>
`_object_stream`'s float branch cast to an explicit `<f8` while `_update_scalar`
packs `struct.pack("=d", value)` and its array fallback uses `np.float64`, both
native order. The two agree on every little-endian host -- which is why the
byte-parity tests pass here -- and disagree on any other, so the helper's
"byte-for-byte identical output is the contract" would not hold on a big-endian
build. The cast is now `np.float64`.
The length prefixes stay explicitly little-endian on purpose: the loop writes
them with `int.to_bytes(8, "little")`, so `_framed`'s `<u8` and the integer
branch's `<u8` lengths are the correct match. The comment now says which is
which. Byte-identity tests unchanged: 34 passed.
Co-Authored-By: Claude Opus <noreply@anthropic.com>
Five places described the code inaccurately, and one comment described a behaviour the code did not have: * the design note said the ASEC memo is keyed by the identity of the `_ISSUED` entry tuple and torn down by the weakref callback that clears `_ISSUED`; it is keyed by `id(owner)` with a weak reference beside the signature, answers only while that reference resolves to the same object, and is cleared when the outermost epoch exits; * the epoch finalizer's comment, the note and the report all said the owner's final validation reaches the nested population's complete file check. That is true only at the outermost close, where the nested memo has been cleared. At an inner nested close the depth stays above zero, `_epoch_exit` refreshes the nested signature, and the owner's validation is a memo hit -- with nothing skipped in net, because the nested capsule's exit has just re-validated it in full. All three now say that; * the report said each mechanism 1 mutation test asserts its refusal twice; the last one asserts it once by design, because the mutation it makes is the one a signature cannot see, so the borrow after it is a hit and the close is the only refusal; * `test_a_rewritten_source_still_refuses_when_only_its_bytes_moved` implied it exercised a stat-preserving rewrite. It does not: st_ctime_ns moves. Its docstring now says so and points at the test that reaches the run-end re-derivation, which is where the genuinely stat-preserving case is closed; * the finalizer's comment promised "a refusal is never dropped, only ordered", while a nested refusal raised alongside this owner's was discarded. The owner's refusal is still the one raised -- its class is what every borrow through it raises -- and the nested one is now chained onto it as its cause. Tier A already kept the live authority and the attached evidence on every borrow, which the note listed under Tier B; that correction landed with the refusal-code fix. Co-Authored-By: Claude Opus <noreply@anthropic.com>
`_stat_or_absent` in `survey_population_preparation.py` had no caller anywhere in the repository; it is deleted. `_path_stat` stays duplicated verbatim in the four capsule modules, and the report now says why instead of leaving a reader to find out. `graph_implementation._dependency_details` records every relative import as an unbound use and every scope-qualified use of the alias it binds, and `_dependency_contract` hashes that set into the `unbound_uses_sha256` the inventory pins per module. Running a shared-helper import through the module's own generator confirms it: `imports` does not move, `unbound_uses_sha256` does. Consolidating would re-pin four authentication capsules in the file that binds what each capsule may reach, on top of the two pins this lane already moves -- a larger and more security-relevant diff than the duplication it removes. The cross-package pair (`survey_population_preparation._stat_identity` and `executor._stat_identity`) would additionally move `imports` and require a new `import_classifications` entry. All 124 contracts recomputed after the deletion: `moved: 0`; all ten stage manifests build. Co-Authored-By: Claude Opus <noreply@anthropic.com>
…misses The report's mechanism 5 section named byte-parity proofs and no mutation test, which left the impression that the "did the kernel mutate its context" guard was unexercised through the new numpy path. It is exercised: `test_executor_detects_mutation_even_when_pandas_replaces_a_buffer` mutates an int64 column inside a kernel and asserts the refusal, and that column now streams through `_object_stream`'s integer branch. The section says so, and says the float and bool branches have byte parity only -- no test mutates a float or bool column in a kernel -- so the coverage is stated rather than implied. Co-Authored-By: Claude Opus <noreply@anthropic.com>
Two stale claims in the report, both now dated rather than deleted, per the repo's journal rule. The eight "lane's own runs" summary lines were pasted as each mechanism landed, at that day's head and machine state, and none of them was reproduced in the CI-shaped battery; several of their test counts have since moved because the verification findings added tests. The section now says so up front, and points at the battery and the post-findings section as what stands for the branch. Open question 4 asked whether to open the main-only split. It exists: draft PR #938, base main, head 8ea4844, 6 files +988/-8, MERGEABLE (checked with `gh pr view 938`). The question is marked answered, and the four graph-shard changes this branch has made since -- the member-symlink follow, the native-order float cast, `RunManifest.verification_epoch` and `run_graph`'s `_verification_epoch` parameter -- are named as the mirroring that is still open. PROGRESS.md's "Next" says the same. Co-Authored-By: Claude Opus <noreply@anthropic.com>
The verification findings changed the code under three of the report's sections and rebased the branch under its diff stat, and the report still described the state before both. * Mechanism 1's guarantee said every borrow pays `_file_stats` as a refusal. Since the refusal-code fix it reads the roster stats into the signature instead, so a moved stat field is a miss whose complete validation raises today's code. The section now says that, names the two tests that reach the memo-miss branch at all, and states what each one pins. * Mechanism 2 gains the rewrite-through-the-memo test beside the one it had. * The five-copies section claimed a cross-package `_stat_identity` import "would require a new entry in `import_classifications`". It would not: `microcosm.graph.executor` is already classified there. What it does move is that module's pinned `imports` list, while `unbound_uses_sha256` stays put, because every stage carrying the file declares the graph package and `_covered_imports` binds everything beneath it -- the mirror image of the four `_path_stat` modules, where `unbound_uses_sha256` moves and `imports` does not. Both halves were re-derived through the modules' own generator rather than argued, and the section now says they were. * The header, the head-sha rows and the diff stat are re-stated for the rebase onto `363a9033b`: 26 files, +5,422 / -819, with two-dot and three-dot now agreeing because no base drift is left. The superseded 27 files / +4,399 / -849 is kept with the reason it was wrong. Re-verified at this head while writing it: all 124 inventory contracts recomputed through `graph_implementation._dependency_contract` give `moved: 0`, all ten stage manifests build, `shasum -a 256` on `acs_person_coverage_authentication.py` is the `9ec68721...d88e8e49f` that `acs_native_coverage_binding.py:38` holds, and the only 16+-hex changes in the three-dot `packages/*/src` diff are that pin and the two `unbound_uses_sha256` lines. Co-Authored-By: Claude Opus <noreply@anthropic.com>
The nine commits that resolve the verification findings changed three source
files and two runner call sites, so the groups that reach them were run again at
this head, on the rebased base. All rc 0, pasted verbatim into a new report
section and summarised in the journal:
769 passed, 1 skipped in 44.56s packages/microcosm-graph/tests
128 passed in 188.39s (0:03:08) the four new test files together
24 passed in 168.66s (0:02:48) test_us_native_verify_once_epoch.py
52 passed in 0.48s test_us_acs_record_fence_scan.py
34 passed in 0.27s test_graph_executor_series_stream.py
18 passed in 0.32s test_graph_executor_source_identity.py
1811 passed, 1 skipped, 38 warnings in 11200.54s (3:06:40) 46 build test files
All checks passed! ruff check .
17 files already formatted ruff format --check, changed .py only
Resolved 125 packages in 4ms uv lock --check
verification=ok ci_test_groups.py --verify
Ran 15 tests in 0.347s / OK the stdlib matrix check
The 46 files are every build test naming `survey_population_preparation`,
`graph_atomic_survey_financial`, `graph_atomic_survey_population` or
`verification_epoch`, or importing `microcosm.graph.executor` or
`microcosm.graph.manifest` by dotted path -- a subset of the 62-file battery.
The single skip is `test_uk_uc_capital_coherence.py:345`, "requires
policyengine-uk extra", confirmed by re-running that file alone with `-rs`.
Three counts moved and the section says why: the graph suite 766 -> 769 and
`test_graph_executor_source_identity` 15 -> 18 for the member-symlink follow,
and the epoch file 17 -> 24 for the two memo-miss tests, the four
refusal-code parametrisations and the real-run record test.
Open question 5 is re-counted while here: 35 build test files reach the graph
shard only through the package re-export, and `--collect-only` over them reports
`992 tests collected in 20.03s`. Collection is all that was run against them.
Nothing above the new section was re-measured, and the journal's third-session
state is marked historical rather than rewritten.
Co-Authored-By: Claude Opus <noreply@anthropic.com>
The whole-branch insertion count moves with every commit to this report, so quoting one number as a standing fact makes the row wrong as soon as the row is written -- which is how it was wrong before. The row now gives the three parts that do not move while only this file changes: `packages/*/src` 12 files, +1,683 / -818; `packages/*/tests` 4 files, +1,780; docs, journal, changelog fragment, `.gitignore` and `experiments/` 10 files, +2,184 / -1. The whole-branch total is kept, anchored to the head it was read at. Co-Authored-By: Claude Opus <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Sep 17, 2026
A country runtime that scopes source verification around a whole run yields a record of what that scope did -- memo hits, signature misses, final validations, capsule count. Nothing carried it out of the scope, so it was dropped the moment the run ended and nothing anywhere said how many full validations a real run performed. `run_graph` takes a private `_verification_epoch` mapping and attaches it to the manifest as `RunManifest.verification_epoch`. It is a live view of the caller's own mapping, kept rather than copied, because such a record is finalised when the scope closes -- which happens after `run_graph` returns and before the caller receives the manifest. `__post_init__` checks it is a mapping of string keys to integer or string values, so it carries counts and labels only: no key, digest or path. Like `source_identities` it is `repr=False`, `compare=False`, and outside the addressed surface: `content_addressed` projects `nodes` and `tier` alone, and `to_json` names its fields explicitly, so neither reaches it. No existing receipt field moves and no digest, key, seal or receipt value changes. The callers that bind such a record are the US native runners on #935's stack and are not on this branch; this is the graph-package half, which is the half that reaches CI here. Co-Authored-By: Claude Opus <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Sep 17, 2026
`_object_stream`'s float branch cast to an explicit `<f8` while the `_update_scalar` loop it reproduces packs `np.asarray([value], dtype=np.float64).tobytes()`, which is native order. The two agree on every little-endian host -- which is why the byte-parity tests pass here -- and disagree on any other, so the helper's "byte-for-byte identical output is the contract" would not hold on a big-endian build. The cast is now `np.float64`. The length prefixes stay explicitly little-endian on purpose: the loop writes them with `int.to_bytes(8, "little")`, so `_framed`'s `<u8` and the integer branch's `<u8` lengths are the correct match. The comment now says which is which. The rationale is stated against `main`'s `_update_scalar`. #935's base carries an exact-Python-float short circuit there (`struct.pack("=d", value)`) that `main` does not; both are native order, so the defect and the fix are the same on either base, and only the code this comment cites differs. No emitted byte changes on a little-endian host, so no digest, key, seal or receipt value moves here. $ uv run --no-sync pytest \ packages/microcosm-graph/tests/test_graph_executor_series_stream.py \ -p no:cacheprovider 34 passed in 0.65s Co-Authored-By: Claude Opus <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Sep 17, 2026
`test_a_rewritten_source_still_refuses_when_only_its_bytes_moved` read as though it exercised a stat-preserving rewrite. It does not: it restores st_mtime_ns and holds the length, but st_ctime_ns still moves, and st_ctime_ns is one of the five fields `_stat_identity` binds -- so the signature moves and the node refuses on the ordinary path. That is the strongest rewrite an unprivileged process can perform here. The genuinely stat-preserving case, all five fields identical, is the residual the run-end re-derivation exists for. The docstring now says which of the two it is and points at `test_a_source_changed_during_a_source_free_node_refuses_at_run_end`, which reaches that re-derivation by the other route. Docstring only: no assertion, no behaviour and no emitted byte changes. The rest of #935's docstring pass corrects the design note, the report and the epoch finalizer's nested-refusal chaining, none of which are on this branch. Co-Authored-By: Claude Opus <noreply@anthropic.com>
…probe
`RunManifest.verification_epoch` is bound and passed by both native runners,
but only the nine-node population runner had a test that read the record back:
`test_a_real_run_records_its_epoch_in_the_manifest`. Deleting
`_verification_epoch=verification` from `run_atomic_survey_financial` failed
nothing -- the runner this record was asked for, and the one the 19-node
measurement uses.
`test_nineteen_node_financial_cold_and_required_replay` now reads the record
off both manifests that runner returns: its own epoch's on the outer manifest,
and the nested nine-node population epoch's on the prefix's. For the cold run
and the required replay alike it asserts the epoch's protocol label, that every
capsule either epoch memoised was re-validated in full as it closed
(`final_validations == capsules >= 1`), and that `to_json` still cannot see any
of it. The module-scoped fixture already holds both runs, so this costs no
additional run. It asserts presence and closure, not counts: literal hit and
miss numbers there would bind an unrelated test's call pattern, and the counts
are the nine-node test's.
No measured run carries the record either, because the committed probe never
read it. `experiments/native-verify-once/probe_verify_once.py` now copies the
returned manifest's record into its output JSON and its console summary as
`verification_epoch` -- `null` until the runner returns, so every mid-run flush
and every ceiling exit carries `null`, and the before tree has no such record
at all. The run values are dropped again immediately so peak RSS stays
comparable with the runs that predate the line. The three measurement files
`out.md` quotes were written before it existed and none of them carries the
field; the uncommitted `.measure/` scripts are left exactly as they ran, and
the report, the README and the design note all say so. Nothing was re-measured.
No digest, key, seal or receipt value moves: the record is outside the manifest
key, outside `to_json`, and outside every node receipt and cache record, and
the probe only reads it.
$ uv run --no-sync pytest \
packages/microcosm-build/tests/test_us_graph_atomic_survey_financial.py \
-p no:cacheprovider
7 passed in 119.98s (0:01:59)
$ uv run --no-sync pytest \
packages/microcosm-build/tests/test_us_native_verify_once_epoch.py \
-p no:cacheprovider
24 passed in 157.61s (0:02:37)
Co-Authored-By: Claude Opus <noreply@anthropic.com>
Open question 4 and this journal's "Next" were written at 06:40 UTC on 2026-09-17 and overtaken eight minutes later. Both said #938 was at head `8ea48447c`, 6 files +988/-8, and that four post-findings graph-shard changes were "not in #938 yet, and mirroring them is the open item". `gh pr view 938` at 07:03 UTC reports head `1884d7f2a3af96799457dd42021cd111220e1ffc`, 6 files +1,124/-8, draft, MERGEABLE, base `main`, with four commits dated 06:45-06:46 UTC that carry exactly those changes: the verification epoch on the manifest with the `_verification_epoch` parameter on `run_graph` (`6206b4a75`), the member-symlink follow in `_source_stat_signature` (`d87196ce9`), the native-order float cast in `_object_stream` (`284252e56`) and the stat-preserving-rewrite docstring fix (`1884d7f2a`). Diffed against this branch at 07:00 UTC: `manifest.py`, `codecs.py` and `test_graph_executor_source_identity.py` are identical, and `executor.py` and `test_graph_executor_series_stream.py` differ only by the documented `import struct` and exact-`float` short-circuit hunks, which belong to #893's base. So the split carries the whole graph shard as this branch has it, and the open item handed to Max is closed. Both documents now say that, with the dates and the shas, and the opening figures are kept as the history they are. Documentation only: no code, no test and no measured number changed. Co-Authored-By: Claude Opus <noreply@anthropic.com>
The second fix pass added 19 lines to `packages/microcosm-build/tests/test_us_graph_atomic_survey_financial.py`, an existing file this branch had not touched, so the PR table's parts moved: the tests part is 5 files / +1,799 rather than 4 / +1,780, the file count is 27 rather than 26, and the docs-and-experiments part rises with each edit to the report itself. Each part is now dated to the sha it was measured at (`db7a3a0ba`), the `packages/*/src` part is marked as the one no edit to this report can move, and the 06:40 figures are kept beside them as what they were, with the one line that explains the difference. `python3 tools/ci_test_groups.py --verify` is `verification=ok`: the pass added assertions to an existing file and no new test file, so the partition is untouched and "all four new test files" is still exactly right. Co-Authored-By: Claude Opus <noreply@anthropic.com>
`_finalize_epoch` validated a memoised capsule and only then recorded the signature that answers the next borrows. At an inner nested close that memo survives into the outer epoch -- `_MEMO` is cleared only at the outermost close -- so a roster file whose stat moved after `_validate`'s own `SOURCE_STAT_CHANGED` comparison and before the recording was absorbed as the new normal, and every outer borrow up to the outermost close was a memo hit. The refusal this capsule owes at the borrow arrived at the end of the run instead, after intervening nodes had written store records. Before the 2026-09-16 fix the cheap tier's own `_file_stats` comparison anchored that borrow; moving the roster stats into the signature removed the anchor without replacing it, so this is a refusal that moved later because of a fix pass. The signature is now taken before validating and again after, and when the two differ no memo answer is recorded at all: the next borrow is a miss and pays the complete validation, which raises the code it raises today. The entry itself stays, because the outermost close re-validates every capsule the memo still holds and dropping it here would drop that pass for a capsule nothing borrows again. Nothing is compared per borrow, so the per-borrow `_file_stats` walk the fix pass removed does not come back. `asec_2024_native_population._epoch_exit` had the same ordering, present since the original head and never anchored by a cheap-tier stat check, and is fixed the same way. Both are pinned by a test that reproduces the shape: an outer epoch, an inner epoch, and a profile hook that moves a source as the inner close's own validation returns -- the only way in, since rebinding a runtime callable refuses with PRODUCER_CHANGED. Against the previous ordering both tests fail with DID NOT RAISE; against this one the next borrow refuses with `SOURCE_STAT_CHANGED` (preparation) and `SOURCE_FILE_CHANGED` (native). `_memoized_validate`'s signature handler also now catches `Exception` rather than `BaseException`, which is what its own comment describes: an interrupt or a process exit landing in `_memo_signature` is not a memo miss and must not buy a complete re-validation -- minutes of SHA-256 over real sources -- before it propagates. Co-Authored-By: Claude Opus <noreply@anthropic.com>
…hing reached `RunManifest.verification_epoch` and `run_graph(_verification_epoch=...)` had no test anywhere in `packages/microcosm-graph/tests`, on this branch or on #938's head, which is the PR that ships them to `main`. `source_identities` beside them has `test_the_manifest_records_the_run_end_identities_without_moving_anything`; the new field had only a `"verification_epoch" not in manifest.to_json()` line in the build shard, which #938 does not carry. `test_graph_verification_epoch.py` asserts, on a synthetic toy graph: that a record moves no node key, no manifest key, no `to_json` and no content-addressed body, compared both against a bare manifest built from the same fields and against a separate cold run with no record at all; that no record is an empty mapping rather than a missing attribute; that the manifest holds the caller's own mapping as a live view and not a copy -- the property the design leans on, since the record is finalised when the caller's scope closes, after `run_graph` has returned -- while staying read-only through the manifest; that a mapping proxy handed in is kept as given rather than wrapped twice; and that each of the three `__post_init__` refusals fires. In the build shard, two branches of the memo had no test either. A roster file removed mid-epoch is the case where no signature can be taken at all, so `_memoized_validate`'s handler runs the complete validation before re-raising: the refusal a caller sees is asserted equal to the one an unmemoised borrow raises for the same removal, on its own fixture. And an epoch whose nested capsule and whose own close both refuse is asserted to raise this owner's error class with the nested one attached as its cause, which is the `raise own from nested` branch. Co-Authored-By: Claude Opus <noreply@anthropic.com>
… old one `cba9d6c3a`'s tests and comments were written while the cheap tier still compared `_file_stats` ahead of the memo lookup. `e36f975d9` moved the roster stats into the signature and removed that comparison, and the prose around it was not revisited, so five places in `test_us_native_verify_once_epoch.py` -- the module docstring and four test docstrings and comments -- and the block comment at `survey_population_preparation.py:1005` still called the roster stats part of the cheap tier, contradicting the `_cheap_checks` docstring twenty-five lines below. None of it affected behaviour; all of it now says what the code does: the cheap tier refuses on live authority, attached owner payloads and the producer encoding, and the roster stats are read into the signature. The design note and the report gain the third pass's own rule -- a close records a signature only when nothing moved while it validated -- in the epoch section, in mechanism 1, in mechanism 2 and in both mutation-test lists, with the two new tests named. The report also: records that no pin moves in this pass and how that was re-derived; replaces "the one new recorded thing" with both recorded things, now that `verification_epoch` has a graph-shard test; corrects the `_stat_identity` citation from `executor.py:2100` to `:2105`; and restates #938 at head `33f3150bb` (7 files, +1,430/-8, draft, MERGEABLE, `gh pr view 938` 2026-09-17 07:43 UTC) with the third pass's mirror commit. Open question 5 now records that the 35 build test files reaching the graph shard through the package re-export (992 tests collected) were not run on this stacked branch in this pass either, and that for the graph-shard changes specifically they are covered by #938's CI on `main` -- with the actual bucket counts at two heads and no green verdict claimed, since that matrix is still running. One line of the committed probe changed and was not run: it read `manifest.verification_epoch` unguarded, which would label an otherwise complete before-tree run `STOPPED_AttributeError`. It now reads with a `getattr(..., None)` default. No quoted figure is affected. Co-Authored-By: Claude Opus <noreply@anthropic.com>
The third pass added one file the PR table had not counted -- `packages/microcosm-graph/tests/test_graph_verification_epoch.py` -- so the row that said 27 files at `db7a3a0ba` is 28 at `3b63b8504`, and every part moved with it: `packages/*/src` +1,750/-818 (the two epoch closes and their comments), `packages/*/tests` 6 files +2,304, docs and `experiments/` 10 files +2,497/-1, whole branch +6,551/-819. Re-derived against the merge-base `363a9033b`, where two-dot and three-dot agree. Both earlier readings are kept beside it as what they were at their own heads, with the one line that explains each file-count step. Co-Authored-By: Claude Opus <noreply@anthropic.com>
A close that cannot take its post-validation signature records no memo answer, and the borrow that follows pays the complete validation and raises the refusal. The outermost close has no borrow after it -- the memo is cleared as it returns -- so a roster file removed after that close's own validation had returned ended the run clean. Before the close took its signature through a guarded helper the same removal escaped as a raw FileNotFoundError and failed the run, which the guard swallowed. The outermost close now pays that deferred validation itself, inside the same translation block, whenever its signature moved or could not be taken. The refusal is the one that validation raises -- SOURCE_ROSTER for a removed roster file, the nested capsule's NATIVE_BINDING_REFUSAL for a removed source -- never a code of the close's own, and a clean signature costs nothing extra. asec_2024_native_population._epoch_exit had the same shape and is fixed the same way; it reaches the window by the other route, because _path_stat never raises, so a removed source moves its signature rather than making it unavailable. Both are pinned by a test per capsule that removes the file from a profile hook at the return of the outermost close's own validation and asserts the close raises exactly what an unmemoised borrow raises for the same removal. Against the swallowing behaviour both fail with DID NOT RAISE. Co-Authored-By: Claude Opus <noreply@anthropic.com>
…he old one The adoption note in the boundary ledger still listed `_file_stats` over the whole source roster inside the cheap tier each borrow pays, which is the behaviour from before the roster stats moved into the signature. It was the one sentence the previous sweep did not reach, because it lives in a file that pass did not otherwise touch. The stats are read on every borrow, into the signature, and what refuses is the complete validation a moved signature runs. The same sentence now also says what a close does when its own signature moved, including at the outermost one. Nothing else on the branch still describes the cheap tier as comparing roster or file stats: the design note's Tier B and its "comparison of those roster stat identities" are the complete validation, which does compare them, and the journal's line is historicized in place. Co-Authored-By: Claude Opus <noreply@anthropic.com>
The two-run test asserted the node keys, the manifest key and the content-addressed body, but not the JSON document, so a record that leaked into `to_json` was caught only by the bare-manifest test beside it. It could not assert the document outright: two runs differ in the three run-level manifest fields and in the two `NodeReceipt` already names as run-level, which is why the manifest key hashes receipts without them. It now compares the whole document with exactly those five removed, taken by name and from `RUN_LEVEL_FIELDS` rather than from a list written in the test, so a field that stopped being serialized raises instead of quietly widening what the comparison hides. With the record written into `to_json`, this test now fails on that assertion alone. Co-Authored-By: Claude Opus <noreply@anthropic.com> (cherry picked from commit ec2b7b4)
… sit The helper's note said all five removed fields are what the manifest's own docstring calls the ones a run may change without changing what was computed, and that this is why the key hashes each receipt without them. That is the argument for `hit` and `wall_time` only. `started_at`, `finished_at` and `host` are not receipt fields at all: they are absent from the content-addressed body the key hashes, which is `nodes` and `tier` alone. The note now gives the two routes separately -- outside `content_addressed` for the three, dropped by `_content_payload` for the two -- so a reader checking it against manifest.py finds each claim where the note says it is. No assertion changes. Co-Authored-By: Claude Opus <noreply@anthropic.com>
… describe it The design note and the report both stopped at the inner close: a close that cannot take its post-validation signature records no memo answer, and the borrow that follows pays the complete validation and refuses. Neither said what happens where no borrow follows. The outermost close is that place -- the memo is cleared as it returns -- and until the commit before this one a roster file removed after that close's own validation ended the run clean, where the head before the guard failed it with a raw FileNotFoundError. Both now say that the outermost close pays the deferred validation itself, in the same place and with the same error translation, that the refusal is the one validation raises rather than a code the close invents, that a clean signature costs nothing extra, and that the two new tests assert the close raises exactly what an unmemoised borrow raises for the same removal. The report also records the discrimination: against the swallowing behaviour restored in place in this worktree, both fail with DID NOT RAISE. Co-Authored-By: Claude Opus <noreply@anthropic.com>
The report said the graph-shard file asserts the same three identities -- the key, the JSON and the content-addressed body -- both against a bare manifest and against a separate cold run. Against the separate cold run it asserted the key and the body and not the JSON, and it could not have asserted the document outright: two cold runs differ in the three run-level manifest fields and in the two the receipt names as run-level. The preceding commit makes the sentence true rather than trimming it: the two-run test now compares the whole document with exactly those five removed. The report says which five, why each is outside the key -- the timestamps and the host are not in the content-addressed body at all, and `_content_payload` drops the other two from every receipt -- and that the test now asserts, rather than assumes, that two cold runs differ in nothing else. It also records the mutant: with the record written into `to_json`, this assertion is the one that fails, and the report quotes what it prints. Co-Authored-By: Claude Opus <noreply@anthropic.com>
The report's two mutation-test inventories, one per capsule, name every test that pins an in-epoch refusal, and the pass that closed the outermost-close window described its two tests in prose without adding them there. Both inventories now carry them. Each also says why these two assert the refusal once rather than twice. The others assert it at the borrow after the mutation and again at the close; a mutation made inside the outermost close has no borrow after it, so the test asserts instead that the close raises exactly what a later unmemoised borrow raises for the same removal -- which is the identity the twice-asserting tests get from asserting both. Co-Authored-By: Claude Opus <noreply@anthropic.com>
The per-node source check answers from a stat signature, so the one
rewrite that signature cannot see -- same length, in place, every field
identical -- is a cache hit. The node proceeds, `_write_node` persists
its columns, frame, weights and cache record under the source identity
the run started with, and only the run-end re-derivation refuses. The
ContentStore had no way to remove an object, so that record survived the
refusal and the next `resume="auto"` run over the same store hit it: the
run returned values the source on disk does not imply. The inline
comments said the refusal precedes every `_write_node`, which is true
only of the rewrites the signature does see.
`ContentStore.recording_writes()` opens a ledger that collects the key
of every object published while it is open -- publications only, so an
object an earlier run already satisfied is not this caller's to remove,
while a `verify_existing=False` replacement is. Blocks nest and close by
identity. `ContentStore.evict(key)` renames the object out of the tree
before deleting it, so a reader sees the whole object or none, returns
whether one was there, and evicting twice leaves the same store as
evicting once. `run_graph` holds one ledger around the run and spends it
on the run-end refusal: everything the run published is evicted before
the refusal propagates. The eviction never replaces the refusal -- an
object that will not go is named in the refusal text, and the run still
refuses.
Both comments now state the guarantee the two checks actually make
together: a rewrite that moves any stat field refuses before the node
that would have persisted work derived from it; a rewrite that moves
none of them is refused at run end, after persistence, and everything
the run persisted is taken back.
Four executor tests and nine store tests. The stat-identical rewrite is
modelled by dropping `st_ctime_ns` from `_stat_identity`, because no
unprivileged process can hold that field still here -- which is exactly
why `test_a_rewritten_source_still_refuses_when_only_its_bytes_moved`
refuses at the node instead. The other four fields do in the test what
they do anywhere: an in-place same-length rewrite leaves device, inode
and size alone and `os.utime(ns=)` puts the modification time back.
Against the pre-fix executor and store, the two tests that matter fail
on the defect itself rather than on a missing attribute:
packages/.../test_a_stat_identical_rewrite_refuses_at_run_end_...
E AssertionError: assert {'11/116c8180...ues.npy', ...} == set()
packages/.../test_the_record_a_stat_identical_rewrite_left_is_not_reused
E AssertionError: assert True is False
E + where True = NodeReceipt(key='92ade67d...', hit=True, ...).hit
No digest, key, seal or receipt value moves: eviction removes
content-addressed objects that are all re-derivable, and the refusal
path gains no `except` that could swallow it.
$ uv run --no-sync pytest packages/microcosm-graph/tests
610 passed, 2 skipped in 19.16s
Co-Authored-By: Claude Opus <noreply@anthropic.com>
`_object_stream`'s integer branch built its eight-byte length prefixes as
`filled.sum(axis=1).astype("<u8")` and then emitted `(lengths + 1).tobytes()`.
The cast is explicit but it is not what reaches `tobytes()`: `+ 1` is a ufunc,
and a ufunc's output carries the host's byte order whatever its operands
carried, so the array that is serialized is native-order again. On a
big-endian host this branch writes big-endian prefixes while the
`_update_scalar` loop it reproduces writes `int.to_bytes(8, "little")` and
`_framed` writes `<u8` -- three constructions, two orders. The comment above
the float branch already says the prefixes stay explicitly little-endian
because the loop writes them that way; the integer branch did not.
`lengths = (filled.sum(axis=1) + 1).astype("<u8")`, serialized directly. The
cast is now the last step, so it pins the order of the bytes that are actually
written.
Three cases pin it against a reference assembled in pure Python --
`len(payload).to_bytes(8, "little") + payload` -- for the integer, boolean and
float branches. Every parity assertion in this file compares one numpy
construction against another, including `_reference_update_series`, which is
itself a copy of the loop; a byte order both sides take from the host cancels
out of that comparison. A reference that takes its order from `int.to_bytes`
and its payloads from Python `bytes` cannot cancel that way.
What these cases do not do is discriminate here. This host is little-endian,
so `<u8` and native agree and the pre-fix code passes all 42 cases in this
file, the three new ones included -- which is the defect's whole shape. Forcing
the cast to `>u8` fails the new integer cases along with the existing parity
ones, so they are wired to the prefix bytes; on a big-endian host the pre-fix
code would fail them for the reason the fix addresses.
No emitted byte changes on a little-endian host, so no digest, key, seal or
receipt value moves here.
$ uv run --no-sync pytest packages/microcosm-graph/tests
618 passed, 2 skipped in 18.58s
Co-Authored-By: Claude Opus <noreply@anthropic.com>
`RunManifest.verification_epoch` is documented as a live view of the caller's own mapping, because the record is finalised when the caller's verification scope closes -- after `run_graph` has returned and before the caller reads the manifest. `__post_init__` honoured that for a `dict` and for a `MappingProxyType` and copied everything else through `dict(...)`. A caller whose record is its own `Mapping` type -- the natural shape for an object a scope keeps writing into -- therefore got a snapshot taken mid-run, reporting a final validation count of zero, with nothing to distinguish it from a field that was working: the same type, the same reads, silently stale. `MappingProxyType` takes any object with the mapping protocol, so every mapping is now wrapped rather than copied, and an already read-only proxy is still passed through untouched. A type registered as a `Mapping` without implementing that protocol cannot be held live; it is refused with a message that says why, rather than accepted as the copy the contract forbids. The class docstring says both. Two tests. A `Mapping` subclass backed by a dict is mutated after `run_graph` returns and the manifest follows it, exactly as the dict case beside it does, and stays read-only through the proxy. The unwrappable type is refused. Against the pre-fix constructor the first fails on the snapshot itself -- E assert 0 == 18 -- and the second on the opaque error a copy attempt raises instead: E TypeError: '_NotSubscriptable' object is not iterable Values are still checked once at construction, which is what a live view can check; no key, digest, JSON field or receipt is touched, and the field remains outside `content_addressed`, `key` and `to_json`. $ uv run --no-sync pytest packages/microcosm-graph/tests 620 passed, 2 skipped in 17.97s Co-Authored-By: Claude Opus <noreply@anthropic.com>
The run-end block justified being inlined partly by "a build test counts calls to that exact code object", naming no test. Nothing at this head references `_source_paths_and_keys` outside executor.py, so a reader checking that claim here finds nothing and has no way to tell whether the constraint is real or left over. The test is real and lives on the stacked branch `native-verify-once`: `test_authenticated_cold_and_materialized_warm_clones_both_sources` in `packages/microcosm-build/tests/test_us_graph_survey_population.py` registers a `sys.setprofile` hook over a table of code-object ids that includes `_source_paths_and_keys.__code__` under the label `source_key`, and times one source-key pass per call/return pair. It profiles rather than asserts a count, which the old wording also got wrong. The comment now gives the file, what the hook does, and a grep that finds it, and says plainly that nothing at this head references the helper outside this module. It also leads with the reason that stands on its own here -- the helper answers from the per-run cache this pass exists to bypass, and re-resolves paths and re-checks codec availability that this pass has no reason to repeat -- with the stacked shard's expectation second. Comment only: no behaviour, no assertion and no emitted byte changes. $ uv run --no-sync pytest packages/microcosm-graph/tests 620 passed, 2 skipped in 19.09s Co-Authored-By: Claude Opus <noreply@anthropic.com>
The mirrored comment arrived from the graph-only branch, where no test references `_source_paths_and_keys` and the note therefore sends the reader to the branch stacked on it. Here that branch is this one: the profiling test is in this tree, and so are two more build tests that call the helper directly. Left as it was, the comment would send a reader looking elsewhere for something at their feet -- the same defect on this branch that naming the test fixed on the other. It now names `test_authenticated_cold_and_materialized_warm_clones_both_sources` in `packages/microcosm-build/tests/test_us_graph_survey_population.py`, says what its `sys.setprofile` hook does, notes the two direct callers, and records that the graph-only branch has none of them. Everything else in the four mirrored commits is verbatim. The changelog fragment also differs by necessity: this branch carries `changelog.d/native-verify-once.changed.md`, not the graph-only branch's fragment, so the eviction sentence was added to this branch's own fragment rather than a second file being introduced. Comment only: no behaviour, no assertion and no emitted byte changes. $ uv run --no-sync pytest packages/microcosm-graph/tests 801 passed, 1 skipped in 47.22s Co-Authored-By: Claude Opus <noreply@anthropic.com>
The run-end re-derivation is the only check that sees a rewrite leaving every stat field identical, and a run that raises never reaches it: a later kernel failure, a per-node refusal, a store error or an interrupt unwound through the write ledger with the poisoned columns, frame, weights and cache record still in the store under the pre-rewrite identity, where the next resume="auto" run would hit them. run_graph now runs the same cache-bypassing re-derivation on the way out of any exception. Sources that still match leave the store alone, so a failed or interrupted run stays resumable. A source that moved, a re-derivation that cannot finish, or a failure before the run had source keys evicts everything the run wrote. The original error is never replaced; what happened is added to it as a note. The run-end pass marks the run settled either way, so its own refusal is not re-derived twice. What no in-process check covers is a process that stops without unwinding after such a rewrite. That residual is now stated on run_graph, at the per-node check, at the run-end pass and in the changelog fragment. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ve-verify-once (0d04abf) The integration branch now carries main through 8c44daa, which includes PR #938. This branch already mirrored #938 commit for commit, so the graph package conflicts were wording only: two comment hunks in executor.py, where this branch's text names the US build tests that exist here and the scalar float packing this tree really uses, and the series-stream test's reference body, which keeps the struct branch that matches it. Both sides were compared file against file first: no non-comment line differs. PROGRESS.md keeps both journals. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
Author
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.
Native source authentication and identity verification ran once per accessor
use and once per executed node. On the 19-node financial graph at
Fraction(1, 1000)— 3,168 expanded households, 5,362 s wall — node executionwas 1,143 s and the other ~79 % was admission and verification of the 3.48 GiB
of staged source, repeated (
~/PolicyEngine/_recovered/pilot-runs/native45-v5/out.md§2).This makes each of those checks happen once per run, without weakening a
refusal and without moving a digest.
docs/us-native-verification-once.mdis the design note — per mechanism: the guarantee today, the change, why the
guarantee survives, and the new cost class. It was written and committed before
any capsule changed. The lane report, with every measurement and every
re-derived pin, is
experiments/native-verify-once/out.md.Measurement (2026-09-16, real 1/1000 sample, same arguments as the accepted v4 cold pilot)
Files:
.measure/repeated-verification-before.json,.measure/after/probe/repeated-verification-measurement.json,.measure/after/harness19/repeated-verification-measurement.jsonin the branch worktree (measurement artifacts, not committed). Peak RSS rose because memoised authentication keeps validated state resident; the remaining CPU sits in the ACS archive streaming (zipfileread/peek/readline, ~380 s), ASEC coverage_capture(~175 s),_series_digest/_frame_identity(~74 s) and the per-node_population_stamp(~61 s), which are the next lane's targets (whole-roster receipt transport and per-node population retention).The contract
in full, exactly as today.
unconditionally, before the run hands anything back.
validation runs and raises exactly what it would have raised.
verification_epoch()nothing is memoised,which is why every existing capsule test — including the two that trace
_producerand_file_identityfiring inside a validation — keeps itsexact meaning, and why
_validateand_validate_stateare untouched.No digest, key, seal or receipt value moves.
_update_seriesis the one placebytes are genuinely rebuilt, and it is proven byte-identical against a verbatim
copy of the pre-change body.
The five mechanisms
AuthenticatedSurveyPopulationPreparation._checked()re-ran the whole authentication on every borrow — and the graph registers it as a per-node population obserververification_epoch(); cheap tier still every borrowAuthenticatedAsec2024NativePopulation.framere-hashed all seven ASEC source files on every accessexecutor.pyre-derived every declared source's content key after every cold node — a 3.48 GiB tree re-read per node_SourceIdentitiescache keyed on stat signatures, plus a full cache-bypassed re-derivation before the manifest is builtacs_person_coverage_authentication._recordswas a byte-at-a-time loop over 2.4 GB of ACS person CSVbytes.find/bytes.countwith cached terminator cursors — 226.9 s → 4.6 s on the real archive, digests identicalexecutor._update_seriesboxed every value of a plain float/int/bool columnWhat gained coverage
test_graph_executor_source_identitypins a refusal that had no test anywherein the repository before this branch: a source mutated while a node runs must
make the run refuse before that node's artifacts reach the content store.
Mechanism 3's cache is measured against that pinned contract rather than against
silence.
Main-only hunks
These graph-shard changes depend on nothing in this branch and can be split into
a PR against
mainas they stand:packages/microcosm-graph/src/microcosm/graph/executor.pypackages/microcosm-graph/src/microcosm/graph/manifest.pypackages/microcosm-graph/src/microcosm/graph/codecs.py(a docstring thatdescribed the per-node check as "post-run")
packages/microcosm-graph/tests/test_graph_executor_series_stream.pypackages/microcosm-graph/tests/test_graph_executor_source_identity.pyEverything else is
microcosm-buildand belongs on this stack.Reviewing the two big diffs
graph_atomic_survey_population.pyandgraph_atomic_survey_financial.pyarean indentation plus one
withline. Proven mechanically in the commitmessage: the statement lists of both function bodies parse to identical ASTs
before and after (50 and 102 statements), argument lists unchanged, docstrings
outside the wrapper, and both files were
ruff formatclean at the base soevery hunk lies inside the wrapped function. The body has to stay in its own
function because a build test asserts
caller.f_code is runner.run_atomic_survey_population.__code__.Pins re-derived
acs_native_coverage_binding._ACCEPTED["acs_person_coverage_authentication.py"]475aa795…fe85bcff→9ec68721…d88e8e49fsurvey_population_preparation.pyunbound_uses_sha25629c09f6f…d296ef91→d114117c…dd4005910asec_2024_native_population.pyunbound_uses_sha25671463df4…d7f287608→7bb20439…40d58cdf4Every other contract in
graph_implementation_inventory.jsonwas recomputedthrough the module's own generator and is unchanged; all ten stage manifests
build.
Before/after native source-verification cost
All numbers below were read from the files named in the "file" column; nothing was re-run and no source file was touched (worktree
/Users/maxghenis/PolicyEngine/_worktrees/microcosm-verify-onceclean at284bc6e9624d211ba9c16dcdf04df63b9e87b10c).1. Run-level numbers, as recorded
f7bb88525, source tree.measure/source-before)CEILING_REACHED_PARTIAL_MEASUREMENT284bc6e96, source tree.measure/source-after)COMPLETED_PREFIX284bc6e96)COMPLETED_NINETEEN_NODEresult: 0, one testpassedrun_atomic_survey_financialcall alone (timing.case_phases)Files. A
/Users/maxghenis/PolicyEngine/_worktrees/microcosm-verify-once/.measure/repeated-verification-before.json· B/Users/maxghenis/PolicyEngine/_worktrees/microcosm-verify-once/.measure/after/probe/repeated-verification-measurement.json· C/Users/maxghenis/PolicyEngine/_worktrees/microcosm-verify-once/.measure/after/harness19/repeated-verification-measurement.json· D/Users/maxghenis/PolicyEngine/_recovered/pilot-runs/native19-v2/run/us-native-postclone-financial19-v4-cold.json· E/Users/maxghenis/PolicyEngine/_recovered/pilot-runs/native19-v2/run/native-financial-manifest.json(timing.case_phases:before_financial_runnerwall 0.00085 s / CPU 0.00084 s →after_financial_runnerwall 4,685.862 s / CPU 4,605.351 s).Every figure quoted in the task statement is confirmed by the files; the task rounded, the files carry more digits (see caveats for the one genuine discrepancy — a second, differently-numbered before-probe JSON).
2. The 9-node probe: what can and cannot be compared
The before probe is partial, so this is not a measured ratio.
.measure/probe-before/graph-storedoes not exist.measure/after/probe/graph-store/objectsTop chains by CPU,
sampled_cpu_ledger(0.25 s stack sampler; chains print innermost → outermost; the ledger lists the top 60 chains only)._read1 <- read <- acs_person_coverage_authentication.py:_records <- _inventory_read1 <- read <- peek <- readline_read1 <- read <- peek <- readlinestring_arrow.py:isin <- algorithms.py:isin <- series.py:isin <- acs_pums.py:_read_archivestring_arrow.py:isin <- … <- acs_pums.py:_read_archive_read1 <- read1 <- acs_person_coverage_columns.py:lines <- _literal_csv_recordsasec_coverage_authentication.py:_capture <- _reconstruct <- authenticate_asec_coverage <- asec_population_catalogue.py:issue_asec_source_catalogueasec…_capture <- … <- asec_2024_native_population.py:load_authenticated_asec_2024_native_population_read1 <- read1 <- acs_person_coverage_columns.py:lines <- _literal_csv_recordsasec…_capture <- … <- asec_population_catalogue.py:issue_asec_source_cataloguecanonical.py:_json_value ×3 <- canonical_jsonread <- _read2 <- _read1 <- readread <- _read2 <- _read1 <- readasec_coverage_authentication.py:_require <- feed <- _capture <- _reconstructencoder.py:_iterencode <- acs_person_coverage_authentication.py:_json <- acs_population_catalogue.py:reserve <- consume_rowasec_current_money_source.py:_series_digest <- _frame_signature <- asec_2024_native_population.py:_frame_identity <- survey_population_preparation.py:_nested_sealsasec…_require <- feed <- _capture <- _reconstructcanonical.py:_json_value ×3 <- canonical_jsonc_parser_wrapper.py:read <- readers.py:read <- get_chunk <- __next__encoder.py:_iterencode <- … <- acs_population_catalogue.py:reserve <- consume_rowThe one chain-level movement the partial run still supports: the ACS record fence, rank 1 before at 760.23 s and still running when the ceiling hit, is 10.80 s in the after probe (mechanism 4). Chains that rank higher in the after than the before (the two
asec…_capturecallers,_series_digest) must not be read as regressions: the before never reached that work.3. The 19-node harness: like-for-like at identical arguments
.measure/harness19_verify_once.pyis.measure/probe_verify_once.pywith only the docstring, the ceilings (7,200 CPU / 9,000 wall / 32 GiB), one import, the scope and status strings, and the call changed; the call isrun_atomic_survey_financial(RUN/"sources", snapshot_root=…, store_root=…, fraction=Fraction(1,1000), seed=20260908, geography_config=geography(seed 20260908), demographic_conditioning=True, n_estimators=2, resume="auto", return_values=True)— the same arguments as the v4 test's own call (/Users/maxghenis/PolicyEngine/_recovered/pilot-runs/native19-v2/run/test_native_nineteen_node_financial.py:130-137), cold store.The second row is the conservative framing: the after number is a whole-process total (imports included) while E excludes everything outside the runner call. The v4 process spent 676.2 s wall / 673.3 s CPU outside the runner (imports and collection 10.4 s, artifact export and readback 210.8 s wall, pending report and final checks 465.4 s wall, guard postcheck 1.8 s), none of which the harness runs.
Corroboration that the after harness did the same amount of work: 5,869 object files under
.measure/after/harness19/graph-store/objectsagainst 5,866 under the v4 run'sgraph-store/objects(comparable volume, not identical keys — every node key moves because each stage'simplementation_hashcovers the module roster).4. Top 10 remaining chains, 19-node after harness (file C)
Process CPU 2,010.07 s; these ten are 864.1 s = 43.0% of it; the 60 listed chains sum to 1,464.8 s of the 2,007.83 s sampled total, so ~543 s sits in chains below the ledger cut.
zipfile/__init__.py:_read1 <- read <- peek <- readlinestring_arrow.py:isin <- algorithms.py:isin <- series.py:isin <- acs_pums.py:_read_archivezipfile/__init__.py:_read1 <- read1 <- acs_person_coverage_columns.py:lines <- _literal_csv_recordsasec_coverage_authentication.py:_capture <- _reconstruct <- authenticate_asec_coverage <- asec_population_catalogue.py:issue_asec_source_catalogueasec_coverage_authentication.py:_capture <- _reconstruct <- authenticate_asec_coverage <- asec_2024_native_population.py:load_authenticated_asec_2024_native_populationasec_current_money_source.py:_series_digest <- _frame_signature <- asec_2024_native_population.py:_frame_identity <- survey_population_preparation.py:_nested_sealszipfile/__init__.py:read <- _read2 <- _read1 <- readsurvey_population_preparation.py:update_cell <- _frame_identity <- survey_atomic_geography.py:_population_stamp <- <genexpr>acs_housing_universe_source.py:_persisted_sha <- acs_native_coverage_binding.py:_verify_sources <- _verify <- verify_acs_native_coveragec_parser_wrapper.py:read <- readers.py:read <- _read <- read_csvThis reproduces the PR body's remaining-cost list exactly: ACS archive streaming (rows 1+3+7 = 379.7 s, "~380 s"), ASEC
_capture(rows 4+5 = 174.5 s, "~175 s"),_series_digest/_frame_identity(row 6 = 73.7 s, "~74 s"), per-node_population_stamp(row 8 = 60.8 s, "~61 s").DISCREPANCY (the only one): a SECOND before-probe result exists on disk with different numbers, not mentioned in the task. /Users/maxghenis/PolicyEngine/_worktrees/microcosm-verify-once/.measure/before/probe/repeated-verification-measurement.json, written 2026-09-16 01:34:11, records verbatim: "measurement": "before f7bb885", "status": "CEILING_REACHED_PARTIAL_MEASUREMENT", "source_tree": "/Users/maxghenis/PolicyEngine/_worktrees/microcosm-verify-once-baseline", process {"cpu_seconds": 1801.100405, "wall_seconds": 1821.7746500000358, "peak_rss_bytes": 8304001024}, "sampled_ledger_total_seconds": 1799.2456869999999, and its rank-1 chain is the same ['init.py:_read1','init.py:read','acs_person_coverage_authentication.py:_records','acs_person_coverage_authentication.py:_inventory'] at 777.4474699999996 s. So the before probe was run twice, on two different copies of the same baseline code (the second worktree is at f7bb885, verified by git rev-parse). The two agree on the finding (neither completed; the record fence dominates) and differ by 0.15% in CPU, which is expected because BOTH were stopped by the 1,800 CPU-s ceiling — the CPU figure of a ceilinged run measures the cap, not the work. The task named the earlier file (.measure/repeated-verification-before.json, written 00:47:24), which is the one I report. Its machine context is also weaker: it carries no "machine" block at all, while the second before run recorded loadavg_at_start [54.72, 66.75, 68.97]. Note also that the two before runs were produced by different scripts: the task-named one by .measure/probe_repeated_verification.py (the v5 pilot copy, LABEL "before" -> BASE .measure/source-before, PROBE .measure/probe-before), the second by .measure/probe_verify_once.py, which is also the script both after runs used. Both scripts call run_atomic_survey_population with the same nine-node arguments and the same 0.25 s sampler.
NO OTHER DISCREPANCY. Every figure the task stated is in the files: before 1,803.8695539999999 CPU-s and 8,612,429,824 B ("1,803.9 CPU-s, 8.6 GB"); after probe 1,444.775832 / 1,451.761529374984 / 12,285,526,016 ("1,445 CPU-s / 1,452 s wall / 12.3 GB"); harness19 2,016.0785499999765 / 2,010.072336 / 13,309,181,952 ("2,016 s wall / 2,010 s CPU / 13.31 GB"); v4 receipt 5,362.050427666982 / 5,278.6107919999995 / 9,313,337,344 ("5,362 s wall / 5,279 s CPU / 9.31 GB"). The before probe's wall time (1,853.894 s, under its 2,400 s wall ceiling) was not in the task statement; it confirms the run was stopped by the CPU ceiling, not the wall ceiling. RSS units are decimal GB throughout, matching the task and the PR body; the v4 peak is 8.67 GiB in the harness docstring and 9.31 GB here — same 9,313,337,344 bytes.
WHAT THE BEFORE/AFTER PROBE PAIR CANNOT SUPPORT. The before run is partial, so there is no before/after ratio for the nine-node prefix. The defensible statements are (a) completion inside the same 1,800 CPU-s cap: before no, after yes with 355.2 CPU-s spare; (b) a lower bound of >=1.25x on the CPU reduction (1,803.87 / 1,444.78), with the true ratio unknown and certainly larger; (c) per-chain absolute CPU at the moment each run ended. Share-of-process percentages on the before side are shares of a truncated run and are not comparable with the after's shares. The before peak RSS (8.61 GB) is a floor, so "peak RSS rose 1.43x" on the probe is an upper bound on the rise, not a measurement of it; the harness/v4 RSS comparison (9.31 -> 13.31 GB, 1.43x) is the one that is like-for-like, and it is a real regression by design (memoised authentication keeps validated state resident), well inside that run's 32 GiB ceiling.
SAMPLER, NOT COUNTERS. In all three JSONs every one of the 16 rows in "checks" reads calls=0, cpu_seconds=0.0, status "not-wrapped: runtime refuses instrumented producers (PRODUCER_CHANGED); see sampled_cpu_ledger". All attribution therefore comes from the 0.25 s stack sampler, is statistical, and truncates each chain to four frames and each ledger to the top 60 chains (harness19 cut at 5.00 s; after probe at 2.83 s; before at 2.11 s). A chain "absent" from a list only means it fell below that cut.
INFERENCE, FLAGGED AS SUCH. Neither before-probe directory (.measure/probe-before, .measure/before/probe) contains a graph-store, while both probe scripts pass store_root=PROBE/"graph-store", and no ledger chain in either before file names executor.py. The file evidence is that no store was ever written; the reading that the before runs exhausted the cap inside source admission before any node artifact was produced is an inference from that plus the chain contents, not something the receipts state.
HARNESS-VS-V4 DIFFERENCES THAT ARE NOT THIS CHANGE (from .measure/harness19_verify_once.py lines 12-17, verbatim): "it ran on source snapshot
2ca11c85a, and itsperson-income-attachment.h5was the pre-restoration5996dcdd...rather than the staged9ebc0ef2.... Both files are exactly 666,333,922 B". I confirmed the v4 receipt's native_before/native_after both carry 5996dcdd... at 666,333,922 B and that the staged file at /Users/maxghenis/PolicyEngine/_recovered/pilot-runs/native45-v5/run/sources/asec/person-income-attachment.h5 is 666,333,922 B; I did not hash the staged file, so the 9ebc0ef2 value is the lane's claim, not something I verified. Further: the v4 receipt is a pytest process (5,362 s covers imports, collection, the runner, artifact export and readback, the pending report and final checks, and the guard postcheck) while the harness is a bare script that ends when the runner returns — hence the two framings in the table. Load context differs and runs against the after: v4's receipt records no loadavg; the after harness started at loadavg [7.34, 7.54, 8.09] and ended at [7.83, 9.33, 11.38], i.e. it shared the machine with other lanes.PROVENANCE I VERIFIED. .measure/source-before/head.txt = f7bb885 and its acs_person_coverage_authentication.py hashes to 475aa795c8a5b49a0dd3405a0877866dddd03012a1f2b9743447fab1fe85bcff (the OLD _ACCEPTED pin); .measure/source-after/head.txt = 284bc6e and the same module hashes to 9ec68721a4cf480ef412c51ab354db9000eb7a7989e35e6e09b1574d88e8e49f (the NEW pin recorded in the lane report and PR body). So the before run measured unchanged code and the after runs measured the branch. Both after JSONs record source_tree .measure/source-after and staged_run_inputs /Users/maxghenis/PolicyEngine/_recovered/pilot-runs/native45-v5/run; the task-named before JSON records no source_tree field (older script), but .measure/probe_repeated_verification.py derives BASE = ROOT/f"source-{LABEL}" and OUT = ROOT/f"repeated-verification-{LABEL}.json", so the file name fixes LABEL="before" and hence BASE=.measure/source-before.
SCOPE. These are descriptive measurements. Every JSON carries "release_eligible": false and a scope line ending "not a build, not a certification, not release eligible". Nothing here says anything about dataset quality, calibration or release eligibility. I re-ran nothing, edited no source file, and the worktree is clean at 284bc6e. I did not verify the contract claims themselves (no digest/key/seal/receipt moves; fail-closed stays fail-closed; a mutation test per memoised path) — the design note, lane report and PR body assert them and name the tests, and the lane report's "Test summaries, verbatim" section is incomplete as the task said, ending "(Remaining runs and the probe measurement follow.)"; confirming them would need the test suites re-run, which this task forbids.
Tests
Main-only subset
PR: #938 — draft, base
main, headgraph-verify-once-main, 6 files, +988/−8. Not marked ready, not merged. CI is running (13 checks pending at time of writing, run 35133268135) — which the stacked PR #935 never gets.Head sha:
8ea48447cf3245a8ac4acc0242cc9178d50acb13Worktree:
/Users/maxghenis/PolicyEngine/_worktrees/microcosm-verify-once-main(created fromorigin/main=d69a543e5d7f0ac01e73c9c6eb4645b0d4b8b9b6)Verbatim test summary lines (all run in that worktree,
.venvPython 3.14.4):536 + 49 = 585: every pre-existing graph test passes unchanged and all 49 new tests are additions. The count differs from the stacked branch's 766 because
maindoes not carrytest_graph_executor_scalar_float.pyortest_graph_storage_parts_whole_series.py. Both new files land infastandengine, neither under[defaulted].tracked_test_files=400here (main's inventory), vs 551 on the stack. Notepackages/microcosm-build/tests/test_ci_test_groups.pydoes not exist onmain, so the stdlib matrix/selection contract check has nothing to run.Conflicts resolved against main by hand (two, both in
packages/microcosm-graph/src/microcosm/graph/executor.pyand its test):git apply --3wayconflicted only on the import block.native-verify-once's base carries an exact-Python-float short circuit in_update_scalar(payload = b"f" + struct.pack("=d", value)) thatmaindoes not have, somain's executor does not importstruct. I tookimport statalone and verifiedstruct.appears nowhere in the resulting file.test_graph_executor_series_stream.pydocuments_reference_update_scalaras "a verbatim copy of the pre-change body". Onmainthat body has notype(value) is floatbranch, so I dropped that branch and the file'simport struct. I then checked mechanically that all three reference helpers (_reference_update_scalar,_reference_update_array,_reference_update_series) are textually identical toorigin/main's_update_scalar,_update_array,_update_series— all three reported IDENTICAL. Both resolutions are inert for emitted bytes (np.asarray([v], dtype=np.float64).tobytes()==struct.pack("=d", v)for an exact Python float), so the byte-level parity the tests prove is the same contract on either base.Three things worth your attention:
graph-verify-once-mainalready held 4 commits made today 00:54–00:57, based on a since-supersededorigin/main(51c3143), never pushed, no PR, no live process, clean tree. I did not delete it — I renamed it tograph-verify-once-main-stale-20260916-0057(still checked out at/Users/maxghenis/PolicyEngine/_worktrees/microcosm-graph-verify-once-main) and built this branch fresh from currentorigin/main. After finishing, I compared: all five graph files in my branch are byte-identical (sha256) to that attempt's, including both resolutions above — an independent confirmation, not a copy. That stale worktree and branch are now safe to delete whenever you want.a904fa41d(numpy digest stream + its parity test),873b0e964(per-run source identity cache, run-end re-derivation,RunManifest.source_identities, refusal tests),3f38fc315(codecs docstring correction),8ea48447c(changelog fragmentchangelog.d/graph-verify-once.changed.md, following the repo's towncrier convention). I checkeda904fa41d's executor parses and contains nostat.or_SourceIdentities, so it stands alone._object_stream's float path casts withdtype="<f8"(explicit little-endian) while the comment above it and_update_scalarsay native order. They agree on every little-endian machine, which is the only kind the tests run on; on a big-endian host they would diverge. Worth raising on Verify native sources once per run, not once per access and per node #935 rather than fixing here.Verified independently:
RunManifest.source_identitiescannot move the manifest identity —content_addressedreads onlynodesandtier,to_jsonnever references it, andrun_graphis the single construction site and passes it by keyword.🤖 Generated with Claude Code