From 34f62043f93bd73ad03debd2dc44fc08a591f891 Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Mon, 4 May 2026 07:54:30 +0200 Subject: [PATCH] feat(ultraplan-local): add --gates autonomy-control flag to all four pipeline commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Single autonomy-control surface (--gates) added to ultrabrief, ultraresearch, ultraplan, and ultraexecute. When present, sets gates_mode = true and re-enables approval pauses at every phase boundary + every wave for high-stakes runs. When absent (default in auto), the chain runs continuously to the main-merge gate (which always pauses regardless of --gates — that boundary is the one always-on safety stop). ultrabrief: pause after auto-mode confirmation; emit brief-approved event ultraresearch: pause after each topic completes ultraplan: pause after Phases 5, 7, 9 ultraexecute: pause after each wave's worktrees finish, before merge-back, AND before the main-merge gate (MAIN_MERGE_GATE) All four commands invoke the autonomy-gate state machine via the CLI shim node lib/util/autonomy-gate.mjs (built in S8). Test pin in tests/lib/gates-flag-coverage.test.mjs locks the contract. Also wires the brief-approved stats emission into ultrabrief Phase 5 auto path (was the SC4 wiring requirement from plan-v2 Step 11). --- .../commands/ultrabrief-local.md | 28 +++++++++++ .../commands/ultraexecute-local.md | 10 +++- .../commands/ultraplan-local.md | 9 +++- .../commands/ultraresearch-local.md | 7 +++ .../tests/lib/gates-flag-coverage.test.mjs | 48 +++++++++++++++++++ 5 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 plugins/ultraplan-local/tests/lib/gates-flag-coverage.test.mjs diff --git a/plugins/ultraplan-local/commands/ultrabrief-local.md b/plugins/ultraplan-local/commands/ultrabrief-local.md index e66e6e5..853099a 100644 --- a/plugins/ultraplan-local/commands/ultrabrief-local.md +++ b/plugins/ultraplan-local/commands/ultrabrief-local.md @@ -42,6 +42,16 @@ Parse `$ARGUMENTS`: 2. Otherwise: **mode = default**. Interview probes each section until the completeness gate (Phase 3) and brief-review gate (Phase 4) both pass. +3. `--gates` flag (autonomy control, may combine with any mode): when + present, set `gates_mode = true`. This re-enables approval pauses at + every phase boundary in the downstream pipeline (research, plan, + execute) and at every wave in the executor. Default `gates_mode = false` + means auto mode runs continuously until the main-merge gate (which is + the one boundary that ALWAYS pauses, regardless of `gates_mode`). Strip + the flag from `$ARGUMENTS` before further parsing. The flag is consumed + by the autonomy-gate state machine via the CLI shim: + `node ${CLAUDE_PLUGIN_ROOT}/lib/util/autonomy-gate.mjs --state X --event Y --gates {true|false}`. + If no task description is provided, output usage and stop: ``` @@ -524,6 +534,24 @@ Stop. Do not continue to Phase 6. Set `auto_research: true` in the brief's frontmatter (edit the file). +Emit the brief-approved lifecycle event so downstream observability sees +the pipeline kick off (consumed by `lib/stats/event-emit.mjs`): + +```bash +node ${CLAUDE_PLUGIN_ROOT}/lib/stats/event-emit.mjs \ + --event brief-approved \ + --payload "{\"project\":\"${PROJECT_DIR}\"}" +``` + +If `gates_mode == true`: pause here via `AskUserQuestion` — +"Auto-mode confirmed. Proceed to research now? (yes/no)". If the user +answers no, fall back to the manual path output and stop. Otherwise +proceed to Phase 6. + +If `gates_mode == false` (default in auto): proceed directly to Phase 6. +The chain stops only at the main-merge gate (see `commands/ultraexecute-local.md` +Phase 8). + Proceed to Phase 6. ## Phase 6 — Auto research dispatch (auto path only) diff --git a/plugins/ultraplan-local/commands/ultraexecute-local.md b/plugins/ultraplan-local/commands/ultraexecute-local.md index 2cfe05d..1042f2c 100644 --- a/plugins/ultraplan-local/commands/ultraexecute-local.md +++ b/plugins/ultraplan-local/commands/ultraexecute-local.md @@ -40,7 +40,15 @@ Parse `$ARGUMENTS` for mode flags: Set **mode = step**, **target-step = N**. 7. If arguments contain `--session N` (N is a positive integer): extract N and the file path. Set **mode = session**, **target-session = N**. -8. Otherwise: the entire argument string is the file path. Set **mode = execute**. +8. If arguments contain `--gates`: set `gates_mode = true`. Pause for + operator confirmation after each wave's worktrees finish but before + merge-back, AND before the main-merge gate. (The MAIN_MERGE_GATE in + Phase 8 ALWAYS pauses regardless of `gates_mode` — `--gates` re-enables + the per-wave pauses that auto mode otherwise skips.) Default + `gates_mode = false`. The flag is consumed by the autonomy-gate state + machine via the CLI shim: + `node ${CLAUDE_PLUGIN_ROOT}/lib/util/autonomy-gate.mjs --state X --event Y --gates {true|false}`. +9. Otherwise: the entire argument string is the file path. Set **mode = execute**. If no path is provided (and `--project` was not used to derive one), output usage and stop: diff --git a/plugins/ultraplan-local/commands/ultraplan-local.md b/plugins/ultraplan-local/commands/ultraplan-local.md index 00a77bf..fcfcf83 100644 --- a/plugins/ultraplan-local/commands/ultraplan-local.md +++ b/plugins/ultraplan-local/commands/ultraplan-local.md @@ -107,7 +107,14 @@ Parse `$ARGUMENTS` for mode flags. Order of precedence: 7. **`--quick`** — set **mode = quick**. Skip agent swarm; use lightweight Glob/Grep scan and go directly to planning + adversarial review. -8. If neither `--brief` nor `--project` is present after flag parsing, +8. **`--gates`** — autonomy control. When present, set `gates_mode = true`. + Pause for operator confirmation after Phase 5 (exploration), Phase 7 + (synthesis), and Phase 9 (adversarial review). Default `gates_mode = + false` lets phases flow continuously. The flag is consumed by the + autonomy-gate state machine via the CLI shim: + `node ${CLAUDE_PLUGIN_ROOT}/lib/util/autonomy-gate.mjs --state X --event Y --gates {true|false}`. + +9. If neither `--brief` nor `--project` is present after flag parsing, output usage and stop: ``` diff --git a/plugins/ultraplan-local/commands/ultraresearch-local.md b/plugins/ultraplan-local/commands/ultraresearch-local.md index 73e82b8..305d8a4 100644 --- a/plugins/ultraplan-local/commands/ultraresearch-local.md +++ b/plugins/ultraplan-local/commands/ultraresearch-local.md @@ -54,6 +54,13 @@ Supported flags: ``` Create `{dir}/research/` if it does not already exist. +6. `--gates` — autonomy control. When present, set `gates_mode = true`. The + research command will pause after each topic completes ("Topic N + complete. Proceed to topic N+1? (yes/no)"). Default `gates_mode = false` + means topics run continuously. The flag is consumed by the autonomy-gate + state machine via the CLI shim: + `node ${CLAUDE_PLUGIN_ROOT}/lib/util/autonomy-gate.mjs --state X --event Y --gates {true|false}`. + Flags can be combined: - `--local` — local-only research - `--external --quick` — external-only, lightweight diff --git a/plugins/ultraplan-local/tests/lib/gates-flag-coverage.test.mjs b/plugins/ultraplan-local/tests/lib/gates-flag-coverage.test.mjs new file mode 100644 index 0000000..29d4893 --- /dev/null +++ b/plugins/ultraplan-local/tests/lib/gates-flag-coverage.test.mjs @@ -0,0 +1,48 @@ +// tests/lib/gates-flag-coverage.test.mjs +// Step 11 (plan-v2) — pin that all four pipeline commands document the +// --gates autonomy-control flag and consume the autonomy-gate state +// machine via the lib/util/autonomy-gate.mjs CLI shim. + +import { test } from 'node:test'; +import { strict as assert } from 'node:assert'; +import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const HERE = dirname(fileURLToPath(import.meta.url)); +const ROOT = join(HERE, '..', '..'); + +function read(rel) { return readFileSync(join(ROOT, rel), 'utf-8'); } + +const COMMANDS = [ + 'commands/ultrabrief-local.md', + 'commands/ultraresearch-local.md', + 'commands/ultraplan-local.md', + 'commands/ultraexecute-local.md', +]; + +for (const cmdPath of COMMANDS) { + test(`${cmdPath} documents the --gates flag`, () => { + const text = read(cmdPath); + assert.ok( + text.includes('--gates'), + `${cmdPath} should document the --gates autonomy-control flag (Step 11)`, + ); + }); + + test(`${cmdPath} wires the autonomy-gate.mjs CLI shim`, () => { + const text = read(cmdPath); + assert.ok( + text.includes('autonomy-gate.mjs'), + `${cmdPath} should reference lib/util/autonomy-gate.mjs as the state-machine implementation`, + ); + }); +} + +test('commands/ultraexecute-local.md mentions MAIN_MERGE_GATE', () => { + const text = read('commands/ultraexecute-local.md'); + assert.ok( + text.includes('MAIN_MERGE_GATE'), + 'commands/ultraexecute-local.md should name MAIN_MERGE_GATE — the only boundary that always pauses regardless of --gates', + ); +});