mirror of
https://github.com/glittercowboy/get-shit-done
synced 2026-04-25 17:25:23 +02:00
* fix(#2636): surface gsd-sdk query failures and add workflow↔handler parity check Root cause: workflows invoked `gsd-sdk query agent-skills <slug>` with a trailing `2>/dev/null`, swallowing stderr and exit code. When the installed `@gsd-build/sdk` npm was stale (pre-query), the call resolved to an empty string and `agent_skills.<slug>` config was never injected into spawn prompts — silently. The handler exists on main (sdk/src/query/skills.ts), so this is a publish-drift + silent-fallback bug, not a missing handler. Fix: - Remove bare `2>/dev/null` from every `gsd-sdk query agent-skills …` invocation in workflows so SDK failures surface to stderr. - Apply the same rule to other no-fallback calls (audit-open, write-profile, generate-* profile handlers, frontmatter.get in commands). Best-effort cleanup calls (config-set workflow._auto_chain_active false) keep exit-code forgiveness via `|| true` but no longer suppress stderr. Parity tests: - New: tests/bug-2636-gsd-sdk-query-silent-swallow.test.cjs — fails if any `gsd-sdk query agent-skills … 2>/dev/null` is reintroduced. - Existing: tests/gsd-sdk-query-registry-integration.test.cjs already asserts every workflow noun resolves to a registered handler; confirmed passing post-change. Note: npm republish of @gsd-build/sdk is a separate release concern and is not included in this PR. * fix(#2636): address review — restore broken markdown fences and shell syntax The previous commit's mass removal of '2>/dev/null' suffixes also collapsed adjacent closing code fences and 'fi' tokens onto the command line, producing malformed markdown blocks and 'truefi' / 'true fi' shell syntax errors in the workflows. Repaired sites: - commands/gsd/quick.md, thread.md (frontmatter.get fences) - workflows/complete-milestone.md (audit-open fence) - workflows/profile-user.md (write-profile + generate-* fences) - workflows/verify-work.md (audit-open --json fence) - workflows/execute-phase.md (truefi -> true / fi) - workflows/plan-phase.md, discuss-phase-assumptions.md, discuss-phase/modes/chain.md (true fi -> true / fi) All 5450 tests pass.
This commit is contained in:
@@ -71,7 +71,7 @@ For each directory found:
|
||||
- Check if PLAN.md exists
|
||||
- Check if SUMMARY.md exists; if so, read `status` from its frontmatter via:
|
||||
```bash
|
||||
gsd-sdk query frontmatter.get .planning/quick/{dir}/SUMMARY.md status 2>/dev/null
|
||||
gsd-sdk query frontmatter.get .planning/quick/{dir}/SUMMARY.md status
|
||||
```
|
||||
- Determine directory creation date: `stat -f "%SB" -t "%Y-%m-%d"` (macOS) or `stat -c "%w"` (Linux); fall back to the date prefix in the directory name (format: `YYYYMMDD-` prefix)
|
||||
- Derive display status:
|
||||
|
||||
@@ -38,7 +38,7 @@ ls .planning/threads/*.md 2>/dev/null
|
||||
For each thread file found:
|
||||
- Read frontmatter `status` field via:
|
||||
```bash
|
||||
gsd-sdk query frontmatter.get .planning/threads/{file} status 2>/dev/null
|
||||
gsd-sdk query frontmatter.get .planning/threads/{file} status
|
||||
```
|
||||
- If frontmatter `status` field is missing, fall back to reading markdown heading `## Status: OPEN` (or IN PROGRESS / RESOLVED) from the file body
|
||||
- Read frontmatter `updated` field for the last-updated date
|
||||
|
||||
@@ -18,7 +18,7 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.milestone-op)
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_CHECKER=$(gsd-sdk query agent-skills gsd-integration-checker 2>/dev/null)
|
||||
AGENT_SKILLS_CHECKER=$(gsd-sdk query agent-skills gsd-integration-checker)
|
||||
```
|
||||
|
||||
Extract from init JSON: `milestone_version`, `milestone_name`, `phase_count`, `completed_phases`, `commit_docs`.
|
||||
|
||||
@@ -41,7 +41,7 @@ When a milestone completes:
|
||||
Before proceeding with milestone close, run the comprehensive open artifact audit.
|
||||
|
||||
```bash
|
||||
gsd-sdk query audit-open 2>/dev/null
|
||||
gsd-sdk query audit-open
|
||||
```
|
||||
|
||||
If the output contains open items (any section with count > 0):
|
||||
|
||||
@@ -87,7 +87,7 @@ This runs in parallel - all gaps investigated simultaneously.
|
||||
**Load agent skills:**
|
||||
|
||||
```bash
|
||||
AGENT_SKILLS_DEBUGGER=$(gsd-sdk query agent-skills gsd-debugger 2>/dev/null)
|
||||
AGENT_SKILLS_DEBUGGER=$(gsd-sdk query agent-skills gsd-debugger)
|
||||
EXPECTED_BASE=$(git rev-parse HEAD)
|
||||
```
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ Phase number from argument (required).
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.phase-op "${PHASE}")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_ANALYZER=$(gsd-sdk query agent-skills gsd-assumptions-analyzer 2>/dev/null)
|
||||
AGENT_SKILLS_ANALYZER=$(gsd-sdk query agent-skills gsd-assumptions-analyzer)
|
||||
```
|
||||
|
||||
Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`,
|
||||
@@ -619,7 +619,7 @@ Check for auto-advance trigger:
|
||||
2. Sync chain flag:
|
||||
```bash
|
||||
if [[ ! "$ARGUMENTS" =~ --auto ]]; then
|
||||
gsd-sdk query config-set workflow._auto_chain_active false 2>/dev/null
|
||||
gsd-sdk query config-set workflow._auto_chain_active false || true
|
||||
fi
|
||||
```
|
||||
3. Read consolidated auto-mode (`active` = chain flag OR user preference):
|
||||
|
||||
@@ -111,7 +111,7 @@ Phase number from argument (required).
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.phase-op "${PHASE}")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_ADVISOR=$(gsd-sdk query agent-skills gsd-advisor-researcher 2>/dev/null)
|
||||
AGENT_SKILLS_ADVISOR=$(gsd-sdk query agent-skills gsd-advisor-researcher)
|
||||
```
|
||||
|
||||
Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_plans`, `has_verification`, `plan_count`, `roadmap_exists`, `planning_exists`, `response_language`.
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
(the user's persistent settings preference):
|
||||
```bash
|
||||
if [[ ! "$ARGUMENTS" =~ --auto ]] && [[ ! "$ARGUMENTS" =~ --chain ]]; then
|
||||
gsd-sdk query config-set workflow._auto_chain_active false 2>/dev/null
|
||||
gsd-sdk query config-set workflow._auto_chain_active false || true
|
||||
fi
|
||||
```
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ Load docs-update context:
|
||||
```bash
|
||||
INIT=$(gsd-sdk query docs-init)
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS=$(gsd-sdk query agent-skills gsd-doc-writer 2>/dev/null)
|
||||
AGENT_SKILLS=$(gsd-sdk query agent-skills gsd-doc-writer)
|
||||
```
|
||||
|
||||
Extract from init JSON:
|
||||
|
||||
@@ -69,7 +69,7 @@ Load all context in one call:
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.execute-phase "${PHASE_ARG}")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS=$(gsd-sdk query agent-skills gsd-executor 2>/dev/null)
|
||||
AGENT_SKILLS=$(gsd-sdk query agent-skills gsd-executor)
|
||||
```
|
||||
|
||||
Parse JSON for: `executor_model`, `verifier_model`, `commit_docs`, `parallelization`, `branching_strategy`, `branch_name`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `plans`, `incomplete_plans`, `plan_count`, `incomplete_count`, `state_exists`, `roadmap_exists`, `phase_req_ids`, `response_language`.
|
||||
@@ -130,7 +130,7 @@ inline path for each plan.
|
||||
```bash
|
||||
# REQUIRED: prevents stale auto-chain from previous --auto runs
|
||||
if [[ ! "$ARGUMENTS" =~ --auto ]]; then
|
||||
gsd-sdk query config-set workflow._auto_chain_active false 2>/dev/null
|
||||
gsd-sdk query config-set workflow._auto_chain_active false || true
|
||||
fi
|
||||
```
|
||||
</step>
|
||||
@@ -1339,7 +1339,7 @@ spawn template, and the two `workflow.drift_*` config keys.
|
||||
Verify phase achieved its GOAL, not just completed tasks.
|
||||
|
||||
```bash
|
||||
VERIFIER_SKILLS=$(gsd-sdk query agent-skills gsd-verifier 2>/dev/null)
|
||||
VERIFIER_SKILLS=$(gsd-sdk query agent-skills gsd-verifier)
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
@@ -45,7 +45,7 @@ First load the mapper agent's skill bundle (the executor's `AGENT_SKILLS`
|
||||
from step `init_context` is for `gsd-executor`, not the mapper):
|
||||
|
||||
```bash
|
||||
AGENT_SKILLS_MAPPER=$(gsd-sdk query agent-skills gsd-codebase-mapper 2>/dev/null || true)
|
||||
AGENT_SKILLS_MAPPER=$(gsd-sdk query agent-skills gsd-codebase-mapper)
|
||||
```
|
||||
|
||||
Then spawn `gsd-codebase-mapper` agents with the `--paths` hint:
|
||||
|
||||
@@ -71,7 +71,7 @@ Load codebase mapping context:
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.map-codebase)
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_MAPPER=$(gsd-sdk query agent-skills gsd-codebase-mapper 2>/dev/null)
|
||||
AGENT_SKILLS_MAPPER=$(gsd-sdk query agent-skills gsd-codebase-mapper)
|
||||
```
|
||||
|
||||
Extract from init JSON: `mapper_model`, `commit_docs`, `codebase_dir`, `existing_maps`, `has_maps`, `codebase_dir_exists`, `subagent_timeout`, `date`.
|
||||
|
||||
@@ -220,9 +220,9 @@ gsd-sdk query commit "docs: start milestone v[X.Y] [Name]" .planning/PROJECT.md
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.new-milestone)
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_RESEARCHER=$(gsd-sdk query agent-skills gsd-project-researcher 2>/dev/null)
|
||||
AGENT_SKILLS_SYNTHESIZER=$(gsd-sdk query agent-skills gsd-research-synthesizer 2>/dev/null)
|
||||
AGENT_SKILLS_ROADMAPPER=$(gsd-sdk query agent-skills gsd-roadmapper 2>/dev/null)
|
||||
AGENT_SKILLS_RESEARCHER=$(gsd-sdk query agent-skills gsd-project-researcher)
|
||||
AGENT_SKILLS_SYNTHESIZER=$(gsd-sdk query agent-skills gsd-research-synthesizer)
|
||||
AGENT_SKILLS_ROADMAPPER=$(gsd-sdk query agent-skills gsd-roadmapper)
|
||||
```
|
||||
|
||||
Extract from init JSON: `researcher_model`, `synthesizer_model`, `roadmapper_model`, `commit_docs`, `research_enabled`, `current_milestone`, `project_exists`, `roadmap_exists`, `latest_completed_milestone`, `phase_dir_count`, `phase_archive_path`, `agents_installed`, `missing_agents`.
|
||||
|
||||
@@ -59,9 +59,9 @@ The document should describe what you want to build.
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.new-project)
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_RESEARCHER=$(gsd-sdk query agent-skills gsd-project-researcher 2>/dev/null)
|
||||
AGENT_SKILLS_SYNTHESIZER=$(gsd-sdk query agent-skills gsd-research-synthesizer 2>/dev/null)
|
||||
AGENT_SKILLS_ROADMAPPER=$(gsd-sdk query agent-skills gsd-roadmapper 2>/dev/null)
|
||||
AGENT_SKILLS_RESEARCHER=$(gsd-sdk query agent-skills gsd-project-researcher)
|
||||
AGENT_SKILLS_SYNTHESIZER=$(gsd-sdk query agent-skills gsd-research-synthesizer)
|
||||
AGENT_SKILLS_ROADMAPPER=$(gsd-sdk query agent-skills gsd-roadmapper)
|
||||
```
|
||||
|
||||
Parse JSON for: `researcher_model`, `synthesizer_model`, `roadmapper_model`, `commit_docs`, `project_exists`, `has_codebase_map`, `planning_exists`, `has_existing_code`, `has_package_file`, `is_brownfield`, `needs_codebase_map`, `has_git`, `project_path`, `agents_installed`, `missing_agents`.
|
||||
|
||||
@@ -33,9 +33,9 @@ Load all context in one call (paths only to minimize orchestrator context):
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.plan-phase "$PHASE")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_RESEARCHER=$(gsd-sdk query agent-skills gsd-phase-researcher 2>/dev/null)
|
||||
AGENT_SKILLS_PLANNER=$(gsd-sdk query agent-skills gsd-planner 2>/dev/null)
|
||||
AGENT_SKILLS_CHECKER=$(gsd-sdk query agent-skills gsd-plan-checker 2>/dev/null)
|
||||
AGENT_SKILLS_RESEARCHER=$(gsd-sdk query agent-skills gsd-phase-researcher)
|
||||
AGENT_SKILLS_PLANNER=$(gsd-sdk query agent-skills gsd-planner)
|
||||
AGENT_SKILLS_CHECKER=$(gsd-sdk query agent-skills gsd-plan-checker)
|
||||
CONTEXT_WINDOW=$(gsd-sdk query config-get context_window 2>/dev/null || echo "200000")
|
||||
TDD_MODE=$(gsd-sdk query config-get workflow.tdd_mode 2>/dev/null || echo "false")
|
||||
```
|
||||
@@ -1470,7 +1470,7 @@ Check for auto-advance trigger using values already loaded in step 1:
|
||||
3. **Sync chain flag with intent** — if user invoked manually (no `--auto` and no `--chain`), clear the ephemeral chain flag from any previous interrupted `--auto` chain. This does NOT touch `workflow.auto_advance` (the user's persistent settings preference):
|
||||
```bash
|
||||
if [[ ! "$ARGUMENTS" =~ --auto ]] && [[ ! "$ARGUMENTS" =~ --chain ]]; then
|
||||
gsd-sdk query config-set workflow._auto_chain_active false 2>/dev/null
|
||||
gsd-sdk query config-set workflow._auto_chain_active false || true
|
||||
fi
|
||||
```
|
||||
|
||||
|
||||
@@ -271,7 +271,7 @@ Write updated analysis JSON back to `$ANALYSIS_PATH`.
|
||||
Display: "◆ Writing profile..."
|
||||
|
||||
```bash
|
||||
gsd-sdk query write-profile --input "$ANALYSIS_PATH" --json 2>/dev/null
|
||||
gsd-sdk query write-profile --input "$ANALYSIS_PATH" --json
|
||||
```
|
||||
|
||||
Display: "✓ Profile written to $HOME/.claude/get-shit-done/USER-PROFILE.md"
|
||||
@@ -350,7 +350,7 @@ Generate selected artifacts sequentially (file I/O is fast, no benefit from para
|
||||
**For /gsd-dev-preferences (if selected):**
|
||||
|
||||
```bash
|
||||
gsd-sdk query generate-dev-preferences --analysis "$ANALYSIS_PATH" --json 2>/dev/null
|
||||
gsd-sdk query generate-dev-preferences --analysis "$ANALYSIS_PATH" --json
|
||||
```
|
||||
|
||||
Display: "✓ Generated /gsd-dev-preferences at $HOME/.claude/commands/gsd/dev-preferences.md"
|
||||
@@ -358,7 +358,7 @@ Display: "✓ Generated /gsd-dev-preferences at $HOME/.claude/commands/gsd/dev-p
|
||||
**For CLAUDE.md profile section (if selected):**
|
||||
|
||||
```bash
|
||||
gsd-sdk query generate-claude-profile --analysis "$ANALYSIS_PATH" --json 2>/dev/null
|
||||
gsd-sdk query generate-claude-profile --analysis "$ANALYSIS_PATH" --json
|
||||
```
|
||||
|
||||
Display: "✓ Added profile section to CLAUDE.md"
|
||||
@@ -366,7 +366,7 @@ Display: "✓ Added profile section to CLAUDE.md"
|
||||
**For Global CLAUDE.md (if selected):**
|
||||
|
||||
```bash
|
||||
gsd-sdk query generate-claude-profile --analysis "$ANALYSIS_PATH" --global --json 2>/dev/null
|
||||
gsd-sdk query generate-claude-profile --analysis "$ANALYSIS_PATH" --global --json
|
||||
```
|
||||
|
||||
Display: "✓ Added profile section to $HOME/.claude/CLAUDE.md"
|
||||
|
||||
@@ -140,10 +140,10 @@ fi
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.quick "$DESCRIPTION")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_PLANNER=$(gsd-sdk query agent-skills gsd-planner 2>/dev/null)
|
||||
AGENT_SKILLS_EXECUTOR=$(gsd-sdk query agent-skills gsd-executor 2>/dev/null)
|
||||
AGENT_SKILLS_CHECKER=$(gsd-sdk query agent-skills gsd-plan-checker 2>/dev/null)
|
||||
AGENT_SKILLS_VERIFIER=$(gsd-sdk query agent-skills gsd-verifier 2>/dev/null)
|
||||
AGENT_SKILLS_PLANNER=$(gsd-sdk query agent-skills gsd-planner)
|
||||
AGENT_SKILLS_EXECUTOR=$(gsd-sdk query agent-skills gsd-executor)
|
||||
AGENT_SKILLS_CHECKER=$(gsd-sdk query agent-skills gsd-plan-checker)
|
||||
AGENT_SKILLS_VERIFIER=$(gsd-sdk query agent-skills gsd-verifier)
|
||||
```
|
||||
|
||||
Parse JSON for: `planner_model`, `executor_model`, `checker_model`, `verifier_model`, `commit_docs`, `branch_name`, `quick_id`, `slug`, `date`, `timestamp`, `quick_dir`, `task_dir`, `roadmap_exists`, `planning_exists`.
|
||||
|
||||
@@ -42,7 +42,7 @@ If exists: Offer update/view/skip options.
|
||||
INIT=$(gsd-sdk query init.phase-op "${PHASE}")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
# Extract: phase_dir, padded_phase, phase_number, state_path, requirements_path, context_path
|
||||
AGENT_SKILLS_RESEARCHER=$(gsd-sdk query agent-skills gsd-phase-researcher 2>/dev/null)
|
||||
AGENT_SKILLS_RESEARCHER=$(gsd-sdk query agent-skills gsd-phase-researcher)
|
||||
```
|
||||
|
||||
## Step 4: Spawn Researcher
|
||||
|
||||
@@ -18,7 +18,7 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.phase-op "${PHASE_ARG}")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_AUDITOR=$(gsd-sdk query agent-skills gsd-security-auditor 2>/dev/null)
|
||||
AGENT_SKILLS_AUDITOR=$(gsd-sdk query agent-skills gsd-security-auditor)
|
||||
```
|
||||
|
||||
Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`.
|
||||
|
||||
@@ -21,8 +21,8 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.plan-phase "$PHASE")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_UI=$(gsd-sdk query agent-skills gsd-ui-researcher 2>/dev/null)
|
||||
AGENT_SKILLS_UI_CHECKER=$(gsd-sdk query agent-skills gsd-ui-checker 2>/dev/null)
|
||||
AGENT_SKILLS_UI=$(gsd-sdk query agent-skills gsd-ui-researcher)
|
||||
AGENT_SKILLS_UI_CHECKER=$(gsd-sdk query agent-skills gsd-ui-checker)
|
||||
```
|
||||
|
||||
Parse JSON for: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_context`, `has_research`, `commit_docs`.
|
||||
|
||||
@@ -18,7 +18,7 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.phase-op "${PHASE_ARG}")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_UI_REVIEWER=$(gsd-sdk query agent-skills gsd-ui-auditor 2>/dev/null)
|
||||
AGENT_SKILLS_UI_REVIEWER=$(gsd-sdk query agent-skills gsd-ui-auditor)
|
||||
```
|
||||
|
||||
Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `commit_docs`.
|
||||
|
||||
@@ -18,7 +18,7 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.phase-op "${PHASE_ARG}")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_AUDITOR=$(gsd-sdk query agent-skills gsd-nyquist-auditor 2>/dev/null)
|
||||
AGENT_SKILLS_AUDITOR=$(gsd-sdk query agent-skills gsd-nyquist-auditor)
|
||||
```
|
||||
|
||||
Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`.
|
||||
|
||||
@@ -32,8 +32,8 @@ If $ARGUMENTS contains a phase number, load context:
|
||||
```bash
|
||||
INIT=$(gsd-sdk query init.verify-work "${PHASE_ARG}")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
AGENT_SKILLS_PLANNER=$(gsd-sdk query agent-skills gsd-planner 2>/dev/null)
|
||||
AGENT_SKILLS_CHECKER=$(gsd-sdk query agent-skills gsd-plan-checker 2>/dev/null)
|
||||
AGENT_SKILLS_PLANNER=$(gsd-sdk query agent-skills gsd-planner)
|
||||
AGENT_SKILLS_CHECKER=$(gsd-sdk query agent-skills gsd-plan-checker)
|
||||
```
|
||||
|
||||
Parse JSON for: `planner_model`, `checker_model`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `has_verification`, `uat_path`.
|
||||
@@ -464,7 +464,7 @@ Run phase artifact scan to surface any open items before marking phase verified:
|
||||
`audit-open` is CJS-only until registered on `gsd-sdk query`:
|
||||
|
||||
```bash
|
||||
gsd-sdk query audit-open --json 2>/dev/null
|
||||
gsd-sdk query audit-open --json
|
||||
```
|
||||
|
||||
Parse the JSON output. For the CURRENT PHASE ONLY, surface:
|
||||
|
||||
73
tests/bug-2636-gsd-sdk-query-silent-swallow.test.cjs
Normal file
73
tests/bug-2636-gsd-sdk-query-silent-swallow.test.cjs
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Regression guard for #2636 — `gsd-sdk query agent-skills <slug>` calls in
|
||||
* workflows must NOT silently swallow failures via a bare `2>/dev/null`.
|
||||
*
|
||||
* Root cause of #2636: when the installed npm `@gsd-build/sdk` was stale and
|
||||
* the `agent-skills` handler was missing, every workflow line of the form
|
||||
* AGENT_SKILLS_X=$(gsd-sdk query agent-skills <slug> 2>/dev/null)
|
||||
* resolved to empty string, and the `agent_skills.<slug>` config was never
|
||||
* injected into spawn prompts. No error ever surfaced.
|
||||
*
|
||||
* Fix: remove `2>/dev/null` from `agent-skills` calls so any SDK failure
|
||||
* (stale binary, unregistered handler, runtime error) prints to the
|
||||
* workflow's stderr and is visible to the user.
|
||||
*
|
||||
* Test scope: ONLY `gsd-sdk query agent-skills …` (the exact noun implicated
|
||||
* in #2636). Other `gsd-sdk query config-get …` patterns commonly use
|
||||
* `2>/dev/null || echo "default"` which IS exit-code aware (the `||` branch
|
||||
* only runs on non-zero exit) and is a documented fallback pattern.
|
||||
*
|
||||
* Scans: get-shit-done/workflows/**\/*.md and commands/**\/*.md
|
||||
*/
|
||||
|
||||
const { describe, test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const REPO_ROOT = path.join(__dirname, '..');
|
||||
const SCAN_ROOTS = [
|
||||
path.join(REPO_ROOT, 'get-shit-done', 'workflows'),
|
||||
path.join(REPO_ROOT, 'commands'),
|
||||
path.join(REPO_ROOT, 'agents'),
|
||||
];
|
||||
|
||||
function walk(dir, out) {
|
||||
let entries;
|
||||
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
||||
for (const entry of entries) {
|
||||
if (entry.name === 'node_modules' || entry.name === '.git') continue;
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) walk(full, out);
|
||||
else if (entry.isFile() && entry.name.endsWith('.md')) out.push(full);
|
||||
}
|
||||
}
|
||||
|
||||
describe('bug #2636 — agent-skills query must not silently swallow failures', () => {
|
||||
test('no `gsd-sdk query agent-skills ... 2>/dev/null` in workflows', () => {
|
||||
const files = [];
|
||||
for (const root of SCAN_ROOTS) walk(root, files);
|
||||
assert.ok(files.length > 0, 'expected to scan some workflow/command files');
|
||||
|
||||
// Match `gsd-sdk query agent-skills <slug>` followed (on the same line)
|
||||
// by `2>/dev/null` — the silent-swallow anti-pattern.
|
||||
const ANTI = /gsd-sdk\s+query\s+agent-skills\b[^\n]*2>\/dev\/null/;
|
||||
|
||||
const offenders = [];
|
||||
for (const file of files) {
|
||||
const lines = fs.readFileSync(file, 'utf8').split('\n');
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (ANTI.test(lines[i])) {
|
||||
offenders.push(path.relative(REPO_ROOT, file) + ':' + (i + 1) + ': ' + lines[i].trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert.strictEqual(
|
||||
offenders.length, 0,
|
||||
'Found `gsd-sdk query agent-skills ... 2>/dev/null` (silent swallow — ' +
|
||||
'root cause of #2636). Remove `2>/dev/null` so SDK failures surface.\n\n' +
|
||||
offenders.join('\n'),
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user