* fix(#4776): resolve the artifact-exists prompt under --auto /gsd-ui-phase <phase> --auto stopped at 'UI-SPEC.md already exists for Phase {N}. What would you like to do?' whenever the file was on disk — which is most often after an earlier run wrote the contract as a draft and ended before its checker ran, exactly the state a re-run exists to verify. Step 4 had no --auto arm; step 9.5 in the same file has had one since it was added, which is how the drift went unnoticed. Step 4 now auto-selects Skip: the existing UI-SPEC is left untouched and the run proceeds to the checker. Skip is the only non-destructive choice — Update re-runs the researcher, which rewrites the whole contract and drops answers a person already recorded in it, and View exits without verifying anything. spec-phase.md's artifact-exists arm auto-selected 'Update it', which is the same defect with the opposite sign: an unattended run regenerating a spec nobody is watching. Per the decision recorded on #4776 — an unattended run reuses an existing artifact rather than regenerating it — it now auto-selects Skip and leaves the spec unchanged. The max-revision-iterations escalation (Force approve / Edit manually / Abandon) is deliberately untouched and pinned by a test: accepting blocking findings is a decision a person makes. Closes #4776 * chore(#4776): add changeset fragment Emitted-Drift-Ack-Growth: ui-phase.md — --auto arm added to the existing-UI-SPEC branch (#4776) Emitted-Drift-Ack-Growth: spec-phase.md — --auto arm reworded to reuse the existing SPEC (#4776) * fix(#4776): extend the reuse-as-is --auto fix to the 3 sibling files The PR's original scope claim -- that ai-integration-phase.md, eval-review.md and ui-review.md were "scoped by triage to their own issues" -- was false; no such issues existed, and it contradicted #4776's own most recent (2026-09-16) triage comment, which explicitly widened the fix to require all 5 files under one recommended fix. Applies the same reuse-as-is pattern: ai-integration-phase.md mirrors ui-phase.md's 3-way Update/View/Skip shape (auto-selects Skip); eval-review.md and ui-review.md have only Re-audit/View (auto-selects View, the only non-regenerating choice). None of the three had any prior --auto handling at all -- each has exactly one AskUserQuestion call site total, and it is the one this fix resolves, so an --auto run through any of them no longer stalls anywhere. Emitted-Drift-Ack-Growth: ai-integration-phase.md — the --auto arm reusing an existing AI-SPEC is the deliverable (#4776) Emitted-Drift-Ack-Growth: eval-review.md — the --auto arm reusing an existing EVAL-REVIEW is the deliverable (#4776) Emitted-Drift-Ack-Growth: ui-review.md — the --auto arm reusing an existing UI-REVIEW is the deliverable (#4776) --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
11 KiB
<required_reading> @~/.claude/gsd-core/references/ui-brand.md </required_reading>
<available_agent_types> Valid GSD subagent types (use exact names — do not fall back to 'general-purpose'):
- gsd-ui-auditor — Audits UI against design requirements </available_agent_types>
0. Initialize
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; }; _gsd_id_ok() { case "$("$1" runtime-identity --raw 2>/dev/null || true)" in '{"packageName":"@opengsd/gsd-core"'*'}') return 0;; *) return 1;; esac; }; _gsd_homes() { _gsd_at "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; }; if _gsd_at "${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif _gsd_homes; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif unset -f gsd_run; _G="$(command -v gsd_run)"; [ -n "$_G" ] && _gsd_id_ok "$_G"; then GSD_TOOLS="$_G"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and no identity-proving gsd_run is on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; GSD_IDENTITY_STATUS=unverified; _gsd_id_ok gsd_run && GSD_IDENTITY_STATUS=ok; export GSD_IDENTITY_STATUS; [ "$GSD_IDENTITY_STATUS" = ok ] || echo "WARNING: \"$GSD_TOOLS\" did not prove it is @opengsd/gsd-core - it is either a different package or an @opengsd/gsd-core older than the runtime-identity verb. See docs/how-to/diagnose-a-foreign-gsd-tools.md" >&2; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --raw --default "" 2>/dev/null || echo "")
INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_UI_REVIEWER=$(gsd_run query agent-skills gsd-ui-auditor)
# workflow.ui_interaction_capture (default false): read here and handed to the auditor
# through its <config> block — the agent carries no gsd_run resolver of its own.
INTERACTION_CAPTURE=$(gsd_run query config-get workflow.ui_interaction_capture --raw 2>/dev/null || echo "false")
[ "$INTERACTION_CAPTURE" = "true" ] || INTERACTION_CAPTURE="false"
If response_language is set: All user-facing output of this workflow — narration between tool calls, status updates, progress notes, findings, questions, prompts, and explanations — MUST be presented in {response_language}. Technical terms, code, file paths, and subagent prompts stay in English — only user-facing output is translated.
Parse: phase_dir, phase_number, phase_name, phase_slug, padded_phase, commit_docs.
UI_AUDITOR_MODEL=$(gsd_run query resolve-model gsd-ui-auditor --raw)
Display banner:
### GSD ► UI AUDIT — PHASE {N}: {name}
1. Detect Input State
SUMMARY_FILES=$(ls "${PHASE_DIR}"/*-SUMMARY.md 2>/dev/null)
UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
UI_REVIEW_FILE=$(ls "${PHASE_DIR}"/*-UI-REVIEW.md 2>/dev/null | head -1)
If SUMMARY_FILES empty: Exit — "Phase {N} not executed. Run /gsd:execute-phase {N} first."
Text mode (workflow.text_mode: true in config or --text flag): Set TEXT_MODE=true if --text is present in $ARGUMENTS OR text_mode from init JSON is true. When TEXT_MODE is active, replace every AskUserQuestion call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Antigravity, etc.) where AskUserQuestion is not available.
If UI_REVIEW_FILE non-empty:
If --auto: Auto-select "View" — keep the existing UI-REVIEW.md untouched and exit without
re-auditing. Log: [auto] UI-REVIEW.md exists — reusing as-is. "Re-audit" is the regenerating
choice; an unattended run reuses an existing artifact instead (#4776).
Otherwise: Use AskUserQuestion:
- header: "Existing UI Review"
- question: "UI-REVIEW.md already exists for Phase {N}."
- options:
- "Re-audit — run fresh audit"
- "View — display current review and exit"
If "View": display file, exit. If "Re-audit": continue.
2. Gather Context Paths
Build file list for auditor:
- All SUMMARY.md files in phase dir
- All PLAN.md files in phase dir
- UI-SPEC.md (if exists — audit baseline)
- CONTEXT.md (if exists — locked decisions)
3. Spawn gsd-ui-auditor
◆ Spawning UI auditor... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
Build prompt:
Read ~/.claude/agents/gsd-ui-auditor.md for instructions.
<objective>
Conduct 6-pillar visual audit of Phase {phase_number}: {phase_name}
{If UI-SPEC exists: "Audit against UI-SPEC.md design contract."}
{If no UI-SPEC: "Audit against abstract 6-pillar standards."}
</objective>
<required_reading>
- {summary_paths} (Execution summaries)
- {plan_paths} (Execution plans — what was intended)
- {ui_spec_path} (UI Design Contract — audit baseline, if exists)
- {context_path} (User decisions, if exists)
</required_reading>
${AGENT_SKILLS_UI_REVIEWER}
<config>
phase_dir: {phase_dir}
padded_phase: {padded_phase}
interaction_capture: {interaction_capture}
</config>
Omit null file paths. interaction_capture is always present — the $INTERACTION_CAPTURE value read in step 0, true only when
workflow.ui_interaction_capture is on; the auditor's <screenshot_approach> branches on it and
falls back to its Playwright-only static capture when it is false or no Chrome binary resolves.
Runtime-aware dispatch (#2508 Phase 4). GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via
gsd_run query resolve-dispatch-type --requested <role> --raw. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps tocoder/explore/planby role-suffix. The persona rides${AGENT_SKILLS_<ROLE>}(Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md.
Model omission (#2517). Omit the
modelparameter entirely when the value it would carry (UI_AUDITOR_MODEL) is"inherit"or empty. An empty value 404s on runtimes without native tier aliases — the default on non-Claude runtimes. Omitting it inherits the orchestrator's model. See @gsd-core/references/model-profile-resolution.md.
Agent(
prompt=ui_audit_prompt,
subagent_type="gsd-ui-auditor",
model="{UI_AUDITOR_MODEL}",
description="UI Audit Phase {N}"
)
ORCHESTRATOR RULE — CODEX RUNTIME: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.
4. Handle Return
If ## UI REVIEW COMPLETE:
Display score summary:
### GSD ► UI AUDIT COMPLETE ✓
**Phase {N}: {Name}** — Overall: {score}/24
| Pillar | Score |
|--------|-------|
| Copywriting | {N}/4 |
| Visuals | {N}/4 |
| Color | {N}/4 |
| Typography | {N}/4 |
| Spacing | {N}/4 |
| Experience Design | {N}/4 |
Top fixes:
1. {fix}
2. {fix}
3. {fix}
Full review: {path to UI-REVIEW.md}
---
## ▶ Next
`/clear` then:
- `/gsd:verify-work {N}` — UAT testing before phase completion
---
Automated UI Verification (when Playwright-MCP is available)
If mcp__playwright__* tools are accessible in this session:
- Navigate to each UI component described in the phase's UI-SPEC.md using
mcp__playwright__navigate(or equivalent Playwright-MCP tool). - Take a screenshot of each component using
mcp__playwright__screenshot. - Compare against the spec's visual requirements — dimensions, color palette, layout, spacing scale, and typography.
- Report any dimension, color, or layout discrepancies automatically as additional findings within the relevant pillar section of UI-REVIEW.md.
- Flag items that require human judgment (brand feel, content tone) as
needs_human_review: truein the findings — these are surfaced to the user separately after the automated pass completes.
If Playwright-MCP is not available in this session, this section is skipped
entirely. The audit falls back to the standard code-only review described above.
No configuration change is required — the availability of mcp__playwright__*
tools is detected at runtime.
5. Commit (if configured)
gsd_run query commit "docs(${padded_phase}): UI audit review" --files "${PHASE_DIR}/${PADDED_PHASE}-UI-REVIEW.md"
<success_criteria>
- Phase validated
- SUMMARY.md files found (execution completed)
- Existing review handled (re-audit/view)
- gsd-ui-auditor spawned with correct context
- UI-REVIEW.md created in phase directory
- Score summary displayed to user
- Next steps presented </success_criteria>