fix: round 7 of the release review of 6.3.0, for 6.3.1

Round 7 (516dffa..45aa9e1, WARN: 12 MAJOR, 0 BLOCKER) and the fence defect
found while writing its review.md. Each fix began with a test that failed on
0f400d3 (v6.3.0); suite 2208 (2206/0/2), 14 tests more.

- Tests-only (operator 2026-10-11): the plan's Files: alone decides, and the
  text says so (red-proof, trekexecute Sub-step B, step-implementer,
  HANDOVER-CONTRACTS). red-proof no longer reads the step's diff, so a change
  already in the tree when the step began no longer counts against it, and a
  plan placed outside .claude/ reads the same. The round-6 test that had the
  diff overrule the plan is rewritten to the new rule.
- The Files: reader keeps a plain file beside a backticked path
  (", Dockerfile", "the CLI entry src/cli.mjs", "and Makefile"). Measured on
  the 520 Files: fields in 182 plans: no field reads differently.
- Red proof reports a command its timeout killed as killed after the timeout,
  not "could not start"; the release check tells a timeout from output past
  its buffer and from another signal. One reading of a stop
  (criteria-runner stoppedBy) for red proof, the criteria runner and the
  release check.
- A trailing `# comment` is dropped in a backticked check too, and in a
  command passed as written, so the words screened are the words run.
- The release check reads a relative --review in --repo.
- The 15-minute timeout is defined once (criteria-runner.mjs).
- The findings check reads each cited file once per validation.
- One isMain helper (lib/util/is-main.mjs) for every CLI: the 31 copies of
  the realpath guard and the 20 modules still on the file:// guard, which
  exited 0 having done nothing under a symlink or a path with a space.
- The Bash guard writes its no-one-to-ask refusal once, on a direct condition.
- The findings check ends a ```json block only at a fence on its own line, so
  three backticks inside a JSON string no longer end it (also in 6.2.0).
- Brief-conformance notes: a test for test-lock green on exit 127 (it
  passed at once; it fails when 127 is read as could-not-run); the prose now
  names the git revision `@~1` as the one leading-@ exception.

Not changed: the example plan's steps 1-2 keep their tests-only warning
(operator decision 2026-10-09; the r5 test records it). Brief note 4 (a
Regression: whose runner is not installed goes to the fix rounds) is a
routing question for the operator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-11 09:11:57 +02:00
commit 29b4ac91b3
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
65 changed files with 582 additions and 485 deletions

View file

@ -46,7 +46,7 @@ import {
} from 'node:fs';
import { spawnSync } from 'node:child_process';
import { join, resolve, dirname, basename, relative, isAbsolute, delimiter, sep } from 'node:path';
import { fileURLToPath } from 'node:url';
import { isMain } from '../util/is-main.mjs';
const TIMEOUT_MS = 180_000;
const DEPCRUISE_CONFIGS = ['.dependency-cruiser.cjs', '.dependency-cruiser.js', '.dependency-cruiser.mjs', '.dependency-cruiser.json'];
@ -255,14 +255,6 @@ export function proveRule({ repo, targets = [], plants, rule }) {
// ---- CLI --------------------------------------------------------------------
function isMain() {
try {
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
} catch {
return false;
}
}
function parseArgs(argv) {
const o = { targets: [], plants: [], prove: false, repo: null, rule: null, bad: null };
const value = (i, flag) => {
@ -287,7 +279,7 @@ function parseArgs(argv) {
return o;
}
if (isMain()) {
if (isMain(import.meta.url)) {
const o = parseArgs(process.argv.slice(2));
let result;
if (o.bad || !o.repo) {

View file

@ -56,8 +56,12 @@ const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
export const PLAN_HEADING = '## Verification';
export const BRIEF_HEADING = '## Success Criteria';
export const DEFAULT_HOOK = join(REPO_ROOT, 'hooks', 'scripts', 'pre-bash-executor.mjs');
// The one timeout every check Voyage runs is stopped by (15 minutes): red
// proof, the done line, the test lock, Phase 7 criteria, regression-check and
// the release check's suite (release review of 6.3.0, round 7).
export const DEFAULT_TIMEOUT_MS = 900_000;
export const DEFAULT_MAX_OUTPUT = 4000;
const MAX_BUFFER = 64 * 1024 * 1024;
// --- parsing ----------------------------------------------------------------
@ -151,24 +155,35 @@ export function looksLikeCommand(span) {
export function firstCommand(text) {
const m = text.match(/`([^`]+)`/);
if (!m) return { command: null, reason: 'no-command' };
const raw = m[1].trim();
const raw = dropComment(m[1]);
// Template placeholders (`{exact command}`) are not commands.
if (raw === '' || /^\{.*\}$/.test(raw)) return { command: null, reason: 'placeholder' };
if (!looksLikeCommand(raw)) return { command: null, reason: 'not-a-command' };
return { command: raw, reason: '' };
}
/**
* A command without its trailing comment (`npm test # all green`): a shell
* drops it, and with no shell (runCommand) its words would reach the screen
* and make an honest check NOT RUN (release review of 6.3.0, round 6). One
* reading for every place a command comes from: a fence line, a backticked
* span (`Verify:`, `Regression:`, `End-to-end:`, a `## Verification` bullet)
* and a command passed as written (red proof, the done line, the test lock),
* so the words screened are the words run (round 7). Only a `#` that opens a
* word on the same line is a comment (`tests/a#b.test.mjs` is a path).
*/
export function dropComment(command) {
return String(command ?? '').replace(/[ \t]+#[^\n]*$/, '').trim();
}
// A command line inside a shell-tagged fence. A console block may prefix the
// line with a `$` or `>` prompt; blank and comment-only lines declare nothing.
// A `#` root prompt is NOT stripped: it is indistinguishable from a comment,
// and running a comment is the worse of the two mistakes. A trailing comment
// (`npm test # all green`) is not part of the command: a shell dropped it,
// and with no shell (runCommand) its words would reach the screen and make an
// honest check NOT RUN (release review of 6.3.0, round 6).
// and running a comment is the worse of the two mistakes.
function fencedCommand(text) {
const line = text.trim();
if (line === '' || line.startsWith('#')) return null;
return line.replace(/^[$>]\s+/, '').replace(/\s+#.*$/, '');
return dropComment(line.replace(/^[$>]\s+/, ''));
}
function parseSection(markdown, heading, prefix) {
@ -233,7 +248,7 @@ export function parseSuccessCriteria(markdown) {
export function screenCommand(command, opts = {}) {
const hookPath = opts.hookPath ?? DEFAULT_HOOK;
const r = spawnSync(process.execPath, [hookPath], {
input: JSON.stringify({ tool_name: 'Bash', tool_input: { command } }),
input: JSON.stringify({ tool_name: 'Bash', tool_input: { command: dropComment(command) } }),
encoding: 'utf8',
timeout: 30_000,
});
@ -456,8 +471,8 @@ export function runnableCheck(criterion, opts = {}) {
function screenShape(command, opts, writeTime) {
const cwd = resolve(opts.cwd ?? process.cwd());
const text = String(command ?? '');
if (text.trim() === '') return deny('the command is empty');
const text = dropComment(command);
if (text === '') return deny('the command is empty');
if (/\n/.test(text)) return deny('a newline: one criterion declares one command');
const operator = text.match(SHELL_OPERATORS);
if (operator) return deny(`a shell operator (${operator[0]}) — one criterion declares one command, unpiped`);
@ -492,7 +507,7 @@ function screenShape(command, opts, writeTime) {
/** The words of a command: what the screen checks is exactly what runs. */
export function commandWords(command) {
return String(command ?? '').trim().split(/\s+/);
return dropComment(command).split(/\s+/);
}
/**
@ -505,7 +520,29 @@ export function commandWords(command) {
*/
export function runCommand(command, { cwd, timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
const [program, ...args] = commandWords(command);
return spawnSync(program, args, { cwd, env: commandEnv(), encoding: 'utf8', timeout: timeoutMs, maxBuffer: 64 * 1024 * 1024 });
return spawnSync(program, args, { cwd, env: commandEnv(), encoding: 'utf8', timeout: timeoutMs, maxBuffer: MAX_BUFFER });
}
/**
* Why a spawnSync run has no exit code of its own, or null when it has one.
* spawnSync sets `error` AND `signal` together when it kills the program
* itself: ETIMEDOUT for the timeout, ENOBUFS for output past `maxBuffer`. So
* the error code is read first, and a bare `error` with no signal is a program
* that never started (not installed). Red proof called a timeout "could not
* start" and the release check called an overflow a timeout (release review of
* 6.3.0, round 7). `started` is false only for a program that never ran.
* @returns {{started: boolean, why: string}|null}
*/
export function stoppedBy(r, { timeoutMs = DEFAULT_TIMEOUT_MS, maxBuffer = MAX_BUFFER } = {}) {
const signal = r.signal ?? 'SIGTERM';
if (r.error?.code === 'ETIMEDOUT') return { started: true, why: `was killed (${signal}) after the ${Math.round(timeoutMs / 1000)} s timeout` };
if (r.error?.code === 'ENOBUFS') {
const size = maxBuffer >= 1024 * 1024 ? `${Math.round(maxBuffer / (1024 * 1024))} MB` : `${maxBuffer} bytes`;
return { started: true, why: `was killed (${signal}): its output passed the ${size} that is read from it` };
}
if (r.signal) return { started: true, why: `was killed (${r.signal})` };
if (r.error || r.status === null) return { started: false, why: `could not start: ${r.error?.message ?? 'no exit code'}` };
return null;
}
// --- running ----------------------------------------------------------------
@ -515,8 +552,9 @@ export function runCommand(command, { cwd, timeoutMs = DEFAULT_TIMEOUT_MS } = {}
// One killed by the timeout did start, and a hang is a red (`failed`).
function defaultExec(command, { cwd, timeoutMs }) {
const r = runCommand(command, { cwd, timeoutMs });
if (r.status === null && r.signal === null && r.error) return { status: null, stdout: '', stderr: '', startError: r.error.message };
const why = r.signal ? `killed (${r.signal})\n` : '';
const stopped = stoppedBy(r, { timeoutMs });
if (stopped && !stopped.started) return { status: null, stdout: '', stderr: '', startError: r.error?.message ?? stopped.why };
const why = stopped ? `${stopped.why}\n` : '';
return { status: r.status, stdout: r.stdout ?? '', stderr: `${r.stderr ?? ''}${why}` };
}

View file

@ -20,12 +20,12 @@
// skills/executing-plans/scripts/task-done: the completion line is appended
// only when the test command exits 0.
import { appendFileSync, existsSync, readFileSync, realpathSync } from 'node:fs';
import { appendFileSync, existsSync, readFileSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { allowedCommand, runCommand, screenCommand } from './criteria-runner.mjs';
import { DEFAULT_TIMEOUT_MS, repoRoot } from './red-proof.mjs';
import { DEFAULT_TIMEOUT_MS, allowedCommand, runCommand, screenCommand } from './criteria-runner.mjs';
import { repoRoot } from './red-proof.mjs';
import { isMain } from '../util/is-main.mjs';
export function doneLineRe(step, commit) {
const sha = commit ? String(commit).replace(/[^0-9a-f]/g, '') : '[0-9a-f]{40}';
@ -100,9 +100,4 @@ export function main(argv) {
return r.status === 'written' ? 0 : r.status === 'failed' ? 1 : 2;
}
function isMain() {
try { return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url)); }
catch { return false; }
}
if (process.argv[1] && isMain()) process.exitCode = main(process.argv.slice(2));
if (isMain(import.meta.url)) process.exitCode = main(process.argv.slice(2));

View file

@ -32,9 +32,9 @@
import { readFileSync, existsSync, realpathSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { resolve, relative, dirname, isAbsolute, sep } from 'node:path';
import { fileURLToPath } from 'node:url';
import { sliceSteps, parseStepFields } from '../parsers/plan-schema.mjs';
import { parseManifest } from '../parsers/manifest-yaml.mjs';
import { isMain } from '../util/is-main.mjs';
// Files Voyage's own process writes while planning and executing.
export const PROCESS_FILES = Object.freeze([
@ -174,15 +174,7 @@ export function sweep({ plan, base, repo = process.cwd(), process: extra = [] })
// ---- CLI --------------------------------------------------------------------
function isMain() {
try {
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
} catch {
return false;
}
}
if (isMain()) {
if (isMain(import.meta.url)) {
const argv = process.argv.slice(2);
const o = { process: [] };
let bad = null;

View file

@ -15,9 +15,9 @@
// run, and only the full run is authoritative.
//
// `--plan <plan.md> --step <N>` adds `tests_only` to a `not-red-at-base`: true
// when every file in the step's `Files:` is a test and so is every file the
// step changed since its start point, so the step can never be red and goes
// up to the plan (see `testsOnly` below).
// when every file in the step's `Files:` is a test, so the step can never be
// red and goes up to the plan. The plan's `Files:` alone decides (see
// `testsOnly` below).
//
// Exit 0 = proven, 1 = rejected (`not-red-at-base` or `not-green-at-result`),
// 2 = could not run (`not-run`: no test files, an unknown start point, a command
@ -55,16 +55,17 @@
// Borrowed: GitHub Spec Kit (MIT, github/spec-kit@2e941fe) - its test-first
// rule is an instruction there; here a command proves it.
import { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, symlinkSync } from 'node:fs';
import { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { tmpdir } from 'node:os';
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
import { fileURLToPath } from 'node:url';
import { allowedCommand, runCommand, screenCommand } from './criteria-runner.mjs';
import { dirname, isAbsolute, join, resolve } from 'node:path';
import { DEFAULT_TIMEOUT_MS, allowedCommand, runCommand, screenCommand, stoppedBy } from './criteria-runner.mjs';
import { commandEnv } from '../util/command-env.mjs';
import { parseStepFields, sliceSteps, stepTestFile, stepTestsOnlyFromFields } from '../parsers/plan-schema.mjs';
import { parseStepFields, sliceSteps, stepTestsOnlyFromFields } from '../parsers/plan-schema.mjs';
import { isMain } from '../util/is-main.mjs';
export const DEFAULT_TIMEOUT_MS = 900_000;
// The one timeout, defined in criteria-runner.mjs (release review of 6.3.0, round 7).
export { DEFAULT_TIMEOUT_MS };
const TAIL = 2000;
function tap(output) {
@ -96,7 +97,8 @@ const LOAD_ERROR = /^\s*SyntaxError: |ERR_MODULE_NOT_FOUND|does not provide an e
function run(cmd, cwd, timeoutMs) {
const r = runCommand(cmd, { cwd, timeoutMs });
const output = `${r.stdout ?? ''}${r.stderr ?? ''}`;
return { exit: r.status, signal: r.signal ?? null, tap: tap(output), tail: output.slice(-TAIL), spawnError: r.error ? r.error.message : null, loadError: LOAD_ERROR.test(output) };
const stopped = stoppedBy(r, { timeoutMs });
return { exit: r.status, signal: r.signal ?? null, tap: tap(output), tail: output.slice(-TAIL), spawnError: r.error ? r.error.message : null, stopped: stopped?.why ?? null, loadError: LOAD_ERROR.test(output) };
}
/** What the red at the start point was: the test file failing to load, or a test failing. */
@ -112,42 +114,31 @@ const notRun = (reason, extra = {}) => ({ status: 'not-run', reason, ...extra })
// (voyage-lekerepo trial 3, 2026-10-09). `null` = the plan or the step could
// not be read; without `--plan` the field is absent and nothing new happens.
//
// The plan's `Files:` is prose, read by a reader that can misread it; the
// step's own diff is a fact. So a step the plan calls tests-only is so only
// when every file it changed since the start point is a test too (release
// review of 6.3.0, round 6). Before building (`--red-only`) the diff holds the
// step's test alone, so there the plan decides. The plan file itself and
// Voyage's own files under .claude/ (progress, approvals) are not the step's change.
function testsOnly(o, cwd, baseSha) {
// The plan's `Files:` alone decides (operator 2026-10-11): the step's diff is
// not read. In the documented flow the helper writes only the test before this
// gate, so the diff could never overrule the plan, and a change already in the
// tree when the step began (a dirty tree is allowed, trekexecute Sub-step 0)
// made a true tests-only step look like it built something (release review of
// 6.3.0, round 7). A path the step changed outside `Files:` is the
// controller's scope finding (trekexecute Sub-step C, Hard rule 2), not this check's.
function testsOnly(o) {
if (!o.plan) return {};
let text;
try { text = readFileSync(o.plan, 'utf8'); }
catch (e) { return { tests_only: null, tests_only_note: `cannot read the plan: ${e.message}` }; }
const step = sliceSteps(text).find((s) => Number(s.n) === Number(o.step));
if (!step) return { tests_only: null, tests_only_note: `no "### Step ${o.step}:" in the plan` };
const fields = parseStepFields(step.body);
if (!stepTestsOnlyFromFields(fields).testsOnly) return { tests_only: false };
const isTest = stepTestFile(fields);
const given = new Set(o.tests ?? []);
const changed = [
...lines(git(cwd, ['diff', '--name-only', '--no-renames', baseSha]).stdout),
...lines(git(cwd, ['ls-files', '--others', '--exclude-standard']).stdout),
];
const planRel = relative(cwd, realpathSync(o.plan)).split(sep).join('/');
const other = [...new Set(changed)].filter((p) => p !== planRel && !p.startsWith('.claude/') && !given.has(p) && !isTest(p));
if (other.length === 0) return { tests_only: true };
return { tests_only: false, tests_only_note: `the plan's Files: names only tests, but the step's diff also changes ${other.slice(0, 5).join(', ')}` };
return { tests_only: stepTestsOnlyFromFields(parseStepFields(step.body)).testsOnly };
}
// A program that could not start (a spawn error: not installed) or a signal
// (the timeout) is not a test verdict at all. With no shell, an exit 126/127
// A program that could not start (a spawn error: not installed), or one this
// run stopped (the timeout, output past the buffer, a signal), is not a test
// verdict at all (criteria-runner `stoppedBy`). With no shell, an exit 126/127
// is the test program's own (a script whose tool the step has not built yet):
// a red like any other, and the control run tells an environment fault apart
// (release review of 6.3.0, round 6).
function unrunnable(r) {
if (r.spawnError) return `the command could not start: ${r.spawnError}`;
if (r.exit === null) return `the command was killed (${r.signal ?? 'timeout'})`;
return null;
return r.stopped ? `the command ${r.stopped}` : null;
}
const git = (cwd, args) => spawnSync('git', args, { cwd, encoding: 'utf8', maxBuffer: 256 * 1024 * 1024 });
@ -241,7 +232,7 @@ export function proveRed(o) {
const baseBroken = unrunnable(base);
if (baseBroken) return notRun(`at the start point: ${baseBroken}`, { base, result, linked });
const out = { base_sha: baseSha, linked, base, result };
if (base.exit === 0) return { status: 'rejected', reason: 'not-red-at-base', ...testsOnly(o, cwd, baseSha), ...out };
if (base.exit === 0) return { status: 'rejected', reason: 'not-red-at-base', ...testsOnly(o), ...out };
if (o.redOnly) return { status: 'proven', reason: 'red at the start point (--red-only: the result was not run)', red_kind: redKind(base), base_sha: baseSha, linked, base };
if (result.exit !== 0) return { status: 'rejected', reason: 'not-green-at-result', ...out };
@ -249,6 +240,8 @@ export function proveRed(o) {
// copy itself cannot run the test green, so its red proved nothing.
applyResult(cwd, tree, baseSha);
const control = run(o.cmd, tree, timeoutMs);
const controlBroken = unrunnable(control);
if (controlBroken) return notRun(`in the control run: ${controlBroken}`, { ...out, control });
if (control.exit !== 0) {
return notRun('the control run (start-point copy + the change) stayed red: the start point failed for an environment reason, not for the missing change', { ...out, control });
}
@ -302,9 +295,4 @@ export function main(argv) {
return r.status === 'proven' ? 0 : r.status === 'rejected' ? 1 : 2;
}
function isMain() {
try { return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url)); }
catch { return false; }
}
if (process.argv[1] && isMain()) process.exitCode = main(process.argv.slice(2));
if (isMain(import.meta.url)) process.exitCode = main(process.argv.slice(2));

View file

@ -34,11 +34,11 @@
// left K10 regressions running differently from Phase 7 criteria (release
// review of 6.3.0, round 5).
import { readFileSync, realpathSync } from 'node:fs';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { sliceSteps, parseStepFields } from '../parsers/plan-schema.mjs';
import { firstCommand, runCriteria } from './criteria-runner.mjs';
import { isMain } from '../util/is-main.mjs';
export const REGRESSION_FIELD = 'Regression';
const EXIT = Object.freeze({ passed: 0, absent: 0, failed: 1, blocked: 1, unrunnable: 1, error: 2 });
@ -99,12 +99,4 @@ export function main(argv) {
return say(checkRegression({ planText, step, cwd }));
}
function isMain() {
try {
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
} catch {
return false;
}
}
if (isMain()) process.exitCode = main(process.argv.slice(2));
if (isMain(import.meta.url)) process.exitCode = main(process.argv.slice(2));

View file

@ -28,7 +28,7 @@
import { existsSync, readFileSync, statSync, realpathSync, accessSync, constants } from 'node:fs';
import { dirname, join, resolve, isAbsolute, extname, delimiter } from 'node:path';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { isMain } from '../util/is-main.mjs';
// ---------------------------------------------------------------------------
// The repository
@ -248,15 +248,7 @@ export function runTool(tool, files, { cwd, timeout = 120_000, env = process.env
// CLI
// ---------------------------------------------------------------------------
function isMain() {
try {
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
} catch {
return false;
}
}
if (isMain()) {
if (isMain(import.meta.url)) {
const i = process.argv.indexOf('--repo');
const start = i !== -1 ? process.argv[i + 1] : null;
if (!start) {

View file

@ -23,13 +23,13 @@
// Credit: the tiers and "not measured" are agent-skills' constraint-driven
// development (MIT, `9d0c60d`); the stop at step end is Cherny's and Kiro's.
import { existsSync, realpathSync } from 'node:fs';
import { existsSync } from 'node:fs';
import { join, extname } from 'node:path';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { resolveRepo, detectTools, runTool } from './repo-tools.mjs';
import { latestPostEditRecords } from '../../hooks/lib/post-edit.mjs';
import { runContract } from '../../hooks/lib/contract-cli.mjs';
import { isMain } from '../util/is-main.mjs';
// NUL-separated and unquoted, so a name with spaces or non-ASCII letters
// comes back as it is on disk (git quotes such names by default).
@ -105,15 +105,7 @@ export function stepCheck({ cwd, full = false }) {
return { status, root: repo.root, full, files, not_covered: notCovered, checks };
}
function isMain() {
try {
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
} catch {
return false;
}
}
if (isMain()) {
if (isMain(import.meta.url)) {
const argv = process.argv.slice(2);
const i = argv.indexOf('--cwd');
const cwd = i !== -1 ? argv[i + 1] : null;

View file

@ -23,11 +23,11 @@
// constraint-driven development - mechanized here as a diff reader so a
// command, not the model's word, says whether a test was weakened.
import { readFileSync, realpathSync } from 'node:fs';
import { readFileSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { isTestPath } from '../util/test-path.mjs';
import { isMain } from '../util/is-main.mjs';
export { isTestPath };
@ -275,9 +275,4 @@ function out(r, json) {
return r.status === 'clean' ? 0 : r.status === 'findings' ? 1 : 2;
}
function isMain() {
try { return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url)); }
catch { return false; }
}
if (process.argv[1] && isMain()) process.exitCode = main(process.argv.slice(2));
if (isMain(import.meta.url)) process.exitCode = main(process.argv.slice(2));