Skip to content

fix: don't send a re-queued task to backlog (#674)#677

Closed
bborn wants to merge 1 commit into
mainfrom
task/4949-feedback-changing-a-blocked-task-to-in-p
Closed

fix: don't send a re-queued task to backlog (#674)#677
bborn wants to merge 1 commit into
mainfrom
task/4949-feedback-changing-a-blocked-task-to-in-p

Conversation

@bborn

@bborn bborn commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Fixes #674.

Problem

Manually changing a blocked task to In Progress (Shift-S) can briefly show it In Progress and then bounce it back to Backlog.

showChangeStatus maps "In Progress" to StatusQueued. If the previous executor's poller (pollTmuxSession) is still alive (e.g. the task was parked at a permission prompt), it sees the internal queued status and returns Interrupted: true. executeTask finalizes every interrupted result to StatusBacklog — treating the deliberate requeue as a user cancellation and immediately undoing it.

Fix

Distinguish a re-queue from a genuine interruption:

  • Add a Requeued field to ExecResult / execResult.
  • pollTmuxSession returns Requeued: true (not Interrupted: true) for the StatusQueued case.
  • executeTask handles Requeued by leaving the task in queued (no backlog write) and waking the worker via TriggerProcessing().

The task then resumes through a fresh executor run. The fresh run's KillAllWindowsByNameAllSessions tears down the stale window, so no duplicate session lingers, and the deferred runningTasks cleanup releases this run's slot. Genuine interruptions (context cancellation, moving to Backlog) still finalize to backlog as before.

Tests

  • New TestPollTmuxSessionRequeuesOnQueuedStatus: a queued task yields Requeued=true / Interrupted=false.
  • Full internal/executor package passes; go vet and golangci-lint clean.

Minimal, targeted change — no unrelated files or behavior touched.

🤖 Generated with Claude Code

Changing a blocked task to "In Progress" writes status=queued. If the
previous executor's poller was still alive, pollTmuxSession saw the queued
status and returned Interrupted, which executeTask finalizes to backlog —
immediately undoing the requeue.

Distinguish a re-queue from a genuine interruption: pollTmuxSession now
returns a new Requeued result for the StatusQueued case, and executeTask
handles it by leaving the task queued (no backlog write) and waking the
worker for a fresh run. The fresh run's KillAllWindowsByNameAllSessions
tears down the stale window, so no duplicate session lingers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bborn

bborn commented Jul 20, 2026

Copy link
Copy Markdown
Owner Author

QA: bug reproduced, and this PR fixes it

Reproduced #674 end-to-end by driving the real TUI + daemon against an isolated throwaway instance (the scripts/qa Tier-3 harness), then verified this branch prevents it.

The precondition matters: the bug only fires when the executor's pollTmuxSession is genuinely parked (claude actively busy / waiting at a prompt). A task that has already reached end-of-turn has no live poller and does not reproduce it — worth knowing for anyone re-testing.

Repro on main (pre-fix) — flip Blocked → In Progress while the poller is parked

t+0.02s  status=queued
t+0.85s  status=queued
t+1.13s  status=backlog     ← bounced to Backlog, not In Progress

Same repro on this branch (4d6ad6b7)

t+0.02s  status=queued
t+0.30s  status=processing  ← resumes as a fresh run; stayed processing (verified 8.7s)

No backlog bounce.

Screenshots

Blocked (before) Bounced to Backlog (main bug) In Progress (this branch)
before bug fixed

Note (duplicate PR)

There are two PRs fixing #674: this one and #676. They arrived via two independent intake paths — #676 was opened directly, and this branch came from TaskYou task 4949, which the Slack-feedback automation auto-created from the #674 report ~24 min later. Both add a Requeued flag so the parked poller preserves queued instead of finalizing to backlog; one nuance is this branch returns execResult{Requeued: true} while #676 returns {Interrupted: true, Requeued: true}. Both pass QA here (the finalizer checks Requeued first). Whichever lands, close the other.

QA harness: isolated ty instance, real executor spawn, VHS-rendered board shots. Not the live daemon/DB.

bborn added a commit that referenced this pull request Jul 21, 2026
Adopts the cleaner signal from the parallel fix (#677): a re-queue is not a
cancellation, so pollTmuxSession now returns {Requeued: true} rather than
{Interrupted: true, Requeued: true}. Verified safe — the only reader of
Interrupted is executeTask's finalizer else-if, which the Requeued branch
already short-circuits (it kills the stale session and returns). Keeps #676's
explicit Kill + early return, which close the brief window where the stale
process would otherwise outlive the handoff. Test updated to assert the two
signals are mutually exclusive.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bborn

bborn commented Jul 21, 2026

Copy link
Copy Markdown
Owner Author

Closing in favor of #676 — both fix #674 the same way (a Requeued flag so the parked poller preserves queued instead of finalizing to backlog), and two open PRs for one issue isn't worth carrying.

This wasn't wasted: #676 has adopted the cleaner signal from this branch — pollTmuxSession now returns {Requeued: true} rather than {Interrupted: true, Requeued: true}, since a requeue isn't a cancellation. #676 keeps its explicit stale-session Kill + early return, which close the brief window where the old process would otherwise outlive the handoff (relevant given this codebase's history of double-session/pane races).

Both were QA-verified against the isolated harness; evidence is on #676. The duplicate came from two intake paths racing on the same issue — worth a dedup guard in the feedback→task automation so an issue that already has an open PR/task doesn't spawn another.

@bborn bborn closed this Jul 21, 2026
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.

Changing a blocked task to In Progress can immediately move it to Backlog

1 participant