diff --git a/scripts/resolvers/outside-voice-steps.ts b/scripts/resolvers/outside-voice-steps.ts index 5a1ce2a35..d489c4410 100644 --- a/scripts/resolvers/outside-voice-steps.ts +++ b/scripts/resolvers/outside-voice-steps.ts @@ -176,7 +176,7 @@ ${outsideVoiceInvocation(ctx, { timeoutMs: 540000, nativeAlreadyRequired: true, ${outsideVoiceFor(ctx).id === 'codex' ? 'The Codex backend uses `codex review --base` without a positional prompt: those arguments are mutually exclusive. Never drop --base to resolve an argv error; prompt-only review changes the diff scope.' : 'The Claude Code backend receives the parent-captured base diff, including committed and working-tree changes, because review mode cannot execute git.'} Present output under \`${outsideVoiceFor(ctx).label.toUpperCase()} SAYS (code review):\` inside a \`tool-output\` fence. -Only a completed response with severity tags or an explicit no-findings conclusion establishes the gate. P0/P1 findings (\`[P0]\`/\`[P1]\` or native \`P0:\`/\`P1:\` labels; \`VERDICT: findings\`) → GATE: FAIL. Completed without P0/P1 → GATE: PASS. Refusal, failure, missing markers or \`OUTSIDE_STATUS: unverified\` → GATE: MISSING COVERAGE: no fix question; say so and never report the review clean. +Only a completed response with severity tags or an explicit no-findings conclusion establishes the gate. P0/P1 findings (\`[P0]\`/\`[P1]\` or native \`P0:\`/\`P1:\` labels; \`VERDICT: findings\`) → GATE: FAIL. Completed without P0/P1 → GATE: PASS. Refusal, failure, missing markers or \`OUTSIDE_STATUS: unverified\` → GATE: MISSING COVERAGE; no fix question. If GATE is FAIL, use AskUserQuestion: \`\`\` diff --git a/scripts/resolvers/plan-gates.ts b/scripts/resolvers/plan-gates.ts index 8ec4a099c..95d9be658 100644 --- a/scripts/resolvers/plan-gates.ts +++ b/scripts/resolvers/plan-gates.ts @@ -467,14 +467,13 @@ The plan completion results augment the existing Scope Drift Detection. If a pla - **NOT DONE items** become additional evidence for **MISSING REQUIREMENTS** in the scope drift report. - **Items in the diff that don't match any plan item** become evidence for **SCOPE CREEP** detection. -- **HIGH-impact plan-file discrepancies** trigger AskUserQuestion: - - Show the investigation findings +- **HIGH-impact plan-file discrepancies** trigger AskUserQuestion showing the investigation findings: - Options: A) Stop this review for implementation, B) Continue this review with P1 TODOs, C) Record the items as intentionally dropped - A ends this invocation before code review or implementation. List the missing work; after implementation, start a fresh /review. - B queues the approved TODO changes for Step 5, not this read-only audit. B/C continue to the final Scope Check and Step 2. None of these choices authorizes shipping or waives required verification. - - Spawned or non-interactive runs ask nothing: report REQUIREMENTS MISSING with the items and continue, writing no TODOs and dropping nothing. + - Spawned or non-interactive: report REQUIREMENTS MISSING, continue; record nothing. -This is **INFORMATIONAL** unless HIGH-impact plan-file discrepancies are found (then it gates via AskUserQuestion). +Otherwise the audit is **INFORMATIONAL**. Discrepancies derived only from fallback sources (commit messages, TODOS.md, PR description) never trigger this question, whatever their IMPACT: report them in the Scope Check as lower-confidence missing requirements. diff --git a/scripts/resolvers/qa.ts b/scripts/resolvers/qa.ts index 9b3cd9e7b..130fbb451 100644 --- a/scripts/resolvers/qa.ts +++ b/scripts/resolvers/qa.ts @@ -80,7 +80,7 @@ Without a total time limit, do not create DEADLINE_FILE; announce finite command Stop when scoped contracts are tested or blocked. Clocks/checkpoints use REPORT_DIR; mixed standalone runs use REPORT_DIR/browser and REPORT_DIR/functional, with one final report at REPORT_DIR. Caller paths win. Names used below (quote every path): -- PROBE_DIR: this surface's probe directory from the line above. DEADLINE_FILE: \`PROBE_DIR/deadline.json\`, which exists only for a bounded run. +- PROBE_DIR: this surface's probe directory per the line above. DEADLINE_FILE: \`PROBE_DIR/deadline.json\`, which exists only for a bounded run. - SECONDS: the total probe budget set above. NNN: a fresh three-digit ID (001, 002, ...) for each capture or checkpoint; never reuse one. - DEADLINE_TOOL = \`${quoteSafePath(ctx.paths.binDir)}/gstack-qa-deadline\`, the deadline guard: \`start\` creates DEADLINE_FILE, \`status\` prints \`remainingMs\` and \`expired\`, and \`run\` executes one command, stopping it at the deadline. - EVIDENCE_TOOL = \`${quoteSafePath(ctx.paths.binDir)}/gstack-qa-evidence\`, the functional evidence recorder: \`capture\` runs one command, stores its exit code, stdout and stderr under \`PROBE_DIR/.qa-evidence/NNN/\`, and prints status \`complete\`, \`incomplete\` or \`sensitive\`. @@ -103,7 +103,7 @@ How one probe works: (1) if bounded, check the clock and choose the next command 1. First demonstrate success: output AND durable effects. Guard if bounded; await completion. 2. **Decide whether another probe is needed.** If bounded, run \`bun DEADLINE_TOOL status DEADLINE_FILE\`. - If expired or no safe next probe remains, STOP exploration; write the report, not a checkpoint: go to §4 (annotations, materialize, then Markdown). + If expired or no safe next probe remains, STOP exploration; write the report (§4), not a checkpoint. ${reportOnly ? ` **Classify the last result before copying it.** For public or synthetic observations, retain the entire result unchanged, including owned fixture paths, IDs, hashes and existing credential placeholders. An absolute state path is not itself a secret. @@ -263,7 +263,7 @@ Never install, import cookies or bootstrap tests. Functional-only skips browser **3. Run smoke and plan checks.** Follow the shared Probe loop for smoke checks and replays until the smoke limit. -Then run required plan checks and revalidation, even after smoke expires, using the same procedure but no smoke guard; never reset the clock. Their checkpoints sit beside DEADLINE_FILE; they skip \`DEADLINE_TOOL status DEADLINE_FILE\` and use \`--timeout-ms\`, not \`--deadline DEADLINE_FILE\`. After smoke expiry, step 4c reruns plan checks only; affected smoke checks are not-run. +Then run required plan checks and revalidation, even after smoke expires, using the same procedure but no smoke guard; never reset the clock. Their checkpoints sit beside DEADLINE_FILE; they skip \`DEADLINE_TOOL status DEADLINE_FILE\` and use \`--timeout-ms\`, not \`--deadline DEADLINE_FILE\`. Post-expiry smoke rechecks are not-run, even in 4c. Use finite command timeouts, capped at the caller's remaining time if it has a deadline.${ship ? '' : ' /review sets none; only an invoker-supplied EARLIER_UTC counts.'} Await clock/guard results before acting. When the caller's deadline expires, mark unfinished checks not-run. diff --git a/scripts/resolvers/test-value.ts b/scripts/resolvers/test-value.ts index da4a4e064..ca5544ce9 100644 --- a/scripts/resolvers/test-value.ts +++ b/scripts/resolvers/test-value.ts @@ -129,7 +129,7 @@ function cardRules(mode: TestValueBarMode, skillName: string): string { qa: skillName === 'qa-only' ? 'Put it under each proposed test.' : 'Put it in the 8e.5 record.', audit: 'Read cards from test header comments when present.', }[mode]; - return `Value card: \`Value: protects=<...>; fails_when=<...>; why_new=<...>; seam=none\` (seam: \`none\` or its name); each field at most ${CARD_FIELD_MAX_BYTES} UTF-8 bytes here (clamp to 157 plus \`...\`; JSON records you write keep full values). ${where} A missing upstream card never blocks: derive it; ignore unknown fields. + return `Value card: \`Value: protects=<...>; fails_when=<...>; why_new=<...>; seam=none\` (seam: \`none\` or its name); each field at most ${CARD_FIELD_MAX_BYTES} UTF-8 bytes here (clamp to 157 plus \`...\`; written JSON keeps full values). ${where} A missing upstream card never blocks: derive it; ignore unknown fields. Example: ${EXAMPLE_CARD} ${EXAMPLE_REJECTED}`; diff --git a/test/helpers/carve-guards.ts b/test/helpers/carve-guards.ts index e79093e71..7ff46a36c 100644 --- a/test/helpers/carve-guards.ts +++ b/test/helpers/carve-guards.ts @@ -668,7 +668,7 @@ do not launch the downstream skill or open a browser.`, }, behavioral: 'prompt', maxSkeletonBytes: 63_500, // + v2.0 {{ASIDE_SETUP}}/{{BROWSE_FALLBACK}} (replaces the browse setup block); measured 61_253 - maxSizeRatio: 1.110, // + v1.81 Aside contract + gstack-browser fallback block (1.080 on v1.91.7.0) + the shared test value bar at 8a.5 ({{TEST_VALUE_BAR:qa}}); measured 1.094 + W1 guarded state-root resolution (`eval gstack-paths; : "${GSTACK_STATE_ROOT:?…}"`) in the Context Recovery preamble, the eureka log and each state-writing bash block; measured 1.101 (2026-09-30) + v1.91.12.0 merge of #2999 (await scope/method Reads, capture --after checkpoints, browser-only empty evidence list) with #3002; measured 1.1028 (2026-10-01). + both untrusted-content marker formats in the browser fallback (W7f); measured 1.1035 (2026-10-02). Re-measured 1.1038 after the W8 probe-loop renames (2026-10-03); the cap already matches. + the annotate-every-capture and superseded rule before materialize (a ship-exploratory-late-input run published evidence that omitted older-snapshot captures); measured 1.1064 (2026-10-03). + v1.91.18.0 (#3023) merged into the v1.91.19.0 wave; measured 93,282 / 84,049 = 1.1099 (2026-10-04). + maxSizeRatio: 1.1102, // + v1.81 Aside contract + gstack-browser fallback block (1.080 on v1.91.7.0) + the shared test value bar at 8a.5 ({{TEST_VALUE_BAR:qa}}); measured 1.094 + W1 guarded state-root resolution (`eval gstack-paths; : "${GSTACK_STATE_ROOT:?…}"`) in the Context Recovery preamble, the eureka log and each state-writing bash block; measured 1.101 (2026-09-30) + v1.91.12.0 merge of #2999 (await scope/method Reads, capture --after checkpoints, browser-only empty evidence list) with #3002; measured 1.1028 (2026-10-01). + both untrusted-content marker formats in the browser fallback (W7f); measured 1.1035 (2026-10-02). Re-measured 1.1038 after the W8 probe-loop renames (2026-10-03); the cap already matches. + the annotate-every-capture and superseded rule before materialize (a ship-exploratory-late-input run published evidence that omitted older-snapshot captures); measured 1.1064 (2026-10-03). + v1.91.18.0 (#3023) merged into the v1.91.19.0 wave; measured 93,282 / 84,049 = 1.1099 (2026-10-04). + reliability follow-ups C2 (judge-named gaps after trimming the added wording: feasible 180 s Browser Quick, PROBE_DIR's source line, stop goes to the §4 report, written JSON keeps full card values; lane S's {user}/timing fields); measured 93,305 / 84,049 = 1.11013 (2026-10-04). minUnionBytes: 69_500, // measured union 70,385 // 'aside repl' pins the Aside contract; '$B goto' pins the fallback block in the always-loaded skeleton. mustContain: ['bug', 'aside repl', '$B goto', 'fix', 'Health Score Rubric', 'regression'], diff --git a/test/outside-voice-preflight.test.ts b/test/outside-voice-preflight.test.ts index c56c74097..8930e5017 100644 --- a/test/outside-voice-preflight.test.ts +++ b/test/outside-voice-preflight.test.ts @@ -30,7 +30,8 @@ test('adversarial outside failures retain the required native pass without dupli expect(output).toContain('GATE: MISSING COVERAGE'); // C2: the gate names what missing coverage does instead of an unnamed "existing user decision flow". if (output.includes('Only a completed response with severity tags')) { - expect(output).toContain('→ GATE: MISSING COVERAGE: no fix question; say so and never report the review clean.'); + expect(output).toContain('→ GATE: MISSING COVERAGE; no fix question.'); + expect(outsideVoiceInvocation(ctx)).toContain('missing coverage is never clean/PASS'); expect(output).not.toContain('preserve the existing user decision flow'); } expect(outsideVoiceInvocation(ctx)).toContain("Use the caller's fallback; missing coverage is never clean/PASS."); diff --git a/test/qa-probe-gates.test.ts b/test/qa-probe-gates.test.ts index c275068c5..134cdb4b0 100644 --- a/test/qa-probe-gates.test.ts +++ b/test/qa-probe-gates.test.ts @@ -147,7 +147,7 @@ describe('QA probe entry and checkpoint gates', () => { const write = text.indexOf('**Publish before probing.**'); expect(decision).toBeGreaterThan(-1); expect(decision).toBeLessThan(write); - expect(text.slice(decision, write)).toMatch(/write the report, not a checkpoint/i); + expect(text.slice(decision, write)).toMatch(/write the report\b[^.\n]*, not a checkpoint/i); expect(text.slice(decision, write)).toMatch(/if expired/i); expect(text.slice(decision, write)).not.toContain('If done or blocked'); if (skillName === 'qa-only') { @@ -314,23 +314,23 @@ describe('judge-named QA workflow gaps (C2)', () => { for (const skillName of ['qa', 'qa-only']) { const text = generateQAExploratory(ctx(skillName)); const layout = text.indexOf('mixed standalone runs use REPORT_DIR/browser and REPORT_DIR/functional'); - const probeDir = text.indexOf("PROBE_DIR: this surface's probe directory from the line above."); + const probeDir = text.indexOf("PROBE_DIR: this surface's probe directory per the line above."); expect(layout).toBeGreaterThan(-1); expect(probeDir).toBeGreaterThan(layout); expect(text.slice(layout, probeDir).split('\n')).toHaveLength(3); - expect(text).toContain('write the report, not a checkpoint: go to §4 (annotations, materialize, then Markdown)'); + expect(text).toContain('write the report (§4), not a checkpoint.'); const final = text.indexOf('## 4. Final report'); expect(final).toBeGreaterThan(-1); expect(text.indexOf('annotations.json', final)).toBeLessThan(text.indexOf('materialize PROBE_DIR', final)); } }); - test('/review and /ship: after smoke expiry only plan checks rerun in revalidation', () => { + test('/review and /ship: step 4c revalidation does not rerun smoke checks after smoke expiry', () => { for (const skillName of ['review', 'ship']) { const text = generateQAReview(ctx(skillName)); - expect(text).toContain('After smoke expiry, step 4c reruns plan checks only; affected smoke checks are not-run.'); - expect(text).toContain('c. Re-review changed or uncertain coverage and repeat step 3 for affected checks.'); - expect(text).not.toContain('Post-expiry smoke rechecks are not-run.'); + const rule = text.indexOf('Post-expiry smoke rechecks are not-run, even in 4c.'); + expect(rule).toBeGreaterThan(-1); + expect(rule).toBeLessThan(text.indexOf('c. Re-review changed or uncertain coverage and repeat step 3 for affected checks.')); } }); }); diff --git a/test/review-workflow-clarity.test.ts b/test/review-workflow-clarity.test.ts index 83fe80151..c48238201 100644 --- a/test/review-workflow-clarity.test.ts +++ b/test/review-workflow-clarity.test.ts @@ -318,8 +318,8 @@ test('review finalization ownership: the plan audit retains its high-impact gate expect(flatAudit).toContain('B/C continue to the final Scope Check and Step 2'); expect(flatAudit).toMatch(/none of these choices authorizes shipping or waives required verification/i); // C2: a spawned or non-interactive review cannot answer the question; its default writes and drops nothing. - expect(flatAudit).toContain('Spawned or non-interactive runs ask nothing: report REQUIREMENTS MISSING with the items and continue, writing no TODOs and dropping nothing.'); - expect(flatAudit.indexOf('Spawned or non-interactive runs ask nothing')).toBeLessThan(flatAudit.indexOf('When continuing after the audit')); + expect(flatAudit).toContain('Spawned or non-interactive: report REQUIREMENTS MISSING, continue; record nothing.'); + expect(flatAudit.indexOf('Spawned or non-interactive: report')).toBeLessThan(flatAudit.indexOf('When continuing after the audit')); }); test('review scope notes remain provisional until the plan section emits the only final scope check', () => { diff --git a/test/test-value-bar.test.ts b/test/test-value-bar.test.ts index 99972cd1a..2438abec1 100644 --- a/test/test-value-bar.test.ts +++ b/test/test-value-bar.test.ts @@ -83,9 +83,9 @@ describe('test value bar render contract', () => { expect(Buffer.byteLength(rendered, 'utf8')).toBeLessThanOrEqual(TEST_VALUE_BAR_MAX_BYTES[mode]); }); - test('the card clamp applies to card lines only; JSON records keep full values (C2)', () => { + test('the card clamp applies to card lines only; written JSON keeps full values (C2)', () => { for (const mode of TEST_VALUE_BAR_MODES) { - expect(bar(mode)).toContain(`each field at most ${CARD_FIELD_MAX_BYTES} UTF-8 bytes here (clamp to 157 plus \`...\`; JSON records you write keep full values)`); + expect(bar(mode)).toContain(`each field at most ${CARD_FIELD_MAX_BYTES} UTF-8 bytes here (clamp to 157 plus \`...\`; written JSON keeps full values)`); } });