Skip to content

fix(native-chat): edit a queued message in place, even while a question is open - #26886

Merged
brennanb2025 merged 13 commits into
mainfrom
brennanb2025/queued-message-inline-edit
Oct 10, 2026
Merged

brennanb2025 merged 13 commits into
mainfrom
brennanb2025/queued-message-inline-edit

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 21 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1671 $\color{#cf222e}{\Huge{\mathbf{−}}}$​87 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1584
Prod 39 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1356 $\color{#cf222e}{\Huge{\mathbf{−}}}$​166 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1190

ELI5

When a message is waiting in line for the agent (a "queued card" above the chat box), "Edit message" now edits that card where it is. Before, it pulled the text out of the card and dropped it into the chat box. While the agent was asking a question the chat box is hidden, so the message looked like it had disappeared.

The problem

A teammate reported: clicking Edit message on a queued card while the agent was asking a question made the message "completely disappear on the page".

Why it happened. Messages waiting for the agent show as queued cards above the chat box: mail from another agent, and messages kept across a quit. On main, Edit message was not an edit. It copied the card's text into the chat box and deleted the card. While the agent asks a question or wants an approval, that question card takes the chat box's place. So the text moved somewhere invisible, and the card was gone.

Even with no question open, the message lost its place in the queue. A card from another agent also lost its "From an agent" sender line, because it came back as the person's own draft.

PR #26004 (already merged) added the "Show full message" expander, so long cards can be read. This PR makes Edit a real edit.

What changed for the user

  • Edit message opens a small editor inside the card. It has a header line ("Editing message" and the card's "From an agent" line, with an X at the right that cancels), the text field at the card's full width, and a Save button under it at the right. Save shows the Enter key (↵) it answers to, in the same small key badge as the new-workspace dialog's "Create worktree ⌘↵" button; there is no separate key-hint text. The card's queue icon and its status line (for example "Waiting for your answer") are hidden while you edit and come back after. The card keeps its place in the queue and its "From an agent" line.
  • It works while a question or approval is open. The editor sits in the card, not in the hidden chat box, so the question stays answerable beside it.
  • Keys: Enter (or Cmd/Ctrl+Enter) saves, Shift+Enter adds a new line, and Escape cancels, like the X. Input-method composition keys are left alone.
  • After saving or cancelling, focus goes back to the open question if there is one, otherwise to the chat box.
  • While someone is editing a card, the agent doesn't pick it up automatically. Cards ahead of it still go out. Pressing Send or Steer on the card still sends it at once.
  • Nothing typed is lost:
    • If the card is sent or removed elsewhere while you edit, your unsaved text goes into the chat box, after anything already there, with one notice.
    • If someone else changed the card first, your text goes into the chat box, with one notice, and the editor reopens on the newer text.
    • Switching chats, closing the pane, or reloading the window moves unsaved text into that chat's box quietly.
  • A host that can't edit in place shows no Edit item. There is no fallback to the old copy-and-delete.

Visual proof

Hidden dev app, driven only through Playwright CDP. A stand-in Codex asks "Which color should I use for the chart?" partway into its turn. The queued card is real agent mail, sent to the chat through the rig's own built orca CLI while the agent works.

BEFORE (main 40716d702e9)

The card waits while the question is open:

before-1-card-and-question.png

After clicking Edit message, the card is gone and its text is nowhere on screen:

before-2-after-edit-card-gone.png

AFTER (this branch; the editing shot is from b507748b53e, the saved shot from 8617fdef4d0, the reload shot from 08216725f4a)

Edit message opens the editor inside the card, and the question stays open below it. The card's status line is hidden while editing. Here the text has been replaced, with Shift+Enter adding a second line, and Save carries its Enter-key badge:

after-1-editor-save-key-r5.png

After Save, the card has the new text in the same place, with the same "From an agent" line, and focus is back on the question:

after-3-saved-in-place-r4.png

After a reload in the middle of an edit, the card is unchanged and the unsaved text is in the chat box:

after-4-reload-keeps-typing.png

How it works

Host (the execution host that owns the queue):

  • Save is a compare-and-set (agentSession.queuedMessageUpdate). The client sends the fingerprint of the card's body as it was when the edit began. The host replaces the text only if the card still holds that body. It changes only the text and keeps the card's id, position, sender, kind and attachments. Commands can't be edited.
    • The answer is updated or unchanged (a resent Save whose result is already there), or changed (newer text landed first, never overwritten), or gone.
    • Sending a card also checks the fingerprint, so a card can never go out with text it no longer holds.
  • In-memory edit leases (agentSession.queuedMessageEditHold: acquire, renew, release) hold a card back from automatic delivery while someone edits it.
    • There is one lease per editor. Each lease lasts 2 minutes, and the editor renews it about every 30 seconds.
    • Every read re-checks the lease against the card (text, state, hand-off). A send, a delete, or another device's Save frees it.
    • Leases die on their deadline. A timer wakes the queue when that happens, and so does any read that finds a lapsed lease.
    • Nothing about leases is stored on disk. A host restart drops them all.
    • Automatic delivery stops at a held card, and the cards behind it wait to keep their order. Cards ahead still go out. Explicit Send or Steer still overrides.
  • New capability agent-session.queued-message-edit.v1. It is separate from the still-dark queued-messages.v1, so agent-mail cards and kept cards are editable today.
  • No marker on the wire. Other clients aren't told that a card is held. They don't need to be, because their Send and Steer still work.

Desktop: the editor lives in the card (NativeChatQueuedMessageEditor), outside the slot that the chat box and the question card share. The focus guard leaves it alone while a question card is open. One editor per pane.

Why this approach

  • Editing in place is the only shape that survives an open question. Any editor in the chat box is hidden exactly when the bug happens.
  • Compare-and-set on the body fingerprint makes Save safe to retry with no receipt table. A lost answer, sent again, is answered from the card as it stands.
  • The hold has to be on the host, because the host's queue is what delivers. A client-side hold can't stop a send the host starts.
  • Leases kept in memory with deadlines can't strand a card. The worst case is a card held for 2 minutes after an editor vanishes.

Differences from the common pattern

  • The editor stays usable while a question or approval is open (intended). In Orca the question card replaces the chat box, so this is the bug being fixed.
  • Leases are in memory, one per editor, and are not published (intended). This matches the common hold-and-FIFO-barrier mechanism. Nothing durable is needed because a lease dies on its own.
  • Save compares the body fingerprint, not a timestamp (intended). Cards already store a body fingerprint, and comparing it answers "did my exact Save land?" directly.
  • Only the text can be edited (intended). Attachments, sender and kind stay as they are. Agent-mail cards stay editable, with the sender kept.
  • Unsaved text from a card that vanished goes into the chat box (intended). It is never thrown away, and there's one notice when the user didn't cause it.
  • The phone app keeps the old Edit, copy-and-delete (TEMPORARY). The follow-up phone PR moves it onto this same contract. The mobile allowlist already includes both new methods for it.

Compatibility between versions

  • New desktop, older or unproven host (for example an SSH or remote host on an older Orca): the host doesn't advertise the capability, so there's no Edit item and no new calls.
  • Older desktop or phone, new host: the old client keeps its copy-and-delete Edit through the existing delete method. Nothing the host publishes changed.
    • A card held by a new client's edit is still sent by the old client's Send, and still removed by its Delete. The new client then puts its typing into the chat box.
  • Wire: two new methods with strict params, and no new stream opcode. Only new clients call the methods, after seeing the capability.

Testing

  • I manually tested these changes locally (hidden dev app on macOS, BEFORE on main and AFTER on this branch; details below)
  • Automated tests added/updated

Automated, run under Node on 52b0d547bf9 (the last commit, 08216725f4a, only moves the notice strings into their own file and was re-checked with the editor tests and orca-ci-checks):

  • every *queued* test in src/main, plus the RPC method and mobile allowlist tests: 30 files, 327 tests, all pass;

  • every test in src/renderer/src/components/native-chat/: 360 files, 3639 tests, all pass;

  • tc:node and tc:web pass;

  • orca-ci-checks: 23/23 on 08216725f4a.

  • Two commits came after QA, from CI:

    • 922d21007cc changes only how the lease timer is unref'd, so the phone's test typecheck accepts it. I re-ran the lease and host edit tests, tc:node, and the mobile typecheck: the lease file no longer errors.
    • 1b53321d4d2 adds the two new methods to the cross-version wire manifest, so both skew directions call them. tests/e2e/cross-version-wire/ passes locally: 34 files, 180 tests.
  • The editor's layout (header with the X, Save footer, status line hidden while editing) changed in 8617fdef4d0, after review. On that commit: every test in src/renderer/src/components/native-chat/ and src/renderer/src/i18n/ passes (382 files, 3878 tests), tc:web passes, and orca-ci-checks (lint, design system, localization) passes 22/22 with the typecheck skipped because tc:web had just run. Its 4 new tests (the X cancels, Save is the only footer action and is disabled while saving or unchanged, the status line hides while editing and comes back, the key hint names the platform chord) fail on the old layout; the key-hint test was later replaced, see the next item. The saved shot above was taken on it with the same hidden rig.

  • The footer's key-hint text ("Enter to save · ⇧ Enter for a new line") was replaced in b507748b53e by a key badge on the Save button itself. The badge is the new-workspace dialog's existing one, moved into a shared ButtonKeyHint component that both buttons now use; the dialog's button looks the same. The badge is hidden from screen readers, so Save is still announced as "Save". On that commit: the editor, queued-list, queued-edit and new-workspace dialog tests plus src/renderer/src/i18n/ pass (34 files, 382 tests), tc:web passes, and orca-ci-checks passes 22/22 (typecheck skipped after tc:web). The editing screenshot above was re-taken on it with the same hidden rig, and the dialog's "Create worktree ⌘↵" button was checked in the same run.

Two of the tests fail on the previous code:

  • a Save whose answer is lost and then shows up in the card closes the editor quietly. It doesn't later show a "sent or removed" notice with a copy in the chat box;
  • a renew that clears another editor's lapsed lease still wakes the queue.

Manual QA, with the same hidden rig on both commits, a stand-in Codex that logs every write, and an isolated home and profile:

  1. Question open, Edit, Save, answer. Main: the card vanished, and its text was nowhere until the question was answered. Branch: the editor opened in the card and the question stayed open. Save kept the card in place with the same id and sender, and focus went back to the question. After answering, the agent got the card once, with the new text. The old text was never sent.
  2. No question:
    • Escape left the card unchanged and leaked nothing into the chat box.
    • Enter saved, and focus went to the chat box.
    • The agent got the edited text once.
  3. Keys: Shift+Enter added a line without saving.
  4. Reload during an edit: the card was unchanged and the unsaved text was in the chat box. The card was then sent once, with its original text.

The real ~/.claude/settings.json and ~/.codex/config.toml hashes were identical before and after the QA.

Not verified:

  • a live SSH or remote host. The host code is keyed by session and runs on the execution host, but I didn't run it remotely;
  • Linux and Windows;
  • a live mixed-version app run. The cross-version wire suite covers only the method surface, in both directions;
  • the phone app, which is unchanged (see above).

Linked Issue

None. A teammate reported this in chat (maintainer PR).

Visual Proof

See "Visual proof" above.

Review

Two review rounds; a summary comment follows on this PR.

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Author: @BrennanKB5
  • Security, cross-platform behaviour (no platform-specific code beyond the existing Cmd/Ctrl check), SSH and remote hosts (gated on the host's capability), mobile (unchanged, temporary), backwards compatibility (above) and performance (one renew every 30 seconds per open editor) were all considered.

Checklist

  • This PR is small and focused (56 files: host, wire and desktop of one feature, with tests and 6 locales)
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

A queued card's Edit used to copy its text into the composer and delete the
card, so the message lost its place, an agent-mail card lost its sender, and
while a question or approval replaced the composer the text vanished.

The execution host now edits the card itself:
- agentSession.queuedMessageUpdate replaces only the card's text, as a
  compare-and-set on the body fingerprint the client last saw. It keeps the
  id, position, sender, kind, send mode and attachments; a command card is not
  editable. A resent Save answers from the card as it stands (unchanged or
  changed), so a lost answer never overwrites newer text.
- agentSession.queuedMessageEditHold acquires, renews and releases an
  in-memory edit lease per caller and edit. While one is live, automatic
  delivery stops at the card (earlier cards still send, later ones wait); an
  explicit Send still overrides. Leases expire after two minutes on their own,
  re-derive validity from the card on every read, and die with the
  conversation. Release uses cleanup admission.
- The published card carries an optional editHeld marker, never `paused`.
- Both methods sit behind the new agent-session.queued-message-edit.v1
  capability, independent of the dark mid-turn queue capability, and are on
  the mobile allowlist for the phone follow-up.
Edit message now opens an editor inside the queued card instead of moving the
text into the composer. The card keeps its place and sender, and the editor
stays usable while a question or approval holds the composer's slot (it does
not lose focus to the prompt card).

- Enter or Cmd/Ctrl+Enter saves, Shift+Enter is a newline, Escape cancels;
  IME composition keys never save or cancel. Save and Cancel replace the
  card's own actions while it is edited; one editor per pane.
- The editor holds the host's edit lease, renewing every 30 seconds and taking
  it again after a lapse. Lease failures are only logged: they never block
  typing, Save or Cancel.
- If the card is sent or removed elsewhere mid-edit, a clean edit just
  closes; changed text goes into the chat box after any existing draft, with
  one toast. A Save that lands and then sends counts as success.
- Against a host without agent-session.queued-message-edit.v1 the card offers
  no Edit (no copy-and-delete fallback); an empty overflow menu is hidden.
…d elsewhere

- Save with an unreadable answer (dropped for a moved runtime fence, or lost)
  now reads the published card: saved text shown -> close; card gone -> the
  sent-as-saved / chat-box path; otherwise keep the editor quietly.
- Send and Steer stay enabled on a card edited on another device; the
  published editHeld marker is gone (no reader left). Automatic delivery
  still stops at an edited card on the host.
- Save answering changed moves the typing to the chat box and reopens the
  editor on the newer text. Notices say when the chat box is hidden by a
  question or approval.
- Closing the pane or switching chats with unsaved typing appends it to that
  chat's box quietly.
- Lease renewal no longer publishes, so an open editor is not idle-sweep
  activity.
- The consume compare-and-set also matches the body fingerprint.
- Saving unchanged text keeps the original blocks.
Conflicts resolved:
- Kept main's queued-message-consume-error.ts, deleted the branch's
  duplicate, and pointed every importer at main's file (dropping the
  now-unused re-export).
- The queued card now builds on main's full-message expander: the editor
  replaces the text, the expander and the actions while it is open.
- The prompt-card focus guard skips both the find bar and an open queued
  editor; a closing edit hands focus to an open prompt card first.
- Locales take main's `fromSender` plus the branch's edit keys.
- max-lines: the queue's deps type moved to journal-store-contracts.ts and
  the reopen-mark rule to queued-message-settlement.ts; the inline-edit list
  tests moved to their own file.
…ke the queue for a pruned lease, keep typing across a reload

A Save whose answer was lost now closes once the card shows the saved text,
and a card that went out with exactly the typed text closes quietly instead
of copying it to the chat box. A renew that prunes another editor's lapsed
lease wakes the queue in place of the timer it cancels. A reload, which
unmounts nothing, hands unsaved typing to the chat box like closing the pane.
…echeck accepts

The lease module reaches the mobile test program through journal-store, where
setTimeout answers a number, so `timer.unref?.()` failed mobile's tests
typecheck ratchet.
…irections

The manifest must name every agentSession method; the two edit methods
reach their host methods on a new build and are only called by clients
on a host advertising agent-session.queued-message-edit.v1.
@brennanb2025

brennanb2025 commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor Author

Review summary

The problem. On main, "Edit message" on a queued card copied its text into the chat box and deleted the card. While the agent asks a question, the question card replaces the chat box, so the message vanished from the page, as a teammate reported. Even without a question, the card lost its place in the queue and its "From an agent" sender.

The user-facing change. Edit now opens an editor inside the card. It works while a question is open, and Save keeps the card's place and sender. The agent doesn't auto-send a card while it's being edited, but Send or Steer still send it at once. Unsaved text is never dropped: it goes to the chat box if the card leaves, changes elsewhere, or the window reloads.

Review round 1: approach confirmed, no P1

Fixed:

  • P2-1: a Save whose answer was lost or dropped now checks the card as published. If the card shows the saved text, the editor closes quietly. If the card has left, the text goes to the chat box. Otherwise the editor stays open. This also removed a false failure notice for a Save that had landed.
  • P2-2: Send and Steer stay enabled on a card that is being edited on another device. The published "held" marker was removed from the wire, because nothing read it. The host still holds the card back from automatic delivery.
  • P3-2: after the editor closes, focus goes to the open question first, then to the chat box.
  • P3-3: when someone else changed the card first, your text goes to the chat box with one notice, and the editor reopens on the newer text once it shows.
  • P3-4: switching chats or closing the pane moves unsaved text into that chat's box quietly.
  • P3-5: renewing a lease no longer counts as chat activity, so an editor left open can't keep an idle chat alive forever.
  • P3-6: sending a card also checks its text fingerprint.
  • P3-8: saving unchanged text changes nothing.
  • P3-1: main was merged once.

Accepted deviations:

  • A real refusal from the host still shows its notice. Silencing it would make Save do nothing visible.
  • With a question open, the notices say "after you answer the agent" instead of "below the question", because the chat box isn't on screen then.
  • The focus fix also covers Steer, Delete and Resume beside an open question.

Deferred:

  • P3-7: Edit can sit read-only while an earlier card's agent start holds the session queue. Fixing it would need a second path that skips that queue, which is not a small change.

Review round 2: no P1 or P2

Fixed:

  • P3-A: a Save whose answer arrived before its own publication left the editor open over the saved text. When the card was then sent, a false "already sent or removed" notice appeared, with a duplicate in the chat box. Now:

    • the editor closes once the card shows exactly what's typed;
    • a card that leaves holding exactly the typed text closes quietly too.

    The reviewer's repro is a permanent test, and it fails on the old code.

  • P3-B: a renew could clear another editor's expired lease after its deadline but before its timer ran. That cancelled the timer's wake, so the freed card could wait indefinitely. Now any read that clears a lapsed lease wakes the queue itself.

    This wake publishes once per lapsed lease, as the timer does. Renewing still publishes nothing. The repro is a permanent test, and it fails on the old code.

  • P3-C: a stale comment.

Found and fixed while preparing QA: a window reload runs no React unmount, so the round-1 "unsaved text goes to the chat box" step never ran on reload. It now also runs when the page is being unloaded. There's a test, and the QA run checks it.

Known, narrow, not fixed:

  • If you keep typing after a Save whose answer was lost, a later Save answers "changed". Your text still goes to the chat box and the editor reopens, so nothing is lost.
  • If you type text that exactly matches what another device just saved, the editor closes.

Verified

  • Tests (Node):

    • every *queued* test in src/main, plus the RPC method and mobile allowlist tests: 30 files, 327 tests, all pass;
    • every native-chat renderer test: 360 files, 3639 tests, all pass.
  • Typechecks: tc:node and tc:web pass.

  • orca-ci-checks: 23/23 pass; PR CI is green on bd623d6af02.

  • The three new round-3 tests (late answer, drained with the typed text, lapsed-lease wake) each fail on the round-2 code and pass now.

  • Hidden-app QA, BEFORE on main and AFTER on this branch, with real agent mail and a stand-in agent that logs every write:

    • with the question open, the card vanished on main; on the branch it was edited in place and the question stayed answerable;
    • the edited card was sent once, with the new text, and the old text was never sent;
    • Escape cancelled, Enter saved, and Shift+Enter added a line;
    • focus went to the question after Save;
    • after a reload, the card was unchanged and the unsaved text was in the chat box.

    Screenshots are in the PR body. The real ~/.claude and ~/.codex config hashes were unchanged.

CI follow-ups after QA:

  • the lease timer's unref now compiles in the phone's test typecheck (922d21007cc);
  • the cross-version wire manifest now calls both new methods (1b53321d4d2);
  • the structured-method count pin is bumped to 36 (bd623d6af02).

Not verified

  • live SSH or remote hosts;
  • Linux and Windows;
  • a live mixed-version app run. The cross-version wire suite covers only the method surface, in both directions;
  • the phone app, which keeps the old Edit until the follow-up phone PR.

Update (8617fdef4d0): the inline editor now has a header line ("Editing message", the sender, and an X that cancels) and a footer with a key hint and Save, and the card hides its status line while you edit. Before, Save and Cancel sat directly on top of "Waiting for your answer" with no spacing. The editing screenshots in the PR body were re-taken on this commit.

@brennanb2025
brennanb2025 marked this pull request as ready for review October 9, 2026 14:41
@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: dee56f57-43fb-4a3c-9125-f20ada856b35

📥 Commits

Reviewing files that changed from the base of the PR and between 8617fde and b507748.


📒 Files selected for processing (11)
  • src/renderer/src/components/ButtonKeyHint.test.tsx
  • src/renderer/src/components/ButtonKeyHint.tsx
  • src/renderer/src/components/native-chat/NativeChatQueuedMessageEditor.test.tsx
  • src/renderer/src/components/native-chat/NativeChatQueuedMessageEditor.tsx
  • src/renderer/src/components/new-workspace/NewWorkspaceComposerFooter.tsx
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json

💤 Files with no reviewable changes (6)
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/i18n/locales/ja.json

🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/src/components/native-chat/NativeChatQueuedMessageEditor.tsx

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review.



📝 Walkthrough

Walkthrough

Adds in-place editing for queued chat messages. Fingerprints guard updates and message consumption. Temporary edit leases affect automatic delivery. The host and RPC layers expose editing operations through a runtime capability. The renderer provides an inline editor with save, cancel, focus handling, and draft handoff for specified outcomes. Tests cover journal updates, lease behavior, RPC calls, and renderer interactions.


Priority: ➖ Normal

Merge Risk: 🔵 Low · up to b5077

A failed draft write could lose an edited message, and current tests would not catch an incomplete update response. These are bounded risks; the inspected production update path returns the required fields.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 45.95% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 74 functions across 53 files. (6 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title clearly and concisely describes the main change: editing queued messages in place while a question is open.
Description check Passed The description is complete and on topic. It explains the user impact, implementation, rationale, compatibility, testing, visual proof, and known verification limits. The missing linked issue is accep…

Full details: Docstring Coverage

Explanation

Docstring coverage is 45.95% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 74 functions across 53 files. (6 skipped: 6 unsupported.)



  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR


🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/e2e/cross-version-wire/structured-agent-session-host-fixture.ts (1)

61-61: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert the complete queued-update success result.

AgentSessionQueuedMessageUpdateResult requires messageId and fingerprint for an updated result. The cross-version surface matcher and RPC test currently check only success envelopes, so they can pass with an invalid incomplete outcome.

Suggested test fix
diff --git a/tests/e2e/cross-version-wire/structured-agent-session-host-fixture.ts b/tests/e2e/cross-version-wire/structured-agent-session-host-fixture.ts
@@
-    queuedMessageUpdate: vi.fn(async () => ({ ok: true, replayed: false })),
+    queuedMessageUpdate: vi.fn(async () => ({
+      ok: true,
+      replayed: false,
+      value: {
+        status: 'updated',
+        messageId: 'queued-1',
+        fingerprint: 'a'.repeat(64)
+      }
+    })),
diff --git a/tests/e2e/cross-version-wire/structured-agent-session-surface-calls.ts b/tests/e2e/cross-version-wire/structured-agent-session-surface-calls.ts
@@
-    result: { ok: true, replayed: false }
+    result: {
+      ok: true,
+      replayed: false,
+      value: { status: 'updated', messageId: 'queued-1', fingerprint: 'a'.repeat(64) }
+    }
diff --git a/src/main/runtime/rpc/methods/structured-agent-session-queued-edit.test.ts b/src/main/runtime/rpc/methods/structured-agent-session-queued-edit.test.ts
@@
-  hostCalls.queuedMessageUpdate = vi.fn(async () => ({ ok: true, value: { status: 'updated' } }))
+  hostCalls.queuedMessageUpdate = vi.fn(async () => ({
+    ok: true,
+    value: { status: 'updated', messageId: 'card', fingerprint: 'a'.repeat(64) }
+  }))
@@
   it.each([
@@
   ])('%s reaches the host as the authenticated caller', async (method, params, name) => {
-    expect(await call(method, params, STRUCTURED_CLIENT)).toMatchObject({ ok: true })
+    const response = await call(method, params, STRUCTURED_CLIENT)
+    expect(response).toMatchObject({ ok: true })
+    if (name === 'queuedMessageUpdate') {
+      expect(response).toMatchObject({
+        ok: true,
+        result: {
+          ok: true,
+          value: { status: 'updated', messageId: 'card', fingerprint: 'a'.repeat(64) }
+        }
+      })
+    }
     expect(hostCalls[name]).toHaveBeenCalledWith({ callerKey: 'trusted-local:runtime' }, params)
   })

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 04fd4d60-791d-4df1-81b9-1a1d12396cc0
📥 Commits

Reviewing files that changed from the base of the PR and between 40716d7 and bd623d6.

⛔ Files ignored due to path filters (1)
  • src/shared/rpc-contract/rpc-params-catalog.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing (59)
  • src/main/native-chat/agent-session-journal/journal-queued-messages.ts
  • src/main/native-chat/agent-session-journal/journal-store-contracts.ts
  • src/main/native-chat/agent-session-journal/journal-store.ts
  • src/main/native-chat/agent-session-journal/journal-submission-hook.ts
  • src/main/native-chat/agent-session-journal/queued-message-bookkeeping-failure.test.ts
  • src/main/native-chat/agent-session-journal/queued-message-edit-leases.test.ts
  • src/main/native-chat/agent-session-journal/queued-message-edit-leases.ts
  • src/main/native-chat/agent-session-journal/queued-message-pause.test.ts
  • src/main/native-chat/agent-session-journal/queued-message-pause.ts
  • src/main/native-chat/agent-session-journal/queued-message-settlement.ts
  • src/main/native-chat/agent-session-journal/queued-message-store.test.ts
  • src/main/native-chat/agent-session-journal/queued-message-table.ts
  • src/main/native-chat/agent-session-journal/queued-message-text-update.test.ts
  • src/main/native-chat/agent-session-journal/queued-message-text-update.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-queued-edit.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-queued-edit.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-queued-messages.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-queued-mutations.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-queued-published-bytes.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-queued-wiring.ts
  • src/main/runtime/mobile-rpc-allowlist.test.ts
  • src/main/runtime/rpc/methods/structured-agent-session-queued-edit.test.ts
  • src/main/runtime/rpc/methods/structured-agent-session-queued-methods.ts
  • src/main/runtime/rpc/methods/structured-agent-session-schemas.ts
  • src/main/runtime/rpc/methods/structured-agent-session.test.ts
  • src/main/runtime/runtime-rpc/runtime-rpc-mobile-agent-session-methods.ts
  • src/renderer/src/components/native-chat/NativeChatQueuedMessageCard.tsx
  • src/renderer/src/components/native-chat/NativeChatQueuedMessageEditor.test.tsx
  • src/renderer/src/components/native-chat/NativeChatQueuedMessageEditor.tsx
  • src/renderer/src/components/native-chat/NativeChatQueuedMessageList.inline-edit.test.tsx
  • src/renderer/src/components/native-chat/NativeChatQueuedMessageList.test.tsx
  • src/renderer/src/components/native-chat/NativeChatQueuedMessageList.tsx
  • src/renderer/src/components/native-chat/native-chat-submission-navigation.test.tsx
  • src/renderer/src/components/native-chat/queued-message-edit-notices.ts
  • src/renderer/src/components/native-chat/structured-agent-session-queued-edit-lease.ts
  • src/renderer/src/components/native-chat/use-native-chat-prompt-card-focus.ts
  • src/renderer/src/components/native-chat/use-structured-agent-session-queued-edit.test.tsx
  • src/renderer/src/components/native-chat/use-structured-agent-session-queued-edit.ts
  • src/renderer/src/components/native-chat/use-structured-agent-session-queued-messages.clear.test.tsx
  • src/renderer/src/components/native-chat/use-structured-agent-session-queued-messages.resume.test.tsx
  • src/renderer/src/components/native-chat/use-structured-agent-session-queued-messages.test.tsx
  • src/renderer/src/components/native-chat/use-structured-agent-session-queued-messages.ts
  • src/renderer/src/components/native-chat/use-structured-agent-session.ts
  • src/renderer/src/components/native-chat/use-structured-native-chat-submit-reveal.test.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/runtime/structured-agent-session-host-capability.ts
  • src/shared/agent-session-queued-message-wire.ts
  • src/shared/protocol-version.ts
  • src/shared/queued-message-text-edit.ts
  • src/shared/rpc-contract/structured-agent-session-queued-edit-params.ts
  • tests/e2e/cross-version-wire/structured-agent-session-host-fixture.ts
  • tests/e2e/cross-version-wire/structured-agent-session-surface-calls.ts
  • tests/e2e/cross-version-wire/structured-agent-session-surface-manifest.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment on lines +224 to +232
} else if (status === 'changed') {
// Someone else's edit landed first: the typing goes to the chat box, and the editor
// opens again on the newer text.
appendNativeChatDraftCache(edit.draftKey, edit.text)
toast.error(
queuedEditNotice(latest.current.promptOpen ? 'editChangedPrompt' : 'editChanged')
)
reopen.current = { scope, messageId: edit.messageId, stale: edit.baseFingerprint }
close()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Return the edited text after a changed answer only when the composer append succeeds.

appendNativeChatDraftCache returns false when the composer-draft write is not durable. The changed branch ignores this return value. It then shows a toast that says "Your edit is in the chat box" and closes the editor. The gone path and handOff ignore the same return value. If storage refuses the write, for example with a quota error, the toast can be false and a reload can lose the typed text. Check the result before you close the editor. If the append fails, keep the editor open or show a different notice.

…r, field and Save footer

While a card is edited, its row shows a pencil, "Editing message", the sender and an X that
cancels; the field at full width; and a footer with the key hint and Save. The card's queue icon
and its status line are hidden until editing ends.
…ave button

The editor footer's muted key hint is gone. Save now carries the same inline
key badge as the new-workspace composer's Create button, extracted into a
shared ButtonKeyHint so both buttons render one badge.
@brennanb2025
brennanb2025 merged commit 59631b8 into main Oct 10, 2026
30 of 32 checks passed
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