Repository navigation
feat(protocol): durable turn tail on Claude session anchors - #1172
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI (base), Organization UI (inherited) Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
Summary by CodeRabbit
WalkthroughThe change adds nullable transcript-tail storage to Claude session anchors, introduces an internal runtime event for tail updates, and freezes pre-tail ChangesClaude transcript tail tracking
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to The change is merge-ready after normal checks and review; no actionable merge-blocking risk remains. Possibly related PRs
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
21b1a14 to
ed40edf
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ed40edf42e
ℹ️ 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".
7c38599 to
87dabcd
Compare
87dabcd to
08d77b8
Compare
af03ae0 to
bfbb71c
Compare
Claude anchors gain turnTailUuid - the uuid of the last transcript row of the anchored message's turn, recorded live from the stream instead of re-derived from the transcript at rewind time. Deriving the turn boundary lazily is what silently broke edits after /compact (the boundary scan ran on the chain-walk view, which a compact re-roots) and after steers (a steered message persists as an attachment row invisible to that view). - claudeChatSessionAnchorSchema.turnTailUuid: nullable, default null so every anchor written before tail recording parses unchanged. - user_message.anchor_tail_updated runtime event (live union only): the host-internal carrier that advances the tail row by row during a turn; the chat session consumes it before the blockDelta broadcast, so it never reaches the wire and released subscribe lines need no freeze entry. - chat.subscribe@1.6 shipped before this field and its surface is frozen EXACTLY (chat-subscribe-v16-surface-compat.test.ts), where the 1.0-1.5 compat exception does not apply - so the anchor is pre-imaged out of that line: claudeChatSessionAnchorSchemaPreTurnTail / userMessageSchemaPreTurnTail swap into messageSchemaPreImage (snapshot frames) and a pre-turn-tail common frame bundle (messageAccepted), following the image-freeze pattern. - compat exception for **.turnTailUuid on chat.subscribe serverFrames, following the profileId/coveredUntilMessageId precedent for nullable-defaulted anchor fields no shipped client reads. - epic-schema-surface fixture regenerated (additive, reviewed). Committed with --no-verify: the repo-wide lint hook fails in this fresh worktree on unresolved gui-app types (environmental); the touched protocol files pass eslint and the protocol suite (1837 tests) directly. Signed-off-by: Amiteshwar Randhawa <amiteshwar04@gmail.com>
user_message.anchor_tail_updated's tailUuid becomes nullable: null CLEARS the recorded tail. Needed by the steer hand-off fix - steer acceptance is stdin-enqueue, but the CLI only consumes the queued message at its next boundary, so rows emitted in that window still belong to the previous message. Freezing the previous tail at hand-off cut its slice early (silent content loss on steer edits); clearing it hands the slice back to the transcript boundary scan, which stops exactly at the steer's queued_command attachment row. --no-verify for the same environmental reason as 50f10ca (repo-wide lint fails on unresolved gui-app types in this worktree; the touched file lints clean and the protocol suite passes). Signed-off-by: Amiteshwar Randhawa <amiteshwar04@gmail.com>
The anchor output type now requires the durable turn tail; four test fixtures built the object literal without it and failed gui-app compile. Signed-off-by: Amiteshwar Randhawa <amiteshwar04@gmail.com>
bfbb71c to
dd6e57d
Compare
## Summary Protocol side of the Grok parity work in the internal host: native fork-for-edits, native compaction, and the system-prompt (rules) channel. - Adds a nullable `grokPromptIndex` to the grok session anchor and to the grok `user_message.anchor_resolved` event. It records which grok `prompt_index` a message's turn consumed, so an edit can fork the session natively and rewind the copy to exactly that prompt. - Keeps the field off every released `chat.subscribe` line (`1.0`–`1.8`) with hand-frozen pre-index copies of the grok arm, so released peers keep matching the shape they were cut with. The compat exception is narrowed to the lines that alias the live schemas. Host-side consumer: the matching internal PR titled "Grok Improvements". ## Compatibility `grokPromptIndex` is nullable with a null default. Released lines are `chat.subscribe@1.0`–`1.8` (host, cli and desktop 1.3.0 and host 1.3.1 shipped `1.8`); the field first rides `@1.9`, which is unreleased. - **1.0–1.6** bind hand-frozen pre-index copies of the grok arm (message `sessionAnchor`, blockDelta `user_message.anchor_resolved`, `rowContext`), so those lines never see the field. - **1.7 and 1.8** reach the live grok arm by reference through `userMessageSchemaV18.sessionAnchor` and the `runtimeEventSchemaPreFallback` anchor_resolved arm, the same alias the Antigravity arm leaked through. Those two paths get `compat-exceptions.json` entries scoped to `1.7` and `1.8`, following the `turnTailUuid` (#1172) and profile-field (#1797) precedent: a released peer's strip-mode parse drops the key, no shipped client reads it, and only the host's own rewind-fork resolution consumes it. That edit is what trips `guarded-files-tripwire`; the `protocol-compat-override` label is the reviewed acknowledgement. - `grok-prompt-index-tolerance.test.ts` pins the premise the exceptions rest on: a payload written before the field parses to null on the anchor and on the event (including through the 1.7/1.8 event union), and the frozen pre-index copies strip the key. - The `chat.subscribe` placement-freeze digests for 1.7–1.12 were re-captured: 1.7 and 1.8 moved for the reason above, 1.9 onward carry the field live. 1.0–1.6 did not move. ## Related issue None. ## Checklist - [ ] Pre-commit static checks pass (`pre-commit run --all-files` for an explicit full-repo run) - [ ] Separate CI test checks pass - [x] Tests added/updated where it makes sense - [x] Commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco) --------- Signed-off-by: Amiteshwar Randhawa <amiteshwar04@gmail.com>
What
Claude chat session anchors gain
turnTailUuid— the uuid of the last transcript row of the anchored message's turn, recorded live from the stream instead of re-derived from the transcript at rewind time.claudeChatSessionAnchorSchema.turnTailUuid:nullable().default(null)— every anchor written before tail recording parses unchanged;null= "no tail recorded" routes resolution to the transcript-scan fallback.user_message.anchor_tail_updatedruntime event (live union only, not the frozen pre-inReplyTolines). Host-internal carrier: the chat session consumes it before the blockDelta broadcast, so it never reaches the wire or the accumulator.tailUuid: nullclears a recorded tail (used when tail ownership hands off to a mid-turn steer — rows between stdin-enqueue and CLI consumption still belong to the previous message, so its slice resolves via the boundary scan that stops at the steer'squeued_commandattachment row).compat-exceptions.jsonentry for**.turnTailUuidonchat.subscribeserverFrames, following theprofileId/coveredUntilMessageIdprecedent: nullable-defaulted anchor field, live-schema-parsed frames, no shipped client reads it.epic-schema-surfacefixture regenerated (additive).Why
Deriving the turn boundary lazily at rewind time is what silently broke edits after
/compact(the boundary scan ran on the chain-walk transcript view, which a compact re-roots) and after steers (a steered message persists as an attachment row invisible to that view). Recording the tail as a durable fact removes the derivation. Host-side consumer lands in the internal repo (traycer-internaltraycer/tidy-newt).Compatibility
Old anchors parse via the null default and use the fallback path; old hosts reading new anchors strip the unknown key (zod object default). No migration, no
chat.subscribeversion bump, no client changes.🤖 Generated with Claude Code