Files
Garry Tanandcapy-ai[bot] a14c28aca7 v1.91.289.0 feat: skill diet pilots for plan-design-review, office-hours and design-review (GSTA-134) (#3525)
* feat(plan-design-review): skill diet (GSTA-134 pilot 1)

SKILL.md 66,063 -> 56,209 B (claude host); review-sections.md 52,243 -> 50,183 B.
Cut model-known prose (cognitive patterns, verbose designer prose, duplicated
formatting rules); UX principles kept as a five-line Krug summary after the
blind judge showed wayfinding loss without it. Section layout unchanged:
autoplan's methodology snapshot requires review-sections.md as the only indexed
section.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* feat(office-hours): skill diet (GSTA-134 pilot 2)

Moves the Phase 3.5 Codex second opinion and the Phase 4.25 visual design
(designer mockups / HTML sketch) out of the eager SKILL.md into on-demand
sections behind STOP pointers; the CODEX_SECOND_OPINION, DESIGN_MOCKUP and
DESIGN_SKETCH resolvers are untouched. Claude-host SKILL.md 73,248 -> 43,885
bytes. skill-diet before/after (Opus, 9 tasks x 3): success 26/27 -> 27/27,
median tokens per successful run 170,197 -> 124,941; blind judge after 10,
before 5 of 15. YC pitch, flattery and promo text unchanged (sections
byte-identical to main).

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* feat(design-review): skill diet (GSTA-134 pilot 3)

Moves the outside voices and the test-framework bootstrap into on-demand
sections (the bootstrap now loads at Phase 8e.5, its only consumer: a
JS-behavior fix that needs a regression test with no known test command),
replaces the UX principles block with a Krug summary, and tightens the
DESIGN_METHODOLOGY prose (design-review is its only consumer). Shared
resolvers TEST_BOOTSTRAP and DESIGN_OUTSIDE_VOICES are untouched.
Claude-host SKILL.md 115,947 -> 84,869 bytes. skill-diet before/after
(Opus, 12 tasks x 3): success 32/36 -> 34/36, median tokens per successful
run 326,662 -> 257,819; blind judge after 9, before 5, tie 1.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test: re-pin size, context and carve guards for the GSTA-134 skill diets

Every pin moves down except office-hours' parity maxSizeRatio (1.1796 ->
1.1921, GStack Minder's call as merge-queue owner): its union grew 1,662 B
because the second-opinion question now sits in the skeleton while the
untouched CODEX_SECOND_OPINION resolver keeps its own copy. design-review
leaves the size allowlist on every host.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: drop the stale <affected-url> placeholder after the design-review Phase 8d diet

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: design-review checklist headers count the items they list after the diet

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs: design-review report path and UX_PRINCIPLES consumers after the skill diets

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* chore: CHANGELOG [Unreleased] for the GSTA-134 skill diets

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-28 04:17:22 +00:00

19 KiB
Raw Permalink Blame History

Visual Design Exploration

_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
D=""
[ -n "$_ROOT" ] && [ -x "$_ROOT/.claude/skills/gstack/design/dist/design" ] && D="$_ROOT/.claude/skills/gstack/design/dist/design"
[ -z "$D" ] && D="$HOME/.claude/skills/gstack/design/dist/design"
_DS=$("$HOME/.claude/skills/gstack/bin/gstack-paths" --get GSTACK_STATE_ROOT 2>/dev/null); _DC=${_DS:+$_DS/design-ready}; _DK=$(ls -diL "$D" 2>/dev/null)
_dt() { if command -v gtimeout >/dev/null; then gtimeout 10 "$@"; elif command -v timeout >/dev/null; then timeout 10 "$@"
elif command -v perl >/dev/null; then perl -e 'alarm(shift);exec(@ARGV)' 10 "$@"; else return 125; fi; }
_RC=0; _F="Fix: cd ${D%/design/dist/design} && ./setup"
if [ ! -x "$D" ]; then _RC=missing
elif [ ! "$_DC" -nt "$D" ] || [ "$(cat "$_DC")" != "$_DK" ]; then _dt "$D" --version >/dev/null 2>&1 </dev/null || _RC=$?
fi
case "$_RC" in
  0) echo "DESIGN_READY: $D"; [ -n "$_DC" ] && echo "$_DK" > "$_DC" 2>/dev/null ;;
  missing) echo "DESIGN_NOT_AVAILABLE: $D is not installed. $_F" ;;
  124|142) echo "DESIGN_NOT_AVAILABLE: $D --version timed out after 10s" ;;
  125) echo "DESIGN_NOT_AVAILABLE: no timeout/gtimeout/perl to bound $D" ;;
  137) echo "DESIGN_NOT_AVAILABLE: $D --version exited 137 (killed at launch; on macOS usually an invalid code signature). $_F" ;;
  *) echo "DESIGN_NOT_AVAILABLE: $D --version exited $_RC" ;;
esac

If DESIGN_NOT_AVAILABLE: Fall back to the HTML wireframe approach below (the existing DESIGN_SKETCH section). Visual mockups require the design binary.

If DESIGN_READY: Generate visual mockup explorations for the user.

Generating visual mockups of the proposed design... (say "skip" if you don't need visuals)

Step 1: Set up the design directory

SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
GSTACK_STATE_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get GSTACK_STATE_ROOT); : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
_DESIGN_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/designs/mockup-$(date +%Y%m%d)"
mkdir -p "$_DESIGN_DIR"
echo "DESIGN_DIR: $_DESIGN_DIR"

Step 2: Construct the design brief

Read DESIGN.md if it exists — use it to constrain the visual style. If no DESIGN.md, explore wide across diverse directions. The brief, and the user's feedback in Step 6, go in private files:

_GT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.gstack/tmp"
mkdir -p "$_GT" && chmod 700 "$_GT" || { echo "Not sent: cannot create $_GT for the text file." >&2; exit 1; }
_EX=$(git rev-parse --git-path info/exclude 2>/dev/null) && mkdir -p "$(dirname "$_EX")" && { grep -qxF '/.gstack/tmp/' "$_EX" 2>/dev/null || echo '/.gstack/tmp/' >> "$_EX"; }
BRIEF_FILE=$(mktemp "${_GT:?}/brief.XXXXXX") || { echo "Not sent: mktemp failed in $_GT." >&2; exit 1; }; echo "BRIEF_FILE: $BRIEF_FILE (name: ${BRIEF_FILE##*/})"
FEEDBACK_FILE=$(mktemp "${_GT:?}/feedback.XXXXXX") || { echo "Not sent: mktemp failed in $_GT." >&2; exit 1; }; echo "FEEDBACK_FILE: $FEEDBACK_FILE (name: ${FEEDBACK_FILE##*/})"

Write the text into each printed file with your file-write tool (Claude Code's Write tool needs a Read of the empty file first), exactly as it should appear. The text never goes into a shell command, heredoc or quoted argument. If a write fails or is refused, do not send: print the cause, the file path and the command below for sending by hand.

Step 3: Generate 3 variants (substitute the printed name for <brief-file-name>)

BRIEF_FILE="$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.gstack/tmp/<brief-file-name>"
[ -s "$BRIEF_FILE" ] || { echo "Not run: $BRIEF_FILE is missing or empty. Write the brief, then rerun this block." >&2; exit 1; }
_OUT=$($D variants --brief "$(cat "$BRIEF_FILE")" --count 3 --output-dir "$_DESIGN_DIR/"); _RC=$?
printf '%s\n' "$_OUT"; echo "EXIT: $_RC"

This generates 3 style variations of the same brief (~40 seconds total).

Round accounting (before any board, check or inline view): $D never overwrites; a taken name is bumped (for example -2), so use only the paths its JSON printed and carry them as literal paths into later blocks (bash blocks do not share variables). Tell the user how many of requested paid images were saved (saved) and name each failures entry. Route on the exit code: 0 continue with saved; 3 continue with saved and say the run stopped early; 2 nothing was saved, so report failures and stop: do not run $D compare or $D check.

Step 4: Show variants inline, then open comparison board

Show each saved image to the user inline first (read the printed paths with Read tool), then create and serve the comparison board:

Write this round's board images (printed paths that passed checks, in order) as a JSON array to $_DESIGN_DIR/board-images.json with the Write tool; board letters A, B, C follow that order. Then archive any earlier Submit so it cannot approve these images, and build the board:

[ -f "${_DESIGN_DIR:?set _DESIGN_DIR to the design dir printed above}/feedback.json" ] && mv "${_DESIGN_DIR:?}/feedback.json" "${_DESIGN_DIR:?}/feedback-$(date -u +%Y%m%dT%H%M%SZ).json"
$D compare --images-file "$_DESIGN_DIR/board-images.json" --output "$_DESIGN_DIR/design-board.html" --serve

This publishes the board to the design daemon, opens it in the user's default browser, and exits; it does not wait for feedback. Read captured stderr for the BOARD_URL: http://127.0.0.1:N/boards/<id>/ line, then wait with AskUserQuestion: "Review <BOARD_URL>, click Submit or Regenerate, then tell me (or paste your preferences here)." After the answer, read feedback.json or feedback-pending.json next to the board HTML.

If the command exits nonzero or prints no BOARD_URL, show each variant inline with Read and fall back to AskUserQuestion: "Which variant do you prefer? Any feedback?"

Step 5: Handle feedback

If the JSON contains "regenerated": true:

  1. Read regenerateAction (or remixSpec for remix requests)
  2. Generate new variants with $D iterate or $D variants using the updated brief, rewritten into the brief file (capture and round accounting as in Step 3)
  3. Rebuild the board with Step 4's board block, without --serve
  4. POST the new HTML to the running board. Parse the board URL from stderr (BOARD_URL: http://127.0.0.1:N/boards/<id>/ — the daemon path) or fall back to the legacy port (SERVE_STARTED: port=N — only emitted under --no-daemon, hits /api/reload root). Daemon path: jq -nc --arg html "$_DESIGN_DIR/design-board.html" '{html: $html}' | curl -sS -X POST "${BOARD_URL}api/reload" -H 'Content-Type: application/json' --data-binary @-
  5. Board auto-refreshes in the same tab

If "regenerated": false: proceed with the approved variant.

Step 6: Save approved choice

Write the user's feedback (none when they gave none) into the feedback file from Step 2, then map the confirmed letter through this board's board-images.json (never the directory listing) and save it, substituting the printed name for <feedback-file-name>:

_IMG=$(jq -r --arg v "<VARIANT>" '.[($v | explode[0]) - 65] // empty' "$_DESIGN_DIR/board-images.json")
if [ -n "$_IMG" ]; then
  FEEDBACK_FILE="$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.gstack/tmp/<feedback-file-name>"
  [ -s "$FEEDBACK_FILE" ] || { echo "Not saved: $FEEDBACK_FILE is missing or empty. Write the feedback, then rerun this block." >&2; exit 1; }
  jq -n --arg approved_variant "<VARIANT>" --arg approved_path "$(basename "$_IMG")" --rawfile feedback "$FEEDBACK_FILE" --arg date "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg screen "mockup" --arg branch "$(git branch --show-current 2>/dev/null)" '$ARGS.named | .feedback |= rtrimstr("\n")' > "$_DESIGN_DIR/approved.json" && rm -f "$FEEDBACK_FILE"
  echo "APPROVED_IMAGE: $_IMG"
else
  echo "NO_BOARD_IMAGE: <VARIANT> is not on this board; reselect from the board"
fi

Reference the printed APPROVED_IMAGE in the design doc or plan.

Visual Sketch (UI ideas only)

If the chosen approach involves user-facing UI (screens, pages, forms, dashboards, or interactive elements), generate a rough wireframe to help the user visualize it. If the idea is backend-only, infrastructure, or has no UI component — skip this section silently.

Step 1: Gather design context

  1. Check if DESIGN.md exists in the repo root. If it does, read it for design system constraints (colors, typography, spacing, component patterns). Use these constraints in the wireframe.
  2. Apply core design principles:
    • Information hierarchy — what does the user see first, second, third?
    • Interaction states — loading, empty, error, success, partial
    • Edge case paranoia — what if the name is 47 chars? Zero results? Network fails?
    • Subtraction default — "as little design as possible" (Rams). Every element earns its pixels.
    • Design for trust — every interface element builds or erodes user trust.

Step 2: Generate wireframe HTML

Generate a single-page HTML file with these constraints:

  • Intentionally rough aesthetic — use system fonts, thin gray borders, no color, hand-drawn-style elements. This is a sketch, not a polished mockup.
  • Self-contained — no external dependencies, no CDN links, inline CSS only
  • Show the core interaction flow (1-3 screens/states max)
  • Include realistic placeholder content (not "Lorem ipsum" — use content that matches the actual use case)
  • Add HTML comments explaining design decisions

Create a private directory for it first — the renderer serves that whole directory over loopback, so it must be yours alone and hold nothing else (never a fixed, shared /tmp name another user could pre-create):

mktemp -d "${TMPDIR:-/tmp}/gstack-sketch.XXXXXX"

Write the sketch to <that directory>/sketch.html (Write tool).

Step 3: Render and capture

gstack-render opens the sketch in the Aside browser when it is running — otherwise in gstack's own headless browser (its first line says which: ENGINE=aside or ENGINE=browse) — and screenshots it:

bun --no-env-file --config=/dev/null run $HOME/.claude/skills/gstack/bin/gstack-render.ts <sketch-dir>/sketch.html --screenshot <sketch-dir>/sketch.png --width 1280

Only if it prints NEEDS_ASIDE or ASIDE_NOT_RUNNING followed by ERROR: no browser available (Aside is not open AND gstack's own browser is not built), skip the render step. Tell the user: "The visual sketch renders through the Aside browser (macOS 15+, aside.com) or gstack's own browser. Open Aside, or run ./setup in the gstack repo, and I'll render the wireframe." Never install either for them.

Step 4: Present and iterate

Show the screenshot to the user. Ask: "Does this feel right? Want to iterate on the layout?"

If they want changes, regenerate the HTML with their feedback and re-render. If they approve or say "good enough," proceed.

Step 5: Include in design doc

Reference the wireframe screenshot in the design doc's "Recommended Approach" section. The screenshot file at <sketch-dir>/sketch.png (name the full path in the doc) can be referenced by downstream skills (/plan-design-review, /design-review) to see what was originally envisioned.

Step 6: Outside design voices (optional)

After the wireframe is approved, offer outside design perspectives:


if ! _OUTSIDE_CFG=$("$HOME/.claude/skills/gstack/bin/gstack-config" get --stored codex_reviews) || [ "$_OUTSIDE_CFG" = disabled ]; then
  echo 'CODEX_MODE: disabled'
elif ( # GSTACK_ACTIVE_HOST names the harness, never the model.
if { [ -n "${CODEX_THREAD_ID:-}" ] || [ -n "${CODEX_SANDBOX:-}" ] || [ "${GSTACK_ACTIVE_HOST:-}" = codex ]; }; then
  echo 'Codex outside review unavailable: harness mismatch; no outside process started. Missing coverage.' >&2
  if { [ -n "${CLAUDECODE:-}" ] || [ "${GSTACK_ACTIVE_HOST:-}" = claude ]; } && { [ -n "${CODEX_THREAD_ID:-}" ] || [ -n "${CODEX_SANDBOX:-}" ] || [ "${GSTACK_ACTIVE_HOST:-}" = codex ]; }; then
    echo 'Inherited harness markers conflict. Run setup --host <actual-harness> (claude or codex); do not guess a replacement provider.' >&2
  else
    echo 'Repair installed skills: run setup --host codex from your gstack checkout.' >&2
  fi
  exit 78
fi
); then
  if command -v codex >/dev/null 2>&1; then echo 'CODEX_MODE: ready'; else echo 'CODEX_MODE: not_installed'; fi
else
  echo 'CODEX_MODE: under_current_harness'
fi

The historical CODEX_MODE variable describes Codex availability here. CODEX_MODE: disabled or a send's consent_missing line: codex_reviews is off or unreadable; do not ask; record outside_status: disabled. Authentication and configured model validity are checked by the actual invocation, without overriding either. Missing/broken CLI: install or repair Codex; authentication failure: run codex login. Otherwise honor this caller’s opt-in/skip choice. Any non-ready outcome is missing outside coverage; follow the caller’s existing fallback. Never substitute another external provider.

If Codex is available, use AskUserQuestion:

"Want outside design perspectives on the chosen approach? Codex proposes a visual thesis, content plan, and interaction ideas. A Claude subagent proposes an alternative aesthetic direction."

A) Yes — get outside design voices B) No — proceed without

If user chooses A, run both independent voices below and wait for both results before synthesis. They may overlap when the host supports parallel tool calls; the native subagent call remains blocking.

  1. Codex (via Bash, model_reasoning_effort="medium"): Prompt: "For this product approach, provide: a visual thesis (one sentence — mood, material, energy), a content plan (hero → support → detail → CTA), and 2 interaction ideas that change page feel. Apply beautiful defaults: composition-first, brand-first, cardless, poster not document. Be opinionated." Include the approved product approach and wireframe source in the prepared prompt.

Write the complete prompt and context, including actual plan/spec/source, to a private file. Substitute its shell-quoted path for <prepared-prompt-file>; never interpolate user text into shell source. Request a complete design proposal ending with Recommendation: because .

# GSTACK_ACTIVE_HOST names the harness, never the model.
if { [ -n "${CODEX_THREAD_ID:-}" ] || [ -n "${CODEX_SANDBOX:-}" ] || [ "${GSTACK_ACTIVE_HOST:-}" = codex ]; }; then
  echo 'Codex outside review unavailable: harness mismatch; no outside process started. Missing coverage.' >&2
  if { [ -n "${CLAUDECODE:-}" ] || [ "${GSTACK_ACTIVE_HOST:-}" = claude ]; } && { [ -n "${CODEX_THREAD_ID:-}" ] || [ -n "${CODEX_SANDBOX:-}" ] || [ "${GSTACK_ACTIVE_HOST:-}" = codex ]; }; then
    echo 'Inherited harness markers conflict. Run setup --host <actual-harness> (claude or codex); do not guess a replacement provider.' >&2
  else
    echo 'Repair installed skills: run setup --host codex from your gstack checkout.' >&2
  fi
  exit 78
fi

_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo 'ERROR: not in a git repo' >&2; exit 1; }
_OUTSIDE_TMP=$(mktemp -d "${TMPDIR:-/tmp}/gstack-outside.XXXXXXXX") || exit 1
trap 'rm -rf "$_OUTSIDE_TMP"' EXIT
_OUTSIDE_INPUT="$_OUTSIDE_TMP/prompt"
cat -- '<prepared-prompt-file>' >"$_OUTSIDE_INPUT" || exit 1

_CODEX_PROBE="$HOME/.claude/skills/gstack/bin/gstack-codex-probe"
_CODEX_OUT=$("$_CODEX_PROBE" select-model exec) || exit 1
_CODEX_SEL=$(echo "$_CODEX_OUT" | sed -n 's/^CODEX_SEL: //p')
_CODEX_SANDBOX_MODE=$(echo "$_CODEX_OUT" | sed -n 's/^CODEX_SANDBOX: //p')
"$_CODEX_PROBE" check-sandbox || exit 1
"$_CODEX_PROBE" show-first-use-notice
_OUTSIDE_EXIT=0
"$_CODEX_PROBE" run-with-timeout --effort medium 300 codex exec - -C "$_REPO_ROOT" -s "${_CODEX_SANDBOX_MODE:?}" -c "model=\"${_CODEX_SEL:?}\"" -c skills.include_instructions=false -c 'web_search="cached"' --json -o "$_OUTSIDE_TMP/text" <"$_OUTSIDE_INPUT" >"$_OUTSIDE_TMP/events" 2>"$_OUTSIDE_TMP/stderr" || _OUTSIDE_EXIT=$?
cat "$_OUTSIDE_TMP/text" 2>/dev/null || tail -n 20 "$_OUTSIDE_TMP/events"

cat "$_OUTSIDE_TMP/stderr" >&2 || { [ "$_OUTSIDE_EXIT" -ne 0 ] || _OUTSIDE_EXIT=1; }
_OUTSIDE_RC=0
bun --no-env-file --config=/dev/null "$HOME/.claude/skills/gstack/lib/outside-review-result.ts" --label 'Codex outside review' --exit "$_OUTSIDE_EXIT" --stderr "$_OUTSIDE_TMP/stderr" --events "$_OUTSIDE_TMP/events" proposal "$_OUTSIDE_TMP/text" || _OUTSIDE_RC=$?
case "$_OUTSIDE_RC" in
  0|3) ;;
  4) echo 'OUTSIDE_STATUS: unverified provider=codex host=claude'; exit 4 ;;
  *) [ "$_OUTSIDE_EXIT" -ne 0 ] && exit "$_OUTSIDE_EXIT"; exit 1 ;;
esac
echo 'OUTSIDE_STATUS: completed provider=codex host=claude'

Use Bash timeout: 360000; show the full response in a tool-output fence. Require successful execution and valid markers. Refusal, empty/malformed output, missing Recommendation markers, timeout or CLI failure means outside_status: unavailable. P0/P1 findings block like native ones; OUTSIDE_STATUS: unverified is missing coverage. Continue completed proposals; native completion does not count as outside coverage. After either outcome, delete only your private prompt; scratch cleanup is automatic.

Retain the historical review-log skill ID; add "host":"claude","outside_provider":"codex","outside_status":"completed|unavailable|disabled|skipped","phase":"design-sketch". Record differing attempt outcomes separately. source:"codex" requires completed CLI output; native uses source:"in-host" (historical source:"claude": native Claude). Availability/native fallback is not outside completion. Preserve all reported modelUsage; unknown model identity stays unknown. Under GSTACK_CODEX_NO_SANDBOX=1 add "codex_sandbox":"danger-full-access".

  1. Claude subagent (via Agent tool, run_in_background: false when available — subagents default to background since Claude Code v2.1.198; A launch receipt means it went background: await its completion notice.): "For this product approach, what design direction would you recommend? What aesthetic, typography, and interaction patterns fit? What would make this approach feel inevitable to the user? Be specific — font names, hex colors, spacing values. Do not fall back on these defaults: a cream ground with a high-contrast serif and terracotta accent; near-black with one neon accent and glowing edges; italic accent words inside headlines; numbered 01 / 02 / 03 section labels; tiny tracked monospace labels; pill-shaped buttons. If your first idea is one of these, name it and choose again."

Present Codex output under CODEX SAYS (design sketch): and subagent output under CLAUDE SUBAGENT (design direction):. Error handling: all non-blocking. On failure, skip and continue.