Amendment 20: keyed draw streams (SeedSource.KEYED, keyed_uniform, fit.qrf uniform-driven draws) - #912
Open
MaxGhenis wants to merge 30 commits into
Open
Amendment 20: keyed draw streams (SeedSource.KEYED, keyed_uniform, fit.qrf uniform-driven draws)#912MaxGhenis wants to merge 30 commits into
MaxGhenis wants to merge 30 commits into
Conversation
Records the scope, the platform of record for the H1 pins, and the reconnaissance findings that shape the plan: the integration branch carries no executor change for KEYED, editing qrf.py moves the fit.qrf@1 implementation hash and all three pinned platform node keys, and graph_parity_fixtures.generate() would drop the two x86_64 pins. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
test_graph_randomness.py holds keyed_uniform to the properties amendment 20 is for — stable coordinates give one draw, different coordinates give another, and order, batching, and unrelated identities cannot reach a draw — plus the documented sha256-u53-v1 formula, recomputed in the test from the specification rather than read back from the code. test_graph_kernel_contract.py adds the SeedSource.KEYED contract: the member is additive (every existing kernel keeps the value it declares, so no existing node key moves), it is part of the capability projection and so of node identity, it round-trips through a manifest receipt, and a keyed kernel draws the same values from two contexts whose generators sit at different positions. Both modules fail to import: microcosm.graph exports no keyed_uniform. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
kernel.py gains the KEYED member — normative stream params and stable draw coordinates — and KernelContext.rng's docstring now names all three seed sources instead of claiming to be the only randomness a kernel may use. Adding an enum member is additive: every existing kernel keeps the value it declares, so no existing node key moves. randomness.py is taken verbatim from origin/microcosm-us-launch-integration-20260909 (69 lines), so the amendment and the integration branch carry one implementation rather than two. A draw is the top 53 bits of SHA-256 over a domain prefix, the canonical stream, and the canonical tagged coordinates; it reads and mutates no numpy RNG state. docs/graph-interface.lock re-records kernel.py at 3483d091b03b19ae35c0268c01cb9e0f76c4cd63742083130567321d70da6048 (decl.py unchanged). The lock is plain sha256 of the file bytes, confirmed by shasum -a 256 -c against the unchanged decl.py line. graph/__init__.py exports keyed_uniform in sorted position among the lowercase callables (graph_to_json, keyed_uniform, load_source). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
test_qrf_stateless.py is taken verbatim from origin/microcosm-us-launch-integration-20260909 so the amendment and the integration branch share one test of the method: the same uniforms reproduce the same draws, permutation and chunking of the recipient batch leave the chained draws alone, a mismatched target set and out-of-range, non-finite or misshapen uniforms are refused before anything is drawn, the empty batch returns typed empty columns, the strict-CDF boundary skips a zero-probability sign at u=0, and an independent RNG replay reproduces predict() exactly across all seven regimes. It asserts the model's RNG position before and after, so "no state consumed" is a checked fact rather than a claim. 14 tests fail with AttributeError: 'FittedRegimeGatedQRF' object has no attribute 'predict_from_uniforms'. microcosm-fit's test_kernels.py gains the companion guard: adding SeedSource.KEYED must not widen QRFKernel, which still accepts only PARAM and EXECUTOR, so no fit.qrf@1 node changes what it declares. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
_draw_target_from_uniforms and FittedRegimeGatedQRF.predict_from_uniforms are taken verbatim from origin/microcosm-us-launch-integration-20260909 (6e3907f); the diff against origin/main applies with no conflict. The method is additive. predict(), _draw_target_with_rng, and the RNG consumption order behind them are untouched, so fit.qrf@1's existing outputs are unchanged; the two code paths differ only in where a row's quantile and sign uniform come from. Draws now depend on caller-supplied per-row uniforms rather than on a position in a generator, so pairing uniforms with stable entity ids makes a batch's results invariant to recipient ordering and chunking. Later targets still condition on earlier draws, as in predict(). One deliberate difference from the RNG path, commented in place: the sign inverse-CDF compares strictly (cumulative > u) and closes the final bin at 1.0, so u=0 skips a zero-probability class instead of selecting it. fit.qrf@1's implementation hash moves with this commit, because QRFKernel.implementation_hash() hashes microcosm.fit.qrf's module bytes. The H1 pins are regenerated in a following commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… all Editing a kernel's source moves its implementation hash and therefore every pinned platform's node key — including the two x86_64/linux pins this machine cannot run. Two things were missing for that. tools/graph_parity_repin.py re-records one case's pins in place. It is a sibling of graph_parity_fixtures.py rather than an addition to it, because ParityCsvSource and ParityRulesEngine are DEFINED in that module: its bytes are inside ParityCsvSource.implementation_hash() and SimulateRulesKernel.implementation_hash(), so editing it would move every parity node key in all three cases — churn caused by tooling rather than by the kernel under amendment. Measured: adding the same code there moved the calibrate node key from 184ccd0a to f8c9ed4b with its implementation hash unchanged. generate() is the wrong instrument here for a second reason: it rewrites pins["platforms"] as the local platform alone, so running it would silently drop the foreign pins and leave H1 on its off-platform branch there, which asserts no bytes at all. The local key is produced by running the graph; every other pinned platform's key is derived, and the derivation is checked against the produced key on every run. Pinned bytes are never derived — each platform's direct.csv is left exactly as that platform recorded it — and a re-pin refuses outright if the local direct call's bytes moved, because that is not a re-pin. test_graph_parity_pins.py makes the foreign pins checkable from anywhere: a node key is a pure function of the declaration, the resolved inputs, the implementation hash, the capability projection and the platform fingerprint STRING, so every pinned key is derivable on every platform. Before this, a stale foreign pin survived until that Linux lane happened to run. Three of its eleven tests are red at this commit, which is the point: the fit.qrf pins went stale when qrf.py moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`uv run python tools/graph_parity_repin.py fit.qrf` on arm64/darwin/py3.14 (Python 3.14.4), the fixture's authoring platform. implementation_hash 02db8f5c → d1f8b192, and with it all three platform keys: arm64/darwin/py3.14 8878352d → 35af6a45 (produced: graph executed here) x86_64/linux/py3.13 f6984280 → 6c43edcb (derived) x86_64/linux/py3.14 9e80ee3a → ecb6b20c (derived) The derivation is proven, not assumed: re-deriving the three OLD keys from this machine with the OLD implementation hash and only the fingerprint string varied reproduces all three pinned values exactly, so the platform reaches a key as that string and nothing else. The tool also re-derives the local key alongside executing the graph on every run and refuses if they disagree. direct.csv is unchanged on every platform — 7b8dbd56c91ee71552ff6d892a42c56494b1813fb5d4b11553a8a8ccc9b90dca before and after, for the authoring copy and both x86_64 copies — which is the evidence that predict_from_uniforms is additive: the wrapped kernel still draws exactly what it drew. pins.json is the only fixture file this commit touches. The same tool run against calibrate and simulate rewrites their pins byte-for-byte identically (184ccd0a, a643736e), so the re-pin round-trip is a no-op where nothing moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The charter records what the US launch integration branch carried without an amendment. The entry says what the member and the helper are, why a draw keyed by coordinates is worth having (C1 and C2's invariance to packing now reaches each individual draw), that C4 is neither weakened nor edited because randomness.py consumes no RNG at all, that fit.qrf@1's existing outputs are unchanged and its implementation identity therefore moves only because its module's source did, that an enum member moves no existing node key, and the one deliberate difference from the generator path at the CDF boundary. It adds no property row and edits none; C1, C2, C4 and H1 are the rows it bears on. NUMBERING, for the merge owner: main's amendment list ends at 18, so by its own arithmetic the next free number is 19. This entry is numbered 20 because the lane brief assigns 20 and assigns the ArtifactValue/KernelContext.artifacts work to a separate lane. Two unmerged commits on the candidate-quality-producer-integration-20260905 branch family already claim both numbers: 3ff92b0 adds a 19 ("Typed artifacts and stable draw coordinates") that bundles typed artifacts WITH this lane's SeedSource.KEYED and keyed_uniform, and d2043d85e adds a 20 ("Failed typed evidence remains a failed gate"). Neither is on main or on the integration branch. Whoever merges owns the reconciliation; renumbering this entry to 19 is a one-token edit and changes nothing else. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Records what landed, the findings that shaped it (foreign-platform keys are derivable; the fixture generator's own bytes are inside three parity node keys), and the two items left open for a reviewer: the spec-engine seed-digest drift this lane causes and declines to re-pin, and the amendment numbering. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The tool writes node keys for platforms this machine is not. That is sound only while every pinned platform shares this machine's locked environment, because source_hash folds each declared dependency's installed version into the implementation hash, and node_key folds that hash in — a channel no platform fingerprint string records. The guard added alongside it reproduces each pinned key from pins["implementation_hash"], which substitutes away the only input that carries those versions, so any environment reproduces the pins and the check cannot see the drift it is there to catch. pins.json already records the versions the keys were taken under; nothing reads them. Three tests for the check that would close it, red; the five covering the refusals that already exist pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
pins.json already recorded the dependency versions the existing keys were taken under; the tool now reads them and refuses unless they equal this machine's installed versions. That is the check that sees the dependency channel — the reproduction loop cannot, because substituting the recorded implementation hash removes the only input those versions reach. The check runs before any key is derived, so a refusal never follows a derivation that already trusted the environment under test. The docstring said the reproduction loop "proves the assumption rather than asserting it" and that "only one locked environment can do that". Neither was true of the code: any environment whose sources match reproduces the pins. It now states what each of the two checks establishes, and the reproduction refusal no longer asserts a cause it has not established — a moved graph_parity_fixtures.py re-keys every parity node and fails on the local platform first, where "re-pin it on that platform" was advice to re-run the command that just refused. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Amendment 20 states two deliberate differences from the generator path: the inverse CDF compares strictly, and it closes its final bin at 1.0. Only the first was pinned. Deleting `cumulative[:, -1] = 1.0` left all fourteen stateless tests green. The guard is load-bearing: predict_proba rows are floating point and need not sum to exactly 1.0, and on a short row a uniform above the sum makes every comparison false, so argmax returns 0 and the draw silently takes the first — most negative — class. The new test is the only one that fails without the closure. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…in prose The docstring promised "the context still offers rng; a keyed kernel simply does not spend it", but the test only compared draws from two contexts whose generators sat at different positions. Its toy body never referenced context.rng, so the equality held by construction and a body that did spend the generator passed unchanged. It now deep-copies the bit generator state across the call and asserts it is where it started; adding `context.rng.random(1)` to the body makes it fail. The docstring also claimed the behaviour was "exercised end to end at the kernel protocol level". It is not: the executor has no seed_source branch, so there is no production path that treats a KEYED node differently. The docstring now says what the toy body pins and what it does not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
test_the_top_level_pin_is_the_authoring_platform read through ``_platforms``, which back-fills the top-level pin into the mapping when it is missing — so the assertion that the authoring platform appears there was satisfied by the helper rather than by pins.json. It now reads the raw mapping. The derived-key test's docstring said the local pin "was produced by running the graph", which is true of the authoring platform and not of anywhere else the test runs. It now names the anchor that actually holds everywhere: test_h1_kernel_parity executes the graph on whatever platform is running and asserts the executor's key equals this same local pin. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The entry said adding an enum member means "no existing node key moves", which reads as a claim about this branch — and this branch moves three fit.qrf keys. Both facts are true of different things: the member adds no projection field, so every kernel keeps projecting the seed source it already declared and no key moves for that reason; the fit.qrf keys move because qrf.py's bytes are inside the kernel's implementation hash. The entry now separates them instead of leaving a reader to reconcile it with the re-pinned fixture in the same diff. Also reflows the paragraph the edit left with a 31-character orphan line, and records in the changelog fragment what the re-pin tool must establish before it writes a key for a platform it is not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Records the second pass (the four findings an adversarial review of the branch turned up and how each was fixed), corrects the first pass's unsound inference that a platform reaches a key through the fingerprint string "and nothing else" — the experiment substituted the implementation hash, which is exactly what removes the dependency channel it would need to have tested — and states the three open decisions with what each now costs: the spec-engine drift is seven red tests in a CI lane that always runs, the numbering gap renders as 19 whatever it is written as, and keyed_uniform gives -0.0 and 0.0 different draws. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The brief's -o path is the repo root's out.md, which is a tracked file holding a different lane's report (the F1 worker-identity round-1 report, 71dbe29). Committing this lane's report there would silently delete that record; leaving it uncommitted means it dies with the worktree. So the report stays in out.md uncommitted, and the identical content is committed under experiments/, which CLAUDE.md names as the home for lane receipts, with the journal pointing at it. Also corrects the journal's State section, which said one item was left red: seven tests are red, in a CI lane that runs on every PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The review agents mutation-tested by editing source files and reverting them, so the headline suite figure was re-measured after all 42 had finished against a tree verified clean of MUTANT markers and of any .py differing from HEAD: 487 passed, exit 0. Also records the review's verdict tally and the exact command exit codes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… QRF change moves Merge of amend-typed-artifacts: the charter carries entries 19 then 20, the lock is re-recorded over the merged decl.py and kernel.py, and both sides' kernel-protocol contract tests stay (one module docstring names all four amendments). fit.qrf's additive predict_from_uniforms moves the module bytes the seed protocol attests, so the seed protocol content digest and the compiled seed map digest are re-pinned to the values the merged tree computes and docs/evidence/spec-engine/us-f0-coverage.json is regenerated; tools/spec_engine_coverage.py --check is clean and both spec-seed suites pass. direct.csv for the H1 fit.qrf fixture is byte-identical, as the amendment entry records. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The country spec envelope folds in the seed protocol, so re-pinning the seed digests moved spec_sha256 from 35a02b6b to c72fb9e1 (visible in the regenerated docs/evidence/spec-engine/us-f0-coverage.json); the live-bundle test still pinned the old digest. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
-0.0 == 0.0 and they hash alike, so a float coordinate must draw alike under either spelling; _coordinate now folds -0.0 to 0.0 the way keys._canonical_tolerance_float already does for tolerances. Contract test and a sentence in the amendment 20 entry. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
graph_parity_repin refuses an unpinned platform before the step these three tests exercise, so on such a platform they would fail on the wrong refusal rather than observe their subject. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Dated notes over the State section and receipts §5, §7.1–7.3 and §8: the seed re-pin, the numbering, the signed-zero fix and the lock test have all landed since the lane wrote them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every country's spec envelope folds in the seed protocol, so amendment 20's seed re-pin moved all three bundle digests, not only the US one; CI on dcaf929 failed exactly these three parametrizations. Values computed with load_bundle(country).spec_sha256 on the merged tree. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… protocol The golden folds the legacy-v1 seed protocol wire, which attests microcosm.fit.qrf's bytes, so it moved with this amendment exactly as the US, AM, BE and UK bundle digests did. Value recomputed from the test's own construction (load_bundle over _rich_minimal). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Without the guard, pytest.raises(SystemExit) passed vacuously there on the 'carries no pin' refusal and the ordering the test pins was never observed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Amendment 20 to the graph interface charter: keyed draw streams. Stacked on #911 (Amendment 19); the base retargets to
mainwhen #911 merges. Extracted from the #893 integration branch and landed the charter's way.What it adds
kernel.py:SeedSource.KEYED— a keyed kernel's draws are a pure function of normative stream parameters (("sha256-u53-v1", experiment_id, replicate, base_seed)) and one stable coordinate per draw, conventionally(person_id, process, period, draw_index); it reads and advances no generator, so a row's draw no longer depends on how many rows were drawn before it.docs/graph-interface.lockre-recorded.microcosm.graph.randomness.keyed_uniformand the executor support forKEYED; C4's static "no RNG consumed" check still holds over the shard.microcosm-fit:FittedRegimeGatedQRF.predict_from_uniformsdraws from caller-supplied per-row uniforms (mismatched target sets refused; no RNG state consumed — asserted).fit.qrf@1's existing outputs are unchanged: the method is additive, and the H1 fixture'sdirect.csvis byte-identical on every pinned platform — that byte-identity is the evidence for the additivity claim. Its implementation identity moves becauseQRFKernel.implementation_hash()hashes the module bytes, so the H1fit.qrfpins are re-recorded through the newtools/graph_parity_repin.py, which checks the environment it derives foreign keys from (the earlier draft's docstring claimed a proof it did not perform; fixed and tested).SeedSource.KEYED,keyed_uniformstability, the uniform-driven draw), sorted exports.Node keys and identities
The enum member moves no node key (no node's canonical projection gains a field). The
fit.qrfkeys move because the implementation hash moved. Because the module bytes the seed protocol attests changed, the seed protocol content digest and the compiled seed map digest are re-pinned ininventory_coverage.pyanddocs/evidence/spec-engine/us-f0-coverage.jsonis regenerated — the same procedure #893 used, withtools/spec_engine_coverage.py --checkclean and both spec-seed suites green.Known, left as is
keyed_uniformdraws differently for-0.0and0.0(equal and hash-equal in Python); two reviewers held this to be the repository's deliberate signed-zero convention. Flagged rather than changed.Verification (direct exit codes)
Red-first at each step;
packages/microcosm-graph/testsandpackages/microcosm-fit/testsgreen on the stacked tree (including the interface-lock test and everytest_acceptance_*);tools/spec_engine_coverage.py --check42156/42156 and 41/41;tools/ci_test_groups.py --verifyclean;ruff checkclean. A 42-agent adversarial pass on the landing found four test-rigor defects, each fixed and mutation-tested (seePROGRESS-amendment-20-keyed-draws.md).🤖 Generated with Claude Code