diff --git a/test/gen-skill-docs.test.ts b/test/gen-skill-docs.test.ts index 75d63dc77..0293e8c04 100644 --- a/test/gen-skill-docs.test.ts +++ b/test/gen-skill-docs.test.ts @@ -1126,6 +1126,19 @@ describe('TEST_COVERAGE_AUDIT placeholders', () => { expect(spec.toLowerCase()).not.toContain('"yagni"'); }); + // Generated by /ship Step 7 coverage audit (GSTA-96). + // Value: protects=the house rule the simplification specialist quotes from ETHOS.md is still stated in ETHOS.md; + // fails_when=ETHOS.md is reworded and the specialist keeps quoting a rule ETHOS no longer states (this PR first left "If A is 70 lines more, choose A"); + // why_new=the pins above cover the specialist's verdict lines only; no test reads its ETHOS quote against ETHOS.md; + // seam=none; no_claim=that the reviewer applies the rule (an eval question) + test('simplification specialist quotes a house rule ETHOS.md still states', () => { + const spec = fs.readFileSync(path.join(ROOT, 'review', 'specialists', 'simplification.md'), 'utf-8'); + const quotes = [...spec.matchAll(/house rule is "([^"]+)" \(ETHOS\.md\)/g)].map(m => m[1]!); + expect(quotes.length).toBeGreaterThan(0); + const ethos = fs.readFileSync(path.join(ROOT, 'ETHOS.md'), 'utf-8').toLowerCase().replace(/\*\*/g, ''); + for (const quote of quotes) expect(ethos, quote).toContain(quote.toLowerCase()); + }); + test('each specialist file has standard header with scope and output format', () => { const specDir = path.join(ROOT, 'review', 'specialists'); const files = fs.readdirSync(specDir).filter(f => f.endsWith('.md')); diff --git a/test/host-conformance.test.ts b/test/host-conformance.test.ts index 0fade5517..6b8de314e 100644 --- a/test/host-conformance.test.ts +++ b/test/host-conformance.test.ts @@ -212,6 +212,24 @@ describe.skipIf(process.platform === 'win32')('host conformance kit', () => { expect(status.stdout).toMatch(/codex global: router is a symlink, which Codex skips\. Fix: cd \S+ && \.\/setup --host codex; section links: [1-9]\d* of \d+ broken \(first: [^)]*\)\. Fix: cd /); }, 60_000); + // Generated by /ship Step 7 coverage audit (GSTA-96). + // Value: protects=--status still checks bare sections/ pointers in a carved Claude skill whose installed sections/ is a directory symlink; + // fails_when=the carved-skill probe stops following a symlinked sections/ dir, so a missing section reads as "all resolve"; + // why_new=the all-host status check above asserts external hosts only; the C3 case breaks a codex pointer that names its skill; + // seam=none; no_claim=non-Linux link layouts (Windows copies real files; this suite skips win32) + test('--status checks a carved Claude skill\'s bare section pointers through its symlinked sections/', () => { + const f = makeFixture(); + const src = makeSource(f, join(f.dir, 'gstack')); + expect(runSetup(f, join(src, 'setup'), ['--host', 'claude']).status).toBe(0); + const sections = join(f.home, '.claude/skills/autoplan/sections'); + expect(lstatSync(sections).isSymbolicLink(), sections).toBe(true); + expect(readFileSync(join(f.home, '.claude/skills/autoplan/SKILL.md'), 'utf8')).toContain('Read `sections/unattended.md`, end here'); + rmSync(join(src, 'autoplan/sections/unattended.md')); + const status = runSetup(f, join(src, 'setup'), ['--status']); + expect(status.status, status.stdout + status.stderr).toBe(1); + expect(status.stdout).toMatch(/claude global: [^\n]*section links: [1-9]\d* of \d+ broken \(first: autoplan\/SKILL\.md names sections\/unattended\.md\)/); + }, 60_000); + test('upgrade from the previous release layout refreshes every installed host (#1925)', () => { const f = makeFixture(); const src = makeSource(f, join(f.home, '.claude/skills/gstack'), '1.91.12.0');