From 356f7cb6ce28d8d5fbdffcda76c5291c697337bc Mon Sep 17 00:00:00 2001 From: garrytan Date: Sat, 3 Oct 2026 01:05:00 +0000 Subject: [PATCH] test(W7c): ship-reentry-gates keeps stop/consent meaning, drops sentence pins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit W7c (t08 F3). One test file. Removed assertion groups (invariant -> remaining protection): - "Retain queued fixes and restore coverage" -> a missing reviewer keeps queued fixes: gate E2E ship-skipped-queued-finding (gate) and judge `ship/SKILL.md workflow` (gate lane); the stop itself stays a free check. - qa decline print line ("Test bootstrap previously declined — skipping.") -> qa skips bootstrap after a saved decline: kept free meaning check `/BOOTSTRAP_DECLINED ... skip the rest of bootstrap/i`. - "with the user's free-text evidence" -> UNVERIFIABLE items are confirmed one by one: kept free `/per-item confirmation/i` here plus the blanket-confirm check in ship-plan-completion-invariants. - Docs reentry sentences ("On reentry, reuse only ...", its duplicate, "base/ input hashes still match", "retain its actual status and scope", "Otherwise use Blocked recovery ...") -> reentry reuses only this invocation's validated evidence: kept free checks "never reuse an audit across invocations" and "reentry never resets the count or authorizes a launch"; gate E2Es ship-docsync-stale-before, ship-docsync-stale-after, ship-docsync-recovery, ship-docsync-late-result (all gate). - "left uncommitted after Step 15" -> late commits take only remaining changes: kept free "never create an empty commit" and "preserve unrelated user files"; gate E2E ship-local-workflow (gate). Loosened to case-insensitive meaning checks (stop/consent/approval lines, always kept): STOP blocks advancement, answer each AskUserQuestion, routine authorization never waives gates, dispatched-reviewer-missing -> stop, CYCLES >= 3 -> stop, Any N -> stop as NOT DONE, resume only after verified, no second deferral, per-item confirmation, explicit Add-tests choice overrides the decline for this invocation only, framework approval, first entry launches the audit, no third attempt, unwaivable redaction/security gates. Kept unchanged: bootstrap branch order, no `rm` in the decline branch, the Step 14 -> 14.5 routing line, stale-string bans, `NEW_TITLE` field and `--title "$NEW_TITLE"`. Cosmetic pins dropped: STOP capitals, "mandatory". Mutation log: .context/mutation-ship-reentry-gates.md (17 mutations, 0 unexpected: 1 reorder, 2 marker and 9 safety mutations red; 5 rewordings green). --- test/ship-reentry-gates.test.ts | 54 ++++++++++++++------------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/test/ship-reentry-gates.test.ts b/test/ship-reentry-gates.test.ts index 3a729a2bb..6f6c5cb8e 100644 --- a/test/ship-reentry-gates.test.ts +++ b/test/ship-reentry-gates.test.ts @@ -8,69 +8,61 @@ const read = (file: string) => readFileSync(new URL(`../${file}`, import.meta.ur const compact = (text: string) => text.replace(/\s+/g, ' '); const ctx = { host: 'claude' as const, skillName: 'ship', tmplPath: '', paths: HOST_PATHS.claude }; -test('ship STOP blocks advancement while retaining the stated repair route', () => { +test('ship stop lines block advancement while retaining the stated repair route', () => { const entry = compact(read('ship/SKILL.md.tmpl')); - expect(entry).toContain('STOP blocks advancement until the stated repair/resume route clears; without one, end this attempt'); - expect(entry).toContain('Answer each AskUserQuestion before continuing'); + expect(entry).toMatch(/stop blocks advancement until the stated repair\/resume route clears; without one, end this attempt/i); + expect(entry).toMatch(/answer each AskUserQuestion before continuing/i); + expect(entry).toMatch(/routine authorization never waives those gates or their required user decisions/i); const review = compact(read('ship/sections/review-army.md.tmpl')); - expect(review).toContain('**Dispatched reviewer output missing:** STOP'); - expect(review).toContain('Retain queued fixes and restore coverage'); - expect(review).toContain('**Third fixing cycle reached (`CYCLES >= 3`):** STOP'); - expect(entry).toContain('Routine authorization never waives those gates or their required user decisions'); + expect(review).toMatch(/\*\*Dispatched reviewer output missing:\*\* stop\b/i); + expect(review).toMatch(/\*\*Third fixing cycle reached \(`CYCLES >= 3`\):\*\* stop\b/i); }); test('a new ship bootstrap choice overrides only the saved decline, not framework selection', () => { const ship = generateTestBootstrap(ctx); const decline = ship.slice(ship.indexOf('**If BOOTSTRAP_DECLINED**'), ship.indexOf('**If NO ecosystem marker matched:**')); - expect(compact(decline)).toContain("Step 5's explicit Add tests choice overrides that marker for this invocation only"); - expect(compact(decline)).toContain('continue to runtime detection and B2–B3, including framework approval'); + expect(compact(decline)).toMatch(/explicit Add tests choice overrides that marker for this invocation only/i); + expect(compact(decline)).toMatch(/including framework approval/i); expect(decline).not.toContain('rm '); expect(ship.indexOf('**If ANY existing-test evidence appears**')).toBeLessThan(ship.indexOf('**If BOOTSTRAP_DECLINED**')); const qa = generateTestBootstrap({ ...ctx, skillName: 'qa' }); - expect(qa).toContain('**If BOOTSTRAP_DECLINED** appears: Print "Test bootstrap previously declined — skipping." **Skip the rest of bootstrap.**'); + expect(compact(qa)).toMatch(/BOOTSTRAP_DECLINED[^\n]{0,120}skip the rest of bootstrap/i); expect(qa).not.toContain("Step 5's explicit Add tests choice"); }); test('an unverified item answered not done enters the existing decision before continuing', () => { const gate = generatePlanCompletionGateShip(ctx); const exits = compact(gate.slice(gate.indexOf('**Exit conditions:**'), gate.indexOf('**Cap.**'))); - expect(exits).toContain('Any N: STOP and report that item as NOT DONE'); - expect(exits).toContain('Resume only after its required work is verified'); - expect(exits).toContain('no second deferral choice'); + expect(exits).toMatch(/any N: stop and report that item as NOT DONE/i); + expect(exits).toMatch(/resume only after its required work is verified/i); + expect(exits).toMatch(/no second deferral choice/i); expect(exits).not.toContain('re-running /ship'); - expect(gate).toContain('Per-item confirmation is mandatory'); - expect(gate).toContain('with the user\'s free-text evidence'); + expect(gate).toMatch(/per-item confirmation/i); }); test('docs reentry distinguishes the initial audit from same-invocation accepted evidence', () => { const docs = compact(read('ship/sections/documentation.md.tmpl')); const entry = docs.slice(0, docs.indexOf('## Prepare the candidate')); - expect(entry).toContain('First entry always launches the initial audit'); - expect(entry).toContain('On reentry, reuse only this invocation\'s validated audit or named-risk decision'); - expect(entry).toContain('Reentry never resets the count or authorizes a launch'); - expect(entry).toContain("this invocation's validated audit or named-risk decision"); - expect(entry).toContain('base/input hashes still match'); - expect(entry).toContain('retain its actual status and scope'); - expect(entry).toContain('Otherwise use Blocked recovery, not an unconditional launch'); - expect(docs).toContain('never a third attempt, even after Step 16 changes'); - expect(docs).toContain('never reuse an audit across invocations'); - expect(docs).toContain('Unconfirmed writers, ownership violations, unauthorized Git mutation and redaction/security gates cannot be waived'); + expect(entry).toMatch(/first entry always launches the initial audit/i); + expect(entry).toMatch(/reentry never resets the count or authorizes a launch/i); + expect(docs).toMatch(/never a third attempt/i); + expect(docs).toMatch(/never reuse an audit across invocations/i); + expect(docs).toMatch(/unauthorized Git mutation and redaction\/security gates cannot be waived/i); }); test('late behavioral repairs rebuild before the docs decision and commit only remaining changes', () => { const entry = compact(read('ship/SKILL.md.tmpl')); expect(entry).toContain('A range ending at Step 14 does not enter Step 14.5'); - expect(entry).toContain('rebuild and compare again before stage 3 decides documentation freshness'); + expect(entry).toMatch(/rebuild and compare again before stage 3/i); const commit = entry.slice(entry.indexOf('### 5. Report, then push'), entry.indexOf('## Step 17:')); - expect(commit).toContain('left uncommitted after Step 15'); - expect(commit).toContain('never create an empty commit'); - expect(commit).toContain('Preserve unrelated user files'); + expect(commit).toMatch(/never create an empty commit/i); + expect(commit).toMatch(/preserve unrelated user files/i); }); test('the title is prefixed once before the exact scanned value is published', () => { const body = compact(read('ship/sections/pr-body.md.tmpl')); - expect(body).toContain("Use Step 18's `NEW_TITLE` unchanged; its version prefix is already present"); + expect(body).toContain("Step 18's `NEW_TITLE` unchanged"); expect(body).not.toContain('`NEW_TITLE`, prefixed with'); expect(body).toContain('--title "$NEW_TITLE"'); - expect(body).toContain('the same scanned `NEW_TITLE`'); + expect(body).toContain('scanned `NEW_TITLE`'); });