Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelog.d/reusable-qrf-models.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Add separate `fit.qrf.train@1` and `fit.qrf.apply@1` graph kernels with typed,
reusable model artifacts and stable entity-coordinate draws. The fitted QRF's
new `predict_from_uniforms` API supports stateless chained predictions while
preserving the existing `predict` RNG stream. Training excludes zero-weight
rows from its effective support and records both source and resolved weight
provenance.
1 change: 1 addition & 0 deletions changelog.d/typed-model-artifacts.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add typed graph artifact dependencies across populations, verified cold/warm model inputs, preserved numeric scope, and stable coordinate-keyed random draws. Preserve legacy node identities and serialization when the new declarations are empty.
1 change: 1 addition & 0 deletions changelog.d/typed-model-transfer-example.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a runnable synthetic destination-transfer graph using one fitted QRF model across two independent recipient populations, explicit annual unit conversion, real household calibration, and held-out evaluation. The example records model/cache identities and separate calibration/evaluation verdicts, with tests for selective invalidation and holdout isolation.
16 changes: 8 additions & 8 deletions docs/evidence/spec-engine/us-f0-coverage.json
Original file line number Diff line number Diff line change
Expand Up @@ -1656,13 +1656,13 @@
"compiler_ir.node_slices"
],
"expected": {
"map_sha256": "6b4902d9a640dd459942e588ae2cc7fb937f1c42ad814365c7c1616c98ce1b68",
"protocol_sha256": "e63bbfa0f05302a672acd9914e3652c151cc7ee690182d8823dfb85329b1e911"
"map_sha256": "fdf77a621dfb74c58278a443f73e7addabcd42ebf9af7105ce36c9da3f9cd35c",
"protocol_sha256": "6a29390792a7111bc359e2d7a6c13a55e894c3404708cb501fa142cc8880f2c9"
},
"failures": [],
"observed": {
"map_sha256": "6b4902d9a640dd459942e588ae2cc7fb937f1c42ad814365c7c1616c98ce1b68",
"protocol_sha256": "e63bbfa0f05302a672acd9914e3652c151cc7ee690182d8823dfb85329b1e911"
"map_sha256": "fdf77a621dfb74c58278a443f73e7addabcd42ebf9af7105ce36c9da3f9cd35c",
"protocol_sha256": "6a29390792a7111bc359e2d7a6c13a55e894c3404708cb501fa142cc8880f2c9"
},
"status": "covered"
},
Expand All @@ -1677,7 +1677,7 @@
"compiler_ir.seed_stream_map"
],
"expected": {
"implementation_sha256": "e63bbfa0f05302a672acd9914e3652c151cc7ee690182d8823dfb85329b1e911",
"implementation_sha256": "6a29390792a7111bc359e2d7a6c13a55e894c3404708cb501fa142cc8880f2c9",
"protocol": "legacy-v1",
"streams": [
"build_model",
Expand All @@ -1698,7 +1698,7 @@
},
"failures": [],
"observed": {
"implementation_sha256": "e63bbfa0f05302a672acd9914e3652c151cc7ee690182d8823dfb85329b1e911",
"implementation_sha256": "6a29390792a7111bc359e2d7a6c13a55e894c3404708cb501fa142cc8880f2c9",
"protocol": "legacy-v1",
"streams": [
"build_model",
Expand Down Expand Up @@ -2599,7 +2599,7 @@
"country": "us",
"schema_id": "country_spec",
"schema_version": 1,
"spec_sha256": "a6bc79878eb6f64637b9f3eceeea6cc2b050c0e5b8f9aca446179258940c44f2"
"spec_sha256": "be31b72f7960a3da54dbaead103c6b7660f8f4cc709ced75a24a19588e477198"
}
},
"report_schema_version": 3,
Expand All @@ -2609,7 +2609,7 @@
"country": "us",
"schema_id": "country_spec",
"schema_version": 1,
"spec_sha256": "a6bc79878eb6f64637b9f3eceeea6cc2b050c0e5b8f9aca446179258940c44f2"
"spec_sha256": "be31b72f7960a3da54dbaead103c6b7660f8f4cc709ced75a24a19588e477198"
},
"status": "pass"
}
23 changes: 23 additions & 0 deletions docs/graph-acceptance.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,29 @@ Amendments so far (each re-locked):
`hit` forced to false) and `load_certified` refuses it. Raised by the
#847 gate review; adopted 2026-09-03.

19. **Typed artifacts and stable draw coordinates.** Nodes may declare
`artifact_outputs` (named nominal type/version contracts) and
`artifact_inputs` (consumer aliases referencing a producer output). Edges
may cross population versions and participate in compilation, identity,
cache validation and gate ancestry. The executor supplies immutable
`ArtifactValue` bytes and producer numeric scopes; consumers validate
decoded payloads. Legacy opaque diagnostics remain legal. Only the two
newly added empty artifact fields are omitted from old declarations and
keys; the base-96faa5d acceptance graph is pinned in
`tests/fixtures/legacy-graph-key-baseline.json`. Typed cache records use
schema 2 and typed manifests schema 3; legacy runs retain their formats.
Numeric scope weakening is refused: platform-bitwise requires a
platform-bitwise consumer, tolerance-bound requires a tolerance-bound
consumer with its own output tolerance, and mixed platform/tolerance
inputs are unsupported. `SeedSource.KEYED` opts into the versioned
`keyed_uniform` helper over normative stream params and stable coordinates;
the default executor RNG remains unchanged. Tests in
`test_artifact_edges.py` and `test_keyed_randomness.py` cover these additions.
The existing B2/context shape assertions include the appended immutable
artifact field. Implemented after the 2026-09-04 Fable plan gate; this
records the interface amendment for owner review, not a claim of code
approval or release certification.

Adding a normative field with a default changes the canonical projection
of every node that carries it, so node keys moved with amendments 11 and
13's sibling field `entrants`; no released artifact pins a graph key yet.
Expand Down
4 changes: 2 additions & 2 deletions docs/graph-interface.lock
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
635fef92c599c298e7f19ca0badfa85aa040bf8e81eafed59f37c48db1fcff06 decl.py
eaf07da2eded1b1895aa0c59f603eb93744ed928df65aa9e65aa633762833949 kernel.py
8229270f3328f537c8f8e83d6c81af39aa75d4ca5aa6a7bd3d37ecfc25aee2fe decl.py
07691fb5cf45ae700a258713ebdc9aa845891a432ec9107e223b8c943b5f3cb6 kernel.py
122 changes: 122 additions & 0 deletions docs/model-artifact-transfer-example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# One fitted model, two synthetic destinations

Run the complete example from an installed Microcosm constellation with the
typed-artifact graph extension:

```bash
python -m microcosm.build.transfer_example --output /tmp/microcosm-transfer-example
```

In a development checkout, prefix the command with `uv run`. No country engine,
download, credentials, or restricted microdata is needed. The command requires
an explicit output directory and writes the following there:

- `report.json`: identities, calibration diagnostics, and held-out comparisons.
- `run_manifest.json`: execution receipts and verified store references.
- `graph.json`: the complete graph declaration.
- `store/`: source frames, fitted model, intermediate columns, and cache records.

Running again with the same output directory reuses verified results. Pickled
QRF models follow the graph's trusted-local-store convention: a digest verifies
bytes, not the safety of executing an externally supplied pickle. This example
generates and fits its own model and accepts no external model files.

## What actually runs

The example uses the real split QRF training/application kernels and the
`calibrate.adam@1` solver. The donor and both destinations are independent graph
populations. The fitted model is a typed artifact edge crossing those population
boundaries; it is not refitted separately for each destination.

```mermaid
flowchart LR
D[Synthetic donors] --> F[Fit QRF once]
F --> M[Typed model artifact]
A[Alpha predictors] --> PA[Apply]
B[Beta predictors] --> PB[Apply]
M --> PA
M --> PB
PA --> UA[Annualize minor units]
PB --> UB[Annualize minor units]
UA --> CA[Calibrate count and size margins]
UB --> CB[Calibrate count and size margins]
CA --> EA[Evaluate consumption]
CB --> EB[Evaluate consumption]
HA[Separate Alpha reference] --> EA
HB[Separate Beta reference] --> EB
```

All data are generated engineering fixtures. There are 64 donor households and
24 recipient households per fictional destination. Household size and a binary
dwelling category predict synthetic monthly consumption. Donors carry unequal
design weights; recipient sources contain no consumption outcomes. Each source
has one linked synthetic person per household to meet Frame's structural
contract. That person is a linkage placeholder: estimates use household weights,
and the number of person rows is not an estimate of population size.

The conversion records the floating-point factor `12 / 100`: twelve monthly
periods and one hundred minor units per base unit. Its input convention is
explicitly **monthly synthetic minor units**. The destination `MonetaryBasis`
declares annual 2024 flows in synthetic `XXX` base currency, with a fictional
household-consumption perimeter. This is an explicit unit conversion, not a
currency exchange-rate assumption. The conversion test checks byte equality
with `raw * recorded_factor`; it does not claim exact rational arithmetic.

Alpha starts at design mass 1,200 households and targets an average household
size of 2.9; Beta starts at 800 and targets 2.1. Calibration uses only household
count and summed household size. The solver, REWEIGHT node, and weight transition
all explicitly declare `mass="free"`. The report records actual initial and
final household mass, residuals, effective sample size, and maximum weight share.
Consumption does not enter the target matrix. The existing calibrator reports
that it does not consume target standard errors; none are supplied here.

## Separate evidence, separate verdicts

Each held-out reference is independently generated from the fixture's declared
process, with 96 households and disjoint identifiers. Monthly reference
consumption is `8000 + 5000 * household_size + 1000 * dwelling`; reference design
weights have a specified size tilt. Donors also carry residual variation. The
reference generator never reads fitted models, recipient predictions, or solver
results. Reference sources are declared only on evaluation nodes.

The report separates `calibration_passed` from `heldout_passed`. Its 2% margin
tolerance and 15% consumption tolerance are engineering fixture expectations,
not reviewed scientific thresholds. Held-out checks compare the consumption
mean and consumption means by household size. The tests deliberately multiply
one reference's consumption by ten: its held-out verdict fails while marginal
calibration remains successful. The fitted model, application outputs, and
calibrated weights remain unchanged.

Every report declares `scope="synthetic_engineering"`. Neither a successful run
nor a passing fixture check certifies a country population, an empirical
transfer method, a monetary target profile, or any tax-benefit result. There is
no rules-engine evaluation or national-release promotion in this example.

## Exercising reuse from Python

```python
from dataclasses import replace
from microcosm.build.transfer_example import (
default_targets,
make_synthetic_inputs,
run_transfer_example,
)

inputs = make_synthetic_inputs()
first = run_transfer_example("/tmp/microcosm-transfer-example", inputs=inputs)
targets = default_targets()
targets["alpha"] = replace(targets["alpha"], size_total=3600.0)
second = run_transfer_example(
"/tmp/microcosm-transfer-example", inputs=inputs, targets=targets
)
assert second.manifest.node("donor.fit").hit
assert second.manifest.node("alpha.apply").hit
assert not second.manifest.node("alpha.calibrate").hit
assert second.manifest.node("beta.calibrate").hit
```

`test_transfer_graph_example.py` also checks real fitting occurs once across
both destinations and warm runs; recipient edits affect their branch only;
donor values or weights invalidate both applications; mismatched destination
bases refuse; and held-out changes affect evaluation alone. The test lives in
the flat build test inventory and is explicitly assigned to shared/spec CI.
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,8 @@
"late_schedule": "e59c019d3d454eac99ac0ac209b6c5b6faaf9bdfcaeee18c36a25be19bf7da2f",
"ownership": "5f64f0aac49e2313177564f71876bffc8c81b3ded4df701e70930e60e9c98356",
"primary_tuples": "987b501c695e31f45521c4a178528f75ab3df22c09bc407b182213b2de99ee57",
"seed_map": "6b4902d9a640dd459942e588ae2cc7fb937f1c42ad814365c7c1616c98ce1b68",
"seed_protocol": "e63bbfa0f05302a672acd9914e3652c151cc7ee690182d8823dfb85329b1e911",
"seed_map": "fdf77a621dfb74c58278a443f73e7addabcd42ebf9af7105ce36c9da3f9cd35c",
"seed_protocol": "6a29390792a7111bc359e2d7a6c13a55e894c3404708cb501fa142cc8880f2c9",
"source_manifest": "cd5ba8924d64da5425ee14cca82a774e3f4b2bb5aabe06df291cc3cc457287a9",
"take_up": "fa186daea0f8dd641cc470e41d1a2953f887d45282ec990201298f47bedf8d4d",
"tail": "ac92829c88a1a4fb6460d61190918d5d99c6c377fc8dd8f62f02b332d09bf59c",
Expand Down
Loading
Loading