Skip to content

fix(temporal): audit set_valid_to closes with an entity_history snapshot#374

Merged
tcconnally merged 1 commit into
mainfrom
fix/wave5-set-valid-to-audit
Jul 2, 2026
Merged

fix(temporal): audit set_valid_to closes with an entity_history snapshot#374
tcconnally merged 1 commit into
mainfrom
fix/wave5-set-valid-to-audit

Conversation

@tcconnally

Copy link
Copy Markdown
Collaborator

Closes #373

Problem

set_valid_to enforced tighten-only semantics but wrote no entity_history snapshot, so a close was invisible to transaction-time reconstruction:

  1. as_of/bitemporal_at at a tx instant before the close reported the close anyway — the live row's valid_to was the only record and it wasn't versioned.
  2. The fix(temporal): audit identical-body re-asserts that move a closed valid period #372 audited-re-assert snapshot then conflated the close into pre-close history: an audited extension's snapshot carried the closed valid_to for the entire pre-extension recorded window.

Fix (mirrors the #371/#372 audited stamp)

src/db.rs, set_valid_to:

  • An effective close/tighten now snapshots the pre-change row to entity_history (invalidated = now with the anti-zero-width bump past the old recorded_at, superseded_by = self), advances the live row's recorded_at, links supersedes, and pins valid_from_unix_ms = COALESCE(valid_from_unix_ms, <old effective from>) so NULL-valid_from rows (pre-v9, still writable by a legacy binary) don't have their effective opening shift to now — the same hardening as fix(temporal): audit identical-body re-asserts that move a closed valid period #372's finding 1. Snapshot + stamp run in one transaction.
  • Tighten-only guards untouched — nothing newly accepted or rejected. A no-op call (the earlier stored close is kept) returns early and writes no snapshot.
  • The verbatim snapshot INSERT shared with remember_impl is factored into a private helper snapshot_live_row_to_history (used by both; no behavior change on the remember path).
  • mimir_supersede funnels through set_valid_to and inherits the audit for free.

Tests (src/tools.rs)

  1. set_valid_to_close_is_audited_and_noop_is_not — close an open fact → exactly one snapshot; bitemporal_at at a pre-close tx instant reconstructs the fact open (valid_to: null, from the snapshot), current knowledge refuses the closed-out instant and reports valid_to = close in-period; same-value and would-extend no-ops add nothing.
  2. supersede_close_inherits_the_audit_snapshot — default supersede writes exactly one snapshot for the old fact; pre-supersede knowledge still believes it open.
  3. Existing fix(temporal): audit identical-body re-asserts that move a closed valid period #372 audited-re-assert tests pass unchanged (their history baselines are captured after the close, so counts stay consistent); all supersede/tighten-only and as_of reconstruction tests untouched.

Verification

  • cargo +stable-x86_64-pc-windows-msvc test --release: 254 passed, 0 failed, 2 ignored (baseline 252 + 2 new)
  • python benchmark/temporal/gate.py: 100% (20/20 checks) PASS
  • CHANGELOG.md updated under Unreleased → Fixed.

🤖 Generated with Claude Code

…hot (#373)

set_valid_to enforced tighten-only semantics but wrote no entity_history
snapshot, so a close was invisible to transaction-time reconstruction:
as_of/bitemporal_at at a tx instant BEFORE the close reported the close
anyway (the live row's valid_to was the only record and unversioned),
and the #372 audited-re-assert snapshot then conflated the close into
the whole pre-extension recorded window.

Mirror the #371/#372 audited stamp in set_valid_to: an effective
close/tighten snapshots the pre-change row (invalidated = now with the
anti-zero-width bump, superseded_by = self), advances the live row's
recorded_at, links supersedes, and pins a still-NULL valid_from to the
old effective opening so pre-v9 rows don't shift. The tighten-only
guards are untouched; a no-op call (earlier stored close kept) writes
no snapshot. mimir_supersede funnels through set_valid_to and inherits
the audit for free.

The verbatim snapshot INSERT shared with remember_impl is factored into
snapshot_live_row_to_history.

Tests: set_valid_to_close_is_audited_and_noop_is_not (exactly one
snapshot; bitemporal_at at a pre-close tx instant reconstructs the fact
OPEN, current knowledge shows the close; same-value and would-extend
no-ops add nothing) and supersede_close_inherits_the_audit_snapshot.
254 passed, 2 ignored; temporal gate 100% (20/20).

Closes #373

Co-Authored-By: Claude Fable 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.

set_valid_to writes no entity_history snapshot — bound changes via the close path are unaudited

1 participant