fix: signal active containers to close when a scheduled task is enqueued#320
fix: signal active containers to close when a scheduled task is enqueued#320aoberoi wants to merge 2 commits intoqwibitai:mainfrom
Conversation
|
@longregen whoops! good catch |
When a scheduled task was enqueued for a group with an active container, it would sit in pendingTasks until the container exited naturally — up to 30 minutes (idle timeout), or indefinitely if users kept resetting it. Now writes the _close sentinel immediately when queuing a task against an active container. The container winds down after its current query, drainGroup fires, and the task runs in a new container within seconds. Fixes qwibitai#293 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
cleaned the commit history |
|
Found another related issue in my testing. Scheduled tasks can sometimes fire more than once because of a race condition. The race is between the The solution I'm working on is to add a new The fix includes all the state transitions to make this work and some tests. |
Introduces a status='running' transition set atomically at enqueue time, preventing the scheduler's 60s poll from re-enqueueing a task that is still executing. Also fixes updateTaskAfterRun to transition running→active (not running→running) for recurring tasks, and adds startup crash recovery via resetRunningTasks(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…qwibitai#320) Migrate the three highest-complexity hooks from bash to TypeScript: - pr-review-loop.sh (452 lines) → src/hooks/pr-review-loop.ts - pr-kaizen-clear.sh (290 lines) → src/hooks/pr-kaizen-clear.ts - kaizen-reflect.sh (197 lines) → src/hooks/kaizen-reflect.ts Shared infrastructure: - src/hooks/hook-utils.ts — stdin JSON parsing, git helpers - src/hooks/parse-command.ts — command parsing (port of lib/parse-command.sh) - src/hooks/state-utils.ts — atomic state writes, typed objects, no stat portability - src/hooks/telegram-ipc.ts — Telegram notification via IPC Improvements over bash: - Atomic state writes (temp file + rename) prevent race conditions - Native JSON parsing (no jq pipelines or sed extraction) - No pipe-splitting corruption (IFS='|' read bug) - No stat portability issues (fs.statSync works everywhere) - Proper typed validation with clear error messages - 115 vitest tests covering all state machine paths Also: - Add priority:critical and priority:high labels to issue taxonomy - Update /pick-work and /make-a-dent to prefer high-priority issues - Old bash hooks deactivated with migration comments - Filed qwibitai#331 (worktree-du migration), qwibitai#332 (CI smoke tests), qwibitai#333 (shared lib retirement) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
First hook migrated to TypeScript, establishing the pattern for all future L3-L4 hook migrations per docs/hook-language-boundaries.md. What changed: - src/hooks/kaizen-reflect.ts: Full TS port of the PostToolUse hook - src/hooks/hook-io.ts: Shared hook I/O (stdin JSON, stdout advisory) - src/hooks/parse-command.ts: TS port of parse-command.sh library - src/hooks/state-utils.ts: TS port of state-utils.sh library - Thin bash wrapper (kaizen-reflect-ts.sh) delegates to npx tsx - settings.json updated to use the TS wrapper - 53 vitest tests covering all three modules - Old kaizen-reflect.sh marked as deactivated (kept for reference) Run tag: batch-260321-1108-3ef8/run-7 Closes Garsson-io/kaizen#320 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…261) First hook migrated to TypeScript, establishing the pattern for all future L3-L4 hook migrations per docs/hook-language-boundaries.md. What changed: - src/hooks/kaizen-reflect.ts: Full TS port of the PostToolUse hook - src/hooks/hook-io.ts: Shared hook I/O (stdin JSON, stdout advisory) - src/hooks/parse-command.ts: TS port of parse-command.sh library - src/hooks/state-utils.ts: TS port of state-utils.sh library - Thin bash wrapper (kaizen-reflect-ts.sh) delegates to npx tsx - settings.json updated to use the TS wrapper - 53 vitest tests covering all three modules - Old kaizen-reflect.sh marked as deactivated (kept for reference) Run tag: batch-260321-1108-3ef8/run-7 Closes Garsson-io/kaizen#320 Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ke tests & policy (qwibitai#320, qwibitai#332) Builds on the kaizen-reflect migration (already in main) to complete Phase 3: - pr-review-loop.ts: multi-round PR self-review state machine (452→240 lines) - pr-kaizen-clear.ts: kaizen gate validation + clearing (290→370 lines, with kaizen #198 waiver elimination) - wrapper-smoke.test.ts: 9 end-to-end smoke tests for bash→tsx→hook chain - resolve-project-root.sh: DRY shared lib for TS wrapper path resolution Both hooks use main's patterns: extracted processHookInput() for testability, async readHookInput from hook-io.ts, import guard, explicit stateDir params. Policy changes: - Policy #18: "Smoke tests ship WITH the feature — never after" - Priority labels (priority:critical, priority:high) in issue taxonomy - /pick-work and /make-a-dent updated to prefer priority-labeled issues 129 tests across 6 files, all passing. Closes Garsson-io/kaizen#320 Closes Garsson-io/kaizen#332 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…wibitai#320, qwibitai#332) (#268) * feat: migrate L3-L4 bash hooks to TypeScript — Phase 3 of #223 (kaizen qwibitai#320) Migrate the three highest-complexity hooks from bash to TypeScript: - pr-review-loop.sh (452 lines) → src/hooks/pr-review-loop.ts - pr-kaizen-clear.sh (290 lines) → src/hooks/pr-kaizen-clear.ts - kaizen-reflect.sh (197 lines) → src/hooks/kaizen-reflect.ts Shared infrastructure: - src/hooks/hook-utils.ts — stdin JSON parsing, git helpers - src/hooks/parse-command.ts — command parsing (port of lib/parse-command.sh) - src/hooks/state-utils.ts — atomic state writes, typed objects, no stat portability - src/hooks/telegram-ipc.ts — Telegram notification via IPC Improvements over bash: - Atomic state writes (temp file + rename) prevent race conditions - Native JSON parsing (no jq pipelines or sed extraction) - No pipe-splitting corruption (IFS='|' read bug) - No stat portability issues (fs.statSync works everywhere) - Proper typed validation with clear error messages - 115 vitest tests covering all state machine paths Also: - Add priority:critical and priority:high labels to issue taxonomy - Update /pick-work and /make-a-dent to prefer high-priority issues - Old bash hooks deactivated with migration comments - Filed qwibitai#331 (worktree-du migration), qwibitai#332 (CI smoke tests), qwibitai#333 (shared lib retirement) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: format hook files with Prettier Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: smoke tests for TS hook wrappers + "tests ship with feature" policy (qwibitai#332) - Add wrapper-smoke.test.ts: 9 tests verifying the full bash→tsx→hook chain for all 3 migrated hooks (pr-review-loop, pr-kaizen-clear, kaizen-reflect) - Fix wrapper path resolution: use `git rev-parse --show-toplevel` instead of `git worktree list` — the old approach resolved to main checkout where the TS hooks don't exist yet in other worktrees - Use randomized PR numbers + isolated STATE_DIR to prevent smoke test state from leaking into production state dir (incident: PR 99999 gate blocked session) - Add policy #18: "Smoke tests ship WITH the feature — never after" to .claude/kaizen/policies.md, CLAUDE.md, and /review-pr skill Closes Garsson-io/kaizen#332 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Type of Change
.claude/skills/Description
Fixes #293. This is equivalent to Option A in that issue. I believe this is the cleanest way to fix the issue. Option B would screw up the workflow for scheduled tasks with context_mode: group, since a parallel container trying to resume a currently active session is going to lead to undefined outcomes. The single container per group invariant is worth preserving. Option C doesn't really provide any benefit if both idleWaiting and processing containers can correctly handle resuming sessions.
Also fixes a race condition (#320 (comment))
When a scheduled task was enqueued for a group with an active container, it would sit in pendingTasks until the container exited naturally — up to 30 minutes (idle timeout), or indefinitely if users kept resetting it.
Now writes the
_closesentinel immediately when queuing a task against an active container. The container winds down after its current query, drainGroup fires, and the task runs in a new container within seconds.I did some investigation to make sure the two kinds of scheduled tasks (context_mode: isolated and group) are considered. Here are all the scenarios:
Scenario 1: Container is idle, isolated task fires
going idle → sessions[group.folder] = "session-A" is current
"session-B" → discarded by scheduler
Scenario 2: Container is busy (mid-query), isolated task fires
wrappedOnOutput updates sessions[group.folder] = "session-A" (or a new turn
ID)
between-query session update marker, but the result OUTPUT_MARKER already
updated sessions)
discarded
Scenario 3: Container is idle, group task fires
session
doesn't create a new session ID, this is fine
task's turns ✓
Scenario 4: Container is busy, group task fires
updated
For Skills