fix(#3841): express the anchor semantically so the pretty payload still verifies
The remote matrix caught a regression I introduced in the previous commit. The anchor check was implemented as a byte-prefix match against IDENTITY_RAW_PREFIX, which describes the `--raw` wire format -- but `cmdRuntimeIdentity` without that flag pretty-prints at indent 2, and the classifier is handed BOTH serializations. Only the shell is restricted to `--raw`. The pre-existing test that runs the real verb with no flag went red: `+ 'unparseable' - 'ok'`. No local gate caught it. build:lib, eslint and lint:ci were green throughout, because none of them execute tests. The anchor now reproduces its two properties semantically instead of byte-wise, and both hold for either serialization: the payload begins at the first byte of stdout, and `packageName` serializes first. IDENTITY_RAW_PREFIX stays exported with its own tests -- it is the wire contract for the shell, not a general classifier predicate, and conflating those was the error. Adds the two rows the matrix was missing: the default pretty serialization verifies, and a pretty payload with `packageName` not first does not. The design and matrix now record that they enumerated only the inputs the SHELL produces and assumed the classifier's input set was the same. Refs #3841 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -190,16 +190,22 @@ export function classifyIdentityProbe(
|
||||
const version = typeof record.version === 'string' ? record.version : undefined;
|
||||
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)) {
|
||||
// ANCHOR PARITY (#3841). The shell preamble cannot parse JSON: it matches
|
||||
// a `case` pattern anchored at the START of stdout, so a payload only
|
||||
// verifies there when it begins at the first byte and serializes
|
||||
// `packageName` first. A purely structural parse would accept
|
||||
// `{"note":"x","packageName":"<us>"}` and a leading-whitespace payload
|
||||
// that the shell rejects -- a FAIL-OPEN disagreement between the two
|
||||
// surfaces.
|
||||
//
|
||||
// Reproduce those two properties SEMANTICALLY rather than byte-matching
|
||||
// IDENTITY_RAW_PREFIX. The prefix describes the `--raw` wire format, but
|
||||
// this classifier is also handed the DEFAULT pretty serialization
|
||||
// (`runtime-identity` with no `--raw`, which is two-space indented) --
|
||||
// byte-matching the compact prefix rejected that, which the remote matrix
|
||||
// caught. `startsWith('{')` and a first-key check hold for both.
|
||||
const firstKey = Object.keys(record)[0];
|
||||
if (!probe.stdout.startsWith('{') || firstKey !== 'packageName') {
|
||||
return {
|
||||
reason: 'unparseable',
|
||||
expected,
|
||||
|
||||
@@ -190,6 +190,19 @@ describe('classifyIdentityProbe', () => {
|
||||
assert.equal(v.reason, 'unparseable');
|
||||
});
|
||||
|
||||
test('the default PRETTY serialization still verifies (not just --raw)', () => {
|
||||
// The classifier is handed both serializations: the shell only ever sees
|
||||
// `--raw`, but `runtime-identity` with no flag emits two-space-indented
|
||||
// JSON. An anchor expressed as a compact byte prefix rejected this.
|
||||
const pretty = JSON.stringify({ packageName: EXPECTED_PACKAGE_NAME, version: '9.9.9' }, null, 2);
|
||||
assert.equal(classifyIdentityProbe({ stdout: `${pretty}\n`, exitCode: 0 }).reason, 'ok');
|
||||
});
|
||||
|
||||
test('a pretty payload with packageName not first does not verify', () => {
|
||||
const pretty = JSON.stringify({ note: 'x', packageName: EXPECTED_PACKAGE_NAME }, null, 2);
|
||||
assert.equal(classifyIdentityProbe({ stdout: `${pretty}\n`, exitCode: 0 }).reason, 'unparseable');
|
||||
});
|
||||
|
||||
test('a foreign packageName is a mismatch wherever it appears in the object', () => {
|
||||
const stdout = '{"note":"x","packageName":"get-shit-done-cc"}';
|
||||
const v = classifyIdentityProbe({ stdout, exitCode: 0 });
|
||||
|
||||
Reference in New Issue
Block a user