Add experimental sent-message editing - #1280
Conversation
d469223 to
29ce093
Compare
|
🚨 SLOP COP 🚨 · I am the SlopCop. I am reviewing this pull request for security, code quality, performance, architecture, and end-to-end behavior. |
SawyerHood
left a comment
There was a problem hiding this comment.
🚨 SLOP COP 🚨 · review
ELI5: This feature lets a user change an old message. BB removes later chat events and asks the provider to answer again.
I found three defects that must be fixed before merge.
- The edit waits for a host rewind without a shared thread lock. A send, queued message, or interaction can change state during that wait.
- The edit API removes the agent caller identity. An agent can edit another root thread and start the replacement with user permission policy.
- The API accepts any operation ID. Two different IDs can map to the same Pi session file and use the wrong checkpoint.
I also found six other defects.
- Each prepared rewind keeps a provider session and two runtime entries until the daemon restarts.
- The inline editor puts the caret at the end. It does not select the existing message.
- A completed new turn restores the edit action only on the latest message. A page reload restores actions on earlier messages.
- A connected client reloads history after realtime already reloads the same history.
- The search suffix delete has no
(threadId, sourceSeq)index. It can scan a full long-thread search history under the write lock. - The client does not check the active background-agent count. It can show an action that the server rejects.
The supported provider list also appears in the client, server, and runtime. A shared provider capability would reduce policy drift.
I tested the feature with the local app and a real Codex thread. A first-message edit worked, and an earlier edit removed the complete later suffix.
The final provider output was FINAL-REPLY. The removed secret and second turn were absent from the server timeline.
The five package type checks passed. The 68 focused app, server, and runtime tests passed.
ad07b02 to
39654f5
Compare
- fold latest-target resolution into POST /edit-message: omitting expectedRequestSequence atomically edits the latest eligible message, replacing the GET latest-message-edit route, SDK method, and the CLI's racy GET-then-POST two-step - parse stored events through parseStoredEvent + type narrowing instead of hand-rolled JSON parsers, and reuse hasRootStoredTurnStarted - build the rewind-prepare command with a destructure-rest spread, dropping the dead disallowedTools branch - derive the request target kind from the staged fork instead of threading a redundant historyReplacement field - reuse threadStartResultSchema for the identical prepare result shape - migrate the inline-queued attachment path onto useDraftAttachmentUploads, inline the one-caller sentMessageEdit helpers, and drop the expectedRequestSequence round-trip and experiment-flag prop threading through the prompt area Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- key staged rewind forks by the server-minted per-attempt leaseId: each attempt owns one fork, dissolving the cross-attempt lease refcount, join validation, cleanup-wait loop, and sha256 staging-id hashing, and dropping operationId from both daemon wire commands - replace the runtime's hardcoded rewind provider allowlist with the existing supportsFork capability, share one staged-discard helper, and merge the preparing/prepared maps into a single lease-keyed map - collapse the editable-turn candidate scan to one query - share one inline-draft-composer builder between the queued and sent-message editors and fold the sent editor's bespoke blocked reasons into the queued editor's ready/unavailable precedent - prune render-wiring and duplicate tests across timeline actions, settings, CLI, and the Pi bridge Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9644f3c to
fd28988
Compare
Summary
editMessagesexperiment enforced by both the app and serverbb thread edit-messageBehavior
Opening or cancelling the inline editor is client-local and non-destructive. Submitting an edit stages provider history through the turn before the selected message, atomically replaces the selected conversation suffix, keeps workspace changes, and starts the edited turn.
Claude Code uses the latest assistant UUID with
forkSessionandupToMessageId. Pi records the session leaf entry and branches through it. Legacy Claude Code or Pi history without a checkpoint rejects safely when a retained turn is required.Review hardening
(thread_id, source_seq)and add a generated migration plus exact query-plan coverageValidation
The Pi smoke pass used
openai-codex/gpt-5.6-lunabecause the configured Anthropic route was out of extra usage.Simplification pass
expectedRequestSequenceon POST/edit-messagenow atomically edits the latest eligible message; the read-onlyGET /latest-message-editroute,threads.getLatestMessageEdit, and the CLI's GET-then-POST resolution are removed (the CLI's edit-latest is no longer racy)leaseId: each attempt owns exactly one fork, which removes the cross-attempt lease refcounting, join validation, and staging-id hashing, dropsoperationIdfrom both daemon rewind commands, and replaces the runtime's provider allowlist with thesupportsForkcapabilityparseStoredEventwith type narrowing, reuseshasRootStoredTurnStarted, builds the rewind-prepare command with a destructure-rest spread, and resolves the editable-turn scan in one queryuseDraftAttachmentUploads; one-caller helpers and redundant prop round-trips are inlinedRelated to #1114