feat(core): relay persists become unconditional versioned exports - #1146
Conversation
…589) Hot-doc canonical, Phase G: while a live session exists the Y.Doc is canonical, so a relay persist supersedes ANY current head, foreign writers included. The superseded version survives as a Tigris object version and the collaboration reconciler surfaces the conflict from the live-update event (higher db_version + foreign writer). Non-relay writers keep full guarded semantics; the deleted-entity 409 and the db_version CAS remain. Subsumes the Phase E relay self-supersede rule. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013wFpjrd126rVe6VisiYVDD Signed-off-by: phernandez <paul@basicmachines.co>
bed1642 to
69c64bb
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 69c64bb3c6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # relay self-supersede lost-ack case (2026-07-23 production | ||
| # incident). Non-relay writers keep the full guarded semantics; the | ||
| # deleted-entity 409 above and the db_version CAS both remain. | ||
| if request.source != NOTE_SOURCE_COLLABORATION_RELAY: |
There was a problem hiding this comment.
Preserve pending foreign writes before relay overwrite
When the current head is a foreign write that has not yet materialized, this condition skips the only stale-base guard and lets a relay request replace the NoteContent row immediately. Production materialization is queued asynchronously and the job carries only db_version/db_checksum; once this relay write advances the row, the earlier foreign job preflights as stale and never writes the foreign markdown as an object version. In that pending/writing/failed window the superseded content is no longer in NoteContent and has no storage version to reconcile from, so keep rejecting relay-over-foreign until the current head is materialized or persist an actual version before superseding.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Right - this was a real hole in the 'nothing is destroyed' invariant, thank you. Fixed in the latest push: relay-over-foreign now requires the current head to be provably in storage (file_write_status synced, or external_change_detected since that content originated in storage); pending/writing/failed foreign heads keep the 409 until materialization lands, which the relay's next store absorbs seconds later. Relay-over-relay stays unconditional (the live Y.Doc is the merge of everything the relay ever persisted - that is the lost-ack wedge fix). New regression test pins the pending-foreign-head rejection; the supersede test now models a synced head.
… (#1589) A pending/writing/failed foreign head has no storage object version yet; superseding it would erase the only copy because its queued materialization preflights as stale and never writes. Require file_write_status synced or external_change_detected before relay-over-foreign; relay-over-relay stays unconditional (the live Y.Doc contains everything the relay ever persisted). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013wFpjrd126rVe6VisiYVDD Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aed70a308c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # deleted-entity 409 above and the db_version CAS both remain. | ||
| current_head_in_storage = current_note_content.file_write_status in ( | ||
| "synced", | ||
| "external_change_detected", |
There was a problem hiding this comment.
Require real storage before relay supersedes
Treating external_change_detected as “in storage” lets a stale relay overwrite a foreign head whose accepted DB markdown never materialized: that status is published when the materialization guard refused to overwrite an unexpected file, and repository tests explicitly keep it out of recovery because re-driving it would clobber the external edit. Fresh evidence since the earlier comment is that storage-origin changes are promoted as synced in plan_note_content_reconciliation, while external_change_detected records a protected conflict checksum, so a relay over this state can still erase the only DB copy of the foreign accepted markdown and overwrite the protected file content.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Correct - external_change_detected is precisely the state where the accepted markdown is NOT in storage, and storage-origin promotions arrive as synced. Fixed in the latest push: the guard is now file_write_status == 'synced' only, and the rejection test is parametrized over pending/writing/failed/external_change_detected.
…#1589) external_change_detected means the accepted DB markdown did NOT materialize (the guard protected an unexpected external file), so it cannot license a supersede; storage-origin changes are promoted as synced. Rejection test parametrized over every non-synced status. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013wFpjrd126rVe6VisiYVDD Signed-off-by: phernandez <paul@basicmachines.co>
Phase G core half of basic-memory-cloud#1589 (hot-doc canonical). Stacked on #1144 — review/merge that first.
While a live session exists the Y.Doc is canonical: a relay persist supersedes ANY current head, foreign writers included. The superseded version survives as a Tigris object version, and the collaboration reconciler surfaces the conflict from the live-update event (higher db_version + foreign writer). Nothing is destroyed; the head may move.
Scope guards: non-relay sources keep the full guarded 409; the deleted-entity 409 stays; the db_version CAS stays. Subsumes #1144's self-supersede rule (its foreign-writer scope test is updated to the new semantics, and a new test pins that non-relay sources still reject).
Ships coordinated with the cloud collab-side Phase G PR (version-arithmetic reconciler + heuristic deletions); the cloud rev bump takes both core changes together.
Tests: 23 runner tests green including the new supersede + non-relay-reject pair.
🤖 Generated with Claude Code
https://claude.ai/code/session_013wFpjrd126rVe6VisiYVDD