* 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>
This commit is contained in:
@@ -33,10 +33,10 @@ Parse JSON for: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded
|
||||
|
||||
Resolve agent models:
|
||||
```bash
|
||||
SELECTOR_MODEL=$(gsd_run query resolve-model gsd-framework-selector 2>/dev/null | jq -r '.model' 2>/dev/null || true)
|
||||
RESEARCHER_MODEL=$(gsd_run query resolve-model gsd-ai-researcher 2>/dev/null | jq -r '.model' 2>/dev/null || true)
|
||||
DOMAIN_MODEL=$(gsd_run query resolve-model gsd-domain-researcher 2>/dev/null | jq -r '.model' 2>/dev/null || true)
|
||||
PLANNER_MODEL=$(gsd_run query resolve-model gsd-eval-planner 2>/dev/null | jq -r '.model' 2>/dev/null || true)
|
||||
SELECTOR_MODEL=$(gsd_run query resolve-model gsd-framework-selector --pick model 2>/dev/null || true)
|
||||
RESEARCHER_MODEL=$(gsd_run query resolve-model gsd-ai-researcher --pick model 2>/dev/null || true)
|
||||
DOMAIN_MODEL=$(gsd_run query resolve-model gsd-domain-researcher --pick model 2>/dev/null || true)
|
||||
PLANNER_MODEL=$(gsd_run query resolve-model gsd-eval-planner --pick model 2>/dev/null || true)
|
||||
```
|
||||
|
||||
Check config:
|
||||
|
||||
@@ -490,7 +490,7 @@ Skill(skill="gsd-code-review", args="${PHASE_NUM} --fix --auto")
|
||||
After execute, read canonical verification:
|
||||
|
||||
```bash
|
||||
VERIFY_STATUS=$(gsd_run query verification.status "${PHASE_DIR}" 2>/dev/null | jq -r '.status//empty')
|
||||
VERIFY_STATUS=$(gsd_run query verification.status "${PHASE_DIR}" --pick status 2>/dev/null || true)
|
||||
```
|
||||
|
||||
If `PHASE_DIR` is absent, re-fetch `init.phase-op ${PHASE_NUM}` and parse `phase_dir`.
|
||||
@@ -548,7 +548,7 @@ Skill(skill="gsd-execute-phase", args="${PHASE_NUM} --no-transition")
|
||||
|
||||
Re-read verification status:
|
||||
```bash
|
||||
VERIFY_STATUS=$(gsd_run query verification.status "${PHASE_DIR}" 2>/dev/null | jq -r '.status//empty')
|
||||
VERIFY_STATUS=$(gsd_run query verification.status "${PHASE_DIR}" --pick status 2>/dev/null || true)
|
||||
```
|
||||
|
||||
If `passed` or `human_needed`: route normally.
|
||||
|
||||
@@ -27,12 +27,12 @@ Extract `commit_docs` and `config.response_language` from init JSON. Extract `de
|
||||
|
||||
Resolve debugger model:
|
||||
```bash
|
||||
debugger_model=$(gsd_run query resolve-model gsd-debugger 2>/dev/null | jq -r '.model' 2>/dev/null || true)
|
||||
debugger_model=$(gsd_run query resolve-model gsd-debugger --pick model 2>/dev/null || true)
|
||||
```
|
||||
|
||||
Read TDD mode from config:
|
||||
```bash
|
||||
TDD_MODE=$(gsd_run query config-get workflow.tdd_mode 2>/dev/null | jq -r 'if type == "boolean" then tostring else . end' 2>/dev/null || echo "false")
|
||||
TDD_MODE=$(gsd_run query config-get workflow.tdd_mode --raw 2>/dev/null || echo "false")
|
||||
```
|
||||
|
||||
## 1a. LIST subcommand
|
||||
|
||||
@@ -24,7 +24,7 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `commit_docs`.
|
||||
|
||||
```bash
|
||||
AUDITOR_MODEL=$(gsd_run query resolve-model gsd-eval-auditor 2>/dev/null | jq -r '.model' 2>/dev/null || true)
|
||||
AUDITOR_MODEL=$(gsd_run query resolve-model gsd-eval-auditor --pick model 2>/dev/null || true)
|
||||
AGENT_SKILLS_AUDITOR=$(gsd_run query agent-skills gsd-eval-auditor)
|
||||
```
|
||||
|
||||
|
||||
@@ -1292,7 +1292,7 @@ Skipping bounce step.
|
||||
**Read pass count:**
|
||||
```bash
|
||||
BOUNCE_PASSES=$(gsd_run query config-get workflow.plan_bounce_passes 2>/dev/null || echo "2")
|
||||
BOUNCE_SCRIPT=$(gsd_run query config-get workflow.plan_bounce_script 2>/dev/null | jq -r '.' 2>/dev/null || true)
|
||||
BOUNCE_SCRIPT=$(gsd_run query config-get workflow.plan_bounce_script --raw 2>/dev/null || true)
|
||||
```
|
||||
|
||||
Display banner:
|
||||
|
||||
@@ -82,8 +82,9 @@ Then re-run: /gsd:plan-review-convergence {PHASE}
|
||||
if [ -z "$REVIEWER_FLAGS" ]; then
|
||||
DEFAULT_REVIEWERS_JSON=$(gsd_run query config-get review.default_reviewers 2>/dev/null || echo "")
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
# jq is a documented production dependency (review.md:244 — "install jq if
|
||||
# missing"). If it is absent we cannot inspect the configured default, so
|
||||
# jq is a documented production dependency (review.md, detect_clis — the
|
||||
# "jq-dependent reviewer lanes" note). If it is absent we cannot inspect
|
||||
# the configured default (it is a JSON array, not a --raw/--pick scalar), so
|
||||
# fail safe with --codex and surface the reason rather than silently
|
||||
# reproducing the #2315 override under degraded conditions.
|
||||
echo "WARNING: jq not on PATH — cannot read review.default_reviewers; falling back to --codex (#2315)" >&2
|
||||
|
||||
@@ -26,19 +26,44 @@ command -v cursor-agent >/dev/null 2>&1 && echo "cursor:available" || echo "curs
|
||||
command -v agy >/dev/null 2>&1 && echo "antigravity:available" || echo "antigravity:missing"
|
||||
|
||||
# Check local model servers (OpenAI-compatible HTTP API — no CLI binary required)
|
||||
OLLAMA_HOST=$(gsd_run query config-get review.ollama_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
|
||||
OLLAMA_HOST=$(gsd_run query config-get review.ollama_host --raw 2>/dev/null || echo "")
|
||||
if [ -z "$OLLAMA_HOST" ] || [ "$OLLAMA_HOST" = "null" ]; then OLLAMA_HOST="http://localhost:11434"; fi
|
||||
curl -s --max-time 2 "${OLLAMA_HOST}/v1/models" >/dev/null 2>&1 && echo "ollama:available" || echo "ollama:missing"
|
||||
|
||||
LM_STUDIO_HOST=$(gsd_run query config-get review.lm_studio_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
|
||||
LM_STUDIO_HOST=$(gsd_run query config-get review.lm_studio_host --raw 2>/dev/null || echo "")
|
||||
if [ -z "$LM_STUDIO_HOST" ] || [ "$LM_STUDIO_HOST" = "null" ]; then LM_STUDIO_HOST="http://localhost:1234"; fi
|
||||
curl -s --max-time 2 "${LM_STUDIO_HOST}/v1/models" >/dev/null 2>&1 && echo "lm_studio:available" || echo "lm_studio:missing"
|
||||
|
||||
LLAMA_CPP_HOST=$(gsd_run query config-get review.llama_cpp_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
|
||||
LLAMA_CPP_HOST=$(gsd_run query config-get review.llama_cpp_host --raw 2>/dev/null || echo "")
|
||||
if [ -z "$LLAMA_CPP_HOST" ] || [ "$LLAMA_CPP_HOST" = "null" ]; then LLAMA_CPP_HOST="http://localhost:8080"; fi
|
||||
curl -s --max-time 2 "${LLAMA_CPP_HOST}/v1/models" >/dev/null 2>&1 && echo "llama_cpp:available" || echo "llama_cpp:missing"
|
||||
|
||||
# jq prerequisite (#2589). The config/model/budget lookups in this workflow no
|
||||
# longer need jq — they use the native --raw/--pick flags. But the lanes listed
|
||||
# under "jq-dependent reviewer lanes" below parse structured JSON that gsd-tools
|
||||
# does not emit (OpenAI-compatible /v1/chat/completions responses, opencode's
|
||||
# JSONL event stream, agy's conversation cache), so they cannot run without jq.
|
||||
# Probe it here rather than letting each lane swallow exit 127 into empty output.
|
||||
command -v jq >/dev/null 2>&1 && echo "jq:available" || echo "jq:missing"
|
||||
```
|
||||
|
||||
**jq-dependent reviewer lanes.** `jq` is a production prerequisite for the
|
||||
`ollama`, `lm_studio`, `llama_cpp`, `opencode`, and `antigravity` lanes only. If
|
||||
`detect_clis` reports `jq:missing`, treat those five as **undetected** — they
|
||||
follow the same "known-but-undetected" path as a missing CLI (info note, ignored;
|
||||
and if they were the only selected reviewers, fail with the actionable message
|
||||
below rather than producing an empty review). Tell the user to install jq:
|
||||
|
||||
```
|
||||
NOTE: jq is not on PATH — the ollama, lm_studio, llama_cpp, opencode, and
|
||||
antigravity reviewer lanes are unavailable. Install jq (https://jqlang.org/download/)
|
||||
or select a lane that does not require it (--gemini, --claude, --codex,
|
||||
--coderabbit, --qwen, --cursor).
|
||||
```
|
||||
|
||||
The remaining lanes (`gemini`, `claude`, `codex`, `coderabbit`, `qwen`, `cursor`)
|
||||
do not require jq and must stay selectable on a jq-less host.
|
||||
|
||||
Parse flags from `$ARGUMENTS`:
|
||||
- `--gemini` → include Gemini
|
||||
- `--claude` → include Claude
|
||||
@@ -241,20 +266,21 @@ Note: `INSTRUCTIONS_BLOCK_FILE`, `ROADMAP_SECTION_FILE`, and `PHASE_DIR` come fr
|
||||
Read model preferences from planning config. Null/missing values fall back to CLI defaults.
|
||||
|
||||
```bash
|
||||
# JSON scalars from gsd-tools.cjs query; use jq -r to strip JSON string quotes (install jq if missing)
|
||||
GEMINI_MODEL=$(gsd_run query config-get review.models.gemini 2>/dev/null | jq -r '.' 2>/dev/null || true)
|
||||
CLAUDE_MODEL=$(gsd_run query config-get review.models.claude 2>/dev/null | jq -r '.' 2>/dev/null || true)
|
||||
CODEX_MODEL=$(gsd_run query config-get review.models.codex 2>/dev/null | jq -r '.' 2>/dev/null || true)
|
||||
OPENCODE_MODEL=$(gsd_run query config-get review.models.opencode 2>/dev/null | jq -r '.' 2>/dev/null || true)
|
||||
# JSON scalars from gsd-tools.cjs query; --raw strips the JSON quotes natively
|
||||
# (no jq dependency — jq is absent on stock Windows/Git-Bash, #2589)
|
||||
GEMINI_MODEL=$(gsd_run query config-get review.models.gemini --raw 2>/dev/null || true)
|
||||
CLAUDE_MODEL=$(gsd_run query config-get review.models.claude --raw 2>/dev/null || true)
|
||||
CODEX_MODEL=$(gsd_run query config-get review.models.codex --raw 2>/dev/null || true)
|
||||
OPENCODE_MODEL=$(gsd_run query config-get review.models.opencode --raw 2>/dev/null || true)
|
||||
# review.models.agy, when set, is passed to agy as --model (escape hatch for a
|
||||
# pinned model that 404s server-side); otherwise agy uses its persisted default.
|
||||
AGY_MODEL=$(gsd_run query config-get review.models.agy 2>/dev/null | jq -r '.' 2>/dev/null || true)
|
||||
AGY_MODEL=$(gsd_run query config-get review.models.agy --raw 2>/dev/null || true)
|
||||
|
||||
# Reasoning effort per reviewer (#2481). Empty unless the host's effortSurface
|
||||
# axis is `argv`. Pass --attempt N to walk ADR-443's escalation ladder.
|
||||
CLAUDE_EFFORT_ARGS=$(gsd_run query resolve-execution gsd-plan-checker --host claude 2>/dev/null | jq -r '.effort_argv_string // ""' 2>/dev/null || true)
|
||||
CODEX_EFFORT_ARGS=$(gsd_run query resolve-execution gsd-plan-checker --host codex 2>/dev/null | jq -r '.effort_argv_string // ""' 2>/dev/null || true)
|
||||
OPENCODE_EFFORT_ARGS=$(gsd_run query resolve-execution gsd-plan-checker --host opencode 2>/dev/null | jq -r '.effort_argv_string // ""' 2>/dev/null || true)
|
||||
CLAUDE_EFFORT_ARGS=$(gsd_run query resolve-execution gsd-plan-checker --host claude --pick effort_argv_string 2>/dev/null || true)
|
||||
CODEX_EFFORT_ARGS=$(gsd_run query resolve-execution gsd-plan-checker --host codex --pick effort_argv_string 2>/dev/null || true)
|
||||
OPENCODE_EFFORT_ARGS=$(gsd_run query resolve-execution gsd-plan-checker --host opencode --pick effort_argv_string 2>/dev/null || true)
|
||||
```
|
||||
|
||||
**No hook-trust bypass (#2479):** the codex invocations below deliberately pass no
|
||||
@@ -644,9 +670,9 @@ prepare_trimmed_prompt_for_reviewer() {
|
||||
}
|
||||
|
||||
# Resolve prompt budget for Ollama: per-reviewer override > global default > null
|
||||
OLLAMA_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens_per_reviewer.ollama 2>/dev/null | jq -r '.' 2>/dev/null || echo "null")
|
||||
OLLAMA_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens_per_reviewer.ollama --raw 2>/dev/null || echo "null")
|
||||
if [ -z "$OLLAMA_REVIEWER_BUDGET" ] || [ "$OLLAMA_REVIEWER_BUDGET" = "null" ]; then
|
||||
OLLAMA_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens 2>/dev/null | jq -r '.' 2>/dev/null || echo "null")
|
||||
OLLAMA_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens --raw 2>/dev/null || echo "null")
|
||||
fi
|
||||
|
||||
# Apply budget trim for Ollama if a budget is configured
|
||||
@@ -670,9 +696,9 @@ if [ -n "$OLLAMA_REVIEWER_BUDGET" ] && [ "$OLLAMA_REVIEWER_BUDGET" != "null" ] &
|
||||
fi
|
||||
|
||||
if [ "$OLLAMA_SKIP" != "1" ]; then
|
||||
OLLAMA_HOST=$(gsd_run query config-get review.ollama_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
|
||||
OLLAMA_HOST=$(gsd_run query config-get review.ollama_host --raw 2>/dev/null || echo "")
|
||||
if [ -z "$OLLAMA_HOST" ] || [ "$OLLAMA_HOST" = "null" ]; then OLLAMA_HOST="http://localhost:11434"; fi
|
||||
OLLAMA_MODEL=$(gsd_run query config-get review.models.ollama 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
|
||||
OLLAMA_MODEL=$(gsd_run query config-get review.models.ollama --raw 2>/dev/null || echo "")
|
||||
if [ -z "$OLLAMA_MODEL" ] || [ "$OLLAMA_MODEL" = "null" ]; then
|
||||
OLLAMA_MODEL=$(curl -s --max-time 2 "${OLLAMA_HOST}/v1/models" 2>/dev/null | jq -r '.data[0].id // "llama3"' 2>/dev/null || echo "llama3")
|
||||
fi
|
||||
@@ -692,9 +718,9 @@ fi
|
||||
**LM Studio (local, OpenAI-compatible):**
|
||||
```bash
|
||||
# Resolve prompt budget for LM Studio: per-reviewer override > global default > null
|
||||
LM_STUDIO_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens_per_reviewer.lm_studio 2>/dev/null | jq -r '.' 2>/dev/null || echo "null")
|
||||
LM_STUDIO_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens_per_reviewer.lm_studio --raw 2>/dev/null || echo "null")
|
||||
if [ -z "$LM_STUDIO_REVIEWER_BUDGET" ] || [ "$LM_STUDIO_REVIEWER_BUDGET" = "null" ]; then
|
||||
LM_STUDIO_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens 2>/dev/null | jq -r '.' 2>/dev/null || echo "null")
|
||||
LM_STUDIO_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens --raw 2>/dev/null || echo "null")
|
||||
fi
|
||||
|
||||
# Apply budget trim for LM Studio if a budget is configured
|
||||
@@ -718,9 +744,9 @@ if [ -n "$LM_STUDIO_REVIEWER_BUDGET" ] && [ "$LM_STUDIO_REVIEWER_BUDGET" != "nul
|
||||
fi
|
||||
|
||||
if [ "$LM_STUDIO_SKIP" != "1" ]; then
|
||||
LM_STUDIO_HOST=$(gsd_run query config-get review.lm_studio_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
|
||||
LM_STUDIO_HOST=$(gsd_run query config-get review.lm_studio_host --raw 2>/dev/null || echo "")
|
||||
if [ -z "$LM_STUDIO_HOST" ] || [ "$LM_STUDIO_HOST" = "null" ]; then LM_STUDIO_HOST="http://localhost:1234"; fi
|
||||
LM_STUDIO_MODEL=$(gsd_run query config-get review.models.lm_studio 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
|
||||
LM_STUDIO_MODEL=$(gsd_run query config-get review.models.lm_studio --raw 2>/dev/null || echo "")
|
||||
if [ -z "$LM_STUDIO_MODEL" ] || [ "$LM_STUDIO_MODEL" = "null" ]; then
|
||||
LM_STUDIO_MODEL=$(curl -s --max-time 2 "${LM_STUDIO_HOST}/v1/models" 2>/dev/null | jq -r '.data[0].id // "local-model"' 2>/dev/null || echo "local-model")
|
||||
fi
|
||||
@@ -745,9 +771,9 @@ fi
|
||||
**llama.cpp (local, OpenAI-compatible):**
|
||||
```bash
|
||||
# Resolve prompt budget for llama.cpp: per-reviewer override > global default > null
|
||||
LLAMA_CPP_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens_per_reviewer.llama_cpp 2>/dev/null | jq -r '.' 2>/dev/null || echo "null")
|
||||
LLAMA_CPP_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens_per_reviewer.llama_cpp --raw 2>/dev/null || echo "null")
|
||||
if [ -z "$LLAMA_CPP_REVIEWER_BUDGET" ] || [ "$LLAMA_CPP_REVIEWER_BUDGET" = "null" ]; then
|
||||
LLAMA_CPP_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens 2>/dev/null | jq -r '.' 2>/dev/null || echo "null")
|
||||
LLAMA_CPP_REVIEWER_BUDGET=$(gsd_run query config-get review.max_prompt_tokens --raw 2>/dev/null || echo "null")
|
||||
fi
|
||||
|
||||
# Apply budget trim for llama.cpp if a budget is configured
|
||||
@@ -771,9 +797,9 @@ if [ -n "$LLAMA_CPP_REVIEWER_BUDGET" ] && [ "$LLAMA_CPP_REVIEWER_BUDGET" != "nul
|
||||
fi
|
||||
|
||||
if [ "$LLAMA_CPP_SKIP" != "1" ]; then
|
||||
LLAMA_CPP_HOST=$(gsd_run query config-get review.llama_cpp_host 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
|
||||
LLAMA_CPP_HOST=$(gsd_run query config-get review.llama_cpp_host --raw 2>/dev/null || echo "")
|
||||
if [ -z "$LLAMA_CPP_HOST" ] || [ "$LLAMA_CPP_HOST" = "null" ]; then LLAMA_CPP_HOST="http://localhost:8080"; fi
|
||||
LLAMA_CPP_MODEL=$(gsd_run query config-get review.models.llama_cpp 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
|
||||
LLAMA_CPP_MODEL=$(gsd_run query config-get review.models.llama_cpp --raw 2>/dev/null || echo "")
|
||||
if [ -z "$LLAMA_CPP_MODEL" ] || [ "$LLAMA_CPP_MODEL" = "null" ]; then
|
||||
LLAMA_CPP_MODEL=$(curl -s --max-time 2 "${LLAMA_CPP_HOST}/v1/models" 2>/dev/null | jq -r '.data[0].id // "local-model"' 2>/dev/null || echo "local-model")
|
||||
fi
|
||||
|
||||
@@ -52,12 +52,19 @@ Verify the work is ready to ship:
|
||||
|
||||
1. **Verification passed?**
|
||||
```bash
|
||||
VERIFICATION=$(gsd_run query verification.status "${PHASE_DIR}" 2>/dev/null)
|
||||
STATUS=$(printf '%s' "$VERIFICATION" | jq -r '.status' 2>/dev/null || echo "")
|
||||
NEXT_ACTION=$(printf '%s' "$VERIFICATION" | jq -r '.next_action' 2>/dev/null || echo "")
|
||||
NEXT_COMMAND=$(printf '%s' "$VERIFICATION" | jq -r '.next_command' 2>/dev/null || echo "")
|
||||
# The gate decides on ONE read. --pick takes a single field, so the two
|
||||
# human-facing fields are read only on the blocking path below — never on the
|
||||
# passing path — rather than issuing three queries up front (#2589).
|
||||
STATUS=$(gsd_run query verification.status "${PHASE_DIR}" --pick status 2>/dev/null || echo "")
|
||||
```
|
||||
Only `passed` may ship. If `$STATUS` is `passed`, verification is complete — continue to the next preflight check. Any other value (including `gaps_found`, `human_needed`, `missing`, and `unknown`) blocks with `PHASE_VERIFICATION_INCOMPLETE`: present `$NEXT_ACTION` to the user and, when `$NEXT_COMMAND` is non-empty, show it as the command to run next. The query already handles missing files and unexpected values, so no per-status arm is needed.
|
||||
Only `passed` may ship. If `$STATUS` is `passed`, verification is complete — continue to the next preflight check; do not read any further verification field.
|
||||
|
||||
Any other value (including `gaps_found`, `human_needed`, `missing`, and `unknown`) blocks with `PHASE_VERIFICATION_INCOMPLETE`. Only then, read the two message fields:
|
||||
```bash
|
||||
NEXT_ACTION=$(gsd_run query verification.status "${PHASE_DIR}" --pick next_action 2>/dev/null || echo "")
|
||||
NEXT_COMMAND=$(gsd_run query verification.status "${PHASE_DIR}" --pick next_command 2>/dev/null || echo "")
|
||||
```
|
||||
Present `$NEXT_ACTION` to the user and, when `$NEXT_COMMAND` is non-empty, show it as the command to run next. These two are message text only — the block/allow decision has already been made from `$STATUS`, so a concurrent write between the reads cannot change the gate's verdict. The query already handles missing files and unexpected values, so no per-status arm is needed.
|
||||
|
||||
2. **Clean working tree?**
|
||||
```bash
|
||||
@@ -346,7 +353,7 @@ Report: "PR #{number} created: {url}"
|
||||
Before prompting the user, check if an external review command is configured:
|
||||
|
||||
```bash
|
||||
REVIEW_CMD=$(gsd_run query config-get workflow.code_review_command 2>/dev/null | jq -r '.' 2>/dev/null || echo "")
|
||||
REVIEW_CMD=$(gsd_run query config-get workflow.code_review_command --raw 2>/dev/null || echo "")
|
||||
```
|
||||
|
||||
If `REVIEW_CMD` is non-empty and not `"null"`, run the external review:
|
||||
|
||||
Reference in New Issue
Block a user