Series catalog: canonical registry for the thesis fact ledger - #128
Series catalog: canonical registry for the thesis fact ledger#128MaxGhenis wants to merge 19 commits into
Conversation
|
An adversarial sol review of the first commit returned BLOCK with six findings; the two follow-ups respond to all of them. Review highlights and dispositions:
Regression tests ( 🤖 Generated with Claude Code |
Focused re-review — series-catalog v2Reviewed range: The seed, current period spellings, FNS geography split, BoE merge, M3 normalization, observed-row unit/cadence rejection, and ordinary stale-catalog CI check all improved. Those passes do not offset the identity failures below. Ranked findings1. [CRITICAL] The commits wholesale remint the registry, and
|
| Catalog identity | Raw/seed evidence | Result |
|---|---|---|
BoE Bank Rate (ledger/series_catalog.json:1619-1650) |
raw ledger/official_observations.jsonl:39-40 |
Correct GB/government/bank-rate identity; both spellings merge, count 2. |
Census M3 orders (ledger/series_catalog.json:1805-1835) |
raw :157; seed ledger/seeds/thesis_docket_series.json:546-568 |
Correct US/economy identity; dated concept and docket seed heal to one observed row. |
Census M3 shipments (ledger/series_catalog.json:1838-1868) |
raw :158; seed :571-592 |
Correct US/economy identity; one observed row. |
FNS national (ledger/series_catalog.json:2845-2873) |
raw ledger/official_observations.jsonl:6,103 |
Correct US-country/household identity; FY2024+FY2025, count 2. |
FNS California (ledger/series_catalog.json:2995-3024) |
raw :54 |
Exact state ID/name and household entity. |
FNS District of Columbia (ledger/series_catalog.json:3115-3144) |
raw :58 |
Exact state-level DC ID/name and household entity. |
FNS Guam (ledger/series_catalog.json:4405-4434) |
raw :61 |
Faithfully retains the raw state-level Guam ID/entity. |
Eurostat May final (ledger/series_catalog.json:2312-2341) |
raw :33 |
Correct EA/household/HICP-all-items identity. |
Eurostat June flash (ledger/series_catalog.json:2344-2374) |
raw :134 |
Correct EA21/economy identity; no longer collapsed with May final. |
Old DOL initial claims (ledger/series_catalog.json:2181-2210) |
raw :18 |
Faithful US/ui_initial_claimant/month metadata. |
Standard weekly initial claims (ledger/series_catalog.json:5520-5551) |
raw :105-106,144,148,162 |
Correct US/ui_claimant/week-ending identity, count 5. |
Second old initial-claims spelling (ledger/series_catalog.json:5552-5584) |
raw :44 |
Period token strips, but row remains separate despite aliasing the first old-DOL concept. |
FNS now splits correctly: 55 observations become 54 geographic identities — one national identity with two periods plus 53 state-level jurisdiction identities. The raw and catalog geography/entity sets agree exactly.
The initial-claims three-row split reflects inconsistent raw metadata rather than three cleanly distinct economic concepts:
- raw row 18:
dol.eta..., entity roleui_initial_claimant, period typemonth; - raw row 44:
us.dol..., the sameui_initial_claimant/monthdimensions, and an alias back to the first concept; - raw rows 105-106, 144, 148, 162:
us.dol..., roleui_claimant, properweek_endingcadence.
The year-bearing token defect is fixed, and future observations matching each normalized bucket will not mint a UUID per week. But the existing semantic duplication is not healed: rows 1 and 2 still have separate UUIDs even though one aliases the other's canonical concept.
Six prior dispositions
| Prior issue | Re-review result |
|---|---|
| 1. Untracked seed / bare check | Partial pass. Seed is tracked, hashed, and bare check uses it; a byte change fails. Missing seed plus regenerated reduced catalog still passes. |
| 2. Period spellings | Qualified pass. All live spellings normalize; BoE and M3 heal and suspects are zero. Initial claims remains three rows, and false-positive stripping is still possible/unflagged. |
| 3. Rename/curation/collision | Fail. The commits remint every UUID; auto aliases can merge/flip concepts; geography can move; parsed-equivalent UUID collisions pass; current alias-linked duplicates persist. |
| 4. Concept-only geography collapse | Partial pass. Current FNS and Eurostat rows are correctly split, but fallback ignores dimensions and geography vintage is absent from the key. |
| 5. Modal unit/cadence | Pass. _modal is gone and synthetic observation unit and cadence conflicts both hard-fail through _sole (scripts/build_series_catalog.py:257-266,402-403). |
| 6. Provenance / no fabricated default geography | Pass narrowly. Both digests match exact bytes; seed-only undeclared countries remain null; UUID state is correctly described as catalog state rather than input derivation. |
CI and validation
The CI step is wired correctly and unconditional for pushes and pull requests: it runs bare --check and the focused tests with no error suppression (.github/workflows/ci.yml:37-40). An ordinary stale derived field, one-byte seed change, observation change, or missing seed against the current 201-row catalog exits 1 and fails the step.
Its boundary is material: because the catalog under check supplies UUID/alias/canonical state, valid UUID remints and persisted alias changes are considered current. Both the 6ab4fbe and HEAD UUID-disjoint catalogs pass the HEAD checker. Missing seed plus a consistently regenerated 155-row catalog also passes. Therefore CI is a derived-data freshness check, not an identity-continuity or required-seed check.
Validation record:
python3 scripts/build_series_catalog.py --check PASS (201)
pytest tests/test_build_series_catalog.py -q PASS (34)
python3 -m doctest scripts/build_series_catalog.py PASS
ruff check script + focused tests PASS
git diff --check 9b4329e..HEAD PASS
one-byte seed change vs current catalog FAIL as stale (correct)
missing seed vs current catalog FAIL as stale (correct)
missing seed + regenerated 155-row catalog INCORRECT PASS
uppercase/hyphenless/braced duplicate UUID INCORRECT PASS
HEAD checker against 6ab4fbe UUID catalog INCORRECT PASS (201)
current FNS split PASS (54 identities / 55 observations)
current UUID syntax/version/parsed uniqueness PASS (201)
final worktree CLEAN
Merge gate
Do not merge until, at minimum:
- The surviving UUID for every pre-existing/merged identity is explicitly curated and the catalog is rebuilt without wholesale reminting; continuity must be checked against the prior committed registry, not only against itself.
- Alias fallback is scoped to compatible identity dimensions (with an explicit docket-placeholder enrichment rule), and derived source concepts are not treated as curated synonyms without provenance or review.
- UUIDs are required to use canonical text and uniqueness is keyed by parsed UUID value.
- Geography boundary vintage participates in identity and required seed absence is a hard error.
- Period normalization either reserves date-shaped segments by contract or flags mismatches/false-positive candidates.
Final recommendation: BLOCK.
Disposition — series-catalog v3 (
|
|
v3 addendum ( |
|
Third-review attempt 1 crashed mid-run (harness stall on a full-suite pytest invocation) after surfacing four reproducible findings; all four are fixed in
Also correcting a disposition count: the one-time migration removed 82 machine alias instances, not 85 as written. State at |
Third adversarial review (sol, ultra) — against
|
Disposition — third review answered (
|
Fourth adversarial review (sol, ultra) — against
|
Disposition — fourth review answered (
|
Fifth adversarial review (sol, ultra) — against
|
Disposition — fifth review answered (
|
Disposition — sixth review answered (
|
Seventh adversarial review (sol, ultra) — against
|
Disposition — seventh review answered (
|
Eighth adversarial review (sol, ultra) — against
|
Disposition — eighth review answered (
|
One row per series family mined from official_observations.jsonl (period
tokens stripped to {P}; release vintages and concept spellings never
merged mechanically), seeded with docket-only rows for Thesis docket
series not yet observed. UUIDs mint once and persist across regeneration,
keyed by concept. Regenerate with scripts/build_series_catalog.py;
--check verifies currency.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Responds to the 2026-08-01 adversarial review (all six findings): - identity key now (concept, geography, entity) per the fact-identity ADR (FNS state error rates split into per-state rows; Eurostat flash/final separate); unit/cadence conflicts are hard errors, never modal picks - token grammar covers every live spelling the review found (underscore dates, abbreviated months, week_ending, month ranges, after_* rate states) plus a semantic pass deriving tokens from each row's own period; surviving year-bearing segments are flagged in suspect_segments (now 0) - UUIDs inherit by identity key, then by unique concept/alias match, so upstream renames and hand merges keep identity; curated aliases persist across regeneration; ambiguous matches and UUID collisions are hard errors; --check validates UUID syntax/version/uniqueness - the docket seed is committed (ledger/seeds/) and digest-bound in the header, so bare --check covers the full input set; docket rows without a declared country carry null geography rather than a fabricated one - regression tests from the review's token table + rename/merge/collision cases; CI runs the seeded --check and the test file Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An observed spelling that inherits identity through a curated alias or a unique prior concept/alias match now keeps the prior row's canonical concept — curation owns naming; observed spellings fold into aliases. Buckets landing on one canonical identity merge; inheriting two different prior UUIDs is a hard error. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Answers the second adversarial review (BLOCK) point by point: 1. Identity continuity is now checked against a prior committed registry, not the catalog itself: ledger/series_uuid_registry.jsonl is an append-only minting ledger (one line per binding; supersedes chains per identity). The builder inherits UUIDs from the registry, --check proves catalog/registry agreement plus registry append-only vs git HEAD, and CI proves append-only vs the PR base. All 201 committed UUIDs are preserved (bootstrap = the 2859ecb catalog's bindings, 201/201 carried forward). A write that would change or drop an existing identity's UUID refuses without --allow-remint --remint-note, which records a supersede event. 2. Concept/alias inheritance is scoped to the same (geography, entity); the only exception is docket-placeholder enrichment (docket-only row, entity unknown, geography absent or equal on level/id). 3. measure.source_concept values are provenance (new per-row source_concepts field), never aliases and never identity. The two docket links that machine labels carried (BLS AHE private, StatCan EI beneficiaries) are now explicit curated aliases. 4. UUID uniqueness keys on the parsed 128-bit value and canonical lowercase text is required everywhere (catalog and registry). 5. A missing docket seed is a hard error in every mode. 6. Geography boundary vintage joins the identity key per the fact-identity ADR. 7. Date-shaped segments strip only when they denote the row's own period (direct spelling or overlapping calendar window); disjoint or impossible tokens stay in the identity and are flagged as suspects. Also fixes a latent v2 bug where MONTHS_ABBREV carried 13 entries, shifting derived abbreviations for October-December. Regenerated artifact: 201 series (155 observed, 46 docket-only), suspects=0, ambiguous_aliases=0, minted=0, superseded=0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Self-found follow-up to v3: deleting the committed catalog and rebuilding from the registry alone loses curated naming/alias memory, so a previously renamed identity would re-key away from its registry binding and fresh-mint a UUID while the old binding goes dormant — a silent remint that survives catalog/registry agreement AND the append-only checks (the registry only grows). Not reachable today (canonical == derived concept for all 201 rows) but opens after the first rename curation. The builder now refuses to rebuild when the prior catalog has no rows while the registry holds bindings, absent --allow-remint --remint-note. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…it, push gate The first run of the third adversarial review crashed before its REPORT but surfaced four reproducible findings; all fixed: 1. Dimension keys were lossy pipe-joins, so crafted geography/entity values could collide across fields (level "a|b" + id "c" vs "a" + "b|c") and None was indistinguishable from "None". Keys are now JSON-encoded (injective, sortable). No identity moved; the only catalog byte effect besides the new header field is the two Eurostat rows swapping into natural EA < EA21 order. 2. The bare-registry rebuild gate only fired on a zero-row catalog: a PARTIAL deletion still silently re-minted renamed identities. The registry now tracks binding liveness with explicit retire/revive events: dropping a row appends a noted retire line (gated by --allow-remint), a returning identity revives its ORIGINAL uuid automatically, and --check enforces the reverse agreement direction — every live binding's uuid must appear on some catalog row. 3. Window-overlap stripping was silent; a statute/cohort/edition label spelling a window that overlaps the row's own period is mechanically indistinguishable from a period label, so every overlap-based strip is now reported in the catalog's overlap_stripped_segments audit list (currently exactly the eight live segments). 4. The CI registry append-only gate ran only on pull requests; direct pushes now verify against github.event.before, and an unfetchable before-sha (history rewrite) fails loudly instead of skipping. 66 tests; ruff, doctest, --check, byte-idempotence all green; uuids/identities unchanged 201/201. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…full strip audit All nine findings from the third review (BLOCK, CRITICAL) addressed: 1. [CRITICAL] Ordinary mints can no longer reuse ANY bound UUID (parsed 128-bit ownership map at registry load + build-time refusal), so re-keyed identities cannot swap or inherit foreign UUIDs; forged registry mint lines sharing a UUID are rejected at load. The one sanctioned reuse is a retire + succeeds event pair (docket placeholder enrichment), and live bindings' UUIDs must be unique. Liveness/agreement are now IDENTITY-AWARE bijections: each live binding needs a catalog row at exactly its identity with exactly its UUID, both directions. 2. [HIGH] The registry-introduction commit cannot be gated by append-only (no prior registry exists), so the reviewed 201-binding identity->uuid map is pinned as a sha256 anchor in the test suite; changing any binding must edit the anchor in the same diff. New- branch pushes (all-zero before-sha) now fall back to verifying against the canonical branch's registry instead of skipping. 3. [HIGH] Docket name claiming is dimension-scoped: an entry declaring a country is only claimed by rows in that country, duplicate docket series ids are hard errors. 4. [HIGH] Placeholder enrichment refuses a declared, conflicting geography vintage. 5. [HIGH] The strip audit now covers EVERY stripped spelling (stripped_segments replaces overlap_stripped_segments; 27 live entries), and malformed periods (month 13, impossible week dates) are hard errors instead of token factories. 6. [MEDIUM] A remint of a returning retired identity stages revive THEN supersede, and stage() revalidates the entire registry before any bytes are written. 7. [MEDIUM] Registry lines parse with duplicate-member rejection. 8. [MEDIUM] Registry bytes must be LF-only and newline-terminated; .gitattributes pins eol for the catalog, registry, and seed. 9. [LOW] Notes must contain alphanumeric content; no-op supersedes (uuid == supersedes) are rejected. 81 tests; ruff, doctest, --check, byte-idempotence green; identity map unchanged (201/201, minted=0, superseded=0; registry still exactly the 201 bootstrap mints). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The review-context-*.md files are per-session adversarial-review briefing materials (prior review texts + instructions), not repo content; an over-broad git add swept them into the previous commit. Ignore the pattern so it cannot recur. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…text
All six findings addressed; identity map unchanged again (201/201,
minted=0, superseded=0):
1. [CRITICAL] Superseding UUIDs must be new to the registry outright —
same-identity U1->U2->U1 cycles that restored the anchor while
hiding the excursion are rejected at load and at write.
2. [HIGH] succeeds lineage is linear and shape-bound: a retired
predecessor is consumed exactly once (forks rejected), must share
the successor's concept, must be entity-less, and must match on
geography level/id (and vintage when declared); the builder's
enrichment branch enforces the same shape before planning events,
so status-flipped committed rows cannot smuggle a UUID across
dimensions.
3. [MEDIUM] A literal "{P}" segment in observation identifiers is a
reserved-placeholder hard error instead of a silent identity
collapse.
4. [MEDIUM] stripped_segments is now an occurrence map (spelling ->
sorted canonical concepts touched), so a second identity absorbing
the same spelling is visible instead of vanishing into a set.
5. [MEDIUM] Calendar tokens live in a 1900-2999 window: fy0000/0000_01/
week_ending_0001_01_01 neither strip nor crash; annual rows gain
bare-year direct variants so year-suffixed annual ids no longer
split per year.
6. [MEDIUM] The all-zero-push fallback's trust anchor is documented:
branch protection on codex/thesis-ledger-facts forbids deletions
and force pushes (verified live via the GitHub API, which the
review sandbox could not reach).
Also raises the supersede/retire note floor to real content (>=8 chars,
>=4 alphanumeric). 90 tests; ruff, doctest, --check, byte-idempotence
green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… are strict
All four findings addressed; catalog and registry bytes unchanged
(201/201, minted=0, superseded=0):
1. [CRITICAL] A handed-over lineage is terminal: reviving a consumed
predecessor is rejected at load, the builder mints FRESH for
observations reappearing under a consumed identity (never revives),
the writer refuses supersede replacements the registry has ever
seen, and live-UUID uniqueness now holds after EVERY event prefix,
not just the final state — the retire/succeed/supersede/revive
detour that recreated a banned U1->U2->U1 excursion is closed at
four layers.
2. [HIGH] The reserved '{P}' guard is central: observation identifiers,
docket series names, and registry concepts (including succeeds
targets) all reject the placeholder segment.
3. [MEDIUM] Calendar clamp completed: month-name tokens and quarter
period descriptors guard the 1900-2999 window (january_1899 /
january_3000 / quarter 1899-01 / 3000-01 neither strip nor crash),
and the year-hint widens to 1800-3099 so impossible out-of-window
tokens (1899_13, 2999_13, 3000_13) are suspect-flagged.
4. [MEDIUM] Strict JSON and identity domains: NaN/Infinity rejected on
every parse (registry lines and observations) and every render
(allow_nan=False), and geography/entity fields must be null or
NONEMPTY strings with known keys — an empty-string vintage can no
longer coexist as a distinct identity beside an absent one, in the
registry or in observations.
103 tests; ruff, doctest, --check, byte-idempotence green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…strict inputs
All five findings addressed:
1. [HIGH] The StatCan docket alias moves to the lineage its own
sourceBinding names: the seed binds v64549350, whose observations
live on the ei_beneficiary row, so the curated alias now sits there
(catalog-only change; no UUIDs move; 46 docket-only stable).
2. [HIGH] A handed-over key that re-forms takes an explicit
'reclaimed' mint: fresh UUID, substantive note, valid only where
the predecessor was consumed by a succeeds event — the previous
markerless mint died in the writer's own stage revalidation.
3. [HIGH] Strictness is uniform: docket seeds and observation rows
parse with duplicate-member rejection and NaN/Infinity refusal;
observation dimensions are validated RAW (""/[] fail loudly instead
of collapsing to null); succeeds objects are schema-checked
(identity fields only, nested dimension domains).
4. [MEDIUM] Registry lines split on physical LF only, so vertical-tab
or U+2028 separators can no longer hide extra events on one line.
5. [MEDIUM] The new-branch fallback fetch is fully qualified
(refs/heads/...), immune to same-named tag shadowing.
113 tests; ruff, doctest, --check, byte-idempotence green; identity
anchor unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s schema-floored All six findings addressed; catalog and registry bytes unchanged: 1. [HIGH] Consumption clears on a valid reclaim: a reclaimed identity is an ordinary lineage again, so later drops retire (gated) and later returns REVIVE the same UUID — the retire/reclaim churn loop that minted fresh UUIDs without ceremony after every approved retirement is closed. A second reclaim without a new handover is invalid. 2. [HIGH] Geography requires level+id and entity requires name+role (when present) in observations AND registry entries — two countries with null ids can no longer merge into one identity. 3. [MEDIUM] A first-event 'reclaimed' marker (true or false) is excluded from the mint branch and fails with "no prior binding". 4. [MEDIUM] Observation lines split on physical LF only, matching the hardened registry parser; VT/U+2028 separators are parse failures. 5. [MEDIUM] Schema floors: period must be an object with a known type and a value; measure.unit and source.source_name must be nonempty strings or null; docket extras must be an object — falsey collapse paths are gone. 6. [LOW] A succeeds object without a concept is a schema finding, not a KeyError. 124 tests; ruff, doctest, --check, byte-idempotence green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gree by number
All eight findings addressed; catalog and registry bytes unchanged:
1. [HIGH] An empty dimension object is invalid EVERYWHERE (observations,
registry entries, succeeds objects): {} can no longer spell the null
identity a second way, so null-vs-{} pairs cannot collapse into one
row.
2. [MEDIUM] Schema floors extended: period {} rejected; docket.series
must be a list of objects with nonempty string names;
extras.targetUnit and measure.source_concept must be nonempty
strings or null.
3. [MEDIUM] render_entry refuses malformed events (non-true markers,
mixed markers) instead of normalizing them, so stage() can never
sanitize an invalid caller event into a different valid one.
4. [MEDIUM] Year-grained labels agree by NUMBER: an fy token on a
calendar-year row strips only when the years are equal — adjacent
years can no longer merge end to end through fiscal-window overlap
(mismatches stay in the identity and are flagged).
5. [MEDIUM] Conflicting geography display names for one level/id/
vintage are a hard error, never order-dependent metadata.
6. [LOW] Genesis review-pinning acknowledged as designed (anchor +
review; no mechanical change possible for a first commit).
7. [LOW] Covered by 1: succeeds.geography {} rejected.
8. [LOW] Period-SHAPED but unparseable segments (fy0000, 9999_13) are
suspects even outside the year-hint window.
134 tests; ruff, doctest, --check, byte-idempotence green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ment The PR base (codex/thesis-ledger-facts) appended three observations while the branch was under review, and the Thesis docket registered irs.actc.total_claims (S.3596 ACTC conditional pair, thesis#132). This data wave rebases onto the moved base and regenerates: - census.construction_spending.total_mom and bls.jolts.hires_rate are the enrichment machinery's first LIVE run: each new observation claimed its docket placeholder via a retire + succeeds event pair, keeping the placeholder's UUID. - bls.jolts.job_openings.june_2026 folded into the existing observed row (count 2) with no identity motion. - irs.actc.total_claims joins the seed snapshot verbatim from the thesis docket and mints one fresh docket-only placeholder. 202 series (157 observed, 45 docket-only); suspects still zero; stripped-spelling key set unchanged; five append-only registry lines (206 total); 199/201 prior identity keys carry their UUIDs unchanged and the two enriched placeholders keep theirs under upgraded keys. The identity anchor moves in the same diff as the events that justify it. 134 tests; ruff, doctest, --check, byte-idempotence green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eck names All three findings addressed; artifact bytes unchanged: 1. [HIGH] Identity strings must equal their stripped form — concepts, record ids, docket names, registry concepts, and every dimension field value. A vintage of "current " can no longer mint a parallel UUID beside "current". 2. [MEDIUM] Alias-linked bucket merges union geography display names and re-run the conflict check, so contradictory place names cannot ride in through a curated alias. 3. [LOW] Shape detection mirrors the parser's after_-qualifier stripping, so qualified out-of-window noise (after_mpc_9999_13) is suspect-flagged like its bare form. 139 tests; ruff, doctest, --check, byte-idempotence green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
0879e1c to
2ece165
Compare
Ninth adversarial review (sol, ultra) — against
|
Disposition — ninth review answered (
|
…arch periods All three findings addressed: 1. [HIGH] Identity strings must be NFC-normalized with no invisible or control characters (category Cc/Cf) — NFC-vs-NFD twins and zero-width-suffixed spellings can no longer mint parallel lineages. Enforced with the whitespace rule everywhere identity strings enter: concepts, record ids, docket names, registry + succeeds concepts, and every dimension field value. 2. [HIGH] Series identity is built from the supersede-aware CURRENT view (check_thesis_facts_append.effective_current_rows — the same view the ledger's aggregate-fact validation uses), so a correction row retires its predecessor instead of leaving a stale assertion alive or hard-erroring as a unit conflict. The catalog now states both counts: observation_rows (raw journal lines, digest-coupled) and current_assertion_rows. Live data is correction-free today, so the artifact change is exactly that one header line. 3. [MEDIUM] Period types align with arch/core.py: calendar_year and tax_year are accepted; both get bare-year direct variants, and a tax year assumes NO calendar bounds — nothing else strips on a tax-year row and date-shaped segments are flagged for curation. 142 tests; ruff, doctest, --check (202), byte-idempotence green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tenth adversarial review (sol, ultra) — against
|
Disposition — tenth review answered (
|
Eleventh adversarial review (sol, ultra) — against
|
…nputs validated
All three findings addressed; artifact bytes unchanged:
1. [HIGH] An alias naming another row's canonical concept in the SAME
geography/entity is contradictory curation and a hard error (finish
the merge or drop the alias); across dimensions the name is surfaced
in ambiguous_aliases, which now lists any alias that resolves to
more than one row — via multiple alias holders OR by colliding with
a canonical concept — and such names never drive inheritance or
docket claims.
2. [HIGH] Curated aliases pass the full identity-string gauntlet
(nonempty, stripped, NFC, no invisible characters, no {P}) at
catalog load, closing the gap that let NFD or zero-width aliases
ride in beside NFC canonicals.
3. [MEDIUM] The supersede-aware boundary enforces its own
preconditions before calling effective_current_rows: assertion ids
must be nonempty unique strings, supersedes links must resolve, and
self-references and cycles are hard errors — standalone
--observations runs are as safe as gate-validated ones.
145 tests; ruff, doctest, --check (202), byte-idempotence green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Disposition — eleventh review answered (
|
Twelfth adversarial review (sol, ultra) — against
|
…ecycle whole All four findings addressed; artifact bytes unchanged: 1. [HIGH] Ambiguous names (multi-holder aliases, or aliases colliding with any canonical concept) are computed at catalog load and excluded from EVERY name-driven tier — fallback matching and docket claims alike. An incoming spelling of an ambiguous name inherits nothing; when that produces a same-dimension canonical/alias collision, the contradiction hard-error fires instead of a silent theft. The reviewer's cross-dimension repro now refuses loudly. 2. [MEDIUM] A retired identity can RETURN via succeeds: the grammar accepts a succeeds event on a retired previous binding, so the placeholder -> enrich -> withdraw -> reclaim -> re-enrich lifecycle completes without manual catalog surgery. 3. [MEDIUM] Fiscal labels never assume a jurisdiction: year-grained tokens (fy/tax/calendar) agree only by NUMBER against year-grained periods; on month/quarter/week rows fiscal labels stay in the identity and are flagged — the hard-coded US October-September window is gone. 4. [LOW] Registry events have a closed keyset: undeclared fields, null-valued markers, and notes on kinds that take none are all load errors. 149 tests; ruff, doctest, --check (202), byte-idempotence green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Disposition — twelfth review answered (
|
Thirteenth adversarial review (sol, ultra) — against
|
…urrency Both findings addressed; artifact bytes unchanged: 1. [HIGH] An identity can never succeed itself: self-handover forged consumption and unlocked a ceremony-free reclaim onto a fresh UUID. The reviewer's five-event forgery is now a load error, with the regression pinned. 2. [LOW] --check compares raw bytes, not universal-newline text, so a CRLF-converted catalog is stale even where Git attributes cannot protect a raw copy. 151 tests; ruff, doctest, --check (202), byte-idempotence green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Disposition — thirteenth review answered (
|
Adds
ledger/series_catalog.json— the canonical series registry the observation file never had — plus its generator.Why: ThesisInstitute/thesis#112. Thesis's docket (75 series) has been acting as a second identity system; bill metrics could only wire to those 75. The catalog makes the ledger the single identity home: one row per series family with a UUID minted exactly once, so downstream consumers (docket, bill mappers, permalinks) reference series by UUID/concept instead of minting their own.
What's in a row:
uuid, canonicalconcept,family_patterns(period tokens{P}-stripped from observed concepts:fy2026,2026-05,may_2026,q1_2026/2026_q1,week_…), modalunit/cadence/geography/entity,aliases(observed concept spellings + source labels likeUNRATE),rid_patterns(source_record_id families, for grading linkage), first/last observed period, count,status(observed|docket-only).Deliberate non-moves: release vintages (
first_print,third_estimate) and concept-spelling drift (abs.cpi.all_groups.yoyvsabs.cpi_indicator.allgroups.yoy) are never merged mechanically — those are curation judgments done by hand-merging rows (survivor keeps its UUID, absorbed spellings become aliases).Determinism: regeneration is byte-identical for the same inputs; UUIDs persist keyed by concept;
--checkmode verifies currency (CI can adopt it);observations_sha256binds the catalog to the exact observation bytes it was built from. Families differing only in{P}placement merge (same series, two period formattings) — the 168 rows' 107 raw concepts collapse to 93 observed series + 48 docket-only = 141.Consumer side (lands in thesis after this):
pin_ledger.pypins the catalog alongside the observations; every docket entry gainsledger: {uuid, concept}; a build-time containment test enforces docket ⊆ catalog; the bill mapper matches hints against catalog ∪ docket.🤖 Generated with Claude Code