Repository navigation
fix: reject empty valid-time windows; last statement wins in a transaction (#436, #477) - #480
Merged
Merged
Conversation
) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015GgLEs8YozJjYhwPEYgTUn
…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 Report❌ Patch coverage is
📢 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
This was referenced Oct 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
transactwhose effective window ends at or before it starts now fails with the new API-019. It writes nothing and takes notx_count. "Effective" means after defaults, so(transact {:valid-to "2020-01-01"} ...)fails because its:valid-fromis the transaction time.The check runs on every write path:
execute, eachWriteTransaction::executestatement,WriteTransaction::commit(again with the commit time, because a defaultvalid_frommoves to it), the browser write path andFactStorage::transact*. Open, WAL replay, migration andLogWriterdon't check it, so files that already hold such facts stay readable.#477 (broadened) — several writes to one fact in one transaction
WriteTransaction: the last statement that writes an(e, a, v)decides it, in any mix oftransactandretract. 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 beforecommit()and was gone after it. Two windows in separate statements, which used to be API-011, now commit the later window.(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 oneretract, are allowed.LogWriterkeeps 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 fromtx.execute.tests/valid_time_window_test.rs: API-019 fromexecute(5 shapes) and fromtx.execute. The cross-statement API-011 test now checks that the later window wins.check_valid_windows,transact_batchtakes notx_counton rejection, the commit collapse, and a defaultvalid_fromthat has passed:valid-toby 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.:valid-towith no:valid-fromnow 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