8000 fix: reject empty valid-time windows; last statement wins in a transaction (#436, #477) by adityamukho · Pull Request #480 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content

fix: reject empty valid-time windows; last statement wins in a transaction (#436, #477) - #480

Merged
adityamukho merged 3 commits into
v3from
fix/436-477-write-semantics
Oct 8, 2026
Merged

adityamukho merged 3 commits into
v3from
fix/436-477-write-semantics

Conversation

@adityamukho
@adityamukho adityamukho commented Oct 7, 2026 •
Copy link
Copy Markdown
Collaborator

Two v3.0.0 write-semantics changes. Both can make writes that succeed today fail or behave differently, so they ship in a major release.

#436 — empty or inverted valid-time windows

A transact whose effective window ends at or before it starts now fails with the new API-019. It writes nothing and takes no tx_count. "Effective" means after defaults, so (transact {:valid-to "2020-01-01"} ...) fails because its :valid-from is the transaction time.

The check runs on every write path: execute, each WriteTransaction::execute statement, WriteTransaction::commit (again with the commit time, because a default valid_from moves to it), the browser write path and FactStorage::transact*. Open, WAL replay, migration and LogWriter don't check it, so files that already hold such facts stay readable.

#477 (broadened) — several writes to one fact in one transaction

  • Across statements of one WriteTransaction: the last statement that writes an (e, a, v) decides it, in any mix of transact and retract. Commit drops the triple's records from earlier statements. Reads inside the transaction already followed statement order, so they now agree with the committed result. Before, retract-then-reassert read as live before commit() and was gone after it. Two windows in separate statements, which used to be API-011, now commit the later window.
  • Within one (transact [...]): two windows for one triple are still API-011. tx.execute() now raises it, stages nothing, and leaves the transaction usable. Identical repeats, and duplicate triples in one retract, are allowed.
  • LogWriter keeps the same rules, so no file it builds holds facts a normal write couldn't produce: API-019 for an inverted window, API-011 for a second window of one fact in one transaction, and the new API-020 for an assertion and a retraction of one fact in one transaction (either order). Each rejected record changes nothing. An unfiltered copy is identical to a source that keeps these rules. A source with older records that break them (v2.x via migration) fails at the first such record, and needs a repair step, which ERROR_REFERENCE describes.

Design: docs/superpowers/specs/2026-10-07-write-validation-and-statement-order-design.md. The decision is also recorded on #477.

Tests

  • tests/statement_order_test.rs (new): every two-statement mix on one fact gives the same rows inside the transaction and after commit, in memory, checkpointed and reopened. Also covers: other facts of an overridden statement survive, the commit is one transaction without the overridden records, and API-011 from tx.execute.
  • tests/valid_time_window_test.rs: API-019 from execute (5 shapes) and from tx.execute. The cross-statement API-011 test now checks that the later window wins.
  • Unit tests: check_valid_windows, transact_batch takes no tx_count on rejection, the commit collapse, and a default valid_from that has passed :valid-to by commit time.
  • tests/log_writer_test.rs: API-019, API-011 and API-020 (both orders) each leave the writer usable and the output exact. Duplicate retractions, identical repeats and the same fact in the next transaction are accepted.
  • tests/model_based_test.rs: the model now applies API-019, API-011 per statement and last-statement-wins, and checks reads inside a write transaction against the commit. With the collapse disabled it fails within 1s; the shrunk seed is committed.
  • Two executor tests that wrote a past :valid-to with no :valid-from now use 2125.

Results: 1360 tests (1351 passing, 9 ignored), cargo clippy -- -D warnings (native, wasm32-unknown-unknown + browser, wasm32-wasip1), and 300 model-based cases.

Refs #436, #477, #383

🤖 Generated with Claude Code

https://claude.ai/code/session_015GgLEs8YozJjYhwPEYgTUn

adityamukho and others added 2 commits October 7, 2026 19:02
…ction (#436, #477)

A transact whose effective valid-time window ends at or before it starts
is now rejected with the new API-019 before a tx_count is taken. The
window is checked after defaults, so a :valid-to in the past without a
:valid-from fails too. Every write path checks it: execute, each
WriteTransaction::execute statement, WriteTransaction::commit (again with
the commit time, since a default valid_from moves to it), the browser
write path and FactStorage::transact*.

When several statements of one WriteTransaction write the same
(e, a, v), in any mix of transact and retract, commit now keeps only the
records of the last of them. Reads inside the transaction already
followed statement order, but the commit stamped one tx_count on every
record and a retraction always won, so retract-then-reassert read as live
before commit and was gone after it. Two windows in separate statements,
formerly API-011, now commit the later window. API-011 is limited to one
transact and is raised by tx.execute, which stages nothing and leaves the
transaction usable.

LogWriter no longer collapses or rejects records of one transaction (the
API-011 check is gone), so an unfiltered copy is identical to its source,
including old transactions that hold several records of one fact.

Writes that succeed today can fail, so this ships in v3.0.0.
Design: docs/superpowers/specs/2026-10-07-write-validation-and-statement-order-design.md

Refs #436, #477, #383

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015GgLEs8YozJjYhwPEYgTUn
@codecov
codecov Bot commented Oct 7, 2026 •
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.77778% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/db.rs 96.29% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

LogWriter now rejects records that a normal write could not produce, so
no file it builds holds facts that break the v3 write rules: an
assertion whose valid-time window ends at or before it starts (API-019),
a second window of one fact in one transaction (API-011), and an
assertion and a retraction of one fact in one transaction, in either
order (new API-020). Records of one transaction share a tx_id and have
no order, so such a pair cannot say which write came last. Each rejected
record changes nothing and the writer stays usable.

An unfiltered copy is identical to a source that keeps these rules. A
source written before them (v2.x via migration, earlier v3 builds) fails
at the first such record; ERROR_REFERENCE describes the repair a
middleware step applies first.

Refs #477, #436, #431

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015GgLEs8YozJjYhwPEYgTUn
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

0