mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-10 12:37:55 +01:00
docs(plans): record the Pi steer duplicate after a restart and its follow-up (#19982)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - A person can steer a running Codex or Pi turn, and since #19970 a queued follow-up steers automatically > - Restart recovery (#19962) delivers a steered message that the model did not read before the restart, exactly once for Codex > - Pi gives the run log no read evidence for a steer, so after a restart a Pi steer that the model already read can arrive again > - The fix needs changes to the closure-pinned Pi distribution, which waits for the next Pi re-qualification > - This pull request records the limit and the follow-up plan so they are not lost > - The benefit is that the next Pi re-qualification can include the definitive fix ## Linked Issues or Issue Description **What is the documentation problem?** The Pi steering limit after a restart, and its planned fix, were only in pull request text and session notes. **Where?** `doc/plans/2026-11-07-pi-steer-restart-limit.md` (new). **What should it say?** Codex delivers an unread steered message exactly once after a restart. Pi can duplicate a message it already read, and never loses one. The note also gives the reasons, the follow-up plan (option A), its costs, and the rejected options. Refs #19962, Refs #19970 ## What Changed - New `doc/plans/2026-11-07-pi-steer-restart-limit.md`. It covers the limit, why Pi has no read evidence, the follow-up (forward Pi's native user `message_start` through pi-acp, acpx, the sidecar and Rust into a `userMessage` item), its re-qualification costs, and the rejected options. ## Verification - Documentation only. Read the note. The facts match `packages/paperclip-runner/src/steering-delivery.ts`, `patches/pi-acp@0.0.33.patch`, `pi-closure-pins.ts` and `rules/acpx-profile-identity.mdc`. ## Risks - None. Documentation only. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, via Claude Code with tool use. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ccce55ab11
commit
46bf913fb9
1 file changed
+36
@@ -0,0 +1,36 @@
|
||||
# Pi steering after a restart: known limit and follow-up
|
||||
|
||||
Status: accepted limit, 2026-11-07. Follow-up planned for the next Pi re-qualification.
|
||||
Context: [#19962](https://github.com/paperclipai/paperclip/pull/19962) (restart recovery delivers unread steered messages) and [#19970](https://github.com/paperclipai/paperclip/pull/19970) (automatic steering).
|
||||
|
||||
## Summary
|
||||
|
||||
- **Codex: exactly once.** Restart recovery reads the run log. Each `userMessage` item after the turn's own input reads the oldest unread steer in that turn. If the read arrives before its acknowledgement, it waits for the next acknowledgement in that turn. The restart continuation delivers only the acknowledged messages that have not been read.
|
||||
- **Pi: never lost, sometimes duplicated.** Pi gives the run log no read evidence for a steer. If Pi read a steer and a restart then interrupts the same turn, the restart continuation delivers that message again. The agent sees it twice. It is never lost.
|
||||
- **This is rare and visible:** it needs a steer, a read, and a restart in the same turn, and the duplicate shows in the transcript.
|
||||
|
||||
## Why Pi has no read evidence
|
||||
|
||||
- Pi's native runtime emits a `message_start` with role `user` when it delivers a queued steer into the conversation.
|
||||
- The pinned pi-acp wrapper (`patches/pi-acp@0.0.33.patch`) does not forward that event, and acpx has no parser for ACP `user_message_chunk` updates.
|
||||
- Runner-side inference is unsafe. For example, "an assistant message started after the acknowledgement": Pi checks its steering queue before the model call, but the assistant `message_start` arrives only when the response starts streaming. A steer queued in that gap would count as read and be lost.
|
||||
- A restored session's history does not help either. A resumed ACPX session loads on its first prompt, so Pi replays its saved history after the continuation prompt is already sent.
|
||||
|
||||
## Follow-up for the next Pi re-qualification (option A)
|
||||
|
||||
1. In pi-acp, forward Pi's native user `message_start` as an ACP update with Pi-native provenance, for the turn's own input and for each delivered steer.
|
||||
2. Map it through acpx, the ACPX sidecar and the Rust ACPX backend to a `userMessage` item in the run log.
|
||||
3. The existing rule in `packages/paperclip-runner/src/steering-delivery.ts` then covers Pi unchanged. The first `userMessage` of a turn is its own input; each later one reads a steer.
|
||||
4. Add a red/green test with a Pi fixture: a steer read before the crash is not delivered again, and an unread one is delivered once.
|
||||
|
||||
Cost, which is why this waits for a re-qualification:
|
||||
|
||||
- The wrapper patch and `pi-acp-runtime.ts` are part of the Pi distribution closure. All three closure pins (`pi-closure-pins.ts`) must be regenerated on their target hosts.
|
||||
- The Pi `commandDigest` changes, so saved Pi sessions start fresh.
|
||||
- Pi paid qualification must run again.
|
||||
- The new runtime event is a Runner/pack contract and changes the pack `contractDigest` (`rules/acpx-profile-identity.mdc`).
|
||||
|
||||
## Options considered and rejected
|
||||
|
||||
- **Dedupe against Pi's replayed history at restore (eager load).** It works only with an eager session load before the continuation, plus changes in five places: an acpx patch, a sidecar command, a Rust pass-through, a harness method, and the recovery filter. Not worth it for a rare duplicate.
|
||||
- **Steer the unread messages into the continuation after the history replay.** This is racier, and it depends on when Pi takes a steer.
|
||||
Reference in new issue
Block a user