Merge pull request #3119 from gsd-build/fix/3072-gsd-sdk-query-resolve-model-error-when-i
fix: guard optional sketch-findings probes from non-zero ls exits
This commit is contained in:
5
.changeset/fix-3072-findings-probe-assertions.md
Normal file
5
.changeset/fix-3072-findings-probe-assertions.md
Normal file
@@ -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.
|
||||
@@ -244,8 +244,8 @@ For each CONTEXT.md read: extract `<decisions>` (locked preferences), `<specific
|
||||
|
||||
**Spike/sketch findings:** Check for project-local skills:
|
||||
```bash
|
||||
SPIKE_FINDINGS=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1)
|
||||
SKETCH_FINDINGS=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1)
|
||||
SPIKE_FINDINGS=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1 || true)
|
||||
SKETCH_FINDINGS=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1 || true)
|
||||
RAW_SPIKES=$(ls .planning/spikes/MANIFEST.md 2>/dev/null)
|
||||
RAW_SKETCHES=$(ls .planning/sketches/MANIFEST.md 2>/dev/null)
|
||||
```
|
||||
|
||||
@@ -253,10 +253,10 @@ Check for existing spike and sketch work that should inform project setup:
|
||||
|
||||
```bash
|
||||
# Check for spike findings skill (project-local)
|
||||
SPIKE_SKILL=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1)
|
||||
SPIKE_SKILL=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1 || true)
|
||||
|
||||
# Check for sketch findings skill (project-local)
|
||||
SKETCH_SKILL=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1)
|
||||
SKETCH_SKILL=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1 || true)
|
||||
|
||||
# Check for raw spikes/sketches in .planning/
|
||||
HAS_SPIKES=$(ls .planning/spikes/MANIFEST.md 2>/dev/null)
|
||||
|
||||
@@ -668,8 +668,8 @@ REVIEWS_PATH=$(_gsd_field "$INIT" reviews_path)
|
||||
PATTERNS_PATH=$(_gsd_field "$INIT" patterns_path)
|
||||
|
||||
# Detect spike/sketch findings skills (project-local)
|
||||
SPIKE_FINDINGS_PATH=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1)
|
||||
SKETCH_FINDINGS_PATH=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1)
|
||||
SPIKE_FINDINGS_PATH=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1 || true)
|
||||
SKETCH_FINDINGS_PATH=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1 || true)
|
||||
```
|
||||
|
||||
## 7.5. Verify Nyquist Artifacts
|
||||
|
||||
@@ -31,7 +31,7 @@ Parse JSON for: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded
|
||||
|
||||
Detect sketch findings:
|
||||
```bash
|
||||
SKETCH_FINDINGS_PATH=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1)
|
||||
SKETCH_FINDINGS_PATH=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1 || true)
|
||||
```
|
||||
|
||||
Resolve UI agent models:
|
||||
|
||||
62
tests/bug-3072-optional-sketch-findings-guard.test.cjs
Normal file
62
tests/bug-3072-optional-sketch-findings-guard.test.cjs
Normal file
@@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const ROOT = path.join(__dirname, '..', 'get-shit-done', 'workflows');
|
||||
|
||||
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'];
|
||||
const offenders = [];
|
||||
|
||||
for (const file of files) {
|
||||
const content = read(file);
|
||||
const probes = extractFindingsProbesFromBashBlocks(content);
|
||||
for (const probe of probes) {
|
||||
if (!probe.hasNonFatalGuard) {
|
||||
offenders.push(`${file}:${probe.lineNumber} ${probe.commandText}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert.deepStrictEqual(offenders, [], `missing non-fatal guard on optional findings probe:\n${offenders.join('\n')}`);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user