Wire /goal kickoff so setting or resuming a goal actually starts a turn - #399
Wire /goal kickoff so setting or resuming a goal actually starts a turn#399TheGreatAxios wants to merge 1 commit into
Conversation
api.kickoff was declared optional on CommandContext.goal and the only construction site (runner.ts) never provided it, so /goal's set and resume paths silently no-op'd: governor state changed but the agent was never told, leaving the operator staring at "Goal set." forever. Extract the wiring into createGoalKickoff (src/tui/goal-kickoff.ts): builds goalKickoffUserMessage and sends it through agentProxy.send, the same queue-safe path every typed prompt and command "send" result already uses, so a goal set mid-turn queues behind it instead of corrupting it. kickoff is now required on CommandContext.goal since runner.ts is confirmed the only constructor. Verified live: /goal set now drives the agent through the full planning -> implementing -> reviewing -> completed lifecycle unprompted.
7e0338e to
6009e9a
Compare
|
Review gauntlet complete: Greybeard, Gaasbot, Critique, Bruckheimer, Neckbeard — no blockers from any of them. One fix applied from Critique's finding: reordered Non-blocking follow-ups raised by reviewers, filed as separate concerns rather than folded into this bugfix:
Still gated on operator read-through per the standing director/prompt-change review rule — not merging this from any agent. |
|
Closing unmerged. The fix is correct and was verified end to end in a real session — The kickoff no-op this fixes ( Continuation belongs to the director unconditionally: |
Summary
/goal <brief>printed "Goal set." and did nothing:api.kickoff?.(...)(src/tui/commands/built-in.ts:257,286) was declared optional (src/tui/commands/registry.ts:20) and the only construction site (src/tui/runner.ts:1797) never provided it, so the optional-chain call silently no-op'd. Governor state changed, the agent was never told.goalKickoffUserMessage(src/agent/goal.ts:322) had zero callers insrc/.createGoalKickoff(src/tui/goal-kickoff.ts) and providing it at runner.ts's only construction site.kickoffis now required onCommandContext.goalsince that's confirmed the only constructor (checked src/exec/ and src/tui-opentui/demo.ts — neither builds this API).Decisions
agentProxy.send, the exact path every typed prompt and every command's{type:"send"}result already uses. It awaitssessionOps.awaitTail()internally, so a goal set mid-turn queues behind the in-flight turn instead of corrupting it.phase: "resume"goes through the identicalcreateGoalKickoffcall; verified via unit test and live run (see below), not assumed symmetric.Second bug reported, not fixed here
The operator also reported the phase line stuck on "working" indefinitely. Traced this: command dispatch (
/goal ...) never touches turn-state (isProcessing/ramp) — that only flips via the composer'ssendPromptpath, which slash commands bypass entirely. So under the old code the "working" label had no route to fire from/goalalone; it looks like an independent report, not a symptom of the kickoff no-op. Flagging for separate ticketing rather than guessing further under this PR's scope.Verified by running
Built the binary and drove it in tmux against a scratch git repo:
/goal add a README file explaining this repo→ agent autonomously ran manage_goal (created a 5-item acceptance checklist), manage_tasks, read the repo, wrote README.md, verified it, and reachedPhase: completedin ~38s — all unprompted after the one/goalcommand.Test plan
bun run typecheckbun run buildbun run test(4143 pass / 0 fail)src/tui/goal-kickoff.test.ts(asserts the kickoff message reaches the send path itself, not just governor state, for bothsetandresume) andsrc/tui/commands/goal.test.tsupdatesThis is a director/prompt-injection change under the project's human-review gate. Not to be merged without operator read-through.