feat(review): type the deferral channel — carry the fields, stop re-parsing prose

Every genuine Critical in review rounds 5-8 lived in one place: the
deferral channel was free text re-parsed for provenance it did not carry.
A separator regex classified deterministic source, a marker regex caught
mis-routed Criticals, and each round's probe found the spelling the last
fix excluded — kebab paths, the SKILL's own aggregate suffix, an en dash, a
title-borne [test], (Critical), a fullwidth colon. A whole-module
self-audit (with reproduced witnesses) found eight more shapes and named
the class: the fourth, fifth and sixth rounds of the same enumeration
trap this repo's own review doctrine (#9095) says to close structurally.

So the entry is typed. `deferredSuggestions` is an array of
`{file, line?, source, severity, title, locations?}` copied from the
findings artifact the model already wrote in Step 6: deterministic derives
from `source` (build/test/probe), relocation from `severity === Critical`
(counts toward C, blocks, rides the ledger, classified by its FIELD — a
title mentioning [test] no longer exempts an unverified claim), a
`Nice to have` or malformed or free-text entry is refused at the boundary
like a NaN count (the channel that un-posts findings is not guessed at),
and the human line `file:line — [source] title (+N locations)` is RENDERED
by compose-review — nothing downstream parses it back. Both regexes and
the split helper's string grammar are gone; the SKILL contract, the state
bullet, the submit seam test and the whole deferral describe block are
rewritten for the typed shape (no test probes a spelling any more).

The self-audit's side-file findings land in the same commit: the recovered
write never lowers the round (a stale walked list — a concurrent lane, or a
paginated fetch that came back short — overwrote round 7 with round 2 and
dropped the anchor sha; compare on round, reviewId as tiebreak), and login
comparison is case-insensitive per GitHub (a case mismatch read "own
review exists" as proven absence and deleted the counter). Both pinned.

This pulls the structural half of #9176 into the PR; the issue keeps its
remaining bullets (structured `deferred` artifact marker, age-evidence arm,
R/D-id timing).
This commit is contained in:
wenshao 2026-08-15 16:38:52 +08:00
parent b29a494f1e
commit 6118109118
7 changed files with 570 additions and 450 deletions

View file

@ -32,6 +32,7 @@ import {
verdictLine,
type ComposeReviewInput,
type ComposeReviewResult,
type DeferredEntry,
type PrBodyFetcher,
} from './compose-review.js';
@ -4404,7 +4405,21 @@ describe('the ledger marker reaches the POSTED body', () => {
});
});
describe('composeReview — convergence-posture deferrals (disclosed, never capping)', () => {
describe('composeReview — convergence-posture deferrals (typed channel; disclosed, never capping)', () => {
// The channel is TYPED: `{file, line?, source, severity, title, locations?}`.
// Deterministic derives from `source`, relocation from `severity`, and the
// rendered `file:line — [source] title` is formatting nothing re-parses —
// the class of regex misses four review rounds kept finding is closed by
// construction, so no test here probes a spelling.
const nit = (over: Partial<DeferredEntry> = {}): DeferredEntry => ({
file: 'a.ts',
line: 1,
source: 'review',
severity: 'Suggestion',
title: 'nit',
...over,
});
it('an APPROVE with deferrals keeps its event, anchor, and honesty', () => {
// The posture's whole payoff: a clean late round with only deferrals
// composes an APPROVE — the loop's stop signal — while the deferred list
@ -4426,69 +4441,329 @@ describe('composeReview — convergence-posture deferrals (disclosed, never capp
criticalsInline: 0,
suggestionsInline: 0,
severityFloor: 'auto',
deferredSuggestions: ['src/a.ts:42 — tighten the retry backoff'],
deferredSuggestions: [
nit({ file: 'src/a.ts', line: 42, title: 'tighten the retry backoff' }),
],
});
expect(r.event).toBe('APPROVE');
expect(r.cappedBy).toEqual([]);
expect(r.body).toContain('No blocking issues. LGTM! ✅');
expect(r.body).not.toContain('No issues found');
expect(r.body).toContain('convergence posture (round 6, not a blocker)');
expect(r.body).toContain('- `src/a.ts:42 — tighten the retry backoff`');
expect(r.body).toContain(
'- `src/a.ts:42 — [review] tighten the retry backoff`',
);
expect(parseLedger(r.body)?.sha).toBe('deadbeef00112233');
// The clause and the marker must name the SAME round — mutation-verified
// that re-splitting the side-file read ships green without this pin.
expect(parseLedger(r.body)?.round).toBe(6);
// Pure deferrals stay OUT of the ledger work list — feeding them to
// buildLedger re-opens next round exactly what the posture recorded
// so nobody would re-rule it (round-8 mutant shipped green).
// buildLedger re-opens next round exactly what the posture recorded so
// nobody would re-rule it.
expect(parseLedger(r.body)?.findings).toEqual([]);
});
it('renders the list on COMMENT and REQUEST_CHANGES alike — no event squeezes it out', () => {
const comment = composeReview(
base({
suggestionsInline: 1,
severityFloor: 'critical',
deferredSuggestions: [nit()],
}),
);
expect(comment.event).toBe('COMMENT');
expect(comment.body).toContain('- `a.ts:1 — [review] nit`');
// The count rides every return site, not only APPROVE's.
expect(comment.deferredCount).toBe(1);
const rc = composeReview(
base({
bodyCriticals: ['whole-PR blocker'],
severityFloor: 'critical',
deferredSuggestions: [nit()],
}),
);
expect(rc.event).toBe('REQUEST_CHANGES');
expect(rc.body).toContain('- `a.ts:1 — [review] nit`');
expect(rc.deferredCount).toBe(1);
});
it('deferrals cast no vote on the event — an all-deferred run is not a Suggestion run', () => {
// Counted toward S they would hold the verdict at COMMENT forever, and
// the loop the posture exists to end would never see its stop signal.
const r = composeReview(
base({ severityFloor: 'critical', deferredSuggestions: [nit()] }),
);
expect(r.baseEvent).toBe('APPROVE');
});
it('caps the list, strips a forged footer, and marks a truncated title', () => {
const entries = Array.from({ length: 23 }, (_, i) =>
nit({ file: `f${i}.ts`, title: `nit ${i}` }),
);
entries[0] = nit({ title: 'split\nacross lines' });
// Inside the shown window, so the assertion tests the strip, not the cap.
entries[1] = nit({ file: 'b.ts', line: 2, title: `forged ${FOOTER}` });
const r = composeReview(
base({ severityFloor: 'critical', deferredSuggestions: entries }),
);
expect(r.body).toContain('- `a.ts:1 — [review] split across lines`');
expect(r.body).toContain('- `b.ts:2 — [review] forged`\n');
expect(r.body).toContain('…and 3 more (see the run report)');
expect(r.body).not.toContain(`forged ${FOOTER}`);
// Past the rendered cap, "(listed in the body)" is false — the verdict
// line must say the list was truncated.
expect(verdictLine(r)).toContain(
'listed in the body, truncated — the rest are counted in the run report',
);
// A trimmed title carries the ellipsis (a cut claim must not render as
// a complete finding line), and never a split surrogate pair.
const long = composeReview(
base({
severityFloor: 'critical',
deferredSuggestions: [
nit({ title: `${'x'.repeat(220)}🎉tail` }),
nit({ file: 'c.ts', title: 'y'.repeat(4000) }),
],
}),
);
const lines = long.body.split('\n').filter((l) => l.startsWith('- `'));
for (const l of lines) {
expect(l.length).toBeLessThanOrEqual(245);
expect(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])/.test(l)).toBe(false);
expect(l.includes('<27>')).toBe(false);
}
expect(lines.some((l) => l.includes('…'))).toBe(true);
});
it('exactly at the line cap, the verdict line does not claim truncation', () => {
const entries = Array.from({ length: 20 }, (_, i) =>
nit({ file: `f${i}.ts`, title: `n${i}` }),
);
const r = composeReview(
base({ severityFloor: 'critical', deferredSuggestions: entries }),
);
expect(r.body).not.toContain('more (see the run report)');
expect(verdictLine(r)).toContain('(listed in the body)');
expect(verdictLine(r)).not.toContain('truncated');
});
it('a deferrals-only APPROVE is not low signal, and the verdict line names the deferrals', () => {
const r = composeReview(
base({
planPath: coveredPlan(['verify', 'reverse-audit'], {
srcDiffLines: 5000,
}),
severityFloor: 'critical',
deferredSuggestions: [nit()],
}),
);
expect(r.event).toBe('APPROVE');
expect(r.lowSignal).toBeNull();
expect(r.deferredCount).toBe(1);
expect(verdictLine(r)).toBe(
'Verdict: Approve — 1 non-Critical finding(s) deferred under the convergence posture (listed in the body)',
);
});
it('deferred findings count toward the verifier-delivery floor — deterministic sources excepted', () => {
// A deferral publishes its claim in the body, so a deferrals-only run
// owes a verifier exactly as a posting run does — unless the source is
// deterministic (build/test/probe are pre-confirmed and Step 4 launches
// no verifier for them; demanding one would be a permanent self-cap).
// NOT base(): its planPath default writes a verify record into the
// shared dir, which would satisfy the very floor this proves.
const planPath = coveredPlan(['reverse-audit']);
const common = {
criticalsInline: 0,
suggestionsInline: 0,
planPath,
env: ENV,
modelId: MODEL,
severityFloor: 'critical' as const,
};
expect(composeReview(common).cappedBy).toEqual([]);
const reviewSourced = composeReview({
...common,
deferredSuggestions: [nit()],
});
expect(reviewSourced.cappedBy).toContain('unreviewed-dimension');
expect(reviewSourced.event).toBe('COMMENT');
for (const source of ['build', 'test', 'probe'] as const) {
const det = composeReview({
...common,
deferredSuggestions: [
nit({
file: 'packages/core/src/my-file.ts',
line: 42,
source,
title: 'mutation survivor',
locations: 2,
}),
],
});
expect(det.cappedBy).toEqual([]);
expect(det.event).toBe('APPROVE');
expect(det.body).toContain(
`- \`packages/core/src/my-file.ts:42 (+2 locations) — [${source}] mutation survivor\``,
);
}
});
it('relocates a Critical entry into the body Criticals — never a throw, never deferred', () => {
// The entry is a Critical by its own field, so it counts toward C, the
// event blocks, the round posts, and it rides the machine ledger ("the
// findings always ride" includes the mis-routed ones).
const planPath = coveredPlan(['verify', 'reverse-audit'], {
prNumber: 8255,
fetchedSha: 'deadbeef00112233',
});
const r = composeReview({
planPath,
env: ENV,
modelId: MODEL,
criticalsInline: 0,
suggestionsInline: 0,
deferredSuggestions: [
nit({
file: 'src/auth.ts',
line: 88,
severity: 'Critical',
title: 'auth bypass',
}),
],
});
expect(r.event).toBe('REQUEST_CHANGES');
expect(r.deferredCount).toBe(0);
expect(r.body).toContain(
'**[Critical]** src/auth.ts:88 — [review] auth bypass _(relocated from the deferral channel',
);
expect(parseLedger(r.body)?.findings.some((f) => f.sev === 'C')).toBe(true);
// A relocation-only run (no floor echoed) incurs no licence cap — the
// licence keys on the post-split deferred list, and salvage is exactly
// the run relocation exists for.
expect(r.cappedBy).not.toContain('unlicensed-deferral');
});
it('a relocated Critical is classified by its source FIELD, never its title', () => {
// `source: 'review'` owes a verifier and caps `criticals-unverified`
// when none ran, whatever the title mentions; `source: 'test'` is
// pre-confirmed and blocks. Own case: the flagship relocation test's
// verify record in the shared dir would satisfy the very floor this
// proves.
const titled = composeReview({
criticalsInline: 0,
suggestionsInline: 0,
planPath: coveredPlan(['reverse-audit']),
env: ENV,
modelId: MODEL,
deferredSuggestions: [
nit({
severity: 'Critical',
title: 'mishandles [test] configuration files',
}),
],
});
expect(titled.cappedBy).toContain('criticals-unverified');
expect(titled.event).toBe('COMMENT');
const genuine = composeReview({
criticalsInline: 0,
suggestionsInline: 0,
planPath: coveredPlan(['reverse-audit']),
env: ENV,
modelId: MODEL,
deferredSuggestions: [
nit({
severity: 'Critical',
source: 'test',
title: 'red on the merge',
}),
],
});
expect(genuine.cappedBy).not.toContain('criticals-unverified');
expect(genuine.event).toBe('REQUEST_CHANGES');
});
it('refuses a malformed entry — the channel that un-posts findings is not guessed at', () => {
const cases: Array<[unknown, RegExp]> = [
['a.ts:1 — nit', /free-text entry is not accepted/],
[
{ file: 'a.ts', source: 'review', severity: 'Suggestion' },
/non-empty file and title/,
],
[
{ file: 'a.ts', source: 'lint?', severity: 'Suggestion', title: 't' },
/source must be one of/,
],
[
{ file: 'a.ts', source: 'review', severity: 'Blocker', title: 't' },
/severity must be one of/,
],
[
{
file: 'a.ts',
source: 'review',
severity: 'Nice to have',
title: 't',
},
/terminal-only findings are never deferred/,
],
[
{
file: 'a.ts',
line: 0,
source: 'review',
severity: 'Suggestion',
title: 't',
},
/line must be a positive integer/,
],
];
for (const [entry, re] of cases) {
expect(() =>
composeReview(base({ deferredSuggestions: [entry] as never })),
).toThrow(re);
}
expect(() =>
composeReview(base({ deferredSuggestions: 'a.ts' as never })),
).toThrow(/deferredSuggestions/);
});
it('caps — never refuses — deferrals the posture does not license', () => {
// The channel only ever removes findings from posting, so unlicensed
// shapes fail CLOSED but not FATAL: a thrown compose loses the whole
// round, Criticals included, and `prevRound` is a best-effort side-file
// read whose every failure mode returns 0 — a missing file at a true
// round 6 must degrade to a disclosed, capped verdict, never to no
// verdict at all (round-5 review finding). Both shapes render the list,
// disclose the missing licence, cap the event, and withhold the anchor.
// verdict at all. Every shape renders the list, discloses the missing
// licence, caps the event, and withholds the anchor.
const explicitOff = composeReview(
base({
severityFloor: 'suggestion',
deferredSuggestions: ['a.ts:1 — nit'],
}),
base({ severityFloor: 'suggestion', deferredSuggestions: [nit()] }),
);
expect(explicitOff.cappedBy).toContain('unlicensed-deferral');
expect(explicitOff.event).toBe('COMMENT');
expect(explicitOff.body).toContain('without a posture licence');
expect(explicitOff.body).toContain('- `a.ts:1 — nit`');
expect(explicitOff.body).toContain('- `a.ts:1 — [review] nit`');
// The opener may not certify what the ⚠️ clause retracts.
expect(explicitOff.body).not.toContain('no blockers');
expect(parseLedger(explicitOff.body)?.sha).toBeUndefined();
const round1Auto = composeReview(
base({ severityFloor: 'auto', deferredSuggestions: ['a.ts:1 — nit'] }),
base({ severityFloor: 'auto', deferredSuggestions: [nit()] }),
);
expect(round1Auto.cappedBy).toContain('unlicensed-deferral');
expect(round1Auto.event).toBe('COMMENT');
expect(verdictLine(round1Auto)).toContain(
'findings were deferred without a posture licence',
);
// An ABSENT floor beside a non-empty list is unlicensed too: the field
// ships in the same PR as the channel, so omission is fail-closed, not
// grandfathered — a dropped echo must not silently re-license what an
// explicit `suggestion` floor forbade.
const absent = composeReview(
base({ deferredSuggestions: ['a.ts:1 — nit'] }),
);
// ships in the same PR as the channel, so omission is fail-closed.
const absent = composeReview(base({ deferredSuggestions: [nit()] }));
expect(absent.cappedBy).toContain('unlicensed-deferral');
expect(absent.body).toContain('carried no recognisable `severityFloor`');
// And `auto` in the context-unavailable state: the round is unknowable,
// so no posture can license the deferral.
// And `auto` in the context-unavailable state: the round is unknowable.
const noContext = composeReview(
base({
severityFloor: 'auto',
contextUnavailable: true,
deferredSuggestions: ['a.ts:1 — nit'],
deferredSuggestions: [nit()],
}),
);
expect(noContext.cappedBy).toContain('unlicensed-deferral');
@ -4496,17 +4771,13 @@ describe('composeReview — convergence-posture deferrals (disclosed, never capp
});
it('an unrecognised severityFloor is unknown — never a throw', () => {
// Round-8 finding: the shape refusal fired unconditionally, so a
// model-transcribed drift ("Critical", "auto ", "") on an ordinary
// zero-deferral round lost the WHOLE composed round over a field that
// changed no output. Unknown folds into the absent state: unlicensed
// (capped, disclosed) with a list, inert without one. Trimmed/cased
// spellings of the three legal values still resolve.
// A model-transcribed drift ("Critical", "auto ", "") on an ordinary
// zero-deferral round must not lose the WHOLE composed round over a
// field that changes no output. Unknown folds into the absent state:
// unlicensed (capped, disclosed) with a list, inert without one.
// Trimmed/cased spellings of the three legal values still resolve.
const withList = composeReview(
base({
severityFloor: 'blocker' as never,
deferredSuggestions: ['a.ts:1 — nit'],
}),
base({ severityFloor: 'blocker' as never, deferredSuggestions: [nit()] }),
);
expect(withList.cappedBy).toContain('unlicensed-deferral');
const inert = composeReview(base({ severityFloor: 'blocker' as never }));
@ -4515,64 +4786,18 @@ describe('composeReview — convergence-posture deferrals (disclosed, never capp
const cased = composeReview(
base({
severityFloor: ' Critical ' as never,
deferredSuggestions: ['a.ts:1 — nit'],
deferredSuggestions: [nit()],
}),
);
expect(cased.cappedBy).toEqual([]);
expect(cased.deferredCount).toBe(1);
});
it('the two deferral regexes agree on separators — one spelling, one verdict', () => {
// Round-6 finding: the deterministic classifier anchored on the em dash
// while the tripwire was separator-agnostic, so the hyphen spelling of
// one [test] finding demanded a verifier that cannot exist and capped
// the very APPROVE the em-dash spelling earned.
const planPath = coveredPlan(['reverse-audit']);
const hyphen = composeReview({
criticalsInline: 0,
suggestionsInline: 0,
planPath,
env: ENV,
modelId: MODEL,
severityFloor: 'critical',
deferredSuggestions: ['a.ts:1 - [test] mutation survivor'],
});
expect(hyphen.cappedBy).toEqual([]);
expect(hyphen.event).toBe('APPROVE');
// Kebab-case paths are this repo's ENFORCED .ts convention, and the
// round-6 negated-class walk stopped at the first path hyphen —
// misclassifying the common shape (round-7 probe). Both separators, and
// the other two deterministic tags, must classify identically.
for (const entry of [
'packages/core/src/my-file.ts:42 — [test] mutation survivor',
'src/my-file.ts:42 - [build] tsc fails on the merge commit',
'a.ts:1 — [probe] sendShellCommand ran twice',
// Round-8 probe: the SKILL-prescribed aggregate line, a leading
// space, and an en dash all classified non-deterministic.
'a.ts:10 (+2 locations) — [test] aggregate survivor',
' a.ts:1 — [test] leading whitespace',
'a.ts:1 [test] en dash',
]) {
const r = composeReview({
criticalsInline: 0,
suggestionsInline: 0,
planPath,
env: ENV,
modelId: MODEL,
severityFloor: 'critical',
deferredSuggestions: [entry],
});
expect(r.cappedBy).toEqual([]);
expect(r.event).toBe('APPROVE');
}
});
it('auto with a recovered previous round licenses the age-rule deferral', () => {
// The round-5 review caught the shipped contradiction: SKILL said
// "carry the RESOLVED floor", so a legal round-3 age deferral arrived
// as 'suggestion' and was refused as the operator's override. The state
// now carries `auto` unresolved and the module licenses it by the round
// it derives itself — this pins the legal rounds-2-5 shape end to end.
// The state carries `auto` unresolved and the module licenses it by the
// round it derives itself — this pins the legal rounds-2-5 shape end to
// end (a round-resolved `suggestion` would have been refused as the
// operator's override — the shipped round-5 regression).
const planPath = coveredPlan(['verify', 'reverse-audit'], {
prNumber: 8255,
fetchedSha: 'deadbeef00112233',
@ -4588,303 +4813,12 @@ describe('composeReview — convergence-posture deferrals (disclosed, never capp
criticalsInline: 0,
suggestionsInline: 0,
severityFloor: 'auto',
deferredSuggestions: ['a.ts:1 — [review] aged-out nit'],
deferredSuggestions: [nit({ title: 'aged-out nit' })],
});
expect(r.cappedBy).toEqual([]);
expect(r.event).toBe('APPROVE');
expect(r.body).toContain('convergence posture (round 3, not a blocker)');
});
it('exactly at the line cap, the verdict line does not claim truncation', () => {
// Mutation-verified boundary: `>` → `>=` printed "truncated" over a body
// that lists every entry, persisting a false record.
const entries = Array.from({ length: 20 }, (_, i) => `f${i}.ts:1 — n${i}`);
const r = composeReview(
base({ severityFloor: 'critical', deferredSuggestions: entries }),
);
expect(r.body).not.toContain('more (see the run report)');
expect(verdictLine(r)).toContain('(listed in the body)');
expect(verdictLine(r)).not.toContain('truncated');
});
it('never cuts a surrogate pair at the per-entry cap', () => {
// Unit 240 landing on the high half of an astral character shipped a
// lone surrogate that serializes as U+FFFD — zh titles ride the list
// untranslated, so the boundary is real input.
// Prefix is exactly 239 UTF-16 units ("a.ts:1 — " is 9), so the pair
// straddles the 240 cut.
const entry = `a.ts:1 — ${'x'.repeat(230)}🎉tail`;
const r = composeReview(
base({ severityFloor: 'critical', deferredSuggestions: [entry] }),
);
const line = r.body.split('\n').find((l) => l.startsWith('- `a.ts:1'))!;
expect(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])/.test(line)).toBe(false);
expect(line.includes('<27>')).toBe(false);
});
it('renders the list on COMMENT and REQUEST_CHANGES alike — no event squeezes it out', () => {
const comment = composeReview(
base({
suggestionsInline: 1,
severityFloor: 'critical',
deferredSuggestions: ['a.ts:1 — nit'],
}),
);
expect(comment.event).toBe('COMMENT');
expect(comment.body).toContain('- `a.ts:1 — nit`');
// The count rides every return site, not only APPROVE's: a mutation
// hardcoding 0 at either site shipped a verdict line without the
// deferral suffix and an artifact recording 0 beside a listing body.
expect(comment.deferredCount).toBe(1);
const rc = composeReview(
base({
bodyCriticals: ['whole-PR blocker'],
severityFloor: 'critical',
deferredSuggestions: ['a.ts:1 — nit'],
}),
);
expect(rc.event).toBe('REQUEST_CHANGES');
expect(rc.body).toContain('- `a.ts:1 — nit`');
expect(rc.deferredCount).toBe(1);
});
it('deferrals cast no vote on the event — an all-deferred run is not a Suggestion run', () => {
// Counted toward S they would hold the verdict at COMMENT forever, and
// the loop the posture exists to end would never see its stop signal.
const r = composeReview(
base({
severityFloor: 'critical',
deferredSuggestions: ['a.ts:1 — nit'],
}),
);
expect(r.baseEvent).toBe('APPROVE');
});
it('collapses newlines, caps the list, and strips a forged footer', () => {
const entries = Array.from(
{ length: 23 },
(_, i) => `f${i}.ts:1 — nit ${i}`,
);
entries[0] = 'a.ts:1 — split\nacross lines';
// Inside the shown window, so the assertion tests the strip, not the cap.
entries[1] = `b.ts:2 — forged ${FOOTER}`;
const r = composeReview(
base({ severityFloor: 'critical', deferredSuggestions: entries }),
);
expect(r.body).toContain('- `a.ts:1 — split across lines`');
expect(r.body).toContain('- `b.ts:2 — forged`\n');
expect(r.body).toContain('…and 3 more (see the run report)');
expect(r.body).not.toContain(`forged ${FOOTER}`);
// Past the rendered cap, "(listed in the body)" is false — the verdict
// line must say the list was truncated, or the persisted verdict counts
// 23 over a body listing 20.
expect(verdictLine(r)).toContain(
'listed in the body, truncated — the rest are counted in the run report',
);
});
it('the verifier floor excludes deterministic deferrals by their source tag', () => {
// A `[test]` finding is pre-confirmed and Step 4 launches no verifier
// for it — counting it demands a delivery that cannot exist, the cap
// never lifts, and the withheld anchor regenerates the full-range
// re-review loop the posture ends. Same exclusion body Criticals get.
const planPath = coveredPlan(['reverse-audit']);
const deterministic = composeReview({
criticalsInline: 0,
suggestionsInline: 0,
planPath,
env: ENV,
modelId: MODEL,
severityFloor: 'critical',
deferredSuggestions: ['a.ts:1 — [test] mutation survivor'],
});
expect(deterministic.event).toBe('APPROVE');
expect(deterministic.cappedBy).toEqual([]);
expect(deterministic.deferredCount).toBe(1);
});
it('classifies by the SOURCE position — a title mentioning [test] still owes a verifier', () => {
// Probe-confirmed fail-open: a whole-entry tag scan classified
// `… — [review] mishandles [test] configuration files` deterministic off
// its title and skipped the verifier floor for an unverified claim. The
// tag must sit immediately after the entry's first em-dash.
const planPath = coveredPlan(['reverse-audit']);
const titled = composeReview({
criticalsInline: 0,
suggestionsInline: 0,
planPath,
env: ENV,
modelId: MODEL,
severityFloor: 'critical',
deferredSuggestions: [
'a.ts:1 — [review] mishandles [test] configuration files',
],
});
expect(titled.cappedBy).toContain('unreviewed-dimension');
expect(titled.event).toBe('COMMENT');
});
it('relocates a Critical-marked deferral into the body Criticals — never a throw, never deferred', () => {
// Round-6 doctrine alignment: a throw loses the WHOLE round, real
// drafted Criticals included. The entry is a Critical by its own
// marker, so it counts toward C, the event blocks, and the round posts.
for (const entry of [
'a.ts:1 — **[Critical]** auth bypass',
'a.ts:1 — Critical: authentication bypass',
'src/auth.ts:88 - Critical: token check',
]) {
const r = composeReview(base({ deferredSuggestions: [entry] }));
expect(r.event).toBe('REQUEST_CHANGES');
expect(r.deferredCount).toBe(0);
expect(r.body).toContain('relocated from the deferral channel');
}
// The relocated blocker rides the machine ledger too — "the findings
// always ride" includes the mis-routed ones, or the next round's ruling
// table loses the id for a round (round-7 finding).
const planPath = coveredPlan(['verify', 'reverse-audit'], {
prNumber: 8255,
fetchedSha: 'deadbeef00112233',
});
const marked = composeReview({
planPath,
env: ENV,
modelId: MODEL,
criticalsInline: 0,
suggestionsInline: 0,
deferredSuggestions: ['a.ts:1 — **[Critical]** auth bypass'],
});
const ledger = parseLedger(marked.body);
expect(ledger?.findings.some((f) => f.sev === 'C')).toBe(true);
// A relocation-only run (no floor echoed) incurs no licence cap — the
// licence keys on the post-split deferred list, and salvage is exactly
// the run relocation exists for (round-8 mutant).
expect(marked.cappedBy).not.toContain('unlicensed-deferral');
});
it('classifies a relocated Critical by source position — a title-borne [test] does not exempt it', () => {
// Round-8 probe: the whole-entry tag scan the model's own body
// Criticals get let a title-borne [test] exempt an unverified relocated
// claim from the floor, and it posted as a blocking RC with no
// verifier. Own describe: base()'s planPath default writes a verify
// record into the shared dir, which would satisfy the very floor this
// proves.
const planPath = coveredPlan(['reverse-audit']);
const titled = composeReview({
criticalsInline: 0,
suggestionsInline: 0,
planPath,
env: ENV,
modelId: MODEL,
deferredSuggestions: [
'a.ts:1 — [review] critical: mishandles [test] configuration files',
],
});
expect(titled.cappedBy).toContain('criticals-unverified');
expect(titled.event).toBe('COMMENT');
// Control: the SAME entry with a source-position [test] tag IS
// deterministic and needs no verifier — relocated, it blocks.
const genuine = composeReview({
criticalsInline: 0,
suggestionsInline: 0,
planPath: coveredPlan(['reverse-audit']),
env: ENV,
modelId: MODEL,
deferredSuggestions: ['a.ts:1 — [test] critical: red on the merge'],
});
expect(genuine.cappedBy).not.toContain('criticals-unverified');
expect(genuine.event).toBe('REQUEST_CHANGES');
// The lookbehind spares hyphenated compounds — the SKILL's own
// "non-Critical" phrasing is a realistic model output, and tripping on
// it would have blocked over a nit.
const compound = composeReview(
base({
severityFloor: 'critical',
deferredSuggestions: [
'a.ts:1 — [review] Non-Critical: drop the unused import',
],
}),
);
expect(compound.event).not.toBe('REQUEST_CHANGES');
expect(compound.deferredCount).toBe(1);
const benign = composeReview(
base({
severityFloor: 'critical',
deferredSuggestions: ['a.ts:1 — critical-path latency nit'],
}),
);
expect(benign.deferredCount).toBe(1);
});
it('refuses a present field of the wrong shape', () => {
expect(() =>
composeReview(base({ deferredSuggestions: 'a.ts' as never })),
).toThrow(/deferredSuggestions/);
});
it('a deferrals-only APPROVE is not low signal, and the verdict line names the deferrals', () => {
// R1-14: the body opener was fixed but `verdictLine` still printed "none
// of the N review agents reported a finding" on exactly the posture's
// canonical end state — agents DID report the findings the same run
// records as deferred. The low-signal premise is "zero findings".
const r = composeReview(
base({
planPath: coveredPlan(['verify', 'reverse-audit'], {
srcDiffLines: 5000,
}),
severityFloor: 'critical',
deferredSuggestions: ['a.ts:1 — nit'],
}),
);
expect(r.event).toBe('APPROVE');
expect(r.lowSignal).toBeNull();
expect(r.deferredCount).toBe(1);
expect(verdictLine(r)).toBe(
'Verdict: Approve — 1 non-Critical finding(s) deferred under the convergence posture (listed in the body)',
);
expect(verdictLine(r)).not.toContain('low signal');
});
it('bounds each entry, not only the entry count — the list must never sink the body', () => {
// Twenty model-written 4,000-char entries would put ~80 KB into a body
// GitHub rejects at 65,536, losing the whole review over its footnote.
const r = composeReview(
base({
severityFloor: 'critical',
deferredSuggestions: [`a.ts:1 — ${'x'.repeat(4000)}`],
}),
);
const listLine = r.body.split('\n').find((l) => l.startsWith('- `a.ts:1'))!;
// '- ' + backticks + 240 units + the truncation ellipsis.
expect(listLine.length).toBeLessThanOrEqual(245);
expect(listLine).toContain('…');
});
it('deferred findings count toward the verifier-delivery floor', () => {
// The deferral list publishes their claims in the body, so a
// deferrals-only run owes a verifier exactly as a posting run does; a
// run with no findings at all still owes none. NOT base(): its planPath
// default calls coveredPlan() unconditionally — the spread replaces the
// value but not the side effect, and the default's `verify` record in
// the shared record dir would satisfy the very floor this test proves.
const planPath = coveredPlan(['reverse-audit']);
const common = {
criticalsInline: 0,
suggestionsInline: 0,
planPath,
env: ENV,
modelId: MODEL,
};
const control = composeReview(common);
expect(control.cappedBy).toEqual([]);
const deferred = composeReview({
...common,
severityFloor: 'critical',
deferredSuggestions: ['a.ts:1 — nit'],
});
expect(deferred.cappedBy).toContain('unreviewed-dimension');
expect(deferred.event).toBe('COMMENT');
expect(deferred.body).toContain('- `a.ts:1 — nit`');
});
});
describe('composeReview — the findings file tag check', () => {

View file

@ -30,7 +30,13 @@ import {
verificationGaps,
TranscriptsUnavailableError,
} from './lib/coverage.js';
import { compressSummary } from './findings.js';
import {
compressSummary,
SEVERITIES,
SOURCES,
type Severity,
type Source,
} from './findings.js';
import {
BUDGET_STOP_PHRASE,
ROUND_CAP_PHRASE,
@ -113,62 +119,157 @@ const MAX_DEFERRED_SUGGESTION_CHARS = 240;
const DETERMINISTIC_TAG_RE = /\[(?:build|test|probe)\]/i;
/**
* The deferral entry's deterministic classification SOURCE POSITION only.
* The entry format is `file:line — [source] title`, and the tag must sit
* immediately after the first em-dash: scanning the whole entry classified
* `a.ts:1 — [review] mishandles [test] configuration files` deterministic
* off its TITLE, which skipped the verifier floor for an unverified claim
* (probe-confirmed fail-open). The position is the FIRST WHITESPACE-FLANKED
* separator after the leading `file:line` token anchored `^\S+\s+`, not a
* negated-class walk: `[^—–-]*` stopped at the first hyphen INSIDE a
* kebab-case path (this repo's enforced .ts convention), misclassifying
* every `packages/.../my-file.ts — [test] …` entry non-deterministic and
* demanding a verifier that cannot exist the self-inflicted permanent
* cap. Separator-agnostic like the Critical tripwire (hyphen/en/em the
* cheapest real spelling drift); the SKILL-prescribed aggregate suffix
* `(+N locations)` between the anchor and the separator is tolerated
* (round-8 probe: the prescribed aggregate line itself was misclassified);
* entries are trimmed before the scan (a leading space defeated the `^`
* anchor); and an entry with no whitespace-flanked separator matches
* nothing: non-deterministic, the fail-closed direction.
* A deferred finding, TYPED. The convergence posture removes findings from
* posting through exactly one channel, and for four review rounds that
* channel was free text re-parsed for provenance it did not carry: a
* separator regex classified deterministic source, a marker regex caught
* mis-routed Criticals, and every round's probe found the spelling each
* regex excluded kebab paths, the SKILL's own aggregate suffix, an en
* dash, `(Critical)`, a title-borne `[test]`. The class closes only by
* carrying the fields: the model already holds `file`/`line`/`source`/
* `severity`/`title` for every finding in the artifact it wrote in Step 6,
* so the entry carries them, `deterministic` derives from `source`, the
* relocation from `severity`, and the rendered `file:line — [source] title`
* is formatting nothing downstream ever parses it back.
*
* Validated at the boundary like every other model-written state field:
* a present entry of the wrong shape is refused (a NaN count is refused
* the same way), because a channel that un-posts findings must not be
* guessed at.
*/
const DETERMINISTIC_DEFERRAL_RE =
/^\S+(?:\s+\([^)]*\))?\s+[—–-]\s*\[(?:build|test|probe)\]/i;
export interface DeferredEntry {
file: string;
line?: number;
/** The finding's source tag — decides deterministic (`build`/`test`/`probe`). */
source: Source;
/**
* The finding's severity. Only `Suggestion` defers; a `Critical` here is
* RELOCATED into the body Criticals (a Critical is never deferred), and a
* `Nice to have` is refused (terminal-only, never publishable).
*/
severity: Severity;
/** One-line claim, rendered inside a code span; a location count may be appended. */
title: string;
/** For a pattern aggregate: how many further locations the finding covers. */
locations?: number;
}
const DETERMINISTIC_SOURCES: ReadonlySet<Source> = new Set([
'build',
'test',
'probe',
]);
/** Render one entry as the human line — formatting only, never re-parsed. */
export function renderDeferredEntry(entry: DeferredEntry): string {
const loc =
entry.line !== undefined ? `${entry.file}:${entry.line}` : entry.file;
const agg =
entry.locations && entry.locations > 0
? ` (+${entry.locations} locations)`
: '';
return `${loc}${agg} — [${entry.source}] ${entry.title}`;
}
function toDeferredEntries(value: unknown): DeferredEntry[] {
if (value === undefined || value === null) return [];
if (!Array.isArray(value)) {
throw new TypeError(
`compose-review: deferredSuggestions must be an array of {file, line?, source, severity, title, locations?} entries, got ${JSON.stringify(value)}`,
);
}
return value.map((raw, i) => {
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
throw new TypeError(
`compose-review: deferredSuggestions[${i}] must be an object {file, line?, source, severity, title, locations?} — a free-text entry is not accepted, the channel is typed`,
);
}
const o = raw as Record<string, unknown>;
const file = typeof o['file'] === 'string' ? o['file'].trim() : '';
const title =
typeof o['title'] === 'string'
? stripReviewFooter(o['title']).trim()
: '';
const source = o['source'];
const severity = o['severity'];
const line = o['line'];
const locations = o['locations'];
if (file === '' || title === '') {
throw new TypeError(
`compose-review: deferredSuggestions[${i}] needs a non-empty file and title`,
);
}
if (typeof source !== 'string' || !SOURCES.includes(source as Source)) {
throw new TypeError(
`compose-review: deferredSuggestions[${i}].source must be one of ${SOURCES.join('|')}, got ${JSON.stringify(source)}`,
);
}
if (
typeof severity !== 'string' ||
!SEVERITIES.includes(severity as Severity)
) {
throw new TypeError(
`compose-review: deferredSuggestions[${i}].severity must be one of ${SEVERITIES.join('|')}, got ${JSON.stringify(severity)}`,
);
}
if (severity === 'Nice to have') {
throw new TypeError(
`compose-review: deferredSuggestions[${i}] is a Nice to have — terminal-only findings are never deferred to the PR; drop it from the state`,
);
}
if (
line !== undefined &&
line !== null &&
(typeof line !== 'number' || !Number.isInteger(line) || line < 1)
) {
throw new TypeError(
`compose-review: deferredSuggestions[${i}].line must be a positive integer when present`,
);
}
if (
locations !== undefined &&
locations !== null &&
(typeof locations !== 'number' ||
!Number.isInteger(locations) ||
locations < 0)
) {
throw new TypeError(
`compose-review: deferredSuggestions[${i}].locations must be a non-negative integer when present`,
);
}
return {
file,
...(typeof line === 'number' ? { line } : {}),
source: source as Source,
severity: severity as Severity,
title,
...(typeof locations === 'number' && locations > 0 ? { locations } : {}),
};
});
}
/**
* The deferral channel's Critical filter, shared by the body composer and
* the ledger marker: entries carrying a Critical marker are RELOCATED into
* the body Criticals (annotated), the rest defer. One split, two readers
* a relocated blocker must ride the machine-ledger work list too, or the
* next round's ruling table loses its id for a round ("the findings always
* ride" includes the ones the model mis-routed).
* The deferral channel's split, shared by the body composer and the ledger
* marker: `Critical` entries are RELOCATED into the body Criticals (a
* Critical is never deferred it counts toward `C`, blocks, and rides the
* machine ledger), the rest defer. One split, two readers, no parsing.
*/
const CRITICAL_DEFERRAL_RE = /\[critical\]|(?<![\w-])critical\s*:/i;
function splitDeferralChannel(raw: unknown): {
deferred: string[];
deferred: DeferredEntry[];
relocated: string[];
/**
* How many relocated entries are DETERMINISTIC by the source-position
* classifier the body composer uses this so a relocated Critical is
* classified by the same position-anchored rule as a deferred one, never
* by the whole-entry tag scan the model's own body Criticals get: a
* title-borne `[test]` in a relocated unverified claim exempted it from
* the floor and posted it as a blocker (round-8 probe).
*/
/** Relocated entries whose `source` is deterministic — no verifier owed. */
relocatedDeterministic: number;
} {
const entries = toStringList(raw, 'deferredSuggestions')
.map((entry) => stripReviewFooter(entry).trim())
.filter((entry) => entry !== '');
const relocatedRaw = entries.filter((x) => CRITICAL_DEFERRAL_RE.test(x));
const entries = toDeferredEntries(raw);
const relocatedEntries = entries.filter((e) => e.severity === 'Critical');
return {
deferred: entries.filter((x) => !CRITICAL_DEFERRAL_RE.test(x)),
relocated: relocatedRaw.map(
(stray) =>
`${stray} _(relocated from the deferral channel — a Critical is never deferred, it posts)_`,
deferred: entries.filter((e) => e.severity !== 'Critical'),
relocated: relocatedEntries.map(
(e) =>
`${renderDeferredEntry(e)} _(relocated from the deferral channel — a Critical is never deferred, it posts)_`,
),
relocatedDeterministic: relocatedRaw.filter((x) =>
DETERMINISTIC_DEFERRAL_RE.test(x),
relocatedDeterministic: relocatedEntries.filter((e) =>
DETERMINISTIC_SOURCES.has(e.source),
).length,
};
}
@ -205,23 +306,20 @@ export interface ComposeReviewInput {
/** Suggestions discarded as unanchorable (offline validation or 422). */
suggestionsDiscarded?: number;
/**
* Non-Critical findings the convergence posture deferred Step 6's
* round-aware posting discipline (from round 6, or under an explicit
* `--severity-floor critical`, and the rounds-2-5 code-age rule). One line
* each, source tag included (`file:line — [source] title` the
* verifier-delivery floor excludes deterministic `[build]`/`[test]`/
* `[probe]` entries by that tag, exactly as it does body Criticals), and
* only **high-confidence Suggestions that
* would otherwise post**: low-confidence and Nice-to-have findings stay
* terminal-only as ever deferral publishes, and must not publish what
* the posting path never would. They are neither drafted inline nor counted
* toward `S` the whole point is that a deferral must not regenerate a
* review round but they must not vanish either: the body renders them as
* a disclosed, NON-capping list, so the record survives on the PR while
* the round stays convergent. A deferral never withholds the ledger
* anchor: it is a posting decision, not unreviewed scope.
* The findings the convergence posture deferred Step 6's round-aware
* posting discipline (from round 6, or under an explicit `--severity-floor
* critical`, and the rounds-2-5 code-age rule). TYPED entries — see
* `DeferredEntry`: only otherwise-postable high-confidence Suggestions
* belong here (a `Critical` is relocated into the body Criticals, a
* `Nice to have` is refused; low-confidence findings stay terminal-only and
* never enter the state). They are neither drafted inline nor counted
* toward `S` a deferral must not regenerate a review round but they
* must not vanish either: the body renders them as a disclosed,
* NON-capping list, so the record survives on the PR while the round
* stays convergent. A deferral never withholds the ledger anchor: it is a
* posting decision, not unreviewed scope.
*/
deferredSuggestions?: string[];
deferredSuggestions?: DeferredEntry[];
/**
* The UNRESOLVED posting floor from the Step 1 verdict (`critical`,
* `suggestion`, or the literal `auto`) never the level `auto` resolved
@ -1167,7 +1265,7 @@ function composeReviewBody(
criticalsInline +
suggestionsInline +
nonDeterministicBodyCriticals +
deferredSuggestions.filter((x) => !DETERMINISTIC_DEFERRAL_RE.test(x))
deferredSuggestions.filter((e) => !DETERMINISTIC_SOURCES.has(e.source))
.length;
const verification = verificationGaps(
input.planPath,
@ -1825,6 +1923,7 @@ function composeReviewBody(
// spare; the findings artifact keeps every entry whole.
const deferredShown = deferredSuggestions
.slice(0, MAX_DEFERRED_SUGGESTION_LINES)
.map(renderDeferredEntry)
.map((entry) => {
const collapsed = entry
.split('\n')

View file

@ -1138,6 +1138,15 @@ describe('latestOwnLedger', () => {
);
expect(absent.recovered).toBeNull();
expect(absent.sawOwnReview).toBe(false);
// Logins compare case-insensitively (GitHub's rule): a case mismatch
// would read "own review exists" as "proven absence" and delete the
// counter (self-audit finding).
const cased = recoverOwnLedger(
[review('Bot', '2026-01-01T00:00:00Z', marker(2))],
'bot',
);
expect(cased.sawOwnReview).toBe(true);
expect(cased.recovered?.ledger.round).toBe(2);
// A PENDING draft is not "seen" either — it is not a submitted review.
const draftOnly = recoverOwnLedger(
[
@ -1264,6 +1273,45 @@ describe('persistRecoveredLedger', () => {
}
});
it('never lowers the round — a stale walk cannot overwrite a newer side file', () => {
// Self-audit finding: a lower-round recovery (a concurrent lane's stale
// list, or a paginated fetch that came back short) overwrote round 7
// with round 2 and dropped the anchor sha. Compare on round, reviewId
// as the tiebreak.
const dir = mkdtempSync(join(tmpdir(), 'prev-ledger-'));
const side = join(dir, 'side.json');
try {
const newer = { ...ledger, round: 7, sha: 'ffff1111', reviewId: 70 };
writeFileSync(side, JSON.stringify(newer));
persistRecoveredLedger(
side,
{
ledger: { ...ledger, round: 2 },
commitId: 'a'.repeat(40),
reviewId: 20,
},
false,
);
expect(JSON.parse(readFileSync(side, 'utf8'))).toEqual(newer);
// Same round, older reviewId: also kept.
persistRecoveredLedger(
side,
{ ledger: { ...ledger, round: 7 }, commitId: null, reviewId: 60 },
false,
);
expect(JSON.parse(readFileSync(side, 'utf8'))).toEqual(newer);
// A genuinely newer recovery still writes.
persistRecoveredLedger(
side,
{ ledger: { ...ledger, round: 8 }, commitId: null, reviewId: 80 },
false,
);
expect(JSON.parse(readFileSync(side, 'utf8')).round).toBe(8);
} finally {
rmSync(dir, { recursive: true, force: true });
}
});
it('a no-recovery run with no side file writes nothing', () => {
const dir = mkdtempSync(join(tmpdir(), 'prev-ledger-'));
const side = join(dir, 'side.json');

View file

@ -697,6 +697,9 @@ export function recoverOwnLedger(
login: string | null,
): { recovered: RecoveredLedger | null; sawOwnReview: boolean } {
if (!login) return { recovered: null, sawOwnReview: false };
// GitHub logins are case-insensitive; a case mismatch here would turn "own
// review exists" into "proven absence" and license deleting the counter.
const me = login.toLowerCase();
let sawOwnReview = false;
let best: {
at: string;
@ -705,7 +708,7 @@ export function recoverOwnLedger(
commitId: string | null;
} | null = null;
for (const r of reviews) {
if (r.user?.login !== login) continue;
if (r.user?.login?.toLowerCase() !== me) continue;
// A PENDING review is an unsubmitted draft — the API serves the caller's
// own drafts in this list — and a draft is not a previous round: a run
// that crashed between creating and submitting one must not hand the
@ -807,6 +810,29 @@ export function persistRecoveredLedger(
};
if (recovered) {
try {
// Never lower the round: a walked review list can be STALE relative
// to a side file another run wrote (a concurrent lane, or a paginated
// fetch that came back short), and overwriting round 7 with round 2
// would drop the anchor sha and rewind the posture clock. Compare on
// `round`, `reviewId` as the tiebreak — both already in the file.
try {
const existing = JSON.parse(readFileSync(sideFilePath, 'utf8')) as {
round?: unknown;
reviewId?: unknown;
};
const exRound =
typeof existing.round === 'number' ? existing.round : -1;
const exId =
typeof existing.reviewId === 'number' ? existing.reviewId : -1;
if (
exRound > recovered.ledger.round ||
(exRound === recovered.ledger.round && exId > recovered.reviewId)
) {
return;
}
} catch {
// No readable existing file: nothing to protect.
}
mkdirSync(dirname(sideFilePath), { recursive: true });
writeAtomic(
JSON.stringify(

View file

@ -1010,7 +1010,15 @@ describe('payload consistency — refuse before GitHub sees it', () => {
...REVIEW.state,
planPath: verifiedPlan(),
severityFloor: 'critical',
deferredSuggestions: ['a.ts:1 — tighten the retry backoff'],
deferredSuggestions: [
{
file: 'a.ts',
line: 1,
source: 'review',
severity: 'Suggestion',
title: 'tighten the retry backoff',
},
],
},
comments: [],
});
@ -1019,7 +1027,9 @@ describe('payload consistency — refuse before GitHub sees it', () => {
expect(ghMock).toHaveBeenCalledOnce();
const sent = JSON.parse(ghMock.mock.calls[0][0] as string);
expect(sent.body).toContain('Deferred under the convergence posture');
expect(sent.body).toContain('- `a.ts:1 — tighten the retry backoff`');
expect(sent.body).toContain(
'- `a.ts:1 — [review] tighten the retry backoff`',
);
});
it('rejects a line that is not a positive whole number', () => {

File diff suppressed because one or more lines are too long

View file

@ -217,11 +217,14 @@ describe('bundled review skill', () => {
expect(body).toContain(
'an unverified claim does not become publishable by being deferred',
);
// ...and the entry format carries the source tag, because the floor's
// deterministic exclusion reads it: dropping the tag from the format
// makes every [test] deferral demand a verifier that cannot exist
// (round-2 review finding).
expect(body).toContain('`file:line — [source] title`');
// ...and the entry is TYPED — one object per finding copied from the
// artifact's own fields, never a sentence: four review rounds of regex
// misses on the free-text form (kebab paths, the aggregate suffix, an
// en dash, a title-borne tag) closed only by carrying the fields.
expect(body).toContain(
"as a **TYPED entry, one object per finding, copied from the artifact's own fields**",
);
expect(body).toContain('never write that line into the state');
// The age command is hostile-input-hardened in both operands (round-1
// review findings: shell injection via unquoted PR-controlled filename;
// glob pathspec matching a sibling file). A "simplify the command"