Round 9 (0f400d3..a10018f, WARN: 0 BLOCKER, 12 MAJOR): seven findings and three brief notes, by operator choice. Each finding began with a test that failed ona10018fon its assertion; suite 2230 (2228/0/2). - A check refused for its comment (a quote, $, backtick or backslash) keeps its command and its reason, and every reader refuses it out loud: a nested ## Verification bullet stays a criterion (PLAN_VERIFICATION_NOT_RUNNABLE) and a brief SC warns (BRIEF_SC_NOT_RUNNABLE). It was folded into a sub-point or read as prose, a regression froma10018f. Readers that took a command as runnable (the end-to-end lists) skip a refused one. - The verdict gate reads the last fenced block's info string and body from one reading, and exits 2 unless the info string is exactly json: a last block opened as ```json {x} held a BLOCKER the gate passed with exit 0 (also in 6.3.0). A last block never closed is exit 2 too. - One fence reader (findings-schema fencedBlocks) for the gate and every trailing-json reader: a fence closes only on a run at least as long as its opener, as in Markdown, so a four-backtick example of a json block is one block; CRLF and an indented fence read alike in the gate and the readers. - Files: a description runs on past a comma and onto the next line until a new entry starts (a list item on a new line, or a backticked path), and a plain path followed by a colon starts its description. Measured over all 939 Files: fields in the .md files under the marketplace and the test repo (560 of them in the 48 plan.md files outside worktrees): 0 read differently, tests-only changed in 0. - The test lock compares --cmd by its words, so other blanks between the same words are the step's command. - release-check decides "two files" on real paths: one review.md reached through a symlinked --repo is one file. - Brief notes: red proof's stopped control run has its own test (green at once, red with the branch deleted); the two tests that assumed jest is not installed run with a PATH that finds only git (red ona10018fwith a jest on PATH, green now); the Files: reader's doc states the measured counts (520 fields in 48 plan files at rounds 7-8, not "182 plans"; 560 now). Not in this round (operator 2026-10-11): a144e9ce, 1dc2e785, dc3a467e, 2fa88853, 26873fa5. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
36 lines
1.4 KiB
JavaScript
36 lines
1.4 KiB
JavaScript
// tests/helpers/bin-path.mjs
|
|
// A PATH on which only the named programs are found: one directory of small
|
|
// wrappers, each starting the program from where the current PATH finds it.
|
|
// A test that needs a test runner to be absent ("could not start") then holds
|
|
// on every machine, also one where that runner is installed (release review
|
|
// of 6.3.0, round 9: two tests assumed `jest` is not on PATH).
|
|
|
|
import { accessSync, constants, mkdtempSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { delimiter, join } from 'node:path';
|
|
|
|
function onPath(name) {
|
|
for (const dir of String(process.env.PATH ?? '').split(delimiter).filter(Boolean)) {
|
|
const p = join(dir, name);
|
|
try {
|
|
accessSync(p, constants.X_OK);
|
|
if (statSync(p).isFile()) return p;
|
|
} catch { /* not here */ }
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param {string[]} names - the programs the PATH finds (git, for the repo work)
|
|
* @returns {{path: string, cleanup: () => void}}
|
|
*/
|
|
export function pathWith(names) {
|
|
const dir = mkdtempSync(join(tmpdir(), 'voyage-bin-'));
|
|
for (const name of names) {
|
|
const found = onPath(name);
|
|
if (!found) throw new Error(`${name} is not on PATH`);
|
|
if (found.includes("'")) throw new Error(`${found}: a quote in the path`);
|
|
writeFileSync(join(dir, name), `#!/bin/sh\nexec '${found}' "$@"\n`, { mode: 0o755 });
|
|
}
|
|
return { path: dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
|
|
}
|