Skip to content

Epoch prep: accept chronicle-era fact domains and schema ids; emit unchanged (#143, mechanism 1) - #228

Open
MaxGhenis wants to merge 10 commits into
mainfrom
epoch-dual-domain
Open

Epoch prep: accept chronicle-era fact domains and schema ids; emit unchanged (#143, mechanism 1)#228
MaxGhenis wants to merge 10 commits into
mainfrom
epoch-dual-domain

Conversation

@MaxGhenis

Copy link
Copy Markdown
Contributor

Summary

Mechanism 1, step 1 of #143 (Max's migration spec, 2026-08-20): dual-domain acceptance, emit unchanged. Fact identity migrates by epoch, never in place. This PR makes every Chronicle reader and validator accept both the ledger-era identifiers and their chronicle-era successors, while everything Chronicle emits stays ledger-named until a later, consumer-gated cutover flips one default.

  • chronicle/epoch.py — the single registry of frozen ledger identifiers and their successors: 15 hash domains (ledger.aggregate_fact.v2chronicle.aggregate_fact.v3, …, ledger.fact.v1chronicle.fact.v2, ledger.source_cell.v1chronicle.source_cell.v2, …) and the schema ids (ledger.bundle.v1chronicle.bundle.v2, ledger.consumer_fact.v1chronicle.consumer_fact.v2, ledger.relational.v1chronicle.relational.v2, ledger.source_package.v1chronicle.source_package.v2, policyengine_ledger.consumer_artifact.v2policyengine_chronicle.consumer_artifact.v3, policyengine_ledger.approved_agents.v1policyengine_chronicle.approved_agents.v2, …). Canonical payloads and digest suffixes are identical across epochs; only the domain or schema string changes. EMIT_EPOCH = Epoch.LEDGER is the one default a future cutover changes.
  • Readers accept both epochs: fact-identity builders and verifiers (consumer_contract.py, core.py), artifact and schema pins (policyengine_chronicle/consumer.py, schema.py), bundle ingestion (bundle.py), the relational load path (database.py), source-package readers and lineage (source_package.py, sources/cells.py, sources/rows.py, sources/offline_fetch.py), suite build (suite.py), and governance validation. Key verification recomputes under the epoch declared by the key's own prefix; a key or schema id outside both accepted forms is rejected with an error naming both.
  • Emitters unchanged: package scaffolds, relational and build artifacts, consumer artifacts all still emit ledger-era ids by default (the two package generators, sources/admin_packages.py and the Statbel builder, are pinned to the registry's ledger default). Chronicle-era output exists only behind an explicit epoch argument, to exercise acceptance before the gated flip.
  • Nothing else moves: no source values, reference periods, publisher provenance, fixture or golden bytes, frozen schemas, witnessed releases, CLI/env/bucket/Supabase names, or consumer-owned contracts. The retired profile-bearing consumer artifact and resolved-target/target-profile contracts stay retired (no successor ids minted for them).

Consumer side: PolicyEngine/microcosm#849 accepts the same pairs (including policyengine_ledger.consumer_artifact.v2 today and .v3 after the flip). The emit flip is a separate PR gated on microcosm and the Thesis-side validators (brier append gate, pin_ledger.py / verify_custody.py) accepting both epochs.

Chronicle Governance

  • Approved Chronicle agent role: ledger-contract-maintainer (identity, provenance, and consumer contracts). Its allowed_paths predate this work and cover only chronicle/core.py, chronicle/consumer_contract.py, policyengine_chronicle/** and two test files; the readers listed above and chronicle/epoch.py are outside that list and are disclosed here rather than the manifest being widened silently (same gap flagged on Register raw microdata releases as source artifacts: licence/access fields, hash-only registration, first UK registrations (#221) #227; follow-up to extend the globs).
  • Deterministic checks run: full suite 783 passed, 1 skipped; ruff check clean; changed Python files formatted; wheel build and clean-venv import smoke passed; a GitNexus impact audit drove additional validator and lineage fixes. Goldens byte-identical (asserted).
  • LLM judge verdicts:
    • ledger-contract: PASS — identity changes preserve provenance; digests unchanged; no Microcosm responsibility moves into Chronicle.
    • ledger-boundary: PASS — no reconciliation, aging, imputation, activation, or solver construction; no microdata content.

Tests

New and extended: test_chronicle_core.py (+304), test_chronicle_consumer_contract.py, test_chronicle_consumer.py, test_policyengine_chronicle_schema.py, test_chronicle_bundle.py, test_chronicle_database.py, test_chronicle_suite.py, test_chronicle_source_package.py, test_chronicle_source_cells.py, test_chronicle_offline_fetch.py, test_chronicle_artifacts.py, test_belgium_targets.py, test_chronicle_governance.py — chronicle-era rows and artifacts accepted end to end, mixed-epoch artifacts accepted, unknown domains rejected naming both forms, and the property that a chronicle-era key hashes the same canonical payload as its ledger-era key.

Authored by a Sol lane (no network); pushed and opened from the dispatching session.

🤖 Generated with Claude Code

MaxGhenis and others added 10 commits September 2, 2026 10:46
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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