* fix(#2589): use --raw/--pick for config/model/verify lookups, drop jq dep
The reviewer/workflow config lookups resolved scalars and object fields with a
`gsd_run query <cmd> … | jq … 2>/dev/null || <default>` shape. On any machine
without jq (the default on Windows/Git-Bash) the jq stage fails with exit 127,
the failure is swallowed by 2>/dev/null + the trailing || default, and the
variable comes back EMPTY — the configured per-lane model/host/budget is
silently dropped and the lane falls back to CLI defaults with no diagnostic.
gsd-tools ships native flags that do the same job with no external dep:
config-get <key> --raw (strips JSON quotes off a scalar)
resolve-model <id> --pick model (descends an object)
resolve-execution … --pick <f> (same)
verification.status … --pick status
Replaced every jq-piped config/model/verify lookup across review.md (×23),
plan-phase.md, ship.md, debug.md (incl. the redundant boolean coercion — --raw
returns true/false as bare tokens natively), autonomous.md (×2),
ai-integration-phase.md (×4), and eval-review.md. The legitimate structured-JSON
jq sites that parse HTTP curl responses (.choices[0], jq -rs, jq -n --rawfile)
are untouched — only the jq-replaceable lookups moved to the native flags.
Adds tests/fix-2589-config-get-no-jq.test.cjs: a source-invariant guard asserting
no audited workflow pipes config-get/resolve-model/resolve-execution/verification.status
to jq (fails-first on the pre-fix text, passes after).
* test(#2589): update autonomous-converge jq assertion to --pick; regen golden fixtures
Two test consequences of the workflow-doc edits in the prior commit:
1. tests/autonomous-converge.test.cjs pinned the OLD jq-dependent shape
(`verification.status … | jq -r '.status//empty'`) as the canonical routing
contract. The test's INTENT is correct (route human validation through
canonical verification.status) but it over-specified the MECHANISM (the jq
pipe). Updated the assertion to match the new native --pick status shape;
the contract being guarded (canonical verification.status read before the
human_needed branch) is unchanged.
2. The golden-install-parity fixtures (19 runtimes) record a content hash of
every installed workflow .md; the 7 edited workflows changed those hashes.
Regenerated via `npm run gen:golden` (the test's own failure message
instructs this). Only the 7 edited workflow hashes changed in each fixture.
* fix(#2589): declare jq a prerequisite for the lanes that still need it; repair test file
Three defects in the first cut of the #2589 fix:
1. tests/autonomous-converge.test.cjs was a JavaScript syntax error. The regex
literal /...2>\/dev/null .../ left the second slash unescaped, terminating the
literal early and parsing `null` as regex flags:
SyntaxError: Invalid regular expression flags
The whole file failed to load, so every assertion in it — including the #1522
and #1526 guards — silently stopped running. Replaced with the string-compare
form already used at line 202 for the sibling shell-snippet assertion.
2. lint:ci failed. tests/fix-2589-config-get-no-jq.test.cjs buckets into the
capped `config` production module via its `config-get-...` effective prefix,
making it a novel offender against the 2-file cap. The test is about workflow
documents, not the config module, so it is renamed to
fix-2589-workflow-jq-dependency.test.cjs (free prefix) rather than growing the
allowlist with a module that does not actually need a 5th test file.
3. The fix deleted the repo's only jq-prerequisite declaration. review.md:244
("install jq if missing") was the anchor plan-review-convergence.md cites by
line number, and it went away with the jq pipes — while the ollama, lm_studio,
llama_cpp, opencode, and agy lanes still hard-require jq to parse HTTP
/v1/chat/completions responses, opencode's JSONL event stream, and agy's
conversation cache. On a jq-less host those five lanes swallow exit 127 into
empty output: the same silent-degradation class #2589 exists to close.
detect_clis now probes jq alongside the other prerequisites and emits
jq:available / jq:missing, and the five dependent lanes are treated as
undetected when it is absent, with an install hint. The six lanes that do not
need jq stay selectable. plan-review-convergence.md now cites the section by
name instead of a line number that moves.
Regression guards added to the renamed test file: review.md must keep the jq
probe and must name all five dependent lanes, and no workflow may cite review.md
by line number. Workflow-size baseline and the 19 install-parity goldens
regenerated for the review.md / plan-review-convergence.md edits.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015TCwhbMuY37DzRMCfzTABJ
* fix(#2589): decide the ship verification gate on a single verification.status read
Isolated review finding (medium). Pre-fix, ship.md captured verification.status
ONCE into $VERIFICATION and picked status / next_action / next_command off that
cached JSON with three jq calls. --pick takes a single dot-path field, so the
mechanical conversion issued three separate queries up front: three node spawns
that each re-read the phase VERIFICATION.md and re-derive the commit-time vs
mtime staleness comparison, on every ship — including the common passing path
that never uses the two message fields. It also meant the gate's verdict and the
message shown to the user were derived from three reads with no guarantee they
observed the same state.
The gate now reads `status` once and decides. The two message-only fields are
read on the blocking path only, after PHASE_VERIFICATION_INCOMPLETE is already
determined — so the passing path costs one query instead of three, and a
concurrent write between reads can no longer make the gate and its message
disagree, because the block/allow decision no longer depends on them.
Adding a multi-field --pick to gsd-tools would have collapsed this to one query,
but that changes the flag's output contract and belongs in its own change.
Regression guard in tests/fix-2589-workflow-jq-dependency.test.cjs: ship.md must
read verification.status exactly three times total, the block decision must
follow the status read, and next_action / next_command must both appear after
the blocking prose so they cannot drift back onto the passing path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015TCwhbMuY37DzRMCfzTABJ
* docs(#2589): document the jq prerequisite for the five reviewer lanes that need it
/gsd-review's ollama, lm_studio, llama_cpp, opencode, and agy lanes parse JSON
GSD does not produce (OpenAI-compatible /v1/chat/completions responses,
OpenCode's JSONL event stream, Antigravity's conversation cache), so they require
jq on PATH. Nothing in docs/ said so. Records which five lanes need it, which six
do not, that reading configured models/hosts/budgets no longer requires jq at
all, and what /gsd-review now does when jq is absent.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015TCwhbMuY37DzRMCfzTABJ
* chore(#2589): backfill changeset pr number (#2673)
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>