fix(siblings): the texts found next to round 4.5a's fixes say what the code does
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
e351e7c29d
commit
8a2a989f79
18 changed files with 284 additions and 71 deletions
|
|
@ -95,6 +95,7 @@ Fix round 4.5a (decisions 57, 67 and 68): the round's 30 findings, where a Voyag
|
|||
**Stats**
|
||||
|
||||
- **No command promises a stats key its record does not write** (finding #46). The "Stats records emit `profile` …" sentences left `/trekbrief`, `/trekplan`, `/trekexecute`, `/trekreview` and `/trekcontinue`; `/trekresearch` keeps only `engine`, which its record writes; `docs/profiles.md`, `docs/operations.md` and README no longer say a profile reaches the stats records.
|
||||
- **The texts next to the fixes say the same** (siblings found during the round): `/trekreview`, the review reference and the Arm B coordinator prompt no longer drop a finding whose rule key is unknown; `/trekbrief` writes its stats record on every exit of its manual and auto paths, says that `--gates` adds one pause before its auto chain, and that the chain ends at the plan; `/trekexecute` says `--gates` changes nothing there, checks the wave marker before a low-effort single session, and names no validator check that does not exist; plan mode goes through the plan approval before it prints the `/trekexecute` line; the gates shim reads `--gates=true` as on.
|
||||
|
||||
### Known issues
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ No cloud dependency. No GitHub requirement. Works on **Mac, Linux, and Windows**
|
|||
|
||||
### Autonomy mode (`--gates`, v3.4.0)
|
||||
|
||||
All four pipeline commands accept `--gates [true|false|closed|open]`. A bare `--gates`, `true` or `closed` turns gating on; `false`, `open` or no flag (the default) leaves it off. Any other next token is not the flag's value, so `/trekplan --gates --project <dir>` is a bare `--gates`. With gating on, `/trekbrief`, `/trekresearch` and `/trekplan` pause at their own boundaries; `/trekexecute` records the setting and pauses on nothing, because its one stop is the merge into main, which always asks. Details: [`docs/operations.md`](docs/operations.md) § Autonomy mode.
|
||||
All four pipeline commands accept `--gates [true|false|closed|open]`. A bare `--gates`, `true` or `closed` turns gating on; `false`, `open` or no flag (the default) leaves it off. Any other next token is not the flag's value, so `/trekplan --gates --project <dir>` is a bare `--gates`. With gating on, `/trekbrief`, `/trekresearch` and `/trekplan` pause at their own boundaries; `/trekexecute` accepts the flag and pauses on nothing, because its one stop is the merge into main, which always asks. Details: [`docs/operations.md`](docs/operations.md) § Autonomy mode.
|
||||
|
||||
Under the hood, `lib/util/autonomy-gate.mjs` runs a small state machine (`idle → gates_on | auto_running → paused_for_gate → completed`), and `lib/stats/event-emit.mjs` records named lifecycle events (the main-merge gate and its answer, among others) to `trekexecute-stats.jsonl` in the plugin data directory. The new `hooks/scripts/post-compact-flush.mjs` PostCompact hook re-injects `.session-state.local.json` after context compaction so multi-session work survives a compaction boundary.
|
||||
|
||||
|
|
|
|||
|
|
@ -185,9 +185,12 @@ The coordinator runs a 4-pass process:
|
|||
2. **HubSpot Judge filters** — Accuracy refutes; Actionability sets aside
|
||||
as unverified; Succinctness never removes a finding (only MINOR/SUGGESTION
|
||||
text is trimmed, and counted).
|
||||
3. **Cloudflare reasonableness** — drop speculative findings without a
|
||||
`file:line` citation; drop findings whose `rule_key` is not in
|
||||
`RULE_CATALOGUE`.
|
||||
3. **Cloudflare reasonableness** — drop a finding with no `file:line`
|
||||
citation (`no-citation`) or whose file is in neither the working tree nor
|
||||
the diff (`file-existence:refuted`); route one whose `rule_key` is not in
|
||||
`RULE_CATALOGUE` (`unknown-rule_key`) or whose file cannot be checked
|
||||
(`file-existence:indeterminate`) to `unverified`, which forbids `ALLOW`;
|
||||
reset a severity that does not match its catalogue tier.
|
||||
4. **Compute verdict** — `BLOCK` if `BLOCKER ≥ 1`, `WARN` if `MAJOR ≥ 1`,
|
||||
else `ALLOW`.
|
||||
|
||||
|
|
|
|||
|
|
@ -54,13 +54,11 @@ Parse `$ARGUMENTS`:
|
|||
false, and nothing else reads the state. On exit 1 or no JSON (a broken
|
||||
shim; the four values cannot cause it), print the error in one line and
|
||||
map in prose instead: bare, `true`, `closed` → true; `false`, `open` →
|
||||
false. The run goes on either way. `gates_mode = true` re-enables
|
||||
approval pauses at every phase boundary in the downstream pipeline
|
||||
(research, plan). 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 (and its value, when it has one) from `$ARGUMENTS` before
|
||||
further parsing.
|
||||
false. The run goes on either way. `gates_mode = true` adds one pause,
|
||||
before the auto chain starts (Phase 5); the chain does not pass
|
||||
`--gates` on to `/trekresearch` or `/trekplan`. Default
|
||||
`gates_mode = false` skips that pause. Strip the flag (and its value,
|
||||
when it has one) from `$ARGUMENTS` before further parsing.
|
||||
|
||||
4. `--approve <project-dir | brief-file>`: set **mode = approve**. No
|
||||
interview, no drafting. Set `APPROVE_ARG` to the argument and resolve it:
|
||||
|
|
@ -1014,13 +1012,13 @@ Then:
|
|||
/trekexecute --project {PROJECT_DIR}
|
||||
```
|
||||
|
||||
Stop. Do not continue to Phase 6.
|
||||
Run Phase 7 (`auto_result: manual`), then stop. Do not continue to Phase 6.
|
||||
|
||||
### Auto path
|
||||
|
||||
**Auto requires an approved intent.** If Phase 4h ended "Leave unapproved",
|
||||
the auto chain would halt at `/trekplan` anyway — print the manual-path
|
||||
output instead and stop.
|
||||
output instead, run Phase 7 (`auto_result: manual`) and stop.
|
||||
|
||||
Set `auto_research: true` in the brief's frontmatter (edit the file).
|
||||
|
||||
|
|
@ -1029,12 +1027,12 @@ stamp — on both paths — so it is not emitted again here.
|
|||
|
||||
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.
|
||||
answers no, fall back to the manual path output, run Phase 7
|
||||
(`auto_result: cancelled`) 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/trekexecute.md`
|
||||
Phase 8).
|
||||
The chain ends when `/trekplan` has written the plan (Step 6e); it never
|
||||
starts `/trekexecute`.
|
||||
|
||||
Proceed to Phase 6.
|
||||
|
||||
|
|
@ -1056,7 +1054,8 @@ Continue?"
|
|||
| **Continue — auto** | Proceed. |
|
||||
| **Cancel — do manual** | Revert to manual path (print commands, stop). |
|
||||
|
||||
If cancelled → fall back to manual path output and stop.
|
||||
If cancelled → fall back to manual path output, run Phase 7
|
||||
(`auto_result: cancelled`) and stop.
|
||||
|
||||
### Step 6b — Run research topics sequentially (inline)
|
||||
|
||||
|
|
@ -1093,7 +1092,8 @@ ls -1 {PROJECT_DIR}/research/*.md | wc -l
|
|||
```
|
||||
|
||||
Expected count: N. If any are missing, report and ask the user how to
|
||||
proceed (retry, skip missing topic, cancel).
|
||||
proceed (retry, skip missing topic, cancel). On cancel, run Phase 7
|
||||
(`auto_result: cancelled`) and stop.
|
||||
|
||||
Update brief frontmatter: `research_status: complete`.
|
||||
|
||||
|
|
@ -1107,7 +1107,8 @@ Invoke the planning command inline in this session:
|
|||
|
||||
The planning pipeline runs all phases (exploration, synthesis, review) in
|
||||
main context. Wait for the plan to be written to `{PROJECT_DIR}/plan.md`
|
||||
before continuing.
|
||||
before continuing. If `/trekplan` stops before it writes the plan, report
|
||||
where it stopped, run Phase 7 (`auto_result: failed`) and stop.
|
||||
|
||||
### Step 6e — Report completion
|
||||
|
||||
|
|
|
|||
|
|
@ -86,8 +86,8 @@ Parse `$ARGUMENTS` for mode flags:
|
|||
shim; the four values cannot cause it), print the error in one line and
|
||||
map in prose instead: bare, `true`, `closed` → true; `false`, `open` →
|
||||
false. The run goes on either way. Default `gates_mode = false`.
|
||||
`/trekexecute` records `gates_mode`; no phase of it pauses on it. The one
|
||||
stop is the merge into main, which always asks (Phase 8).
|
||||
`gates_mode` changes nothing in `/trekexecute`: no phase of it pauses on
|
||||
it. The one stop is the merge into main, which always asks (Phase 8).
|
||||
It no longer adds a question of the executor's own before a merge. The
|
||||
wave merge-back (Phase 2.6 Step 3) and the merge into main (Phase 8) are
|
||||
asked by the guard, which asks before every merge into main and every push,
|
||||
|
|
@ -126,7 +126,7 @@ Modes:
|
|||
--step N Execute only step N (foreground)
|
||||
--session N Execute only session N from the plan's Execution Strategy
|
||||
--profile Profile for this run's resolver; the step helper and the controller stay on opus
|
||||
--gates Record gates_mode: bare, true or closed = on; false or open = off
|
||||
--gates Accepted, pauses on nothing: bare, true or closed = on; false or open = off
|
||||
|
||||
Examples:
|
||||
/trekexecute .claude/plans/trekplan-2026-04-06-auth-refactor.md
|
||||
|
|
@ -465,7 +465,7 @@ Determine how to execute this plan:
|
|||
- `--step N` mode
|
||||
- `--resume` mode
|
||||
- `--session N` mode (runs only that session's steps, foreground)
|
||||
- `phase_signal_result.effort == 'low'` → one session, steps in order — never while a wave is in flight (`WAVE_IN_FLIGHT`)
|
||||
- `phase_signal_result.effort == 'low'` → one session, steps in order, unless a wave is in flight: when `.claude/trekplan-sessions/{slug}/orders/wave.json` exists in the main checkout (the first entry of `git worktree list`), this bullet does not apply and the multi-session path below collects the wave. The check only reads the file and never stops the run.
|
||||
- Plan has no `## Execution Strategy` section
|
||||
- Plan has Execution Strategy with only 1 session
|
||||
|
||||
|
|
@ -616,9 +616,8 @@ These sessions cannot safely run in parallel.
|
|||
Fix the Execution Strategy in the plan, or use --fg for sequential execution.
|
||||
```
|
||||
|
||||
Stop execution. This is a defense-in-depth check — /trekplan's validator
|
||||
should have prevented this, but verifying at execution time catches plans
|
||||
that were manually edited or have bugs.
|
||||
Stop execution. This check runs at execution time, so it also catches a plan
|
||||
that was edited by hand after planning.
|
||||
|
||||
### Check 4 — Stale worktree cleanup
|
||||
|
||||
|
|
@ -2450,10 +2449,11 @@ Record in progress file:
|
|||
>
|
||||
> **Commands:** Phase 7.5 step 3's `git rev-list --count`, step 5's `bash -n`,
|
||||
> step 6's `git diff --name-only` and step 7's `done-line.mjs --check`; the
|
||||
> criteria runner behind clauses 2 and 4; clause 5's `arch-rules.mjs` and
|
||||
> `plan-scope-sweep.mjs`. **Executor judgement:** steps 1, 2 and 4, the expected
|
||||
> count in step 3, and clause 3's formula. A script for the judgement parts is
|
||||
> part c of the hardening.
|
||||
> criteria runner behind clauses 2 and 4 for a trekplan (a session spec's
|
||||
> clause 2 is Phase 7's `## Exit Condition` checklist, and clause 4 is a
|
||||
> trekplan's only); clause 5's `arch-rules.mjs` and `plan-scope-sweep.mjs`.
|
||||
> **Executor judgement:** steps 1, 2 and 4, the expected count in step 3, and
|
||||
> clause 3's formula. The judgement parts are not yet a script.
|
||||
>
|
||||
> The review verdict (Phase 7.2) is decided after this gate, by the lock in
|
||||
> Phase 8: in the closing run, a closed lock or a declined merge turns
|
||||
|
|
|
|||
|
|
@ -325,9 +325,9 @@ Determine the output directory from the plan slug:
|
|||
|
||||
Launch the **session-decomposer** agent:
|
||||
|
||||
Pass `model: {phase_signal_result.model}` at this spawn; without it (the
|
||||
`--brief` branch, where the resolver does not run, or a resolver that printed
|
||||
nothing) the agent's own `model:` applies.
|
||||
Phase 1 runs the model resolver only for `--project`, so `--decompose` has no
|
||||
resolved model: this spawn passes no `model:`, and the agent's own `model:`
|
||||
applies.
|
||||
|
||||
```
|
||||
Plan file: {plan path}
|
||||
|
|
@ -1201,9 +1201,11 @@ plan-approval prompt:
|
|||
plan back.
|
||||
3. **Approved:** the session leaves plan mode. Copy the plan file to the plan
|
||||
destination from Phase 3, then run the gate on the destination — it must
|
||||
exit 0 again, which also proves the copy is the reviewed text. Then
|
||||
print the `/trekexecute` line (Phase 11) and stop — leaving plan mode is
|
||||
not an order to implement.
|
||||
exit 0 again, which also proves the copy is the reviewed text. Then go
|
||||
through Phase 11 (its plan approval, then the `/trekexecute` line) and
|
||||
stop — leaving plan mode is not an order to implement. Phase 11 asks again
|
||||
because the plan-mode approval is logged under Claude Code's plan file, and
|
||||
`/trekexecute` checks the copy at the plan destination.
|
||||
4. **"No, keep planning":** revise per the operator's words, run one Phase 9
|
||||
round, and present again from step 1.
|
||||
|
||||
|
|
|
|||
|
|
@ -510,8 +510,12 @@ The coordinator runs the 4-pass process documented in
|
|||
Succinctness never removes a finding: only MINOR/SUGGESTION text is
|
||||
trimmed, and every trim is counted. A long BLOCKER or MAJOR keeps every
|
||||
character.
|
||||
3. **Cloudflare reasonableness** — remove speculative or catalogue-violating
|
||||
findings (skipped in `quick` mode).
|
||||
3. **Cloudflare reasonableness** (skipped in `quick` mode) — drop a finding
|
||||
with no `file:line` citation (`no-citation`) or whose file is in neither
|
||||
the working tree nor the diff (`file-existence:refuted`); route one whose
|
||||
`rule_key` is not in `RULE_CATALOGUE` (`unknown-rule_key`) or whose file
|
||||
cannot be checked (`file-existence:indeterminate`) to `unverified`, which
|
||||
forbids `ALLOW`; reset a severity that does not match its catalogue tier.
|
||||
4. **Verdict** — BLOCK / WARN / ALLOW per the threshold table.
|
||||
|
||||
**Fail-closed.** Every removal in Pass 2 and Pass 3 is either
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ If `{project_dir}/architecture/overview.md` exists (typically produced by an opt
|
|||
| `--step N` | Execute only step N |
|
||||
| `--fg` | Force foreground — run all steps sequentially, ignore Execution Strategy |
|
||||
| `--session N` | Execute only session N from plan's Execution Strategy |
|
||||
| `--gates [true\|false\|closed\|open]` | (v3.4.0) Autonomy flag: bare, `true` or `closed` = on; `false` or `open` = off; any other next token is not its value. See `## Autonomy mode` in `docs/operations.md`. `/trekexecute` records `gates_mode` and pauses on nothing; it adds no question of its own before a merge: the guard asks there (operator decision 2026-10-08). |
|
||||
| `--gates [true\|false\|closed\|open]` | (v3.4.0) Autonomy flag: bare, `true` or `closed` = on; `false` or `open` = off; any other next token is not its value. See `## Autonomy mode` in `docs/operations.md`. `/trekexecute` accepts the flag and pauses on nothing; it adds no question of its own before a merge: the guard asks there (operator decision 2026-10-08). |
|
||||
| `--profile <name>` | (v4.1.0) Profile for this run's resolver; the step helper and the controller stay on opus. Inherited from plan.md frontmatter `profile:` if present. |
|
||||
|
||||
Every mode that executes refuses a plan whose approval in the approval log does not cover its current sha256 (K2). A run ends with the review it starts by itself (`/simplify` → `/trekreview` → verdict gate); a BLOCK or an undecided verdict keeps the merge into main closed. `TREKEXECUTE_MAX_REVIEW_ROUNDS` (default 3) caps the reviews per run.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Imported from `CLAUDE.md` via pointer.
|
|||
|
||||
## Autonomy mode (`--gates`, v3.4.0)
|
||||
|
||||
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` records `gates_mode` and pauses on nothing: its one stop is the merge into main, which always asks (below).
|
||||
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`.
|
||||
|
||||
|
|
|
|||
|
|
@ -500,8 +500,9 @@ export function judgeFilter(findings) {
|
|||
|
||||
/**
|
||||
* Pass 3 — Cloudflare reasonableness (deterministic subset): drop findings
|
||||
* with no citation (empty file / line < 0) or an unknown rule_key; CORRECT a
|
||||
* severity that does not match the catalogue tier (a correction, not a drop).
|
||||
* with no citation (empty file / line < 0) and
|
||||
* route an unknown rule_key to `unverified`; CORRECT a severity that does not
|
||||
* match the catalogue tier (a correction, not a drop).
|
||||
* The fs file-existence glob is excluded (I/O) — its indeterminate branch is
|
||||
* prose-side, tokenised as `file-existence:indeterminate`.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@
|
|||
// CLI shim:
|
||||
// node lib/util/autonomy-gate.mjs --state X --event Y [--gates [true|false|closed|open]]
|
||||
// (bare --gates = on; true|closed = on, false|open = off; any other value
|
||||
// is a usage error, exit 1; case-sensitive)
|
||||
// is a usage error, exit 1; case-sensitive. One token --gates=<value> is
|
||||
// read as --gates <value>; an empty value is the same usage error)
|
||||
// → JSON: { ok: true, next_state: "..." } (success)
|
||||
// → JSON: { ok: false, error: "..." } (invalid transition; exit 1)
|
||||
//
|
||||
|
|
@ -104,27 +105,37 @@ export function isTerminal(state) {
|
|||
|
||||
// ---- CLI shim ----------------------------------------------------------------
|
||||
|
||||
/** A --gates value: true|closed = on, false|open = off, else undefined. */
|
||||
function gatesValue(v) {
|
||||
if (v === 'true' || v === 'closed') return true;
|
||||
if (v === 'false' || v === 'open') return false;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const GATES_USAGE = 'usage: --gates takes true, false, closed or open';
|
||||
|
||||
function parseArgs(argv) {
|
||||
const out = {};
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const a = argv[i];
|
||||
if (a === '--state') out.state = argv[++i];
|
||||
else if (a === '--event') out.event = argv[++i];
|
||||
else if (a === '--gates') {
|
||||
else if (a.startsWith('--gates=')) {
|
||||
// The one-token form, read as `--gates <value>`; an empty value is no value.
|
||||
const g = gatesValue(a.slice('--gates='.length));
|
||||
if (g === undefined) out.error = GATES_USAGE;
|
||||
else out.gates = g;
|
||||
} else if (a === '--gates') {
|
||||
// Bare `--gates` (last, or followed by another flag) means on and
|
||||
// consumes nothing. A value is case-sensitive: true|closed = on,
|
||||
// false|open = off; anything else is a usage error.
|
||||
const v = argv[i + 1];
|
||||
if (v === undefined || v.startsWith('--')) {
|
||||
out.gates = true;
|
||||
} else if (v === 'true' || v === 'closed') {
|
||||
out.gates = true;
|
||||
i++;
|
||||
} else if (v === 'false' || v === 'open') {
|
||||
out.gates = false;
|
||||
i++;
|
||||
} else {
|
||||
out.error = 'usage: --gates takes true, false, closed or open';
|
||||
const g = gatesValue(v);
|
||||
if (g === undefined) out.error = GATES_USAGE;
|
||||
else out.gates = g;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ function coordinatorPrompt(merged, a) {
|
|||
'(file,line,rule_key) in JS. Do NOT re-dedup. Apply ONLY bounded operations:',
|
||||
'HubSpot Judge filters (accuracy / actionability; succinctness NEVER removes a',
|
||||
'finding — trim only MINOR/SUGGESTION text, keep BLOCKER/MAJOR whole), Cloudflare',
|
||||
'reasonableness (drop speculative or catalogue-violating findings), then compute',
|
||||
'reasonableness (drop a finding with no file:line citation or whose file is in neither the tree nor the diff; keep, never drop, a finding whose rule_key is not in the catalogue or whose file cannot be checked), then compute',
|
||||
'the verdict (BLOCK if any BLOCKER; WARN if any MAJOR; else ALLOW).',
|
||||
'Return {verdict, findings} via StructuredOutput. Synthesis across files is forbidden.',
|
||||
`The brief (the contract) is at: ${a.briefPath} — Read it if needed for context.`,
|
||||
|
|
|
|||
10
tests/fixtures/jsonl-schemas.md
vendored
10
tests/fixtures/jsonl-schemas.md
vendored
|
|
@ -19,14 +19,14 @@
|
|||
|
||||
| schema_id | fields | writer_path | line_ref | v4.1 additive | PII |
|
||||
|-----------|--------|-------------|----------|---------------|-----|
|
||||
| trekbrief-stats | ts, task, slug, mode, interview_turns, review_iterations, brief_quality, research_topics, auto_research, auto_result, intent_approved, project_dir, order_id, order_claimed_at, order_reason | commands/trekbrief.md (orchestrator-emit Phase 7; order_* added by lib/stats/stats-append.mjs) | trekbrief.md:1140-1164 | profile, phase_models, profile_source | none |
|
||||
| trekresearch-stats | ts, question, mode, scope, engine, slug, project_dir, brief_path, dimensions, dimensions_baseline, dimensions_baseline_preserved, effort, conv_turns, empty_turns, unique_sources, agents_local, agents_external, gemini_used, confidence, contradictions, open_questions | lib/stats/stats-append.mjs (trekresearch.md Stats tracking) | trekresearch.md:806-839 | profile, phase_models, parallel_agents, external_research_enabled, profile_source | none |
|
||||
| trekplan-stats | ts, task, mode, slug, brief_path, project_dir, codebase_size, codebase_files, agents_deployed, deep_dives, research_briefs_used, research_scout_used, critic_verdict, guardian_verdict, outcome | lib/stats/stats-append.mjs (trekplan.md Phase 12) | trekplan.md:1305-1333 | profile, phase_models, parallel_agents, profile_source | none |
|
||||
| trekbrief-stats | ts, task, slug, mode, interview_turns, review_iterations, brief_quality, research_topics, auto_research, auto_result, intent_approved, project_dir, order_id, order_claimed_at, order_reason | commands/trekbrief.md (orchestrator-emit Phase 7; order_* added by lib/stats/stats-append.mjs) | trekbrief.md:1141-1165 | profile, phase_models, profile_source | none |
|
||||
| trekresearch-stats | ts, question, mode, scope, engine, slug, project_dir, brief_path, dimensions, dimensions_baseline, dimensions_baseline_preserved, effort, conv_turns, empty_turns, unique_sources, agents_local, agents_external, gemini_used, confidence, contradictions, open_questions | lib/stats/stats-append.mjs (trekresearch.md Stats tracking) | trekresearch.md:822-855 | profile, phase_models, parallel_agents, external_research_enabled, profile_source | none |
|
||||
| trekplan-stats | ts, task, mode, slug, brief_path, project_dir, codebase_size, codebase_files, agents_deployed, deep_dives, research_briefs_used, research_scout_used, critic_verdict, guardian_verdict, outcome | lib/stats/stats-append.mjs (trekplan.md Phase 12) | trekplan.md:1307-1335 | profile, phase_models, parallel_agents, profile_source | none |
|
||||
| trekexecute-stats (Phase 9 record) | ts, plan, plan_type, mode, result, steps_total, steps_passed, steps_failed, steps_skipped, failed_at_step | lib/stats/stats-append.mjs (trekexecute.md Phase 9) | trekexecute.md:2969-2991 | profile, phase_models, profile_source | none |
|
||||
| trekexecute-stats (autonomy events) | ts, event, known_event, payload | lib/stats/event-emit.mjs `emit()` | event-emit.mjs:78-99 | payload.profile, payload.phase_models, payload.profile_source | none |
|
||||
| trekexecute-stats (PostToolUse Bash) | ts, session_id, command_excerpt, duration_ms, success | hooks/scripts/post-bash-stats.mjs (Bash PostToolUse) | post-bash-stats.mjs:42-54 | none (hook is plugin-level, not profile-aware) | command_excerpt (CWE-212) |
|
||||
| trekreview-stats | ts, slug, verdict, counts (BLOCKER/MAJOR/MINOR/SUGGESTION), reviewed_files_count, mode, duration_ms | lib/stats/stats-append.mjs (trekreview.md Phase 8) | trekreview.md:636-686 | profile, phase_models, profile_source | none |
|
||||
| trekcontinue-stats | ts, project, next_session_label, status | lib/stats/stats-append.mjs (trekcontinue.md Phase 5) | trekcontinue.md:293-297 | profile, profile_source | none |
|
||||
| trekreview-stats | ts, slug, verdict, counts (BLOCKER/MAJOR/MINOR/SUGGESTION), reviewed_files_count, mode, duration_ms | lib/stats/stats-append.mjs (trekreview.md Phase 8) | trekreview.md:640-690 | profile, phase_models, profile_source | none |
|
||||
| trekcontinue-stats | ts, project, next_session_label, status | lib/stats/stats-append.mjs (trekcontinue.md Phase 5) | trekcontinue.md:304-308 | profile, profile_source | none |
|
||||
| token-usage | ts, session_id, scope, model, tokens_input, tokens_output, tokens_cache_creation, tokens_cache_read, cost_usd, is_estimate, price_table_version | lib/stats/token-usage.mjs `captureTokenUsage` (via hooks/scripts/otel-export.mjs Stop hook, opt-in `VOYAGE_TOKEN_METER`) | token-usage.mjs:buildRecord | n/a (SKAL-2 schema) | session_id (stripped at export) |
|
||||
|
||||
## Field-allowlist input for Step 11
|
||||
|
|
|
|||
|
|
@ -191,3 +191,18 @@ test('--gates false → auto_running', () => {
|
|||
test('--gates open → auto_running', () => {
|
||||
assert.equal(shimState(['--state', 'idle', '--event', 'start', '--gates', 'open']), 'auto_running');
|
||||
});
|
||||
|
||||
// The `=` form reaches the shim whole as one token; it is read as `--gates <value>`.
|
||||
test('[#1] sibling: --gates=<value> is read as --gates <value>; an unknown or empty value is the usage error', () => {
|
||||
assert.equal(shimState(['--state', 'idle', '--event', 'start', '--gates=true']), 'gates_on');
|
||||
assert.equal(shimState(['--state', 'idle', '--event', 'start', '--gates=closed']), 'gates_on');
|
||||
assert.equal(shimState(['--state', 'idle', '--event', 'start', '--gates=false']), 'auto_running');
|
||||
assert.equal(shimState(['--state', 'idle', '--event', 'start', '--gates=open']), 'auto_running');
|
||||
for (const bad of ['--gates=maybe', '--gates=']) {
|
||||
const r = runShim(['--state', 'idle', '--event', 'start', bad]);
|
||||
assert.equal(r.code, 1, `expected exit 1 for ${bad}, got ${r.code}: ${r.out}`);
|
||||
const parsed = JSON.parse(r.out.trim());
|
||||
assert.equal(parsed.ok, false);
|
||||
assert.match(parsed.error, /usage: --gates takes true, false, closed or open/);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2084,8 +2084,11 @@ function schemaRow(id) {
|
|||
// Each row is pinned to the span it names: its phase heading to the closing
|
||||
// fence of its stats-append stdin block. The trekreview anchor is the full
|
||||
// "## Phase 8 — Validate output" so the Phase 8.5 / 8.7 headings never match.
|
||||
// The trekresearch anchor is "### Stats tracking", the one such heading in
|
||||
// commands/trekresearch.md.
|
||||
const STATS_ROWS = [
|
||||
{ id: 'trekbrief-stats', cmd: 'trekbrief', heading: /^## Phase 7\b(?!\.)/ },
|
||||
{ id: 'trekresearch-stats', cmd: 'trekresearch', heading: /^### Stats tracking$/ },
|
||||
{ id: 'trekplan-stats', cmd: 'trekplan', heading: /^## Phase 12\b(?!\.)/ },
|
||||
{ id: 'trekreview-stats', cmd: 'trekreview', heading: /^## Phase 8 — Validate output/ },
|
||||
{ id: 'trekexecute-stats (Phase 9 record)', cmd: 'trekexecute', heading: /^## Phase 9\b(?!\.)/ },
|
||||
|
|
|
|||
|
|
@ -111,10 +111,28 @@ function dropsOnlyWithTrace(part, where) {
|
|||
assert.match(part, /drop[^.]{0,60}only when you can say why it is wrong[^.]{0,120}Declined to Judge[^.]{0,60}with that reason/i,
|
||||
`${where}: a drop must carry its reason on a Declined to Judge line`);
|
||||
assert.match(part, /catalogue('s)? (tier|severity)/i, `${where}: the catalogue tier stays the default`);
|
||||
// Every sentence that drops (other than "not / never dropped") carries the reason and the trace.
|
||||
for (const s of part.split(/(?<=\.)\s+/)) {
|
||||
if (!/\bdrop/i.test(s) || /\b(not|never) dropped\b/i.test(s)) continue;
|
||||
assert.match(s, /only when you can say why it is wrong/i, `${where}: a drop sentence names no reason: ${s}`);
|
||||
assert.match(s, /Declined to Judge/i, `${where}: a drop sentence leaves no Declined to Judge line: ${s}`);
|
||||
}
|
||||
}
|
||||
|
||||
const toBlank = (l) => l.trim() === '';
|
||||
|
||||
// Untagged: these test the helper itself, so they pass at ab6b32c by construction.
|
||||
const COMPLIANT_DROP = 'If a finding feels less severe than its catalogue tier, keep the catalogue tier; drop it only '
|
||||
+ 'when you can say why it is wrong, and then write one Declined to Judge line with that reason.';
|
||||
|
||||
test('step 25: dropsOnlyWithTrace throws on a compliant off-tier sentence followed by " Drop it."', () => {
|
||||
assert.throws(() => dropsOnlyWithTrace(`${COMPLIANT_DROP} Drop it.`, 'planted'), /a drop sentence names no reason: Drop it\./);
|
||||
});
|
||||
|
||||
test('step 25: dropsOnlyWithTrace does not throw on the compliant sentence alone (control)', () => {
|
||||
assert.doesNotThrow(() => dropsOnlyWithTrace(COMPLIANT_DROP, 'control'));
|
||||
});
|
||||
|
||||
test('[#111] code-correctness-reviewer dimension 4: a nice-to-have test gap is dropped only with a Declined to Judge line', () => {
|
||||
dropsOnlyWithTrace(partAt('agents/code-correctness-reviewer.md', 'When the brief explicitly asks for tests', toBlank),
|
||||
'code-correctness-reviewer dimension 4');
|
||||
|
|
@ -164,6 +182,41 @@ test('[#111] review-coordinator input: a rule_key outside the catalogue is route
|
|||
'review-coordinator ## Input');
|
||||
});
|
||||
|
||||
// The coordinator's Pass 3 as its 4-pass lists say it (agents/review-coordinator.md ### Pass 3).
|
||||
const PASS3_ITEM = '**Cloudflare reasonableness** — drop a finding with no `file:line` citation (`no-citation`) or whose '
|
||||
+ 'file is in neither the working tree nor the diff (`file-existence:refuted`); route one whose `rule_key` is not in '
|
||||
+ '`RULE_CATALOGUE` (`unknown-rule_key`) or whose file cannot be checked (`file-existence:indeterminate`) to '
|
||||
+ '`unverified`, which forbids `ALLOW`; reset a severity that does not match its catalogue tier.';
|
||||
|
||||
/** Item 3 of a 4-pass list: from "3. **Cloudflare reasonableness**" to item 4. */
|
||||
const pass3Item = (rel) => partAt(rel, '3. **Cloudflare reasonableness**', (l) => /^4\. /.test(l));
|
||||
|
||||
/** No item 3 says an unknown rule_key or a "catalogue-violating" finding is dropped. */
|
||||
function pass3DropsNoUnknownKey(item, where) {
|
||||
assert.doesNotMatch(item, /catalogue-violating/, `${where}: item 3 still drops catalogue-violating findings`);
|
||||
assert.doesNotMatch(item, /drop findings whose `rule_key` is not in/, `${where}: item 3 still drops an unknown rule_key`);
|
||||
}
|
||||
|
||||
test('[#111] sibling: agents/review-orchestrator.md item 3 says what Pass 3 does', () => {
|
||||
const item = pass3Item('agents/review-orchestrator.md');
|
||||
assert.ok(item.includes(`3. ${PASS3_ITEM}`), `item 3 is not the Pass 3 sentence: ${item}`);
|
||||
pass3DropsNoUnknownKey(item, 'agents/review-orchestrator.md');
|
||||
});
|
||||
|
||||
test('[#111] sibling: commands/trekreview.md item 3 says what Pass 3 does, skipped in quick mode', () => {
|
||||
const item = pass3Item('commands/trekreview.md');
|
||||
const want = `3. ${PASS3_ITEM.replace('**Cloudflare reasonableness** —', '**Cloudflare reasonableness** (skipped in `quick` mode) —')}`;
|
||||
assert.ok(item.includes(want), `item 3 is not the Pass 3 sentence: ${item}`);
|
||||
pass3DropsNoUnknownKey(item, 'commands/trekreview.md');
|
||||
});
|
||||
|
||||
test('[#111] sibling: the reasonablenessFilter docblock routes an unknown rule_key to `unverified`', () => {
|
||||
const doc = partAt('lib/review/coordinator-contract.mjs', 'Pass 3 — Cloudflare reasonableness (deterministic subset)',
|
||||
(l) => /^\s*\*\/\s*$/.test(l));
|
||||
assert.ok(doc.includes('route an unknown rule_key to `unverified`'), `the docblock does not route an unknown rule_key: ${doc}`);
|
||||
assert.ok(!doc.includes('or an unknown rule_key;'), 'the docblock still drops an unknown rule_key');
|
||||
});
|
||||
|
||||
// Steps 2-24 use these helpers; referenced here so the file states its interface.
|
||||
test('helpers: line and bash are the shared line-runner pair', () => {
|
||||
assert.equal(typeof line, 'function');
|
||||
|
|
@ -187,7 +240,8 @@ test('[#2] /trekexecute never sends anyone to re-run the planning reference docu
|
|||
assert.match(text, /Re-run \/trekplan — the plan is not executable\./);
|
||||
assert.match(text, /Fix: re-run \/trekplan — the plan must use v1\.7 format/);
|
||||
assert.match(text, /Fix: re-run \/trekplan — every step needs a Manifest YAML block/);
|
||||
assert.match(text, /\/trekplan's validator should have prevented this/);
|
||||
assert.doesNotMatch(text, /validator should have prevented this/, 'Check 3 still names a validator check that does not exist');
|
||||
assert.match(text, /Stop execution\. This check runs at execution time, so it also catches a plan that was edited by hand after planning\./);
|
||||
});
|
||||
|
||||
test('[#2] sibling: commands/trekbrief.md names /trekplan as what finishes', () => {
|
||||
|
|
@ -282,10 +336,13 @@ test('[#52] Phase 10 menu: "execute" is for the /trekexecute line, and a stopped
|
|||
assert.match(p10, /`Execution is blocked: \{the gate's reasons\}` and do not print the `\/trekexecute` line, whatever is said next, until the gate exits 0\./);
|
||||
});
|
||||
|
||||
test('[#52] sibling: plan mode step 3 prints the /trekexecute line and stops', () => {
|
||||
test('[#52] sibling: plan mode step 3 goes through Phase 11 (its plan approval, then the /trekexecute line) and stops', () => {
|
||||
const pm = flat(section(read('commands/trekplan.md'), '### In plan mode'));
|
||||
assert.ok(pm.includes('Then go through Phase 11 (its plan approval, then the `/trekexecute` line) and stop — leaving plan mode is not an order to implement. '
|
||||
+ 'Phase 11 asks again because the plan-mode approval is logged under Claude Code\'s plan file, and `/trekexecute` checks the copy at the plan destination.'),
|
||||
'plan mode step 3 does not go through Phase 11\'s plan approval');
|
||||
assert.doesNotMatch(pm, /Then continue with the operator's choice \(Phase 11\)\./, 'plan mode still continues with the operator\'s choice');
|
||||
assert.match(pm, /Then print the `\/trekexecute` line \(Phase 11\) and stop — leaving plan mode is not an order to implement\./);
|
||||
assert.ok(!pm.includes('Then print the `/trekexecute` line (Phase 11) and stop'), 'plan mode still skips Phase 11\'s plan approval');
|
||||
});
|
||||
|
||||
test('[#52] hard rule: "No premature execution" holds, and nothing in Phases 10-11 implements the plan', () => {
|
||||
|
|
@ -334,7 +391,6 @@ test('[#48] commands/trekplan.md pins no spawn to `model: "opus"`', () => {
|
|||
});
|
||||
|
||||
for (const [where, marker] of [
|
||||
['session-decomposer (Phase 1.5)', 'Launch the **session-decomposer** agent:'],
|
||||
['brief-reviewer (Phase 4b)', 'Launch the **brief-reviewer** agent.'],
|
||||
['### Always launch (Phase 5)', '### Always launch (all codebase sizes):'],
|
||||
['research-scout (Phase 5)', 'launch **research-scout** in'],
|
||||
|
|
@ -347,6 +403,14 @@ for (const [where, marker] of [
|
|||
});
|
||||
}
|
||||
|
||||
test('[#48] sibling: session-decomposer (Phase 1.5): --decompose has no resolved model, so the spawn passes no model:', () => {
|
||||
const part = paraAfter('commands/trekplan.md', 'Launch the **session-decomposer** agent:');
|
||||
assert.ok(part.includes('Phase 1 runs the model resolver only for `--project`, so `--decompose` has no resolved model: this spawn '
|
||||
+ 'passes no `model:`, and the agent\'s own `model:` applies.'), `the session-decomposer paragraph does not say it passes no model: ${part}`);
|
||||
assert.ok(!part.includes('phase_signal_result'), 'the session-decomposer spawn still names phase_signal_result');
|
||||
assert.doesNotMatch(part, /model: "opus"/);
|
||||
});
|
||||
|
||||
test('[#48] convention-scanner (Phase 5): the spawn takes phase_signal_result.model, the frontmatter as the fallback', () => {
|
||||
const part = paraAfter('commands/trekplan.md', 'use the `convention-scanner` plugin agent');
|
||||
assert.match(part, /\(model: phase_signal_result\.model, the agent's frontmatter as the fallback\)/);
|
||||
|
|
@ -514,7 +578,7 @@ test('[#47] Phase 5: a brief with no research topics prints /trekplan --project
|
|||
assert.match(opening, /Phase 6 is never entered without the operator's opt-in\./);
|
||||
const manual = flat(section(text, '### Manual path (default)'));
|
||||
assert.match(manual, /Then: \/trekplan --project \{PROJECT_DIR\} Then: \/trekexecute --project \{PROJECT_DIR\}/);
|
||||
assert.match(manual, /Stop\. Do not continue to Phase 6\./);
|
||||
assert.match(manual, /Run Phase 7 \(`auto_result: manual`\), then stop\. Do not continue to Phase 6\./);
|
||||
});
|
||||
|
||||
test('[#47] Phase 6 runs only after the operator opted into auto mode and Phase 4h approved the intent', () => {
|
||||
|
|
@ -524,7 +588,43 @@ test('[#47] Phase 6 runs only after the operator opted into auto mode and Phase
|
|||
|
||||
test('keep: /trekbrief\'s auto path still requires an approved intent', () => {
|
||||
assert.equal(partAt('commands/trekbrief.md', '**Auto requires an approved intent.**', toBlank),
|
||||
'**Auto requires an approved intent.** If Phase 4h ended "Leave unapproved", the auto chain would halt at `/trekplan` anyway — print the manual-path output instead and stop.');
|
||||
'**Auto requires an approved intent.** If Phase 4h ended "Leave unapproved", the auto chain would halt at `/trekplan` anyway — print the manual-path output instead, run Phase 7 (`auto_result: manual`) and stop.');
|
||||
});
|
||||
|
||||
// Every exit of /trekbrief's manual and auto paths before the plan runs Phase 7
|
||||
// first, with the auto_result Phase 7 lists: manual, cancelled or failed.
|
||||
const BRIEF_OLD_EXITS = [
|
||||
'Stop. Do not continue to Phase 6.',
|
||||
'print the manual-path output instead and stop.',
|
||||
'fall back to the manual path output and stop.',
|
||||
'If cancelled → fall back to manual path output and stop.',
|
||||
];
|
||||
|
||||
/** No old exit sentence stands in commands/trekbrief.md (case-sensitive). */
|
||||
function noOldBriefExit() {
|
||||
const f = flat(read('commands/trekbrief.md'));
|
||||
for (const old of BRIEF_OLD_EXITS) assert.ok(!f.includes(old), `an exit still stops without Phase 7: ${old}`);
|
||||
}
|
||||
|
||||
test('[#47] sibling: Phase 5 runs Phase 7 before each of its three exits', () => {
|
||||
const p5 = flat(section(read('commands/trekbrief.md'), '## Phase 5 — Auto-orchestration opt-in (if research_topics > 0)'));
|
||||
for (const s of [
|
||||
'Run Phase 7 (`auto_result: manual`), then stop. Do not continue to Phase 6.',
|
||||
'print the manual-path output instead, run Phase 7 (`auto_result: manual`) and stop.',
|
||||
'fall back to the manual path output, run Phase 7 (`auto_result: cancelled`) and stop.',
|
||||
]) assert.ok(p5.includes(s), `Phase 5 is missing the exit: ${s}`);
|
||||
noOldBriefExit();
|
||||
});
|
||||
|
||||
test('[#47] sibling: Phase 6 runs Phase 7 before each of its three exits', () => {
|
||||
const p6 = flat(section(read('commands/trekbrief.md'), '## Phase 6 — Auto research dispatch (auto path only)'));
|
||||
for (const s of [
|
||||
'If cancelled → fall back to manual path output, run Phase 7 (`auto_result: cancelled`) and stop.',
|
||||
'ask the user how to proceed (retry, skip missing topic, cancel). On cancel, run Phase 7 (`auto_result: cancelled`) and stop.',
|
||||
'Wait for the plan to be written to `{PROJECT_DIR}/plan.md` before continuing. If `/trekplan` stops before it writes the plan, '
|
||||
+ 'report where it stopped, run Phase 7 (`auto_result: failed`) and stop.',
|
||||
]) assert.ok(p6.includes(s), `Phase 6 is missing the exit: ${s}`);
|
||||
noOldBriefExit();
|
||||
});
|
||||
|
||||
// --- #1 ----------------------------------------------------------------------------
|
||||
|
|
@ -533,7 +633,8 @@ test('keep: /trekbrief\'s auto path still requires an approved intent', () => {
|
|||
// never makes. Each of the four commands now holds one rule: the next token is
|
||||
// the flag's value only when it is true, false, closed or open; the shim maps
|
||||
// it, run once; a broken shim falls back to the same mapping in prose and the
|
||||
// run goes on. /trekexecute records gates_mode and pauses on nothing.
|
||||
// run goes on. `gates_mode` changes nothing in `/trekexecute`: its one stop is
|
||||
// the merge into main.
|
||||
|
||||
const GATES_COMMANDS = ['trekbrief', 'trekresearch', 'trekplan', 'trekexecute'];
|
||||
|
||||
|
|
@ -578,17 +679,47 @@ test('keep: every command\'s shim line maps a bare --gates to gates_on and open
|
|||
}
|
||||
});
|
||||
|
||||
const GATES_ONE_PAUSE = '`gates_mode = true` adds one pause, before the auto chain starts (Phase 5); the chain does not pass '
|
||||
+ '`--gates` on to `/trekresearch` or `/trekplan`. Default `gates_mode = false` skips that pause.';
|
||||
|
||||
test('[#1] sibling: commands/trekbrief.md promises no execute pause and no pause at every wave in the executor', () => {
|
||||
const f = flat(read('commands/trekbrief.md'));
|
||||
assert.doesNotMatch(f, /every wave in the executor/, 'trekbrief still promises a pause at every wave in the executor');
|
||||
assert.doesNotMatch(f, /\(research, plan, execute\)/, 'trekbrief still promises an execute pause');
|
||||
assert.match(f, /approval pauses at every phase boundary in the downstream pipeline \(research, plan\)\./);
|
||||
assert.ok(f.includes(GATES_ONE_PAUSE), 'trekbrief does not say --gates adds one pause before its auto chain');
|
||||
});
|
||||
|
||||
test('[#1] sibling: commands/trekexecute.md records gates_mode and pauses only at the merge into main', () => {
|
||||
test('[#1] sibling: /trekbrief Phase 1 says --gates adds one pause, before the auto chain', () => {
|
||||
const p1 = flat(section(read('commands/trekbrief.md'), '## Phase 1 — Parse mode and validate input'));
|
||||
assert.ok(p1.includes(GATES_ONE_PAUSE), 'Phase 1 does not say --gates adds one pause before the auto chain');
|
||||
assert.ok(!p1.includes('every phase boundary in the downstream pipeline'), 'Phase 1 still promises a pause at every downstream phase boundary');
|
||||
assert.ok(!p1.includes('runs continuously until the main-merge gate'), 'Phase 1 still says the auto chain runs until the main-merge gate');
|
||||
});
|
||||
|
||||
test('[#1] sibling: /trekbrief Phase 5 says the auto chain ends at the plan, never at the main-merge gate', () => {
|
||||
const p5 = flat(section(read('commands/trekbrief.md'), '## Phase 5 — Auto-orchestration opt-in (if research_topics > 0)'));
|
||||
assert.ok(p5.includes('The chain ends when `/trekplan` has written the plan (Step 6e); it never starts `/trekexecute`.'),
|
||||
'Phase 5 does not say the chain ends when /trekplan has written the plan');
|
||||
assert.ok(!p5.includes('The chain stops only at the main-merge gate'), 'Phase 5 still says the chain stops only at the main-merge gate');
|
||||
});
|
||||
|
||||
test('[#1] sibling: docs/operations.md, docs/command-modes.md and README.md say /trekexecute accepts the flag and pauses on nothing', () => {
|
||||
for (const rel of ['docs/operations.md', 'docs/command-modes.md', 'README.md']) {
|
||||
assert.ok(flat(read(rel)).includes('`/trekexecute` accepts the flag and pauses on nothing'), `${rel}: missing "accepts the flag and pauses on nothing"`);
|
||||
}
|
||||
for (const rel of ['docs/operations.md', 'docs/command-modes.md', 'README.md', EXEC]) {
|
||||
const f = flat(read(rel));
|
||||
for (const old of ['records `gates_mode`', 'Record gates_mode', 'records the setting']) {
|
||||
assert.ok(!f.includes(old), `${rel}: still says "${old}"`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('[#1] sibling: commands/trekexecute.md: `gates_mode` changes nothing in `/trekexecute`, and its one stop is the merge into main', () => {
|
||||
const f = flat(read('commands/trekexecute.md'));
|
||||
assert.doesNotMatch(f, /consumed by the autonomy-gate state machine/);
|
||||
assert.match(f, /`\/trekexecute` records `gates_mode`; no phase of it pauses on it\. The one stop is the merge into main, which always asks \(Phase 8\)\./);
|
||||
assert.ok(f.includes('`gates_mode` changes nothing in `/trekexecute`: no phase of it pauses on it. The one stop is the merge into main, which always asks (Phase 8).'),
|
||||
'trekexecute does not say gates_mode changes nothing there and the one stop is the merge into main');
|
||||
});
|
||||
|
||||
test('[#1] sibling: docs/operations.md gives the four values and the bare form, and no executor strictness table', () => {
|
||||
|
|
@ -675,7 +806,7 @@ test('[#30] the usage block names --profile and --gates, and its --profile line
|
|||
assert.ok(usage.includes('/trekexecute --profile <name> <plan.md>'), 'the usage block has no --profile line');
|
||||
assert.ok(usage.includes(`--profile Profile for this run's resolver; ${SAME_ON_OPUS}`),
|
||||
'the Modes list has no --profile line, or it does not keep the helper and the controller on opus');
|
||||
assert.ok(usage.includes('--gates Record gates_mode: bare, true or closed = on; false or open = off'),
|
||||
assert.ok(usage.includes('--gates Accepted, pauses on nothing: bare, true or closed = on; false or open = off'),
|
||||
'the Modes list has no --gates line');
|
||||
});
|
||||
|
||||
|
|
@ -771,7 +902,9 @@ test('[#33] Phase 2.5 names low effort as one session, steps in order, never whi
|
|||
assert.ok(start !== -1, 'Phase 2.5 must still list when it runs as a single session');
|
||||
const end = sec.findIndex((l, i) => i > start && l.trim() === '');
|
||||
const bullets = sec.slice(start + 1, end);
|
||||
assert.ok(bullets.includes("- `phase_signal_result.effort == 'low'` → one session, steps in order — never while a wave is in flight (`WAVE_IN_FLIGHT`)"),
|
||||
assert.ok(bullets.includes("- `phase_signal_result.effort == 'low'` → one session, steps in order, unless a wave is in flight: when "
|
||||
+ '`.claude/trekplan-sessions/{slug}/orders/wave.json` exists in the main checkout (the first entry of `git worktree list`), '
|
||||
+ 'this bullet does not apply and the multi-session path below collects the wave. The check only reads the file and never stops the run.'),
|
||||
`the single-session list does not name low effort: ${bullets.join(' | ')}`);
|
||||
});
|
||||
|
||||
|
|
@ -825,6 +958,10 @@ test('[#28] the completion gate names its commands and its executor judgement, a
|
|||
assert.ok(commands.includes(cmd), `the Commands part does not name ${cmd}`);
|
||||
assert.ok(audit.includes(cmd), `Phase 7.5 does not run ${cmd}, so the gate cannot call it a command`);
|
||||
}
|
||||
// `git diff --name-only` in its step-6 form (step 5's line has no `-- {path}`).
|
||||
assert.ok(commands.includes("step 6's `git diff --name-only`"), 'the Commands part does not name step 6\'s git diff --name-only');
|
||||
assert.ok(audit.includes('`git diff --name-only {session_start_sha} HEAD -- {path}`'),
|
||||
'Phase 7.5 step 6 does not run git diff --name-only {session_start_sha} HEAD -- {path}');
|
||||
for (const s of ['criteria runner', 'arch-rules.mjs', 'plan-scope-sweep.mjs']) {
|
||||
assert.ok(commands.includes(s), `the Commands part does not name ${s}`);
|
||||
}
|
||||
|
|
@ -833,6 +970,14 @@ test('[#28] the completion gate names its commands and its executor judgement, a
|
|||
}
|
||||
});
|
||||
|
||||
test('[#28] sibling: the completion gate says the judgement parts are not yet a script, and qualifies clauses 2 and 4', () => {
|
||||
const gate = completionGate();
|
||||
assert.ok(gate.includes('The judgement parts are not yet a script.'), 'the gate does not say the judgement parts are not yet a script');
|
||||
assert.ok(gate.includes('the criteria runner behind clauses 2 and 4 for a trekplan (a session spec\'s clause 2 is Phase 7\'s '
|
||||
+ '`## Exit Condition` checklist, and clause 4 is a trekplan\'s only);'), 'the gate does not qualify the criteria runner behind clauses 2 and 4');
|
||||
assert.ok(!gate.includes('part c of the hardening'), 'the gate still points at part c of the hardening');
|
||||
});
|
||||
|
||||
// ---- Architecture rule (plan, Step 12): lib/ never imports from scripts/ ----
|
||||
|
||||
/**
|
||||
|
|
@ -1566,6 +1711,17 @@ test('CHANGELOG: ### Doors has the two-way line for fix round 4.5a', () => {
|
|||
'the Doors part has no single two-way line for fix round 4.5a');
|
||||
});
|
||||
|
||||
const SIBLINGS_ENTRY = '- **The texts next to the fixes say the same** (siblings found during the round): `/trekreview`, the review '
|
||||
+ 'reference and the Arm B coordinator prompt no longer drop a finding whose rule key is unknown; `/trekbrief` writes its stats '
|
||||
+ 'record on every exit of its manual and auto paths, says that `--gates` adds one pause before its auto chain, and that the chain '
|
||||
+ 'ends at the plan; `/trekexecute` says `--gates` changes nothing there, checks the wave marker before a low-effort single session, '
|
||||
+ 'and names no validator check that does not exist; plan mode goes through the plan approval before it prints the `/trekexecute` '
|
||||
+ 'line; the gates shim reads `--gates=true` as on.';
|
||||
|
||||
test('CHANGELOG: ### Fixed holds the siblings entry, exactly', () => {
|
||||
assert.ok(roundBullets('### Fixed').includes(SIBLINGS_ENTRY), '### Fixed has no siblings entry with the exact text');
|
||||
});
|
||||
|
||||
test('CHANGELOG: the released v6.5.0 section is byte-equal to its text at ab6b32c (sha256)', () => {
|
||||
const part = changelogPart((l) => l.startsWith('## v6.5.0 — '), 'v6.5.0');
|
||||
assert.equal(createHash('sha256').update(part, 'utf8').digest('hex'), V650_SHA256,
|
||||
|
|
|
|||
|
|
@ -153,9 +153,10 @@ test('resolveStatsPath honors CLAUDE_PLUGIN_DATA, else the stats-append director
|
|||
assert.equal(resolveStatsPath({ HOME: '/h', CLAUDE_PLUGIN_DATA: '' }), join('/h', DATA_REL, 'trekexecute-stats.jsonl'));
|
||||
});
|
||||
|
||||
// gates_mode_flag_override stays a known event; no command emits it since fix round 4.5a
|
||||
test('the Phase 8 and override events are known events', () => {
|
||||
for (const e of ['main-merge-approved', 'main-merge-declined', 'gates_mode_flag_override']) {
|
||||
assert.equal(buildRecord(e).known_event, true, `${e} is emitted by /trekexecute by design`);
|
||||
assert.equal(buildRecord(e).known_event, true, `${e} is a known event`);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,21 @@ test('armB — a coordinator ALLOW over a BLOCKER finding is recomputed to BLOCK
|
|||
assert.equal(r.verdict, 'BLOCK');
|
||||
});
|
||||
|
||||
test('[#111] sibling: the Arm B coordinator prompt keeps, never drops, a finding whose rule_key is not in the catalogue', async () => {
|
||||
const f = { severity: 'MAJOR', rule_key: 'MISSING_TEST', file: 'a.mjs', line: 1, title: 't', detail: 'd' };
|
||||
const s = stubs({
|
||||
reviewers: [{ reviewer: 'c', findings: [f] }, { reviewer: 'k', findings: [] }],
|
||||
coordinator: { verdict: 'WARN', findings: [f] },
|
||||
});
|
||||
await exec({ ...BASE, enginePayloads: [engine('claude-code-review'), engine('claude-security-review')] }, s);
|
||||
const prompt = (s.calls.coordinatorPrompt ?? '').replace(/\s+/g, ' ');
|
||||
assert.ok(prompt.includes('keep, never drop, a finding whose rule_key is not in the catalogue'),
|
||||
'the coordinator prompt does not keep a finding whose rule_key is not in the catalogue');
|
||||
assert.ok(prompt.includes('drop a finding with no file:line citation or whose file is in neither the tree nor the diff'),
|
||||
'the coordinator prompt does not name what Pass 3 drops');
|
||||
assert.ok(!prompt.includes('catalogue-violating'), 'the coordinator prompt still drops catalogue-violating findings');
|
||||
});
|
||||
|
||||
test('armB — quick mode expects only the /code-review engine', async () => {
|
||||
const s = stubs({ reviewers: [{ reviewer: 'k', findings: [] }] });
|
||||
const r = await exec({ ...BASE, quick: true, enginePayloads: [engine('claude-code-review')] }, s);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue