Skip to content

fix(worker-wake): stop inbox nudges from duplicating on a swallowed Enter - #736

Open
abdelnabiii wants to merge 2 commits into
HarnessMD:mainfrom
abdelnabiii:fix/inbox-nudge-pty-delivery
Open

abdelnabiii wants to merge 2 commits into
HarnessMD:mainfrom
abdelnabiii:fix/inbox-nudge-pty-delivery

Conversation

@abdelnabiii

@abdelnabiii abdelnabiii commented Oct 13, 2026 •

Copy link
Copy Markdown

Summary

  • The inbox-nudge delivery (text, then a fixed 140ms wait, then \r) never checked that either write actually reached the TUI. A swallowed Enter left the text sitting unsubmitted in the input box, and the next wake beat typed a second full nudge on top of it -- the reported "stuck in the input box, retries duplicate it" bug.
  • Shortened the nudge to one line (hiveNudge.ts): the full "read inbox, move to inbox/.done/, act autonomously" prose is already injected once into every agent's system prompt at spawn (hive.ts's HIVE PROTOCOL block), so restating it on every nudge only gave a slow/occluded PTY more text to swallow and more to duplicate when it did.
  • Added nudgeDelivery.ts: wait 400-600ms for the PTY to echo the typed text (polling, not a fixed guess) before sending Enter; after Enter, confirm the PTY actually reacted within 500ms; if not, resend only Enter (never the text) with 300/600/1200ms backoff, up to 3 times, then give up. Every phase (typed/enter-sent/confirmed/stuck/enter-retry/gave-up) is logged via hive.appendLog to log.jsonl.
  • Added in-flight tracking to WorkerWakeWatchdog (markNudging/clearNudging, new 'in-flight' hold reason) so a second delivery can never start for an agent whose first one hasn't resolved -- this was the actual mechanism behind the duplication, not just the fixed-delay timing.
  • Hold-if-busy and coalesce were already implemented well in the existing watchdog (mid-turn/booting/cooldown/edge-triggered announce); this only adds the missing in-flight piece on top.

Caveat (stated plainly, not hidden)

"Check whether the prompt line still holds the text" is done via a heuristic -- did the PTY produce any new output -- not true terminal-buffer inspection; the main process has no headless xterm reading rendered screen state (xterm.js only runs in the renderer). It's strictly better than the old fixed-delay/no-feedback sequence, but not literal line inspection.

Scoped to the main-process watchdog's own nudge path (nudgeWorker/runWorkerWakeBeat) only. The renderer's separate submitToPty (useHive.ts, used for slash commands, seed prompts, work orders -- not just nudges) is untouched; flagging it as a known follow-up rather than silently leaving it inconsistent.

Before

Old delivery: a swallowed Enter left the nudge text sitting unsubmitted, and the next beat retyped it -- duplicated in the input box. Reproduced in evidence/demo-nudge-fix.cjs against the removed pre-fix nudgeWorker logic:
before

After

Fixed delivery (nudgeDelivery.ts, this PR): the same swallowed-Enter scenario now retries Enter alone, confirms the PTY reacted, and never retypes the text:
after

Test plan

  • New test/nudge-delivery.test.cjs (6 tests, including a 3-concurrent-agents-with-different-retry-depths scenario)
  • Updated test/worker-wake.test.cjs (added in-flight coalescing tests, incl. "3 agents nudged at once, one mid-task")
  • Updated test/hive-nudge.test.cjs for the shortened text
  • node --test test/*.test.cjs: 970 tests, 25 pre-existing failures (confirmed via git stash to fail identically without this change -- Windows symlink-permission and Electron-runtime tests, unrelated)
  • npx tsc --noEmit -p tsconfig.node.json: clean

🤖 Generated with Claude Code

https://claude.ai/code/session_01MBNN17AULnHcT97qhTc4ja

…nter

The nudge delivery sequence wrote the text, waited a fixed 140ms, then wrote
"\r" with no check that either write actually reached the TUI. A swallowed
Enter left the text sitting unsubmitted in the input box, and the next beat
(seeing new mail, or off cooldown) typed a second full nudge on top of it,
duplicating it.

- Shorten the nudge to one line (hiveNudge.ts): the full "read inbox, move to
  inbox/.done/, act autonomously" prose is already injected once into every
  agent's system prompt at spawn, so restating it on every nudge only gave a
  slow/occluded PTY more text to swallow and more to duplicate when it did.
- Add nudgeDelivery.ts: wait 400-600ms for the PTY to echo the typed text
  (polling, not a fixed guess) before sending Enter; after Enter, confirm the
  PTY reacted within 500ms; if not, resend ONLY Enter (never the text) with
  300/600/1200ms backoff, up to 3 times, then give up. Every phase is logged.
- Add in-flight tracking to WorkerWakeWatchdog (markNudging/clearNudging, new
  'in-flight' hold) so a second delivery can never start for an agent whose
  first one hasn't resolved -- the actual mechanism behind the duplication.

Tested: 31 new/updated unit tests (nudge-delivery.test.cjs, including the
3-concurrent-agents-one-mid-task scenario; updated worker-wake.test.cjs,
hive-nudge.test.cjs), all passing. Full suite: no new regressions (25
pre-existing failures confirmed via git stash, unrelated -- Windows
symlink-permission and Electron-runtime tests). tsc --noEmit clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MBNN17AULnHcT97qhTc4ja
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

✅ Evidence received. Before and after are both attached. Thanks — this is what makes a PR reviewable in one pass.



Referenced in the PR description's Before/After sections -- a standalone
script (not part of the test suite) demonstrating the swallowed-Enter
duplication bug against the removed pre-fix logic, then the same scenario
against the real nudgeDelivery.ts showing it confirmed, never duplicated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MBNN17AULnHcT97qhTc4ja
@abdelnabiii

Copy link
Copy Markdown
Author

@chaitanyagiri friendly ping on this one -- CI is green (before/after evidence check passed) and it's been open since Oct 13. Happy to address any review feedback. Thanks!

This branch has not been deployed

No deployments
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