fix(#3841): honor the payload anchor in the classifier, dedup the fixture

Three review findings, all fixed.

The isolated security review found a SECOND divergence the design missed. The
classifier parses structurally, so it accepted `packageName` at any key
position; the shell's `case` is anchored at the start of stdout. For
`{"note":"x","packageName":"<us>",...}` the classifier said ok and the shell
said unverified -- a fail-open disagreement, and none of the original ten parity
rows caught it because every one put `packageName` first. Certifying agreement
that does not hold would have been worse than shipping no parity suite. The
classifier now honors the anchor on its ok arm, which is what the module already
claimed to do: IDENTITY_RAW_PREFIX is documented as "ANCHORED, never a substring
search". A foreign packageName stays identity_mismatch wherever it appears, so
that arm is untouched. Parity rows P11-P13 added.

The spec review found the test matrix marked the empty-string `packageName` row
as already covered. It was not -- the `.length > 0` guard is a distinct path
from "no packageName key at all", which is tested. Added, and the matrix
corrected to say it was wrong.

The standards review flagged ~45 lines of fixture helpers duplicated verbatim
between the two preamble describes. Extracted to one `makeIdentityFixture()`.

Changeset rewritten: it described only the exit-code half of the diff.

Refs #3841

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
sim
2026-08-25 08:34:51 -04:00
committed by Tom Boucher
parent 3b8e4f3e5a
commit 5e997de5f0
3 changed files with 91 additions and 65 deletions

View File

@@ -188,9 +188,26 @@ export function classifyIdentityProbe(
// older check. The shell anchor is deliberately open in the middle for the
// same reason, and the parity suite pins both halves of that (case P10).
const version = typeof record.version === 'string' ? record.version : undefined;
return actual === expected
? { reason: 'ok', expected, actual, version }
: { reason: 'identity_mismatch', expected, actual, version };
if (actual !== expected) return { reason: 'identity_mismatch', expected, actual, version };
// ANCHOR PARITY (#3841). The shell preamble cannot parse JSON; it matches
// a `case` pattern anchored at the START of stdout, so `packageName` must
// serialize first. A structural parse alone would accept
// `{"note":"x","packageName":"<us>"}` — a shape this package never emits —
// while the shell rejected it, so the two surfaces would disagree in the
// FAIL-OPEN direction. Honoring the anchor here is also what the module
// already claims to do: IDENTITY_RAW_PREFIX is documented as "ANCHORED,
// never a substring search", and buildIdentityPayload inserts packageName
// first precisely so a genuine payload always satisfies it.
if (!probe.stdout.startsWith(IDENTITY_RAW_PREFIX)) {
return {
reason: 'unparseable',
expected,
detail: 'payload names this package but is not in the anchored wire shape',
};
}
return { reason: 'ok', expected, actual, version };
}
}