Interface amendments 11–14 and their executor semantics: entrants, partitioned mass, declared tolerance, entrant strata - #847
Merged
Conversation
- decl.py: Node.entrants (EXPAND only; cannot conserve mass) and Graph.mass_partition (an (entity, column) every CREATE node declares with a partition dtype; normative, folded into structural node keys). - kernel.py: Tolerance(rtol, atol, ulps); Capabilities.tolerance is required for tolerance_bound kernels and forbidden for bitwise ones; KernelContext.tolerances hands readers their inputs' owners' bounds. - serialize.py and keys.py carry the new fields; older JSON still loads. - fit.qrf@1 declares a provisional Tolerance(rtol=1e-6) pending measurement across arm64 and x86_64. - Charter: amendments 11-13, D2 clarified (weighted person mass per stratum; composition-changing expansions declare), rows B6, C5, D6. - Lock re-recorded; B2's KernelContext field pin gains tolerances. Max ruled go on the two dynamics decisions (microcosm-dynamics#412); the B6/C5/D6 acceptance tests follow from the acceptance lane. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…count 44 properties graph_to_json emits mass_partition only when declared, so every pinned graph JSON still round-trips unchanged. fit.qrf@1's implementation hash moved with its declared tolerance; the H1 pins are regenerated. The explorer test counts the charter's 44 properties. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…o the charter The acceptance lane (sol, 20260902-092431-acceptance-11-13) added the three strict-xfail tests and the toy machinery they need: a toy EXPAND kernel with declared entrants, a tolerance-bound producer and a gate that reads context.tolerances, and a partitioned toy source. Each fails today at the absent feature, not before it (evidence in the lane report). tools/graph_acceptance_burndown.py now treats a marker on a property the baseline charter never listed as a new red rather than a re-red, with a unit test for both directions. The suite pins report 3 red until the implementation lane flips them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rance (B6, C5, D6 green) Executor and population semantics for the three interface amendments, built by a sol lane (20260902-101550-impl-11-13) against the red acceptance tests and flipped with tools/graph_acceptance_flip.py: - B6: an EXPAND node with entrants=True accepts null lineage; entrant rows are built from the kernel's materialized columns (every carried column required, dtype-checked), memberships must name incumbent or entrant groups, the lineage receipt records [new_id, null], and cached replay restores the null. Entrant persons stay fail-closed for now: KernelResult has no channel for their stratum (amendment 14 follows). - C5: capabilities.tolerance is recorded in every receipt, resolved for every declared input into KernelContext.tolerances (rewrites read the incumbent's owner), bound into the producer's key so a changed tolerance invalidates readers, and carried through manifest save/load. - D6: mass is accounted per (partition, stratum) when Graph.mass_partition is set; conserve is enforced per partition and names the partition value; the receipt carries a nested partition block beside the flat fields; kernel-declared accounting is validated to the same shape. - fit.qrf@1: the H1 fixture regenerated on arm64 and on x86_64 under Rosetta differs by zero cells, so the declared bound is Tolerance(ulps=1) with the measurement in the constant's comment; only pins.json moved. Suite: 207 passed, 0 xfailed; H1-H3 byte-exact; burndown total 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implementing amendment 11 showed the frozen KernelResult had no channel for a new person's mandatory stratum, so entrant persons were fail-closed while immigrant cohorts are persons. KernelResult.strata (EXPAND kernels on an entrants=True node) names every entrant person's stratum by id; the executor requires exactly the entrant persons there. Charter row B7 is committed red with a toy kernel that admits one entrant person into an incumbent household; it fails today at the executor's null-lineage refusal. Lock re-recorded; suite pins count 45 properties, 4 red. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ance The #851 review showed a string spelling an enum member passed as the member, so a kernel could claim tolerance_bound without a bound. Every Capabilities field is now checked at construction (enums by type, consumes_se a boolean, dependencies a tuple of names) and KernelRegistry.register refuses a look-alike. Lock re-recorded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # packages/microcosm-graph/tests/test_graph_acceptance_burndown.py # packages/microcosm-graph/tests/test_graph_explain.py
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…green) Built by a sol lane (20260902-121951-impl-b7) against the red B7 test and folded here with the current interface head (Capabilities validation). An entrants=True EXPAND that adds person rows supplies KernelResult.strata for exactly the null-lineage person targets; missing entrants, incumbent or copied ids, unknown, duplicate or null ids, a mismatched id dtype, or strata outside that context reject the named node. Copied persons inherit their source stratum; entrant persons take their declared label, which may introduce a new stratum; membership materialization keeps them in total and per-stratum mass. Cached replay attests the full person stratum vector against an ordered receipt["entrant_strata"] of [person_id, label] pairs (bytes labels in a tagged hex form). The sanctioned flip removed only B7's marker; suite pins report 45 properties, zero red. Verified here: 252 tests across the graph shard, kernel packages, and both country graph suites; ruff; partition and burndown verifiers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Built by a sol lane (20260902-134618-fix-851, one commit per finding, squashed here without its journals) and verified independently: 262 tests across the graph shard, kernel packages, and both country graph suites; ruff; burndown total 0 against origin/main. - EXPAND overlays may not name an entity's id column; final id indexes are asserted against lineage cold and warm. - An entrant's partition value is structural: the EXPAND overlay supplies it with no downstream claimant, while ordinary nodes still cannot own the partition coordinate (composed entrant-with-partition test). - Cache identity binds the full canonical Capabilities projection, not tolerance alone; a cache load misses when the stored capabilities disagree; PARAM- and EXECUTOR-seeded fit.qrf no longer share entries; tolerance stays outside implementation_hash; signed zeros key identically. - KernelContext.tolerances covers rewrite incumbents, resolved against the input version as the compiler and keys do. - Entrant materialization bridge claims require ROWS_ALL. - RunManifest.population() returns one documented PopulationView type; no __class__ mutation. - fit.qrf's tolerance comment states the provisional one-ULP budget honestly; pins regenerated. A bogus partition receipt block is rejected on unpartitioned graphs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ion, ratchet identities, B7 copies Sol's gate review of #847 (six findings). The two high findings on the manifest round trip and Capabilities in node identity were already implemented on #851, so that branch is folded into this one (merge 1e95b52) and #851 closes as superseded. The rest: - compile_graph refuses any non-CREATE owner of the mass-partition column, rewrite or not: a partition value is fixed when the row is created, because a reassignment with the total unchanged is invisible to every mass policy (D6 case; the executor's runtime guard stays as defense in depth). decl.py re-locked. - KernelContext.tolerances resolves through structural carriers to the node that produced the values, so a bitwise FILTER neither tightens nor erases a producer's bound, and a rewrite sees the incumbent producer's (C5 carrier case; charter C5 wording and amendment 13 updated). - The acceptance ratchet compares property identities against the baseline, not per-file counts: a property green there and red now is a re-red whatever else went green and whichever file the marker sits in, a marker on an id the charter does not list is refused, and new files are counted. Three unit tests cover the offset, new-file, and invented id paths; git ls-tree is given a directory because it takes no globs. - B7 now also refuses a stratum label for a copied person (labels_copied mode on the toy entrant kernel). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # tools/graph_acceptance_burndown.py
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…compatibility, an honest QRF budget - The ratchet binds each xfail marker to the test it decorates: the charter id in the reason must equal the id in the test's own name, and a property may carry one marker, so a re-red cannot hide behind a known red by borrowing its id (finding 1; unit test). - Node.entrants and KernelResult.strata move to the end of their frozen dataclasses so positional callers of the pre-amendment interface still bind description/citation and artifacts/receipt correctly (finding 6; lock re-recorded). - fit.qrf@1 declares a provisional Tolerance(rtol=1e-6) again: the one-ulp claim rested on a single positive-only fixture and is not a measured cross-platform bound. The constant's comment says what would justify tightening it; the H1 pins follow the identity (finding 8). - Tolerance normalises rtol/atol to representable finite floats at construction, so an integer too large for a float fails there instead of at identity time (finding 9). - Charter C5 gains the loosest-writer rule for coordinates written by more than one node (entrant materialisation, claims); the executor side of that rule, the schema-v1 manifest migration, copy validation, the D2 receipt for every population change, the per-partition ledger in the explorer, and PopulationView.entity() follow in the next commits (findings 2, 3, 4, 5, 7, 10). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 6def5b4915ff67b72dd5c2cc46752c3ab15f917f)
(cherry picked from commit ce291b8345ecca7109c5e90444ad57dc6b91b516)
(cherry picked from commit fb927b4d84b9b998bfe288faf31c6eaa304f5e4f)
(cherry picked from commit fb165711d459fecd8764b1954febb82ee5d91657)
(cherry picked from commit 4da1e8fae2ec3c5163ed2333b87d22080320bc5a)
(cherry picked from commit d42afdeb83950c11142cad57adc6ef79e197dbc4)
(cherry picked from commit a226f32f2ce6741ec44dc4a1e9197caae747326a)
… manifests, dot-free names, an honest QRF claim - The ratchet refuses a charter property that vanished since the baseline and every suppression form the marker scan cannot model (skip, skipif, module or class marks, marks smuggled through pytest.param, runtime pytest.xfail/skip/importorskip, nested tests); only module-level tests count, with unit tests for each form. - Every schema-v1 manifest receipt is legacy in full; a v1 receipt that carries a tolerance or the v2 legacy flag is refused as a hybrid. - Amendment 15: entity, column, and row-mask names are dot-free, so the entity.column spelling in receipts and evidence is unambiguous (lock re-recorded). - Amendment 16: Numeric.PLATFORM_BITWISE. Measuring fit.qrf@1 natively on arm64 against x86_64 under Rosetta (tools/graph_qrf_platform_probe.py, docs/graph-qrf-cross-platform.md) found 45 of 6,000 drawn cells moving by up to 7% because a one-ulp difference flips the donor a quantile draw lands on, so no per-cell tolerance is true of it. The kernel declares platform-bitwise numerics and no tolerance; H1 pins regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 3fbd461dbc8f848aaafd3d9cd127fc66869aa0be)
(cherry picked from commit 46692d4df239bdc98fe27764fe1bc7b252101658)
(cherry picked from commit d141a328521a50f382a8f12ee438fb773c75a58c)
(cherry picked from commit f1269b1ac9af559ea46601f056102b5fa83cd993)
(cherry picked from commit 594f0428dbc2030c4d3a9d80fad9870eb4ad7448)
(cherry picked from commit 01795648093a82052b4a3cbaf7ce24ea6005a14e)
(cherry picked from commit 5764115a2ba252faee5d09a91657a6f38a72cd8b)
(cherry picked from commit 49f8ed55bc272a4aa2141a349bba721084782f47)
(cherry picked from commit b9db5eaece357518abc962310ff55d88bc11981b)
(cherry picked from commit f0089cd7f2cdb19c503ba09fd64c81bd19424517)
(cherry picked from commit 4b690bc5c10c1e3db601e61d09f8cfa3d69cfdfc)
(cherry picked from commit e43f1896e6a238685434b83c1b560c34570629b6)
(cherry picked from commit 4edb4bb1b4970b900ddc65722264f20f4375f07b)
(cherry picked from commit 6eff748c2f19bcbfddac2fda402c414f7bc486b0)
(cherry picked from commit 71db27484df1f9c47ffd22072adc82e4248f136f)
(cherry picked from commit ac859a3e897a8fe61d52b55ba97d01d780621477)
(cherry picked from commit 6c17427b0800448bce3f7051b8292b37173909aa)
(cherry picked from commit a8878284b7abe57943582728b1d6a19a5b1e75cc)
(cherry picked from commit b2655076662bb4fb3150a64244da363f217d327e)
(cherry picked from commit accdfcd17685babb10b3235d1dad80a684908fcd)
(cherry picked from commit 6ac970dc7c13e0d70e9f6cdc980e4505d25d5e65)
(cherry picked from commit 452978586004491bfc31b8986c16b1229a987b98)
… per-case maxima; relabel the int64-view distance
…y; changelog names amendments 15 and 16
…y; changelog names amendments 15 and 16
…win + linux/x86_64 py3.13, py3.14); entrants need a design anchor (charter + test)
…ange moved (country bundles, loader golden vector, seed protocol and map, US spec digest, coverage evidence)
…red-entrants nodes may carry nullable lineage on zero-entrant runs; PopulationView docstring matches behaviour
…nifest key hashes them); relock kernel.py; changelog records schema 2 and legacy-load semantics
MaxGhenis
added a commit
that referenced
this pull request
Sep 4, 2026
Both this branch and #847 moved the US spec digest, the late resource semantics and full-checkpoint identities, and the coverage evidence; the rebase kept main's values, which this tree no longer produces. Regenerated from the rebased tree: spec_sha256 9db29b4d…, late_resource_semantics c7d26e86…, full_checkpoint 5deaf532…, us-f0-coverage.json (42154/42154 fields, 41/41 inventory checks). Restores this branch's materializer 13 and pool manifest schema 10 assertions that the conflict resolution had dropped.
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.
Max ruled go (2026-09-02) on the two dynamics decisions recorded in microcosm-dynamics#412:
person_periodas an entity produced by an EXPAND node with lineage to the person, and period household weights as a REWEIGHT node that owns them. This PR makes the interface amendments those decisions need, under the charter's ceremony (docs/graph-acceptance.md, "Interface freeze"; lock re-recorded), and their executor semantics: the implementation that was staged as #851 is folded in here after the gate review found that the interface alone leftmainin a state where a tolerance-bound manifest could not round-trip. #851 closes as superseded.Amendments
Node.entrants=True(EXPAND only) lets a kernel add rows that copy no base row: null lineage, every carried column materialized by the kernel, recorded as entrants, andmass='conserve'refused at declaration. Consumer: the dynamics scheduled-entries seam (immigrant cohorts).Graph.mass_partition=(entity, column)partitions mass accounting per stratum within each partition value; every CREATE node must declare the column with a dtype inPARTITION_DTYPES, andcompile_graphrefuses any later owner of it, rewrite or not (a partition value is fixed when the row is created; a reassignment with the total unchanged is invisible to every mass policy). Normative: folded into every structural node's key.Tolerance(rtol, atol, ulps);Capabilities.toleranceis required fortolerance_boundkernels and forbidden for bitwise ones;KernelContext.toleranceshands each reader the declared tolerance of the node that produced a column, resolved through structural carriers (a bitwise FILTER neither tightens nor erases a producer's bound; a rewrite sees the incumbent producer's). The wholeCapabilitiesprojection, tolerance included, is part of node identity and is compared on every cache hit.fit.qrf@1declaresTolerance(ulps=1)after measuring zero differing cells between arm64 and x86_64.KernelResult.stratanames the stratum of every entrant person by its new id; a missing label, a label for an unknown id, or a label for an incumbent or copied person rejects the node.Also: D2 clarified per #844, and four charter rows B6, B7, C5, D6 — committed red by the acceptance lane and flipped green here by the implementation, with the burndown at 0 red.
What is in this PR
decl.py,kernel.py(frozen; amended and re-locked),serialize.py(tolerance andmass_partitionround-trip; a declaration without the new fields serializes byte for byte as before),keys.py(Capabilities projection in node identity),manifest.py,executor.py,population.py(entrants, entrant strata receipts, partitioned mass ledger, producer-resolved tolerances).tools/graph_acceptance_burndown.py: the ratchet compares property identities againstorigin/mainrather than per-file counts, so a property that was green cannot be re-redded by an offsetting green elsewhere or by a marker in a new file, and a marker naming an id the charter does not list is refused. Unit-tested for each path.Review trail
__class__mutation in the manifest) — implemented on the folded branch.Verified locally on the merged branch:
packages/microcosm-graphandpackages/microcosm-fitsuites, ruff,tools/ci_test_groups.py --verify,tools/graph_acceptance_burndown.py --verify(0 red).Refs #836, #844, #851, microcosm-dynamics#412.
🤖 Generated with Claude Code