refactor(gates): the autonomy gate is the --gates flag parser
lib/util/autonomy-gate.mjs keeps only gatesValue + parseGatesArgs and the
CLI ({"ok":true,"gates":true|false}; exact usage error on a bad value;
the old --state/--event arguments are a usage error). The four commands
run 'autonomy-gate.mjs --gates [value]' and read gates. Prose (README,
operations, architecture, sdlc-playbook-gap) and the S24, gates-flag
and [#1] tests follow (R9).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
651be3468b
commit
308b149e70
12 changed files with 165 additions and 355 deletions
|
|
@ -6,11 +6,11 @@ Imported from `CLAUDE.md` via pointer.
|
|||
|
||||
`lib/` contains zero-dep validators, parsers, and autonomy primitives wired into the four commands:
|
||||
|
||||
- `lib/util/{frontmatter,result,atomic-write,autonomy-gate}.mjs` — shared YAML-frontmatter parser + Result helpers + `atomicWriteJson(path, obj)` for tmp+rename writes + autonomy-gate state machine (v3.4.0)
|
||||
- `lib/util/{frontmatter,result,atomic-write,autonomy-gate}.mjs` — shared YAML-frontmatter parser + Result helpers + `atomicWriteJson(path, obj)` for tmp+rename writes + `--gates` flag parser (v3.4.0)
|
||||
- `lib/parsers/{plan-schema,manifest-yaml,project-discovery,arg-parser,bash-normalize,jaccard,finding-id}.mjs` — pure parsers (no I/O), unit-tested. `manifest-yaml` extended in v3.4.0 with additive `skip_commit_check` + `memory_write` flags (forward-compat: unknown keys ignored)
|
||||
- `lib/review/{rule-catalogue,plan-review-dedup}.mjs` — version-pinned rule catalogue (17 keys) + Phase 9 inline dedup helpers (v3.4.0)
|
||||
- `lib/review/{verdict-gate,engine-findings,recurrence}.mjs` — the review gate (K7: 0 ALLOW, or WARN with every counting MAJOR answered · 1 BLOCK · 2 never green), Claude Code `/code-review` + `/security-review` findings → reviewer payload, recurring finding classes → operator-approved project-memory lines (review_version 1.2); `lib/review/fill-finding-ids.mjs` computes the coordinator's `@@ID_<n>@@` placeholders over `(file, line, rule_key)`; `lib/validators/brief-review-stamp.mjs` lets `/trekplan` skip a brief review `/trekbrief` already ran on the same text
|
||||
- `lib/stats/event-emit.mjs` — single-source stats event emitter for autonomy-gate transitions and main-merge-gate (v3.4.0)
|
||||
- `lib/stats/event-emit.mjs` — single-source stats event emitter for named lifecycle events (`brief-approved`, the main-merge gate and its answer, `user_input`) (v3.4.0)
|
||||
- `lib/validators/{brief,research,plan,progress,session-state}-validator.mjs` — schema validators with CLI shims (`node lib/validators/X.mjs --json <path>`)
|
||||
- `lib/validators/architecture-discovery.mjs` — drift-WARN external-contract discovery for `architecture/overview.md`
|
||||
- `lib/util/research-loop-cap.mjs` — stateful, **default-off** turn budget for the `/trekresearch` bounded conversation loop. `allowTurn()` derives the used-turn count from its own append-only JSONL ledger; it never asks the caller how many turns it has spent, because a cap that does is not a cap. Each grant first claims a turn **slot** with `O_EXCL` under `trekresearch-loop-claims/`, so the bound survives several callers deciding at once — counting the ledger and then appending is read-then-write, and Phase 4.5/5 can spawn several agents in one message. Budget = `TREKRESEARCH_MAX_CONV_TURNS` (default `3`, invalid values fall back to `3`) × `maxDimensions` (8, `settings.json:16`). **Default-off:** grants 0 unless `VOYAGE_STORM_ENABLED=1`, which is also the second condition on Phase 4.5's skip-guard (that phase does not call this module): unset, **both** STORM phases are inert. `resolveDataRoot()` is the single root for everything the loop writes — `CLAUDE_PLUGIN_DATA` when the harness sets it, `~/.claude/voyage` when it does not (it is empty in the Bash tool's process env, which is where the loop actually runs); the cap hook resolves through the same function, so writer and reader cannot disagree. A ledger that cannot be **written** denies the turn, and one that exists but cannot be **read** denies it too — only `ENOENT` counts as zero turns spent, that being the legitimate first-turn state (fail-closed — the opposite of `event-emit.mjs`, which is telemetry and must never block). The exported `readLedger()` is the single counting rule; the cap hook calls it rather than keeping a private copy. `checkDimensionCeiling()` is the reader for the OTHER axis of the bounded-cost NFR — the size of the whole dimension list after Phase 4.5 discovery — against the same `MAX_TOTAL_DIMENSIONS`, so the two axes cannot enforce different numbers for one `settings.json:16` value; an unreadable count is rejected, not waved through. CLI shim, two modes: `--run-id ID --dimension D --effort E` (budget gate) and `--check-dimensions N` (ceiling, no run id/effort/flag required since Phase 4.5 never calls the budget gate)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Imported from `CLAUDE.md` via pointer.
|
|||
|
||||
All four pipeline commands accept `--gates [true|false|closed|open]`. A bare `--gates`, `--gates true` or `--gates closed` turns gating **on**; `--gates false`, `--gates open` or no flag (the default) leaves it off. The next token is the flag's value only when it is one of those four; any other token stays the command's own argument, so `/trekplan --gates --project <dir>` and `/trekexecute --gates plan.md` are a bare `--gates`. Each command maps the value once through `lib/util/autonomy-gate.mjs`; a shim that fails falls back to the same mapping in prose, and the run goes on. With gating on, `/trekbrief` pauses before its auto chain, `/trekresearch` after each topic, and `/trekplan` after exploration, synthesis and adversarial review. `/trekexecute` accepts the flag and pauses on nothing: its one stop is the merge into main, which always asks (below).
|
||||
|
||||
Under the hood: `lib/util/autonomy-gate.mjs` runs the state machine `idle → gates_on | auto_running → paused_for_gate → completed`. `start` routes to `gates_on` when `--gates` is bare, `true` or `closed`, else `auto_running`; a `phase_boundary` from `gates_on` pauses at `paused_for_gate` (awaiting `resume`); `finish` reaches the terminal `completed`. The module is pure data with no I/O; the commands run only its `start` transition, to map the flag. Separately, `lib/stats/event-emit.mjs` records named lifecycle events (`brief-approved`, `main-merge-gate`, `main-merge-approved`, `main-merge-declined`, `user_input`) to `trekexecute-stats.jsonl` in the plugin data directory (`CLAUDE_PLUGIN_DATA`, else `~/.claude/plugins/data/voyage-ktg-plugin-marketplace/`, the directory `scripts/yardstick.mjs` reads); a write that fails is reported, never skipped. The main-merge gate is the final autonomy boundary before HEAD lands on `main`.
|
||||
Under the hood: `lib/util/autonomy-gate.mjs` is the `--gates` flag parser. It prints `{"ok":true,"gates":true}` when `--gates` is bare, `true` or `closed`, `{"ok":true,"gates":false}` for `false`, `open` or no flag, and a usage error (exit 1) for anything else. The module is pure data with no I/O. Separately, `lib/stats/event-emit.mjs` records named lifecycle events (`brief-approved`, `main-merge-gate`, `main-merge-approved`, `main-merge-declined`, `user_input`) to `trekexecute-stats.jsonl` in the plugin data directory (`CLAUDE_PLUGIN_DATA`, else `~/.claude/plugins/data/voyage-ktg-plugin-marketplace/`, the directory `scripts/yardstick.mjs` reads); a write that fails is reported, never skipped. The main-merge gate is the final autonomy boundary before HEAD lands on `main`.
|
||||
|
||||
**The main-merge gate is a lock, and the guard asks the one question** (operator decision 2026-10-08). The gate opens only when the run is `completed` and the review's verdict gate (`lib/review/verdict-gate.mjs`, K7) exits 0; BLOCK (1) and "could not decide" (2) keep it closed. When it opens, `/trekexecute` runs the merge into main itself and `pre-bash-executor` answers "ask" before it runs, in auto mode too, so that question is the only one. In the permission mode where Claude Code shows no prompts, the guard refuses the merge instead, and every other call it would ask about (push, tag or publish, destructive git), since no one can be asked (operator decisions 2026-10-09). The executor no longer asks "ready to merge?" first, and `--gates` no longer adds a pause before the wave merge-back: the guard asks there as well, once per session, because the push and the merge go in one call. Before this, one merge cost two questions in three places: the `--gates` pause before the wave merge-back, the gate's own question after the waves were merged, and the gate's question on the single-session path followed by the guard's at the merge itself.
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ By stage — Plan 0/1/0 · Design 0/0/1 · Build 2/3/1 · Test 0/2/0 · Deploy 0
|
|||
| P1 | Capture intent as a committed artifact (Plan) | partial | `commands/trekbrief.md` Phase 2.5 + `## Intent`; `lib/validators/brief-validator.mjs` | The brief requires an `## Intent` section and a `framing:` declaration with no skip path, enforced as a BLOCKER — but the artifact lands under `.claude/projects/` (gitignored), the pipeline never commits it, and no approval marker exists |
|
||||
| P2 | Requirements and design as a separate spec (Design) | none | — | No artifact between brief and plan; none of the 7 handovers in `docs/HANDOVER-CONTRACTS.md` is a spec, and no organisational policy is applied while writing |
|
||||
| P3 | Plan mode as the default start (Build) | partial | `commands/trekplan.md` Phase 9; `commands/trekexecute.md` Phase 2 (`plan-validator.mjs --strict`) | The `plan_version 1.7` manifest gate is real code that stops the run — but plan-critic is prose ("if blockers are found: revise the plan"), the plan is never committed by the pipeline, and execution starts on the word `execute` with no approved-plan marker |
|
||||
| P4 | Auto mode and longer autonomous sessions (Build) | full | `lib/util/autonomy-gate.mjs`; `commands/trekexecute.md` (waves, `--resume`) | Auto mode, headless worktree sessions and after-the-fact artifact review all exist. Note: `--gates` is a boolean, not a three-way mode |
|
||||
| P4 | Auto mode and longer autonomous sessions (Build) | full | `lib/util/autonomy-gate.mjs` (`--gates` flag parser); `commands/trekexecute.md` (waves, `--resume`) | Auto mode, headless worktree sessions and after-the-fact artifact review all exist. Note: `--gates` is a boolean, not a three-way mode |
|
||||
| P5 | `CLAUDE.md` as institutional memory (Build) | partial | `agents/convention-scanner.md`; `agents/brief-reviewer.md` dim. 6 | Conventions and operator memory are read; nothing in the pipeline ever writes to a memory file in any repo, so the "wrong twice → into the file" loop is absent |
|
||||
| P6 | Skills as institutional knowledge (Build) | none | — | `find . -iname 'SKILL.md'` and `find . -type d -name skills` are both empty; the plugin ships 7 commands and 23 agent files and zero skills |
|
||||
| P7 | Hooks as build-time guardrails (Build) | partial | `hooks/hooks.json` (7 wirings/6 events); `hooks/scripts/pre-write-executor.mjs`, `pre-bash-executor.mjs` | Path protection and credential blocks exist as PreToolUse hooks; there is no PostToolUse Edit/Write hook at all, so "formatter/linter after edit" is missing |
|
||||
|
|
@ -31,7 +31,7 @@ By stage — Plan 0/1/0 · Design 0/0/1 · Build 2/3/1 · Test 0/2/0 · Deploy 0
|
|||
| P9 | Give the agent a feedback loop (Test) | partial | `lib/verification/criteria-runner.mjs`; `commands/trekexecute.md` Phase 7 / 7.5 / 7.6 | Exit-code-as-truth and an independent fresh-context manifest audit are real code — but "failing test first, protected from rewriting" is explicitly off: `Test first:` is optional and the file says "do not block on test-first failures" |
|
||||
| P10 | Continuous evals in CI (Test) | partial | `tests/lib/doc-consistency.test.mjs`, `tests/lib/agent-frontmatter.test.mjs`; `tests/lib/gold-eval.test.mjs` + `lib/review/gold-scorer.mjs` | A suite does regression-test the agent configuration (agent count, frontmatter, model pin, command table, hook count — derived from the source, so a config change fells it), and a scored gold eval exists. What is missing: any CI at all (`.forgejo/workflows` and `.github` both absent), automatic triggering on config change, a merge gate on pass rate, and any live-agent eval |
|
||||
| P11 | AI in the PR review loop (Deploy) | partial | `commands/trekreview.md`; `lib/review/rule-catalogue.mjs`; `lib/review/coordinator-contract.mjs` | Independent reviewers, a judge, a versioned rule catalogue and a four-level severity taxonomy are strongly implemented — but the input is a SHA-range diff, not a PR: no forge integration, no code-owner approval, and the verdict blocks nothing |
|
||||
| P12 | Hooks as approval gates (Deploy) | partial | `lib/util/autonomy-gate.mjs`; `commands/trekexecute.md` main-merge gate | Human gates exist and the main-merge gate always pauses — but they are `AskUserQuestion` prose in command files; no hook returns an "ask" decision, and the hooks are binary (exit 0 / exit 2) |
|
||||
| P12 | Hooks as approval gates (Deploy) | partial | `hooks/scripts/pre-bash-executor.mjs` (asks before merge into main and push); `commands/trekexecute.md` main-merge gate | Human gates exist and the main-merge gate always pauses — but they are `AskUserQuestion` prose in command files; no hook returns an "ask" decision, and the hooks are binary (exit 0 / exit 2) |
|
||||
| P13 | CI/CD integration and deployment (Deploy) | none | — | No CI workflows, no deploy/status/rollback tooling, no per-environment autonomy ladder |
|
||||
| P14 | Close the loop from maintenance (Maintain) | none | `hooks/scripts/otel-export.mjs`, `lib/exporters/`, `lib/stats/` | Observability exists but measures Voyage's own runs, not a product in production; no control bands, and the one feedback path (review → plan) is started by a human |
|
||||
| P15 | Recurring codebase scans (Maintain) | none | `hooks/scripts/pre-bash-executor.mjs` | No scheduler and no periodic entry point; the only `cron` hits are the rule that blocks crontab persistence |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue