Skip to content

Guard Chat delivery across retries and terminal transitions - #42

Closed
Tiyouw wants to merge 6 commits into
enowdev:mainfrom
Tiyouw:codex/chat-resume-stb-20261105-41b6
Closed

Tiyouw wants to merge 6 commits into
enowdev:mainfrom
Tiyouw:codex/chat-resume-stb-20261105-41b6

Conversation

@Tiyouw

@Tiyouw Tiyouw commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Refs #41.

A lost response or a state change during Chat's paste delay can leave terminal input uncertain. Route ordinary Chat messages through a per-program session.deliver ledger so retries retain one key and cannot repeat paste or Enter. Start the existing 300ms settle interval after successful paste PTY write, then validate input/state/history/readiness atomically with final enqueue. An interrupted accepted paste blocks new automatic deliveries until program replacement.

Chat queues behind busy/waiting or unconfirmed messages and acknowledges only a newly recorded full user entry, including attachment paths. The protocol addition refuses unsafe fallback on older daemons. Existing slash-command/settings input, upstream restart behavior and host permissions are preserved.

Pinned MyGo v0.6.2 lacks atomic screen-check/enqueue. A version/hash-checked preparer copies it privately and adds guarded-input methods; build entry points bootstrap for the host and quote native modfile flags. Repository go.mod/go.sum and cached source stay unchanged.

Validation on Windows, count=1, private pinned MyGo:

  • Passed session/client/proto/chatview/hostcontrol packages; synthetic daemon delivery and original hook-transition, late-receipt and cross-key timeout regressions; terminal redraw/enqueue, ownership and write-completion tests; handler coverage and focused go vet.
  • Skipped TestReplacingASessionTwiceAtOnce and TestWorkingGoesStaleWhenTheScreenStandsStill (shell prerequisites), and TestComposerLook (opt-in visual test).
  • Passed go test ./ui -run '^$' compile/link without UI test cases or GUI execution. Initial offline setup failures were resolved by staging the exact pinned SDK and 17 transitives privately, verifying module/go.mod checksums against committed go.sum, then compiling offline.

No full suite/lint, actual Make, platform packaging, native reliability or authenticated fresh/resume/fork/clear/account lifecycle certification is claimed. Earlier intermittent native consumption remains unexplained; enqueue and PTY write do not prove recorded UserMessage. Prior scoped build-portability reviews remain evidence; their suites and native experiments were not repeated on this base. Keeping this PR in Draft pending standalone CI/review.

Reviewed branch base: cd071c6
Head: 1255f94
Tree: a7ff36e1d2bdbf7ddfcbcce4a2c06c55bc3b1ea3

Six DCO commits preserve original authors, sign-offs and cherry-pick provenance. The first adapts the reviewed delivery function to upstream's newer pane/daemon body and preserves inputThroughDaemon; the other five have identical added/removed lines to their originals. This delta excludes account-sharing, conflict polling, conversation-binding and watcher/icon/report work.

Target main advanced to e306b3b before publication. Restricted comparison found separate hunks in shared paths; a clean merge-tree preserves the reviewed delta exactly. Local checks above cover the branch head, not the merged tree; standalone CI remains pending.

Signed-off-by: Ahimsa Jenar Bramsaifstyo Kusuma <tio.rmx@gmail.com>
(cherry picked from commit a30c62f49ae8f64c20f1d15254fe10cd0b1ed0ad)
Validate input history, hook epoch, program identity and observed readiness while queueing paste and delayed Enter. Keep interrupted attempts in the delivery ledger so retries cannot repaste. Preserve done state across multiline bracketed paste.

The terminal screen sample and Send still use separate MyGo operations. This fixes the reproduced hook race, but atomic exclusion of raw terminal redraws remains a release gate.

Signed-off-by: Ahimsa Jenar Bramsaifstyo Kusuma <tio.rmx@gmail.com>
(cherry picked from commit d815ff770d5a0733940bc3eaf0c172fdeebaa009)
Add a 22-line terminal-owner SendChecked operation that holds the terminal and input queue locks through the pure readiness predicate and FIFO insertion. Keep the session cancellation epoch and delivery ledger from d815ff7.

Apply the version- and source-hash-checked dependency addition from repository-owned source through a private modfile, without changing cached MyGo files or checking in a fork. Route build entry points through preparation and add a deterministic redraw-boundary regression.

Signed-off-by: Ahimsa Jenar Bramsaifstyo Kusuma <tio.rmx@gmail.com>
(cherry picked from commit 32a72e3ddb79a510d650fda047d30e1fb0c4bb4d)
Build the preparer for GOHOSTOS/GOHOSTARCH while preserving the child target and selected Go executable. Generate quoted modfile flags from native paths so spaced homes and Git Bash paths remain valid for Go.

Signed-off-by: Ahimsa Jenar Bramsaifstyo Kusuma <tio.rmx@gmail.com>
(cherry picked from commit 0788997ec2f57291092d94df466a25ea664e4d59)
A delayed input writer can drain paste and Enter back to back even though the enqueue timer expired 300ms apart. Wait for the FIFO write callback before starting that same interval, while retaining the final readiness guard and delivery ledger. Bound missing completion by program lifetime and a five-second write wait, and anchor delayed paste echo to its still-current input sequence.

Signed-off-by: Ahimsa Jenar Bramsaifstyo Kusuma <tio.rmx@gmail.com>
(cherry picked from commit 9009d02e17a54ffd3f2677551f3a51fc6196fe63)
A timed-out paste can remain ahead of later messages in the terminal FIFO. Retain run-level uncertainty after any interrupted delivery that queued its paste, so a different key cannot send Enter behind those bytes. Preserve the original same-key result and bounded write wait; only a replacement program gets a fresh gate.

Signed-off-by: Ahimsa Jenar Bramsaifstyo Kusuma <tio.rmx@gmail.com>
(cherry picked from commit a3c4a57e7ce44cb55cd3590481ba8e2d47f7d131)
@enowdev

enowdev commented Nov 5, 2026

Copy link
Copy Markdown
Owner

The bug is real on main, and I can point at it: ui/chat.go:263-292 writes the paste to the pane stream, sleeps 50ms, then writes "\r", never re-checking the screen or state; daemon/schedule.go:286-294 does the same with pasteSettle and only checks agentGone. Since client.Call has no retry, the Retry button re-sends the whole text, so a lost response or a transition during the delay can paste twice or submit the wrong thing.

The delivery ledger and the Chat-side acknowledgement are the right shape, and the protocol addition is clean: session.deliver is additive, api.txt only gains lines, and refusing to fall back to session.input on an old daemon is the correct call.

The terminalpatch bootstrap is the part I can't take. It copies the whole pinned module out of GOMODCACHE, injects two files, adds a replace to a temporary go.mod, and puts that behind every build entry point. That means the shipped binary is not built from the dependency set go.mod names: mygo v0.6.2 there, a patched copy here. Anyone building from the tag with plain go build, or go install, or a distro packager, or a dependency scanner, gets a different program than the release. And the repo no longer builds with the plain toolchain at all — core/session/delivery.go:123 calls SendCheckedWritten, which v0.6.2 does not have, as the README says. Your pinned hash does match the module cache, so this is reproducible, but it is still a second dependency graph that only the wrapper knows about.

It also isn't green: TestBuildScriptBootstrap, TestMakeBootstrapTarget and TestBootstrapWrapperTarget fail on both ubuntu and macOS (exit status 1, then TempDir cleanup hitting read-only files under the copied module cache), plus TestPlatformE2E.

So: does the bootstrap belong in this repository? I don't think so, in this form. The two added files belong in MyGo as a real API, or behind a replace to a committed module that go.mod can name — not a copy of the module cache made at build time.

What I'd like instead: keep the ledger and the protocol change, and drop the patch for now. DeliveryPaste can paste and Enter through the existing Text()/Send() under the session's own inputMu, with the narrower guarantee documented in docs/chat-view.md — a screen check is then best-effort rather than atomic, and that's fine to say. That removes the supply-chain question and most of the 41 files, and the hard part (one key per message, one paste, no Enter after a transition, quarantine until the program is replaced) is still yours.

Two smaller things while you're in there: the new lock order is manager -> input -> session -> terminal; please write down the terminal-callback contract, since OnTitle/OnUpdate exist and a callback that took the session lock would invert it. And with no fallback, a new app on an old daemon can't send Chat messages until reconnect — worth confirming that's wanted.

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.

2 participants