Skip to content

v0.2.0 — M2: replay and regression

Choose a tag to compare

@kjpatel kjpatel released this 29 Jul 17:32

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 verify

124 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