fix(gates): one --gates rule in every command, run through the shim, and no executor pause the executor lacks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
e2fcf4060c
commit
af36578f12
11 changed files with 190 additions and 52 deletions
|
|
@ -523,3 +523,108 @@ 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.');
|
||||
});
|
||||
|
||||
// --- #1 ----------------------------------------------------------------------------
|
||||
// `--gates` meant one thing in the shim (a boolean), another in the docs (an
|
||||
// open/closed/adaptive strictness policy), and /trekexecute promised pauses it
|
||||
// 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.
|
||||
|
||||
const GATES_COMMANDS = ['trekbrief', 'trekresearch', 'trekplan', 'trekexecute'];
|
||||
|
||||
const GATES_RULE = [
|
||||
'`--gates` takes the next token as its value only when it is `true`, `false`, `closed` or `open` (strip both); any other next token is not its value (strip only the flag).',
|
||||
'Run the shim once to map the value, bare when there is no value:',
|
||||
'`next_state: gates_on` sets `gates_mode = true`, `auto_running` sets it 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.',
|
||||
];
|
||||
|
||||
/** The command's one shim line, `[value]` filled in with `value` (left out when ''). */
|
||||
function gatesShim(cmd, value) {
|
||||
const l = line(read(`commands/${cmd}.md`), 'util/autonomy-gate.mjs', '--event start');
|
||||
assert.match(l, / --state idle --event start --gates \[value\]$/, `${cmd}: the shim line must end "--gates [value]"`);
|
||||
return l.replace(' [value]', value ? ` ${value}` : '');
|
||||
}
|
||||
|
||||
/** Run a shim line from the prose; its `next_state`. */
|
||||
function shimNext(cmdLine) {
|
||||
const r = bash(cmdLine, ROOT);
|
||||
assert.equal(r.status, 0, `shim exited ${r.status}: ${r.stdout}${r.stderr}`);
|
||||
return JSON.parse(r.stdout).next_state;
|
||||
}
|
||||
|
||||
for (const cmd of GATES_COMMANDS) {
|
||||
test(`[#1] commands/${cmd}.md holds the shared --gates rule and a shim line that maps closed to gates_on`, () => {
|
||||
const text = read(`commands/${cmd}.md`);
|
||||
const f = flat(text);
|
||||
for (const s of GATES_RULE) assert.ok(f.includes(s), `${cmd}: missing the shared --gates sentence: ${s}`);
|
||||
assert.doesNotMatch(text, /--state X --event Y/, `${cmd}: the placeholder shim line is still there`);
|
||||
assert.doesNotMatch(f, /consumed by the autonomy-gate/, `${cmd}: still says the flag is consumed by the state machine`);
|
||||
assert.equal(shimNext(gatesShim(cmd, 'closed')), 'gates_on');
|
||||
});
|
||||
}
|
||||
|
||||
// Review focus: `/trekplan --gates --project d` and `/trekexecute --gates plan.md`
|
||||
// are a bare --gates (the next token is not a value), so the shim runs bare.
|
||||
test('keep: every command\'s shim line maps a bare --gates to gates_on and open to auto_running', () => {
|
||||
for (const cmd of GATES_COMMANDS) {
|
||||
assert.equal(shimNext(gatesShim(cmd, '')), 'gates_on', `${cmd}: bare --gates`);
|
||||
assert.equal(shimNext(gatesShim(cmd, 'open')), 'auto_running', `${cmd}: --gates open`);
|
||||
}
|
||||
});
|
||||
|
||||
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\)\./);
|
||||
});
|
||||
|
||||
test('[#1] sibling: commands/trekexecute.md records gates_mode and pauses only at 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\)\./);
|
||||
});
|
||||
|
||||
test('[#1] sibling: docs/operations.md gives the four values and the bare form, and no executor strictness table', () => {
|
||||
const text = read('docs/operations.md');
|
||||
const sec = flat(section(text, '## Autonomy mode (`--gates`, v3.4.0)'));
|
||||
assert.doesNotMatch(text, /\{open\\?\|closed\\?\|adaptive\}/);
|
||||
assert.doesNotMatch(sec, /\| `gates_mode` \| Derived from/, 'the executor strictness table is still there');
|
||||
assert.doesNotMatch(sec, /\| `adaptive` \|/, 'the executor strictness table is still there');
|
||||
assert.doesNotMatch(sec, /how strict/);
|
||||
assert.doesNotMatch(sec, /gates_mode_flag_override/, 'still names an override record the executor does not write');
|
||||
assert.match(sec, /accept `--gates \[true\|false\|closed\|open\]`/);
|
||||
assert.match(sec, /A bare `--gates`, `--gates true` or `--gates closed` turns gating \*\*on\*\*/);
|
||||
for (const s of ['gates_on', 'auto_running', 'paused_for_gate']) assert.ok(sec.includes(s), `the state ${s} stays named`);
|
||||
});
|
||||
|
||||
test('[#1] sibling: docs/architecture.md does not call --gates open the executor\'s low effort', () => {
|
||||
const text = read('docs/architecture.md');
|
||||
assert.doesNotMatch(text, /--gates open/);
|
||||
assert.match(text, /\(`\/trekexecute` low effort = one session at a time\)/);
|
||||
});
|
||||
|
||||
test('[#1] sibling: docs/command-modes.md gives every --gates row the four values and no strictness policy', () => {
|
||||
const text = read('docs/command-modes.md');
|
||||
assert.doesNotMatch(text, /--gates \{true\\\|false\}/);
|
||||
assert.doesNotMatch(text, /\{open\\?\|closed\\?\|adaptive\}/);
|
||||
assert.doesNotMatch(text, /Policy \(`gates_mode`\)/, 'a row still points at a gates_mode strictness policy');
|
||||
const rows = text.split('\n').filter((l) => l.startsWith('| `--gates'));
|
||||
assert.equal(rows.length, 4, 'one --gates row per command');
|
||||
for (const r of rows) {
|
||||
assert.ok(r.startsWith('| `--gates [true\\|false\\|closed\\|open]` |'), `row does not give the four values: ${r}`);
|
||||
assert.match(r, /`## Autonomy mode` in `docs\/operations\.md`/);
|
||||
}
|
||||
});
|
||||
|
||||
test('[#1] sibling: README.md gives the four values, and no strictness table', () => {
|
||||
const text = read('README.md');
|
||||
const sec = flat(section(text, '### Autonomy mode (`--gates`, v3.4.0)'));
|
||||
assert.doesNotMatch(text, /\{open\|closed\|adaptive\}/);
|
||||
assert.doesNotMatch(sec, /\| Value \| Behavior \|/, 'the strictness table is still there');
|
||||
assert.doesNotMatch(sec, /`adaptive` \(default\)/);
|
||||
assert.match(sec, /accept `--gates \[true\|false\|closed\|open\]`/);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue