Skip to content

feat(core): relay persists become unconditional versioned exports - #1146

Merged
phernandez merged 3 commits into
mainfrom
feat-1589-relay-export
Jul 23, 2026
Merged

feat(core): relay persists become unconditional versioned exports#1146
phernandez merged 3 commits into
mainfrom
feat-1589-relay-export

Conversation

@phernandez

Copy link
Copy Markdown
Member

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

Base automatically changed from feat-1589-self-write to main July 23, 2026 18:36
…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>
@phernandez
phernandez marked this pull request as ready for review July 23, 2026 18:40
@phernandez
phernandez force-pushed the feat-1589-relay-export branch from bed1642 to 69c64bb Compare July 23, 2026 18:40

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@phernandez
phernandez merged commit d3b7163 into main Jul 23, 2026
24 checks passed
@phernandez
phernandez deleted the feat-1589-relay-export branch July 23, 2026 19:14
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