66 lines
2.5 KiB
Markdown
66 lines
2.5 KiB
Markdown
|
|
# src/hooks/todo-continuation-enforcer/ — Boulder Continuation Mechanism
|
||
|
|
|
||
|
|
**Generated:** 2026-02-19
|
||
|
|
|
||
|
|
## OVERVIEW
|
||
|
|
|
||
|
|
14 files (~2061 LOC). The "boulder" — Continuation Tier hook that forces Sisyphus to keep rolling when incomplete todos remain. Fires on `session.idle`, injects continuation prompt after 2s countdown toast.
|
||
|
|
|
||
|
|
## HOW IT WORKS
|
||
|
|
|
||
|
|
```
|
||
|
|
session.idle
|
||
|
|
→ Is main session (not prometheus/compaction)? (DEFAULT_SKIP_AGENTS)
|
||
|
|
→ No abort detected recently? (ABORT_WINDOW_MS = 3s)
|
||
|
|
→ Todos still incomplete? (todo.ts)
|
||
|
|
→ No background tasks running?
|
||
|
|
→ Cooldown passed? (CONTINUATION_COOLDOWN_MS = 30s)
|
||
|
|
→ Failure count < max? (MAX_CONSECUTIVE_FAILURES = 5)
|
||
|
|
→ Start 2s countdown toast → inject CONTINUATION_PROMPT
|
||
|
|
```
|
||
|
|
|
||
|
|
## KEY FILES
|
||
|
|
|
||
|
|
| File | Purpose |
|
||
|
|
|------|---------|
|
||
|
|
| `handler.ts` | `createTodoContinuationHandler()` — event router, delegates to idle/non-idle handlers |
|
||
|
|
| `idle-event.ts` | `handleSessionIdle()` — main decision gate for session.idle |
|
||
|
|
| `non-idle-events.ts` | `handleNonIdleEvent()` — handles session.error (abort detection) |
|
||
|
|
| `session-state.ts` | `SessionStateStore` — per-session failure/abort/cooldown state |
|
||
|
|
| `todo.ts` | Check todo completion status via session store |
|
||
|
|
| `countdown.ts` | 2s countdown toast before injection |
|
||
|
|
| `abort-detection.ts` | Detect MessageAbortedError / AbortError |
|
||
|
|
| `continuation-injection.ts` | Build + inject CONTINUATION_PROMPT into session |
|
||
|
|
| `message-directory.ts` | Temp dir for message injection exchange |
|
||
|
|
| `constants.ts` | Timing constants, CONTINUATION_PROMPT, skip agents |
|
||
|
|
| `types.ts` | `SessionState`, handler argument types |
|
||
|
|
|
||
|
|
## CONSTANTS
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
DEFAULT_SKIP_AGENTS = ["prometheus", "compaction"]
|
||
|
|
CONTINUATION_COOLDOWN_MS = 30_000 // 30s between injections
|
||
|
|
MAX_CONSECUTIVE_FAILURES = 5 // Then 5min pause (exponential backoff)
|
||
|
|
FAILURE_RESET_WINDOW_MS = 5 * 60_000 // 5min window for failure reset
|
||
|
|
COUNTDOWN_SECONDS = 2
|
||
|
|
ABORT_WINDOW_MS = 3000 // Grace after abort signal
|
||
|
|
```
|
||
|
|
|
||
|
|
## STATE PER SESSION
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface SessionState {
|
||
|
|
failureCount: number // Consecutive failures
|
||
|
|
lastFailureAt?: number // Timestamp
|
||
|
|
abortDetectedAt?: number // Reset after ABORT_WINDOW_MS
|
||
|
|
cooldownUntil?: number // Next injection allowed after
|
||
|
|
countdownTimer?: Timer // Active countdown reference
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## RELATIONSHIP TO ATLAS
|
||
|
|
|
||
|
|
`todoContinuationEnforcer` handles **main Sisyphus sessions** only.
|
||
|
|
`atlasHook` handles **boulder/ralph/subagent sessions** with a different decision gate.
|
||
|
|
Both fire on `session.idle` but check session type first.
|