fix(stop-continuation): persist stop state across user messages (#3276)

The /stop-continuation command was ineffective because the stop-
continuation-guard cleared its stopped state on the very next
chat.message event. Since any user message (including normal chat
after stopping) triggers chat.message, the continuation would
resume immediately.

Root cause: the chat.message handler called clear(sessionID) on
every user message, treating it as a 'user resumed work' signal.
But the user expects /stop-continuation to persist until they
explicitly start work again.

Changes:
- stop-continuation-guard chat.message: no longer clears stop state
- tool-execute-before: /start-work, /ralph-loop, /ulw-loop now
  explicitly clear the stop state (so continuation resumes when
  user intentionally restarts work)
- Updated and added tests: 12 pass (3 new), 125 related tests pass

Closes #3276
This commit is contained in:
YeonGyu-Kim
2026-04-09 21:36:10 +09:00
parent dc7a46809f
commit ab515b77d0
3 changed files with 56 additions and 6 deletions
@@ -162,7 +162,7 @@ describe("stop-continuation-guard", () => {
expect(guard.isStopped(session2)).toBe(false)
})
test("should clear stopped state on new user message (chat.message)", async () => {
test("should NOT clear stopped state on new user message (chat.message)", async () => {
// given - a session that was stopped
const guard = createStopContinuationGuardHook(createMockPluginInput())
const sessionID = "test-session-4"
@@ -172,7 +172,38 @@ describe("stop-continuation-guard", () => {
// when - user sends a new message
await guard["chat.message"]({ sessionID })
// then - stop state should be cleared (one-time only)
// then - stop state should persist (not cleared by user messages)
// Stop is only cleared by explicit work-starting commands (/start-work, /ralph-loop, /ulw-loop)
// or session deletion. This prevents /stop-continuation from being ineffective.
expect(guard.isStopped(sessionID)).toBe(true)
})
test("should persist stop state across multiple user messages", async () => {
// given - a session that was stopped
const guard = createStopContinuationGuardHook(createMockPluginInput())
const sessionID = "test-session-persist"
guard.stop(sessionID)
// when - user sends multiple messages
await guard["chat.message"]({ sessionID })
await guard["chat.message"]({ sessionID })
await guard["chat.message"]({ sessionID })
// then - stop state remains active
expect(guard.isStopped(sessionID)).toBe(true)
})
test("should clear stop state only via explicit clear() call", () => {
// given - a session that was stopped
const guard = createStopContinuationGuardHook(createMockPluginInput())
const sessionID = "test-session-explicit-clear"
guard.stop(sessionID)
expect(guard.isStopped(sessionID)).toBe(true)
// when - clear is called (simulating /start-work or /ralph-loop)
guard.clear(sessionID)
// then - stop state is cleared
expect(guard.isStopped(sessionID)).toBe(false)
})