From b331c48261814b5598195715f71cf73e3c1feb07 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Mon, 4 May 2026 23:28:52 -0400 Subject: [PATCH] test(3072): parse bash blocks for findings probe guard checks --- .../fix-3072-findings-probe-assertions.md | 5 +++ ...72-optional-sketch-findings-guard.test.cjs | 39 +++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 .changeset/fix-3072-findings-probe-assertions.md diff --git a/.changeset/fix-3072-findings-probe-assertions.md b/.changeset/fix-3072-findings-probe-assertions.md new file mode 100644 index 000000000..a959c2a59 --- /dev/null +++ b/.changeset/fix-3072-findings-probe-assertions.md @@ -0,0 +1,5 @@ +--- +type: Fixed +pr: 3119 +--- +**Optional findings probe guard checks now use structured parsing** — regression tests now parse fenced bash blocks and validate sketch/spike findings probes as structured command records, ensuring non-fatal `|| true` guards are enforced without raw source grep assertions. \ No newline at end of file diff --git a/tests/bug-3072-optional-sketch-findings-guard.test.cjs b/tests/bug-3072-optional-sketch-findings-guard.test.cjs index bcee46bae..c22e603d6 100644 --- a/tests/bug-3072-optional-sketch-findings-guard.test.cjs +++ b/tests/bug-3072-optional-sketch-findings-guard.test.cjs @@ -11,6 +11,37 @@ function read(rel) { return fs.readFileSync(path.join(ROOT, rel), 'utf8'); } +function extractFindingsProbesFromBashBlocks(markdown) { + const probes = []; + const fenceRe = /```bash\n([\s\S]*?)```/g; + let fenceMatch; + + while ((fenceMatch = fenceRe.exec(markdown)) !== null) { + const block = fenceMatch[1]; + const baseLine = markdown.slice(0, fenceMatch.index).split('\n').length; + const lines = block.split('\n'); + + lines.forEach((line, idx) => { + if (!line.includes('.claude/skills/')) return; + const kind = line.includes('sketch-findings-*/SKILL.md') + ? 'sketch' + : line.includes('spike-findings-*/SKILL.md') + ? 'spike' + : null; + if (!kind) return; + + probes.push({ + lineNumber: baseLine + idx, + commandText: line.trim(), + kind, + hasNonFatalGuard: /\|\|\s*true/.test(line), + }); + }); + } + + return probes; +} + describe('bug #3072: optional sketch/spike findings probes are non-fatal', () => { test('all sketch/spike findings SKILL.md ls probes include || true', () => { const files = ['ui-phase.md', 'plan-phase.md', 'discuss-phase.md', 'new-project.md']; @@ -18,10 +49,10 @@ describe('bug #3072: optional sketch/spike findings probes are non-fatal', () => for (const file of files) { const content = read(file); - const lines = content.split('\n'); - for (const line of lines) { - if (/\.claude\/skills\/(?:sketch|spike)-findings-\*\/SKILL\.md/.test(line) && !/\|\|\s*true/.test(line)) { - offenders.push(`${file}: ${line.trim()}`); + const probes = extractFindingsProbesFromBashBlocks(content); + for (const probe of probes) { + if (!probe.hasNonFatalGuard) { + offenders.push(`${file}:${probe.lineNumber} ${probe.commandText}`); } } }