fix(execute): the Phase 8 payload goes on stdin; /trekreview findings closed

/trekreview of 5680591..84a0621 (verdict WARN: 0 BLOCKER, 3 MAJOR, 1 MINOR,
1 MAJOR suppressed as unverified for length only). All closed red -> green:

- Suppressed MAJOR 1d0171a7 (verified live): Phase 8 spliced {plan-path}
  into a single-quoted --payload. A lone quote broke the shell parse (event
  lost, no written:false to report); a quoted pair ran a command
  substitution and still reported written:true. event-emit now reads
  `--payload -` from stdin and the three Phase 8 blocks pass the payload
  through a quoted heredoc. Checked under bash and zsh.
- 570ba19b: the SC5 set-step extractor read line by line and never saw the
  wrapped Step 0 sentinel (`blocked`); it now matches across the break and
  the test asserts `blocked` is read.
- c69a865a: the Phase 9 line_ref in jsonl-schemas.md was 9 lines off. Both
  trekexecute rows are now pinned to the exact span by a test.
- 0004fa23 (MINOR): the gates_mode_flag_override block says what to do on
  written:false (print the reason, continue).
- emit() docstring no longer says failures are swallowed silently.
- 7ffaede5: chosen to KEEP the prose-only legacy `passed` tolerance, because
  a public-plugin run started under the 6.1.0 prose would otherwise re-run
  every finished step on resume; recorded as an accepted deviation in the
  (local-only) brief. No code change.

Suite 1400 (1398/0/2), was 1393.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-05 18:16:46 +02:00
commit cb68c4f125
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
7 changed files with 130 additions and 28 deletions

View file

@ -357,12 +357,13 @@ function fencedBlocksWith(sec, needle) {
}
/** Fill the prose placeholders an executor fills in, then run the block. */
function runBlock(block, home) {
function runBlock(block, home, { planPath = '.claude/projects/2026-10-05-x/plan.md', cwd } = {}) {
const cmd = block.split('${CLAUDE_PLUGIN_ROOT}').join(ROOT)
.split('{plan-path}').join('.claude/projects/2026-10-05-x/plan.md')
.split('{plan-path}').join(planPath)
.split('{wave-count}').join('1');
return spawnSync('bash', ['-c', cmd], {
encoding: 'utf8',
cwd,
env: { ...baseEnv(), CLAUDE_PLUGIN_DATA: '', HOME: home },
});
}
@ -409,6 +410,31 @@ test('Phase 8: each event block, run as written, lands one known event', () => {
}
});
// The plan path is operator-chosen (positional [plan.md], --project <dir>). Spliced
// into a single-quoted argument, a lone quote broke the shell parse (event lost,
// no written:false to report) and a quoted pair let a command substitution run.
test('Phase 8: a plan path with a quote character lands literally and the shell runs nothing in it', () => {
const blocks = fencedBlocksWith(section(TE, 'Phase 8'), 'event-emit.mjs');
assert.equal(blocks.length, 3, 'main-merge-gate, main-merge-approved, main-merge-declined');
for (const plan of [".claude/projects/2026-10-05-it's/plan.md", ".claude/projects/a'$(touch PWNED)'b/plan.md"]) {
for (const block of blocks) {
const home = mkdtempSync(join(tmpdir(), 'stats-landing-'));
try {
const r = runBlock(block, home, { planPath: plan, cwd: home });
assert.ok(!existsSync(join(home, 'PWNED')), `the shell ran a command from the plan path:\n${block}`);
assert.equal(r.status, 0, `${block}\n${r.stdout}${r.stderr}`);
const [rec] = records(join(home, DATA_REL, 'trekexecute-stats.jsonl'));
assert.equal(rec.payload.plan, plan);
} finally { rmSync(home, { recursive: true, force: true }); }
}
}
});
test('gates_mode_flag_override: a written:false is printed in one line and the run continues', () => {
const flat = section(TE, 'Composition rule (v5.1)').replace(/\s+/g, ' ');
assert.match(flat, /On `"written": false` \(exit 1\), print its `reason` in one line and continue/);
});
test('Phase 8: a written:false is printed in one line and the run continues', () => {
const sec = section(TE, 'Phase 8');
const flat = sec.replace(/\s+/g, ' ');