Skip to content

Let a fact record the pair it was stored from - #404

Draft
MaxGhenis wants to merge 2 commits into
codex/thesis-ledger-factsfrom
feat/recorded-base-unit-pair
Draft

MaxGhenis wants to merge 2 commits into
codex/thesis-ledger-factsfrom
feat/recorded-base-unit-pair

Conversation

@MaxGhenis

Copy link
Copy Markdown
Contributor

Status: draft. This is part of the base-unit migration Max ruled on 2026-10-21 ("why not enter all chronicle facts as numbers (ie 1000000)?", then "Yes, migrate everything"). No data changes. It lands after #306 and before any row carries the pair.

What this PR does

Chronicle's Thesis ledger is moving to plain numbers: 7716000 in count, not 7.716 in millions. Each row keeps what it was recorded as in source.recorded_value and source.recorded_unit.

  • SourceProvenance accepts the pair. arch/store.py and tests/test_policyengine_ledger.py build it with **source, so without this change they refuse the first row that carries it. fact_to_mapping leaves out an empty pair, so facts with no pair serialize as they did and the bundles don't move.
  • A new test holds the ledger to its pairs. Every recorded pair must state the stored value in exact decimals, and every unit_restatement must repeat the pair of the row it names. The test covers tampered rows too.
  • Two tests stop assuming a row is never restated. I found both by running the suite against a simulated post-migration ledger, and both would have turned Arch checks red at the migration:
    • The strip fixture test counted lines with a given week, and a restatement adds a second one.
    • The strict xfail that documents full-file mode rewrites line 1. Once a later row supersedes line 1, the rewrite breaks that link and the check catches it, so the xfail would pass. It now rewrites a row nothing names, which is the case it documents.

No gate-surface file changes.

Invariants

  • For every row with a recorded pair, Decimal(value) == Decimal(recorded_value) × 10^power, and measure.unit is the base unit of recorded_unit.
  • A unit_restatement names an earlier row in a scaled unit and repeats that row's value and unit as its recorded pair.
  • A fact with no pair serializes without those keys, and a fact with a pair survives fact_to_mapping and fact_from_mapping.

Evidence

  • On this branch: tests/test_policyengine_ledger.py, test_build_series_catalog.py and test_thesis_append_adversarial.py give 300 passed, 5 xfailed. test_thesis_append_shim_isolation.py and test_receipt_shim_transparency.py give 53 passed.
  • Simulated post-migration ledger (this branch merged with Resolve catalog supersede links against every row's effective assertion id #306, plus 82 restatement rows built from the real ledger, with the catalog regenerated):
    • build_series_catalog.py --check passes.
    • The first three files above plus test_release_chain.py and test_arch_facts.py give 373 passed, 5 xfailed.
    • The shim tests fail in that simulation because it cuts no release for the new lines: "HEAD release lineCount 253 does not match working-tree line count 335". The real migration cuts one, and its append gate runs those checks before it merges.

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits November 2, 2026 19:35
Chronicle's Thesis ledger is moving to plain numbers: 7716000 in count, not
7.716 in millions. A row in a base unit keeps what it was recorded as in
source.recorded_value and source.recorded_unit, so SourceProvenance has to
accept them, or arch/store.py and the ledger tests would refuse the row.

Facts with no pair serialize as they did, so the bundles don't move.

A test holds every recorded pair in the ledger to its stored value in exact
decimals, and holds every unit_restatement to the row it names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A restatement adds a second line with the same week as the row it restates,
so the strip fixture test now counts records rather than lines.

The full-file test rewrites line 1 to show that mode trusts the manifest. Once
a later row supersedes line 1, the rewrite breaks that link and the check
catches it, which would pass this strict xfail. The test now rewrites a row
nothing names, which is the case it was written to document.

Co-Authored-By: Claude Opus 5.5 <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