Skip to content

Amendment 20: keyed draw streams (SeedSource.KEYED, keyed_uniform, fit.qrf uniform-driven draws) - #912

Open
MaxGhenis wants to merge 30 commits into
mainfrom
amend-keyed-seed-and-uniform-draws
Open

Amendment 20: keyed draw streams (SeedSource.KEYED, keyed_uniform, fit.qrf uniform-driven draws)#912
MaxGhenis wants to merge 30 commits into
mainfrom
amend-keyed-seed-and-uniform-draws

Conversation

@MaxGhenis

Copy link
Copy Markdown
Contributor

Amendment 20 to the graph interface charter: keyed draw streams. Stacked on #911 (Amendment 19); the base retargets to main when #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.lock re-recorded.
  • microcosm.graph.randomness.keyed_uniform and the executor support for KEYED; C4's static "no RNG consumed" check still holds over the shard.
  • microcosm-fit: FittedRegimeGatedQRF.predict_from_uniforms draws 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's direct.csv is byte-identical on every pinned platform — that byte-identity is the evidence for the additivity claim. Its implementation identity moves because QRFKernel.implementation_hash() hashes the module bytes, so the H1 fit.qrf pins are re-recorded through the new tools/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).
  • Deliberate difference from the generator path, commented where it lives: the sign gate's inverse CDF compares strictly and closes its final bin at 1.0, so a uniform of exactly zero skips a zero-probability class and a CDF summing to just under one cannot silently select the first class. Pinned by a test.
  • Charter entry 20, changelog fragment, contract tests (SeedSource.KEYED, keyed_uniform stability, 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.qrf keys 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 in inventory_coverage.py and docs/evidence/spec-engine/us-f0-coverage.json is regenerated — the same procedure #893 used, with tools/spec_engine_coverage.py --check clean and both spec-seed suites green.

Known, left as is

keyed_uniform draws differently for -0.0 and 0.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/tests and packages/microcosm-fit/tests green on the stacked tree (including the interface-lock test and every test_acceptance_*); tools/spec_engine_coverage.py --check 42156/42156 and 41/41; tools/ci_test_groups.py --verify clean; ruff check clean. A 42-agent adversarial pass on the landing found four test-rigor defects, each fixed and mutation-tested (see PROGRESS-amendment-20-keyed-draws.md).

🤖 Generated with Claude Code

MaxGhenis and others added 22 commits September 11, 2026 19:49
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>
@MaxGhenis
MaxGhenis changed the base branch from amend-typed-artifacts to main September 12, 2026 07:33
MaxGhenis and others added 7 commits September 12, 2026 03:33
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant