v0.2.0 — M2: replay and regression
Decisions were already deterministic in v0.1.0. This milestone makes that property continuously provable, adds the time dimension the architecture was designed around, and turns rule changes into a reviewable event.
What is new
Bitemporal fact store (store/) — append-only event log on SQLite with a Postgres-portable schema. Facts are never mutated; corrections supersede. Projections take both dials: as_of(case, knowledge, effective). The query the architecture exists to answer now runs: what would we have concluded in March, knowing what we knew in March? Ingest a correction recorded later and the old knowledge horizon still replays the original decision byte-for-byte, while the new horizon reflects the correction. Correction-of-a-correction chains are walkable in either direction. The store never reads the wall clock — knowledge time always comes from the facts themselves.
Golden corpus (golden/) — 250 committed synthetic cases with their adjudicated receipts, generated from a seeded deterministic generator (python -m duly_assurance generate). Coverage is deliberate: notice margins crossing each state threshold in both directions, 60+ cases evaluating under superseded rule versions, nonpayment grounds that exercise exception-and-defeat chains, and TRID amounts above, at, and below baseline. Regeneration from the same seed is byte-identical.
Replay verification (python -m duly_assurance verify) — re-adjudicates all 250 cases and asserts byte-identical receipts, catching both body edits and hash edits. Runs in CI on every push, so determinism is proven continuously rather than asserted in a README.
Rule-change impact analysis (python -m duly_assurance impact) — re-adjudicates the corpus under working-tree rule packs and reports every decision that changes. PRs touching rulepacks/ now get a sticky comment: "N of M decisions flip" with before/after receipts, plus reasoning-only changes (same verdict, different rules fired) tracked separately — the distinction an auditor cares about. Rule maintenance becomes a reviewed event with a visible blast radius.
Florida and California rule packs — verified against official statute text, not model recall:
| State | Nonrenewal minimum | Nonpayment | Source |
|---|---|---|---|
| New York | 45 days | 15 days | N.Y. Ins. Law § 3425 |
| Florida | 120 days | 10 days | Fla. Stat. § 627.4133(2)(b) |
| California | 75 days | 10 days | Cal. Ins. Code § 678(c), § 677.4 |
Three jurisdictions, three thresholds, one generic deficiency rule — the long-tail argument made concrete. Sub-cases outside the fact vocabulary carry explicit # SCOPE: comments; unconfirmed enactment dates carry # TODO(verify): markers rather than guesses.
Pack validator: provable disjointness — equal-priority rules are permitted when the validator can prove they never co-apply, via disjoint effective windows (rule versioning) or contradictory equality guards (jurisdiction scoping). It earned its keep immediately: it caught that the NY nonpayment rule lacked a state guard and would have applied its 15-day figure to Florida and California cases.
Verify it yourself
uv sync
uv run pytest kernel/tests demo/tests assurance/tests store/tests
uv run python -m duly_assurance verify124 tests. CI runs the full suite plus replay verification on every push.
Honest labels
Corpus cases are synthetic and use attestation grounding rather than fabricated documents. Extraction remains a scripted stub pending real adapters (M3). One pre-2026 NY rule version is marked DEMO-SYNTHETIC and exists only to demonstrate effective-dated replay. Breaking changes expected until v1.0.
Full changelog: v0.1.0...v0.2.0
🤖 Generated with Claude Code