test(scripts): require the ratchet child to run to completion

The child-process guard proved the gate *started* reporting, not that it
finished. Status 1 plus the report heading was accepted as a completed
policy decision, so a child that printed the heading -- or the whole
report -- and then crashed still read as the gate having decided.

The eval script now writes a fixed sentinel to stdout after the awaited
`runFileSizeCheck` returns, and the harness requires it on both the
exit-0 and exit-1 paths. The two questions are separated: the sentinel
proves normal completion, and only then does the exit status carry the
policy result.

Proven against the case the old guard missed. Injecting a throw after the
full violation report is emitted but before the gate returns:

  old guard (heading only)  9 passing / 0 failing   <- crash read as a pass
  new guard (sentinel)      7 passing / 2 failing   <- "did not run to
                                                       completion ... crashed
                                                       rather than gated"

A throw injected immediately after the heading also fails with the same
assertion rather than being misattributed to a missing violation line.

This is the same defect the PR closes, one level up: a check that cannot
distinguish the outcome it reports from a failure to reach it.

Signed-off-by: ss-dev-01 <11939edb7df583f855dbef923f2358f1184538f88ca452e19e7e35e42ad6d796@buzz.block.builderlab.xyz>
Co-authored-by: Bradley Axen <baxen@squareup.com>
Signed-off-by: Bradley Axen <baxen@squareup.com>
This commit is contained in:
ss-dev-01
2026-08-24 16:59:11 -07:00
parent ce1f1d4275
commit b9d77258d6

View File

@@ -152,6 +152,8 @@ test("every script root governs .mjs alongside .ts and .tsx", () => {
// Runs the real desktop rule table against a throwaway repository in a child
// process. A child keeps `process.exitCode` and `console.error` out of this
// test's own process, where a leaked exit code would mark the whole file failed.
const COMPLETION_SENTINEL = "__file_size_gate_completed__";
function runDesktopGate({ fixtureRoot, baseRef }) {
const result = spawnSync(
process.execPath,
@@ -166,6 +168,9 @@ function runDesktopGate({ fixtureRoot, baseRef }) {
rules,
label: "Desktop",
});
// Printed only if the awaited call returned normally. On stdout so it
// cannot be confused with any part of the violation report.
process.stdout.write(${JSON.stringify(COMPLETION_SENTINEL)});
`,
],
{
@@ -181,15 +186,20 @@ function runDesktopGate({ fixtureRoot, baseRef }) {
},
);
// Node exits 1 for an uncaught exception too, so status alone cannot tell a
// ratchet violation from a crash. The runner's only output is its violation
// report, so a failing status must carry that report to count as a real
// violation; anything else is the harness breaking, not the gate deciding.
// ratchet violation from a crash. Split the two questions: the sentinel proves
// the gate ran to completion, and only then does the exit status mean a policy
// decision. Asserting on the report heading alone would accept a child that
// started printing violations and then crashed part-way through.
assert.ok(
result.stdout.includes(COMPLETION_SENTINEL),
`the ratchet child did not run to completion (exit ${result.status}, signal ${result.signal}), so it crashed rather than gated:\n${result.stderr}`,
);
const failed = result.status === 1;
if (failed) {
assert.match(
result.stderr,
/file size ratchet failed \(base /,
`the ratchet child exited 1 without reporting a violation, so it crashed rather than gated:\n${result.stderr}`,
`the ratchet child exited 1 without reporting a violation:\n${result.stderr}`,
);
} else {
assert.equal(