Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1eeb3bf
Start the chronicle dual-acceptance journal
MaxGhenis Sep 2, 2026
413b8da
Accept chronicle-era Chronicle schema ids and add the CHRONICLE_* env…
MaxGhenis Sep 2, 2026
ca98a9c
Test dual-era acceptance across the fact-consumption surface
MaxGhenis Sep 2, 2026
0aff29b
Carry per-row Chronicle schema ids instead of gating on them
MaxGhenis Sep 2, 2026
cff65d9
Ground the observed ledger-era domain map in the feed it claims to de…
MaxGhenis Sep 2, 2026
c2d58a9
Say in the changelog that only the manifest id is gated
MaxGhenis Sep 2, 2026
4115573
Record the regression and the arch-namespace question in the journal
MaxGhenis Sep 2, 2026
061544e
Rename the CHRONICLE_* env dual-read window to LOGBOOK_*
MaxGhenis Sep 2, 2026
ee73a62
Reframe the env dual-read window as a Logbook cleanup, not chronicle#143
MaxGhenis Sep 2, 2026
a5121a1
Apply ruff import-sort and formatting to the renamed files
MaxGhenis Sep 2, 2026
b98edd8
Report the CHRONICLE_* to LOGBOOK_* correction on PR #849
MaxGhenis Sep 2, 2026
47df8f4
Correct out.md's head-sha accounting for its own commit
MaxGhenis Sep 2, 2026
1f78847
Drop the lane's root out.md; the repo keeps task journals as PROGRESS…
MaxGhenis Sep 2, 2026
e76bffd
Open a round-2 journal with the verified Chronicle identity ground truth
MaxGhenis Sep 2, 2026
dd244fb
Accept the artifact ids producers actually stamp, from a declared reg…
MaxGhenis Sep 2, 2026
05cd32e
Cut every test in the build shard off from a live Logbook
MaxGhenis Sep 2, 2026
1f5619d
Stop the barrel shadowing logbook_env, and export the Chronicle commi…
MaxGhenis Sep 2, 2026
e511176
Carry the Chronicle epoch witnesses into the UK run's own evidence
MaxGhenis Sep 2, 2026
edac801
Advertise the preferred Logbook variable names in the CLI help
MaxGhenis Sep 2, 2026
5f14424
Historicize the first-pass journal's identity and design claims
MaxGhenis Sep 2, 2026
3c4ddf2
Drop two prose claims the declared registry falsified
MaxGhenis Sep 2, 2026
65a25d8
Record the round-2 state and the structural change for the next reader
MaxGhenis Sep 2, 2026
a15f153
Record the round-2 verification, including the two pre-existing failures
MaxGhenis Sep 2, 2026
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
89 changes: 89 additions & 0 deletions PROGRESS-chronicle-dual-accept-round2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Round-2 review fixes — PR #849 (`chronicle-dual-accept`)

Journal for the gate-findings pass on top of `1f78847c`. History, not state:
check git/GitHub for current truth.

## Verified ground truth (read this session, 2026-09-02)

Read from `PolicyEngine/chronicle` at `origin/main`:

| Identity | Where | Epoch |
|---|---|---|
| `policyengine_ledger.consumer_artifact.v2` | `policyengine_chronicle/consumer.py:30`, README line 299 | ledger |
| `ledger.consumer_fact.v1` | `chronicle/consumer_contract.py:28` | ledger |
| `ledger.fact.v1` | `chronicle/core.py:103` | ledger |
| `ledger.source_cell.v1` | `chronicle/sources/cells.py:24` | ledger |
| `ledger.source_row.v1`, `ledger.source_column.v1`, `ledger.source_row_value.v1` | `chronicle/sources/rows.py:27-29` | ledger |
| `ledger.{aggregate_fact,semantic_fact,concept_alignment,dimension_set,observed_measure,source_release,source_series,universe_constraint_set}.v2` | `chronicle/consumer_contract.py`, `policyengine_chronicle/consumer.py:195` | ledger |

`policyengine_ledger.consumer_artifact.v1` is what **Microcosm** mints
(`import_entry_facts.py:164`), never what Chronicle emits — it is ledger-era
history that must keep loading.

There is **no** `origin/epoch-dual-domain` branch on chronicle
(`git ls-remote --heads origin | grep -i epoch` → empty), so the chronicle-era
successors are declared here by the migration rule chronicle#143 states —
new namespace, same family, version bumped by one — and the brief pins the two
that matter: `policyengine_chronicle.consumer_artifact.v3` and
`chronicle.consumer_fact.v2`.

## State

Round 2 complete and pushed to `chronicle-dual-accept`; the PR body carries a
"Review fixes (round 2)" section with the same content as this journal's
verification block. **DO NOT MERGE** — the lane was authorized to fix and
push only.

## Verification (2026-09-02)

Both full `packages/microcosm-build` suites — clean shell and the
polluted-Logbook shell finding 2 specifies — returned **outcome-identical**
results: 7116 passed, 47 skipped, 2 failed, out of 7165.

The 2 failures are `test_release_target_parity.py::TestRegeneration` and are
**not this branch's**. They reproduce identically at `1f78847c` (this
branch's previous head) and at `origin/main` `47c74225`, both verified in a
throwaway worktree this session:

ValueError: Ledger target reference
'cbo.revenue_projection.ty2024.income_by_source.adjusted_gross_income.projected_amount'
assertion_policy='observed_only' does not allow resolved fact assertion
'source_projection'.

They run only where the pinned feed exists —
`~/PolicyEngine/_buildh-runtime/inputs/consumer_facts_buildn_v9_4.jsonl`,
outside the repo — and skip everywhere else, PR CI included. Worth a separate
issue: on a machine holding that feed, the committed
`cbo.revenue_projection.*` reference and the feed row it resolves to disagree
about assertion class.

`uv run ruff format --check .` is red repo-wide (116 files) at this branch
*and* at its merge base: ruff 0.15.16's formatter against a repo pinned to
`ruff>=0.8`, whose CI lint lane runs `ruff check` only. Every file this
branch touches is format-clean before and after.

## What changed structurally, for the next reader

The first pass treated epoch as something you could *read off* a key's
namespace. That is why it accepted a two-id set that excluded the id
Chronicle actually emits, and why `chronicle.anything.vN` was witnessed as
Chronicle-issued identity. Round 2 replaces it with a declared registry:
`DECLARED_IDENTITIES` in `chronicle_epoch.py`, keyed by
`(namespace, family, version)`. Anything not in that table is `undeclared`
and is reported as such. If Chronicle publishes its own successor
enumeration, that table is the one place that changes.

## Done

- [x] (1) Declared identity registry keyed by `(namespace, family, version)`
- [x] (2) Autouse Logbook env isolation + fail-closed network in tests
- [x] (3) UK `_ledger_provenance` delegates to `provenance()`
- [x] (4) Undeclared chronicle-namespace identities labelled `undeclared`
- [x] (5) `microcosm.build.logbook_env` module import no longer shadowed
- [x] (6) `CHRONICLE_US_SOURCE_COVERAGE_CONTRACT_COMMIT` in the us_runtime barrel
- [x] (7) Non-string `schema_version` raises the documented ValueError
- [x] (8) `tools/logbook.py --remote` help prefers `LOGBOOK_*`

## Next

Nothing in this lane. Reviewer decides on merge.
163 changes: 163 additions & 0 deletions PROGRESS-chronicle-dual-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Chronicle dual-acceptance (consumer side) — chronicle#143

Branch `chronicle-dual-accept`. Journal for this lane. Root journals are
history, not state (see CLAUDE.md); this one is current only while the branch
is open.

> **Corrected 2026-09-02 by the round-2 review pass.** Several identity and
> design claims below were accurate to the first pass and are wrong now. The
> accepted artifact set is three ids, not two; the chronicle-era successor of
> the artifact id is `.v3`; epoch resolution is a declared registry, not
> namespace parsing; and the env half is `LOGBOOK_*`, not `CHRONICLE_*`. Each
> is annotated in place below. See `PROGRESS-chronicle-dual-accept-round2.md`
> and the PR's "Review fixes (round 2)" section for what is actually in the
> branch.

## Goal

Microcosm must accept BOTH ledger-era and chronicle-era Chronicle identities
*before* Chronicle flips emit:

- schema ids: `policyengine_ledger.consumer_artifact.v1` **and**
`policyengine_chronicle.consumer_artifact.v2`; `ledger.consumer_fact.v1`
**and** `chronicle.consumer_fact.v2`

*(Corrected 2026-09-02: wrong at both ends. Chronicle's `main` emits
`policyengine_ledger.consumer_artifact.v2` today, so that pair rejected
every artifact Chronicle publishes; and the chronicle-era successor of the
v2 id is `policyengine_chronicle.consumer_artifact.v3`. The branch now
accepts all three.)*
- hash domains: `ledger.<x>.v2` **and** `chronicle.<x>.v3` (same canonical
payload, new domain string)

*(Corrected 2026-09-02: the version numbers differ by family. The
source-side domains are ledger `v1` → chronicle `v2`; only the derived
families are `v2` → `v3`.)*
- env names: `CHRONICLE_*` preferred, legacy honored with a once-per-process
deprecation warning

*(Corrected on the PR before merge, and again here: these are the
**Logbook** store's credentials, so the preferred spellings are
`LOGBOOK_*`. See the PR review comment of 2026-09-01.)*

Frozen (microcosm#639): nothing on disk or in artifacts renames. Diagnostic
field names (`ledger_aggregate_fact_key`, `ledger_commit`), H5 attrs,
`populace_*` ids, fact keys, goldens and fixtures stay at v1.

**Acceptance widens; nothing narrows.** This is the rule the lane's own first
pass broke and the second pass restored — see "The regression that mattered".

## State

Implementation and tests landed; verification run recorded in the PR body and
in the lane's report. PR #849, open, do not merge.

## Done

- `microcosm/build/chronicle_epoch.py` — the single epoch authority. Epoch
detection is **structural** (the namespace segment of a key domain), not a
lookup in a frozen domain list, because chronicle#143 declares the `v3`
spelling only for the aggregate and semantic fact families. Only identity
strings the spec names explicitly are pinned as literals.

*(Corrected 2026-09-02: structural detection reported
`chronicle.<anything>.vN` as Chronicle-**issued** identity, which is a
guess dressed as a witness. Resolution is now a declared registry keyed by
`(namespace, family, version)`; an undeclared spelling in a Chronicle
namespace is reported as `undeclared`. The claim that chronicle#143
"declares the `v3` spelling" for two families also overstated the issue,
which lists `dual-hash window or v3 domains` as open options.)*
- `microcosm/build/chronicle_env.py` — the env dual-read window, one helper,
one `DeprecationWarning` per process per legacy name.

*(Renamed before merge to `microcosm/build/logbook_env.py`; there is no
`chronicle_env.py` on the branch.)*
- `ledger_artifact.py` — the manifest `schema_version` is a membership test
over both eras; per-row schema ids and fact keys are carried as published;
`provenance()` records the observed manifest id, `schema_epoch`,
`fact_key_epochs`, and `fact_schema_versions`.
- `us_trade/import_entry_facts.py` — emission stays ledger-era (bytes are
pinned; these rows are minted from Census/CBP bytes, so there is no source
epoch to inherit), but the declared id is now an argument checked against
both eras.
- Chronicle-era aliases for the four `LEDGER_*` module constants that only
look like env vars to a grep.
- Tests: `test_chronicle_epoch.py`, `test_chronicle_env.py`, plus mixed-epoch
cases in `test_ledger_targets.py`, epoch-independence of the minted
`microcosm.derived_fact.*` keys in
`test_us_congressional_district_vintage.py`, and dual-era emission and
acceptance in `test_us_trade_facts.py`.

## The regression that mattered

The lane's first pass added a per-row `schema_version` check to
`_load_fact_rows` that rejected any id outside the two chronicle#143 names —
validation `main` never performed, on a field `main` never read. Real feeds do
not honor that set. The pinned US fiscal-refresh feed
`consumer_facts_buildn_v9_4.jsonl`, which the release path loads through this
loader, declares `arch.consumer_fact.v1` on 37,006 of its first 200,000 rows
and `ledger.consumer_fact.v1` on 399. The check failed the build closed on its
own pinned input, and **PR CI could not see it**: the feed is gated data
outside PR CI. Reproduced directly, fixed, and pinned by a test.

The same pass witnessed fact-key epochs from only the four fields targets
resolve a fact by. Published rows carry eleven key-bearing paths; the pinned
feed exercises all of them. A row straddling the cutover — ledger-era
aggregate key, chronicle-era source-release key — was reported as pure
ledger-era. The inventory is now complete and pinned to the captured feed
fixture.

## Audit result

Every site that compares, parses, or mints a Chronicle fact key or schema id
was inventoried. After this branch, no non-test source file hard-codes a
single epoch in a validator. `ledger_targets.py` carries keys opaquely and
contains no epoch literal at all; `congressional_district_vintage.py` and
`us_trade/import_entry_facts.py` mint into Microcosm-owned namespaces from
digest payloads that contain no Chronicle key, so a source row's epoch cannot
move a minted key; the UK runtime is clean; no golden embeds a Chronicle
domain and no H5 attribute name embeds `ledger`. `require_pins` does not exist
in this repo.

## Open question for the Chronicle side

The pinned feeds carry an `arch.*` key namespace alongside `ledger.*` — 37,006
rows against 399 in `consumer_facts_buildn_v9_4.jsonl`, across all eleven
families. Microcosm treats those keys opaquely, so nothing here depends on
what `arch` is, and the epoch module reports it as outside both declared eras.
Whether chronicle#143's cutover is meant to re-epoch `arch.*` rows too is a
question for the Chronicle lane; this branch does not guess.

## Next

Nothing outstanding on this branch. Chronicle's own acceptance half lands in
the parallel lane; the emit flip is a separate, later cutover.

## Correction (2026-09-02): the env half was misnamed

PR #849 review (Fable, main) caught that `POPULACE_LEDGER_URL` /
`_KEY` / `_API_KEY` / `_EXPORT_KEY` are **Logbook** store credentials
(Supabase `logbook` schema, `logbook_writer` / `logbook_exporter` roles —
see `logbook.py`'s docstring and `logbook/README.md`), not Chronicle fact-
store ones. "Ledger" there was the generic build-ledger sense renamed to
Logbook on 2026-08-08 (microcosm#632) specifically to stop colliding with
Chronicle. Naming the preferred spellings `CHRONICLE_*` would have recreated
that exact collision.

The epoch half above (`chronicle_epoch.py`, `ledger_artifact.py`,
`import_entry_facts.py`, `ledger_targets.py`) is unaffected and was approved
as-is. The env half only: `chronicle_env.py` renamed to `logbook_env.py`,
`CHRONICLE_*_ENV`/`chronicle_env`/`chronicle_env_names`/
`describe_chronicle_env`/`reset_chronicle_env_deprecation_warnings` renamed
to `LOGBOOK_*_ENV`/`logbook_env`/`logbook_env_names`/`describe_logbook_env`/
`reset_logbook_env_deprecation_warnings`, and every caller (`logbook.py`,
`tools/logbook.py`, `build/__init__.py` exports, `logbook/README.md`, the
changelog fragment, this file, and the module-path comments in
`firm_generation.py`/`source_coverage.py`) updated to match. The
`POPULACE_LEDGER_*` legacy names and the once-per-process
`DeprecationWarning` behavior are unchanged; the warning text now cites
microcosm#632 instead of chronicle#143, since this window is a Logbook
naming cleanup riding along on this branch, not part of the chronicle#143
epoch migration. `CHRONICLE_*_BANDS` and
`CHRONICLE_US_SOURCE_COVERAGE_CONTRACT_COMMIT` are untouched — those really
do translate Chronicle ids and pin a Chronicle commit.
1 change: 1 addition & 0 deletions changelog.d/chronicle-dual-accept.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Accept chronicle-era Chronicle identities everywhere Microcosm consumes them, from a declared identity registry keyed by `(namespace, family, version)`. Consumer-artifact manifests load under every id a real producer stamps — Microcosm's own minted `policyengine_ledger.consumer_artifact.v1`, the `policyengine_ledger.consumer_artifact.v2` Chronicle emits today, and the chronicle-era `policyengine_chronicle.consumer_artifact.v3` successor — and per-row `ledger.consumer_fact.v1` / `chronicle.consumer_fact.v2` alike. Mixed-epoch feeds compile, and provenance records the observed manifest id, its epoch, the fact-key epoch labels present in the feed, any undeclared Chronicle-namespace key domain among them, and the per-row schema ids the rows declare. An identity in a Chronicle namespace that the registry does not carry is reported as `undeclared`, never counted as Chronicle-issued. Only the manifest id is gated: per-row schema ids and fact keys are carried as published, since real feeds mint rows in namespaces belonging to neither era. Nothing renames on disk: emitted ids, diagnostic field names, `populace_*` key namespaces, goldens and fixtures stay frozen at v1.
1 change: 1 addition & 0 deletions changelog.d/chronicle-dual-accept.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Give the Logbook store's environment variables a `LOGBOOK_*` dual-read window, with the `POPULACE_LEDGER_*` names honored under a once-per-process deprecation warning and preferred in the `tools/logbook.py --remote` help text (microcosm#632). These are the Logbook store's own credentials, unrelated to the Chronicle fact store. Every test in the build shard is now detached from a live Logbook by an autouse fixture that clears both generations of the variables and refuses unstubbed Logbook HTTP.
6 changes: 4 additions & 2 deletions logbook/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ for `published` and `certified` rows.

## The live store

The best-effort Supabase insert (`POPULACE_LEDGER_URL` +
`POPULACE_LEDGER_KEY`, the migration's insert-only `logbook_writer` role)
The best-effort Supabase insert (`LOGBOOK_URL` + `LOGBOOK_KEY`, the
migration's insert-only `logbook_writer` role; the ledger-era
`POPULACE_LEDGER_URL` / `POPULACE_LEDGER_KEY` stay honored for the
Logbook dual-read window (microcosm#632) and warn once per process)
uses the same scope rule as the archives after this PR's migration is applied
by the project owner. The writer key is unaffected: rows are still inserted
through the same role and the scope is derived from the hashed `pipeline`
Expand Down
Loading
Loading