Skip to content

feat: --exit-confirmations, --watch-prompt-file, --no-classifier (v1.3.0)#14

Merged
JonyanDunh merged 1 commit intomainfrom
feat/exit-confirmations-watch-no-classifier-v1.3.0
Apr 12, 2026
Merged

feat: --exit-confirmations, --watch-prompt-file, --no-classifier (v1.3.0)#14
JonyanDunh merged 1 commit intomainfrom
feat/exit-confirmations-watch-no-classifier-v1.3.0

Conversation

@JonyanDunh
Copy link
Copy Markdown
Owner

Three new flags for /watchdog:start that turn the loop's exit semantics into something you can actually tune. Fully backward compatible: v1.2.4 state files load with the new fields defaulted to their pre-1.3.0 values.

What's new

--exit-confirmations <N> (default 1)

Require N consecutive clean NO_FILE_CHANGES verdicts from the Haiku classifier before the loop is allowed to exit. Strict reset semantics: any other verdict (FILE_CHANGES, AMBIGUOUS, CLI_MISSING, CLI_FAILED) and any pure-text turn (no tool invocations) reset the streak counter to 0. Convergence has to be unbroken to count.

/watchdog:start \"Refactor cache.ts. Iterate until tests pass.\" --exit-confirmations 3 --max-iterations 20

Default 1 is identical to pre-1.3.0 behavior, so an existing watchdog that didn't pass this flag continues to exit on the first clean verdict. Mutually exclusive with --no-classifier.

--watch-prompt-file (boolean, requires --prompt-file)

The Stop hook re-reads the prompt file at the start of every iteration. If the content has changed since the previous turn, the new version becomes the next user turn and the --exit-confirmations streak counter is reset to 0 (a redefined task should not inherit convergence from the old task).

/watchdog:start --prompt-file ./tmp/task.md --watch-prompt-file --max-iterations 30

Hot-reload never crashes the loop: missing / empty / unreadable file silently keeps the cached prompt and the loop continues. You can edit, rename, or temporarily move the file mid-loop without breaking anything. Standalone --watch-prompt-file (no --prompt-file) is an error.

--no-classifier (boolean)

Disable the Haiku classifier entirely. The Stop hook short-circuits before `askHaiku()` is ever called; the loop only exits via `--max-iterations` or `/watchdog:stop`. The `claude` CLI is not even required in this mode (the Haiku subprocess is never spawned).

/watchdog:start \"Keep iterating until I /watchdog:stop.\" --no-classifier --max-iterations 0

Compatible with --prompt-file and --watch-prompt-file. Mutually exclusive with --exit-confirmations — the streak counter is meaningless when no classifier returns verdicts.

Architecture

  • New lib/prompt-file.js — shared readPromptFile() helper used by both setup-watchdog.js (initial read) and stop-hook.js (hot-reload). Centralized BOM strip, trim, ENOENT/EISDIR/EACCES error mapping, resolved-path return value. The inline copy in setup-watchdog.js is removed.
  • lib/state.jscreate() accepts `exitConfirmations`, `promptFile`, `watchPromptFile`, `noClassifier` as named options. New state file fields (`exit_confirmations`, `no_change_streak`, `prompt_file`, `watch_prompt_file`, `no_classifier`) all default to backward-compatible values. `isValid()` is unchanged so existing v1.2.4 state files still load.
  • hooks/stop-hook.js — major rewrite of the verdict handler:
    • v1.3.0 fields read with defensive defaults so v1.2.4 state files behave exactly as before
    • Hot-reload happens early, before the max-iterations check, so a fresh prompt-file content takes effect on the same iteration that detects it
    • --no-classifier short-circuit happens before `askHaiku()` so no subprocess is ever spawned
    • NO_FILE_CHANGES verdict bumps the streak and only exits when `streak >= exit_confirmations`; otherwise logs progress (e.g. "1/3 - need 2 more, continuing loop") and re-feeds
    • All non-`NO_FILE_CHANGES` verdicts and pure-text turns reset `effectiveStreak = 0` (strict reset semantics)
    • Iteration bump and streak/prompt persist happen in a single `update()` patch to keep the atomic-write contract intact
  • `scripts/setup-watchdog.js` — three new flags + cross-flag validation. `exitConfirmations` is left undefined when not passed (so we can distinguish "user typed --exit-confirmations 1 explicitly" from "user left it off") and the validation in `main()` uses that distinction to error on `--no-classifier + --exit-confirmations` conflict. The resolved absolute prompt-file path is captured at create-time so the hook's hot-reload doesn't have to re-resolve a relative path against a possibly-different cwd later.

Backward compatibility

Concern Status
Existing v1.2.4 state files (no v1.3.0 fields) Loaded with defaults: exit_confirmations=1, no_change_streak=0, no_classifier=false, watch_prompt_file=false, prompt_file=null. Behavior is bit-for-bit identical to v1.2.4.
Existing inline /watchdog:start \"prompt\" calls Unchanged — no new flags required, no behavior change.
Existing `--prompt-file` calls without `--watch-prompt-file` Unchanged — file read once at start, no hot-reload.
Hook recursion guard (Haiku subprocess) Unchanged — still works via process-ancestry PID lookup (introduced in v1.2.0).

Verified by running the entire pre-existing test suite unmodified plus 30 new tests.

Test plan

  • 124-test suite passes locally (124 active + 2 skipped-inside-Claude-Code, 0 failures) — 30 net new tests since v1.2.4
  • `setup.test.js`: arg parse + validation for all new flags + every error path + every mutual exclusion combination
  • `state.test.js`: new fields default correctly, explicit values stored, v1.2.4-shape state files still pass `isValid()`
  • `stop-hook.test.js`:
    • backward-compat for v1.2.4 state files
    • pure-text turn resets `no_change_streak` even when it was non-zero
    • `--no-classifier` short-circuits Haiku entirely (test strips `claude` from PATH and uses `process.execPath` to confirm no spawn attempt)
    • `--no-classifier` still respects `--max-iterations` as the only escape
    • hot-reload: unchanged content, changed content, deleted file, empty file, watch flag off, missing file
  • `stop-hook-haiku.test.js`:
    • `--exit-confirmations 3`: streak progression `1 -> 2 -> 3` then exit with `3/3` log
    • `FILE_CHANGES` mid-streak resets to 0
    • `AMBIGUOUS` mid-streak resets to 0
    • `CLI_FAILED` mid-streak resets to 0
    • default `exit_confirmations=1` still exits on first `NO_FILE_CHANGES` (regression check, asserts no `1/1` log either)
  • CI will confirm on the standard matrix (ubuntu / macos / windows × Node 18 / 20 / 22)

Scope

  • `lib/prompt-file.js` — new shared helper
  • `lib/state.js` — extended `create()` schema with v1.3.0 fields
  • `scripts/setup-watchdog.js` — three new flags, mutual-exclusion validation, uses shared prompt-file lib
  • `hooks/stop-hook.js` — hot-reload, no-classifier short-circuit, streak counter
  • `commands/start.md` — `argument-hint` update
  • `commands/help.md` — full new-flag reference + updated exit conditions + version anchor 1.2.0 → 1.3.0
  • `test/setup.test.js` — +13 tests
  • `test/state.test.js` — +3 tests
  • `test/stop-hook.test.js` — +8 tests
  • `test/stop-hook-haiku.test.js` — +5 tests
  • `README.{md,zh,es,ja,ko,vi,pt}.md` — three new subsections under Commands ("Stricter convergence with `--exit-confirmations`", "Hot-reload the prompt mid-loop with `--watch-prompt-file`", "Disable the classifier entirely with `--no-classifier`") and two new rows ("Convergence flexibility", "Prompt evolution") in the Watchdog vs ralph-loop comparison table
  • `.claude-plugin/plugin.json` — version → 1.3.0
  • `.claude-plugin/marketplace.json` — version → 1.3.0

Install / upgrade

```bash
/plugin marketplace update claude-code-watchdog
/reload-plugins
```

Verify with `/watchdog:help` — look for the three new options under `/watchdog:start`.

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

…3.0)

Three new flags for /watchdog:start, plus a small library refactor that
moves the prompt-file reader into a shared lib so the stop hook can
re-use it for hot-reload. Fully backward compatible: v1.2.4 state files
load with the new fields defaulted to their pre-1.3.0 values.

## What's new

- `--exit-confirmations <N>` (default 1) — require N **consecutive**
  clean `NO_FILE_CHANGES` verdicts from the Haiku classifier before
  the loop is allowed to exit. Strict-A reset semantics: any other
  verdict (FILE_CHANGES / AMBIGUOUS / CLI_MISSING / CLI_FAILED) AND
  any pure-text turn (no tool invocations) reset the streak counter
  to 0. Convergence has to be unbroken to count.

- `--watch-prompt-file` — boolean flag, requires --prompt-file. The
  Stop hook re-reads the prompt file at the start of every iteration.
  If the content changed since the previous turn, the new version
  becomes the next user turn AND the --exit-confirmations streak is
  reset to 0 (a redefined task should not inherit convergence from
  the old task). Hot-reload is silent on read failure: missing /
  empty / unreadable file keeps the cached prompt and continues the
  loop. Standalone --watch-prompt-file (no --prompt-file) is an
  error.

- `--no-classifier` — disable the Haiku classifier entirely. The
  Stop hook short-circuits before askHaiku() is ever called; the
  loop only exits via --max-iterations or /watchdog:stop. The
  `claude` CLI is not even required in this mode (the Haiku
  subprocess is never spawned). Mutually exclusive with
  --exit-confirmations (the streak counter is meaningless when no
  classifier returns verdicts).

## Architecture

- New lib/prompt-file.js — shared readPromptFile() helper used by
  both setup-watchdog.js (initial read) and stop-hook.js (hot-
  reload). BOM strip, trim, ENOENT/EISDIR/EACCES error mapping,
  resolved-path return value. setup-watchdog.js's inline copy is
  removed.

- lib/state.js — create() now accepts exitConfirmations,
  promptFile, watchPromptFile, noClassifier as named options. New
  state file fields: `exit_confirmations` (int, default 1),
  `no_change_streak` (int, default 0), `prompt_file` (string|null,
  default null), `watch_prompt_file` (bool, default false),
  `no_classifier` (bool, default false). isValid() unchanged so
  existing v1.2.4 state files still load.

- hooks/stop-hook.js — major rewrite of the verdict handler:
  * v1.3.0 fields read with defensive defaults so v1.2.4 state
    files behave exactly as before
  * Hot-reload happens early, before the max-iterations check, so a
    fresh prompt-file content takes effect on the same iteration
    that detects it
  * --no-classifier short-circuit happens before askHaiku() so no
    subprocess is ever spawned (not even a --version probe — but
    we already removed that in 1.2.x)
  * NO_FILE_CHANGES verdict now bumps the streak and only exits
    when streak >= exit_confirmations; otherwise logs progress
    (e.g. "1/3 - need 2 more, continuing loop") and re-feeds
  * All non-NO_FILE_CHANGES verdicts and pure-text turns reset
    effectiveStreak = 0 (strict-A semantics)
  * iteration bump and streak/prompt persist happen in a single
    update() patch to keep the atomic-write contract intact

- scripts/setup-watchdog.js — parses the three new flags. exit-
  confirmations is left undefined when not passed (so we can tell
  the difference between "user typed --exit-confirmations 1
  explicitly" and "user left it off") and the validation checks
  in main() use that distinction to error on
  --no-classifier + --exit-confirmations conflict. resolvedPromptFile
  captured at create-time so the hook's hot-reload doesn't have to
  re-resolve a relative path against a possibly-different cwd later.

- commands/start.md, commands/help.md — argument-hint and full
  reference updated to document all three new flags, mutual
  exclusion rules, and the strict-A streak semantics. Requirements
  table now notes the `claude` CLI is optional under
  --no-classifier. Version anchor bumped 1.2.0 -> 1.3.0.

## Test plan

- [x] 124-test suite passes locally (124 active + 2 skipped-inside-
      Claude-Code, 0 failures) — 30 net new tests since v1.2.4
- [x] setup.test.js: arg parse + validation for all new flags +
      mutual exclusion + every error path
- [x] state.test.js: new fields default correctly, explicit values
      stored, v1.2.4-shape state files still validated
- [x] stop-hook.test.js: backward-compat for v1.2.4 state files,
      pure-text turn resets streak, --no-classifier short-circuits
      Haiku (PATH stripped to prove no spawn attempt), --no-classifier
      still respects --max-iterations, hot-reload happy/unchanged/
      changed/deleted/empty/disabled paths
- [x] stop-hook-haiku.test.js: --exit-confirmations 3 streak
      progression 1->2->3 then exit; FILE_CHANGES, AMBIGUOUS, FAIL
      mid-streak each reset to 0; default-1 still exits on first
      NO_FILE_CHANGES (regression check)
- [ ] CI will confirm on the standard matrix (ubuntu/macos/windows
      × Node 18/20/22)

## What changed (file list)

- lib/prompt-file.js — NEW shared helper
- lib/state.js — extended create() schema
- scripts/setup-watchdog.js — three new flags, cross-flag validation,
  uses shared prompt-file lib
- hooks/stop-hook.js — hot-reload, no-classifier, streak counter
- commands/start.md — argument-hint update
- commands/help.md — full new-flag reference + updated exit conditions
  + version anchor
- test/setup.test.js — +13 tests
- test/state.test.js — +3 tests
- test/stop-hook.test.js — +8 tests
- test/stop-hook-haiku.test.js — +5 tests
- README.{md,zh,es,ja,ko,vi,pt}.md — three new subsections under
  Commands ("Stricter convergence with --exit-confirmations", "Hot-
  reload the prompt mid-loop with --watch-prompt-file", "Disable
  the classifier entirely with --no-classifier") and two new rows
  ("Convergence flexibility", "Prompt evolution") in the Watchdog
  vs ralph-loop comparison table
- .claude-plugin/plugin.json — version 1.2.4 -> 1.3.0
- .claude-plugin/marketplace.json — version 1.2.4 -> 1.3.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@JonyanDunh JonyanDunh merged commit 5ae7ab0 into main Apr 12, 2026
11 checks passed
@JonyanDunh JonyanDunh deleted the feat/exit-confirmations-watch-no-classifier-v1.3.0 branch April 12, 2026 15:46
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.

1 participant