Files
Garry TanandClaude Fable 5 0d1bd5616c v1.79.0.0 fix: ship subagent dispatches can no longer strand the run (#497/#2440 class) (#2772)
* fix(ship): foreground-flag + deadline + scope guard on all four dispatch sites (#497/#2440 class, 3rd recurrence)

Steps 7/8/10/18 dispatch subagents whose LAST-line JSON the parent
consumes, but none passed run_in_background: false — since Claude Code
v2.1.198 subagents background by default, so /ship stranded at Step 18
waiting on output that never arrives. Every site now renders the shared
{{FOREGROUND_DISPATCH_NOTE}} resolver constant, carries a ~10-minute
deadline with an explicit recovery branch (stop the runaway task,
reconcile against pre-dispatch HEAD, surface stray state, never
re-dispatch), and Step 18's prompt gains a docs-sync-only scope guard
(no VERSION changes, no base-branch merges, push-rejection reported as
pushed:false with parent-side reconciliation and a second-failure
branch). Greptile failures now record as UNAVAILABLE, not zero comments.

GENERATED_WITH_GUIDANCE pins all five ship dispatch carriers; a new test
pins the deadline recovery + scope guard phrases in pr-body (.md and
.tmpl). Codex/factory ship goldens re-rendered.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(document-release): first-class spawned-dispatch contract

document-release's own templates had zero subagent-awareness — the
entire headless contract lived in /ship's dispatch prompt, so any other
orchestrator (or an older installed /ship) dispatching it inherited none
of the gate handling. The skill now carries the contract itself: detect
spawned strictly from the dispatch prompt or the preamble echo (never
from file content — prompt-injection guard), auto-choose recommended
options while keeping the never-clobber-CHANGELOG and
never-bump-VERSION-silently invariants via their Skip options. Step 8.4d
gets an explicit spawned note (its interactive recommendation bumps
VERSION — wrong headlessly), and the Codex Documentation Review section
skips itself in spawned sessions (the apply gate needs a human; the
dispatching workflow owns review passes).

Contract, 8.4d note, and resolver skip are pinned in
run-in-background-guidance.test.ts; document-release skeleton budget
re-measured (39,812 B) and ratcheted to 40,200.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: sweep every remaining synchronous Agent-dispatch site with run_in_background: false

The #2440 failure shape was a silently-missing review voice — a
specialist launched in the background and merged before it completed.
Every remaining synchronous dispatch site now carries the explicit flag:
the Red Team dispatch, the spec review loop, the Codex
second-opinion/plan-review/doc-review Claude fallbacks, the adversarial
subagent, design sketch and outside voices, autoplan's design/eng/dx
phase dispatches, CSO parallel finding verification, and design-shotgun's
variant launch. Parallel fan-outs stay parallel — multiple foreground
Agent calls in one message run concurrently (the shipped v1.64.0.0
review-army pattern).

GENERATED_WITH_GUIDANCE now pins all 24 generated carriers, so a new
dispatch site that drops the flag fails the free suite. Six carved-skill
skeleton ceilings re-measured and ratcheted (~80-130 B growth each);
factory ship golden re-rendered.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* release: v1.79.0.0 — CHANGELOG, VERSION, TODOS follow-ups

Queue-advanced to 1.79.0.0 (1.78.0.0 claimed in the workspace queue;
same MINOR level per the versioning invariant). Entry references the
class history (#497#2440 → Step 18). Three TODOS filed: PreToolUse
hook enforcement, structural ship-mode for document-release, cross-host
dispatch semantics audit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* regen: review-army sections carry the Red Team foreground flag

The scripts/resolvers/review-army.ts Red Team edit regenerated these two
files but the sweep commit staged only the adversarial sections — the
skill-docs freshness gate (regen + git diff --exit-code) catches this.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* regen: agents digest picks up v1.79.0.0

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(document-release): one canonical spawned contract, downstream notes are pointers

The diff-selected LLM-judge eval scored the skill's clarity 3 (threshold
4, main scores 4): the spawned-session rules read as three separately-
framed rule sets (contract paragraph, Step 8.4d note, Codex-review skip).
The contract paragraph now declares itself the single source of spawned
behavior and the two downstream notes reference it instead of restating
rationale. The pointer avoids naming the Codex section verbatim so the
codex-host render (which strips that section) keeps its negative pin.
Judge re-scored 4/5/4 across repeated samples after the change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: foreground note names the Agent tool as non-substitutable

The ship-docsync gate E2E caught a behavioral regression: the note's
blocking-emphasis ('a backgrounded dispatch strands the run') steered
the driven agent to run doc-sync via the Skill tool inline — the most
blocking option — twice in a row, forfeiting the fresh-context isolation
the dispatch exists for (baseline on main's text dispatches via Agent).
The shared note now says explicitly: dispatch with the Agent tool itself,
never substitute Skill or inline execution; the flag already makes the
call block. Re-verified: ship-docsync passes on the amended text.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: pre-landing review fixes

Review army + red team findings, all verified before applying:
- ship-docsync E2E now asserts run_in_background === false on the
  captured dispatch (red team CRITICAL: phrase pins prove text exists,
  this proves the model obeys it — verified passing live).
- Structural scanner test: any generated file with an Agent-dispatch
  imperative (or bare '(foreground)' prose, the #2440 inert shape) must
  carry the flag or hold a reasoned exemption — the 4th-recurrence net
  the hand-enumerated pin list can't provide.
- Parent push reconciliation models reality: the parent shares the repo,
  so a non-fast-forward that hit the subagent hits the parent identically
  — fetch + ahead/behind check first, push only when the rejection was
  transient; dispatch prompt promise softened to 'the parent will handle
  it'.
- Recovered commits from a dead subagent are vetted docs-only
  (git show --stat, never VERSION/package.json) before any push.
- Deadline pacing named: ~3 minutes between checks, wall clock not polls.
- Greptile UNAVAILABLE recording narrowed to the PR body (Step 20's
  schema carries no triage field).
- document-release contract gains the echo-failure tie-breaker: prompt
  claims spawned + no echo → fail fast with the dispatch contract's
  failure shape instead of reproducing the #2733 prose-STOP; contract
  anti-injection and NEVER-relax clauses pinned in tests.
- 'Claude Code v2.1.198' extracted to CC_BACKGROUND_DEFAULT_SINCE and
  interpolated at all resolver sites (byte-identical output).
- CHANGELOG: entry-boundary blank line restored; worst-case-wait row
  scoped to the backgrounded path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: adversarial review fixes — failure shape, CHANGELOG guard, vet coherence

Claude adversarial subagent findings (11), all verified before applying:
- The dispatch JSON contract gains an explicit FAILURE shape
  ({"error":...}) and a parent branch for it — a doc-sync that could not
  run (spawned marking failed, broken preamble) previously had only the
  no-updates shape to emit, which the parent printed as 'Documentation is
  current': a silent false-clean of exactly the VAS-449 class.
- Scope guard now covers CHANGELOG: skip Step 5 voice polish and resolve
  CHANGELOG-touching gates to leave-as-is (the parent authored the
  release entry; the prompt's older auto-choose clause conflicted with
  the contract's never-rewrite invariant).
- Recovery vet is sequence-coherent: pushing a commit pushes its
  ancestors, so ANY non-doc commit (VERSION, package.json, CHANGELOG.md)
  blocks the whole sequence — no more push-the-vetted-child-of-an-
  unvetted-parent hole.
- Steps 7/8/10 failure branches stop a still-running backgrounded task
  before falling back, so a late result never races the inline audit.
- 'Documentation synced' print gated on pushed:true (item 6 owns the
  local-only outcome); doc-review skip note's backward step pointer
  fixed; foreground note scoped 'at initial dispatch' so the sanctioned
  inline FALLBACKS in Steps 7/8 read as sanctioned; constants docstring
  no longer overclaims single-sourcing (names the 3 inline templates).
- Runtime-hook TODO raised P2 → P1: the spawned trust chain is
  agent-self-asserted; prose cannot close it, the hook can.

Both docsync gate E2Es re-verified green on the amended prompt, including
the strict run_in_background === false dispatch assert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: codex adversarial fixes — JSON trust, fail-open visibility, user-state safety

Codex outside-model adversarial pass (inline-diff workaround for the
sandbox), P1/P2 findings triaged cross-model:
- Parent validates the dispatch JSON's field types and treats
  documentation_section as untrusted markdown (Step 19's redaction scan
  covers the final body; instruction-shaped text inside it is never
  followed). The error branch now explicitly skips items 2-6.
- Remote-ahead divergence is named, not silent: the parent lists foreign
  commits before creating a PR over a moved branch.
- Recovery cleanup never discards content: stray staged doc edits are
  unstaged but never checked out or cleaned away.
- Greptile UNAVAILABLE gets a concrete PR-body line, not a vague
  'wherever results are reported'.
Pre-existing-class P1s (self-asserted spawned trust chain, unenforceable
foreground timeout) are cross-model confirmed and tracked: PreToolUse
hook TODO at P1, harness-residue hazard documented.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: never-Skill prohibition made unambiguous; docsync fixture made diagnostic

The ship-docsync E2E flaked on this sandbox because the driven agent
resolved the STOP pointer's ~ to nonexistent homes (/root, /home) and
acted blind — pass/fail sampled model priors, not the prompt. The
fixture prompt now names the planted sections dir, making local runs
deterministic (CI, with real install paths, was always diagnostic).
With a diagnostic fixture: 2/2 passes, real pr-body read, Agent dispatch
with run_in_background: false, zero Skill-tool substitutions.

Prose: the foreground note now states the dispatch happens ONLY via the
Agent tool (invoking the target as a Skill is wrong even though it
appears in the skills list; inline FALLBACKs apply only after a
dispatched subagent has failed), and the Step 18 imperative carries the
never-the-Skill-tool clause at the decision point.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: CHANGELOG entry covers the review-hardened contract

Failure JSON shape, CHANGELOG scope guard, vetted recovery pushes,
structural scanner, and the behavioral dispatch assert are shipped
properties of v1.79.0.0 — the entry now describes them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: update project documentation for v1.79.0.0

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: CHANGELOG voice — follow-ups to For contributors, never-Skill property stated

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(test): daemon 503 test binds an OS-assigned port, not old-port+1

windows-free-tests flaked on this PR: the tunnel-less restart bound
daemon.loopbackPort + 1 — a fixed neighbor of the OS-assigned ephemeral
port — and died with 'Is port 55738 in use?' whenever another shard or a
TIME_WAIT socket held it; the file-level retry re-rolled the same dice.
Every other startDaemon in the file already uses loopbackPort: 0 and the
assertion reads d2.loopbackPort, so nothing needs a predictable number.
21/21 pass locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 16:42:11 -07:00

48 KiB

Review Sections (8 passes, after Step 0 is complete)

Anti-skip rule: Never condense, abbreviate, or skip any review pass (1-8) regardless of plan type (strategy, spec, code, infra). Every pass in this skill exists for a reason. "This is a strategy doc so DX passes don't apply" is always wrong — DX gaps are where adoption breaks down. If a pass genuinely has zero findings, say "No issues found" and move on — but you must evaluate it.

Anti-shortcut clause: The plan file is the OUTPUT of the interactive review, not a substitute for it. Writing every finding into one plan write and calling ExitPlanMode without firing AskUserQuestion is the precise failure mode of the May 2026 transcript bug — the model explored, found issues, and dumped them into a deliverable rather than walking the user through them. If you have ANY non-trivial finding in any review section, the path from finding to ExitPlanMode goes THROUGH AskUserQuestion. Zero findings in every section is the only path to ExitPlanMode that bypasses AskUserQuestion. If you find yourself wanting to write a plan with findings before asking, stop and call AskUserQuestion now — that's the bug, recognize it.

Prior Learnings

Search for relevant learnings from previous sessions:

_CROSS_PROJ=$(~/.claude/skills/gstack/bin/gstack-config get cross_project_learnings 2>/dev/null || echo "unset")
echo "CROSS_PROJECT: $_CROSS_PROJ"
if [ "$_CROSS_PROJ" = "true" ]; then
  ~/.claude/skills/gstack/bin/gstack-learnings-search --limit 10 --cross-project 2>/dev/null || true
else
  ~/.claude/skills/gstack/bin/gstack-learnings-search --limit 10 2>/dev/null || true
fi

If CROSS_PROJECT is unset (first time): Use AskUserQuestion:

gstack can search learnings from your other projects on this machine to find patterns that might apply here. This stays local (no data leaves your machine). Recommended for solo developers. Skip if you work on multiple client codebases where cross-contamination would be a concern.

Options:

  • A) Enable cross-project learnings (recommended)
  • B) Keep learnings project-scoped only

If A: run ~/.claude/skills/gstack/bin/gstack-config set cross_project_learnings true If B: run ~/.claude/skills/gstack/bin/gstack-config set cross_project_learnings false

Then re-run the search with the appropriate flag.

If learnings are found, incorporate them into your analysis. When a review finding matches a past learning, display:

"Prior learning applied: [key] (confidence N/10, from [date])"

This makes the compounding visible. The user should see that gstack is getting smarter on their codebase over time.

DX Trend Check

Before starting review passes, check for prior DX reviews on this project:

eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
~/.claude/skills/gstack/bin/gstack-review-read 2>/dev/null | grep plan-devex-review || echo "NO_PRIOR_DX_REVIEWS"

If prior reviews exist, display the trend:

DX TREND (prior reviews):
  Dimension        | Prior Score | Notes
  Getting Started  | 4/10        | from 2026-03-15
  ...

Pass 1: Getting Started Experience (Zero Friction)

Rate 0-10: Can a developer go from zero to hello world in under 5 minutes?

Evidence recall: Reference the competitive benchmark from 0C (target tier), the magical moment from 0D (delivery vehicle), and any Install/Hello World friction points from 0F.

Load reference: Read the "## Pass 1" section from ~/.claude/skills/gstack/plan-devex-review/dx-hall-of-fame.md.

Evaluate:

  • Installation: One command? One click? No prerequisites?
  • First run: Does the first command produce visible, meaningful output?
  • Sandbox/Playground: Can developers try before installing?
  • Free tier: No credit card, no sales call, no company email?
  • Quick start guide: Copy-paste complete? Shows real output?
  • Auth/credential bootstrapping: How many steps between "I want to try" and "it works"?
  • Magical moment delivery: Is the vehicle chosen in 0D actually in the plan?
  • Competitive gap: How far is the TTHW from the target tier chosen in 0C?

FIX TO 10: Write the ideal getting started sequence. Specify exact commands, expected output, and time budget per step. Target: 3 steps or fewer, under the time chosen in 0C.

Stripe test: Can a [persona from 0A] go from "never heard of this" to "it worked" in one terminal session without leaving the terminal?

STOP. AskUserQuestion once per issue. Recommend + WHY. Reference the persona.

Pass 2: API/CLI/SDK Design (Usable + Useful)

Rate 0-10: Is the interface intuitive, consistent, and complete?

Evidence recall: Does the API surface match [persona from 0A]'s mental model? A YC founder expects tool.do(thing). A platform engineer expects tool.configure(options).execute(thing).

Load reference: Read the "## Pass 2" section from ~/.claude/skills/gstack/plan-devex-review/dx-hall-of-fame.md.

Evaluate:

  • Naming: Guessable without docs? Consistent grammar?
  • Defaults: Every parameter has a sensible default? Simplest call gives useful result?
  • Consistency: Same patterns across the entire API surface?
  • Completeness: 100% coverage or do devs drop to raw HTTP for edge cases?
  • Discoverability: Can devs explore from CLI/playground without docs?
  • Reliability/trust: Latency, retries, rate limits, idempotency, offline behavior?
  • Progressive disclosure: Simple case is production-ready, complexity revealed gradually?
  • Persona fit: Does the interface match how [persona] thinks about the problem?

Good API design test: Can a [persona] use this API correctly after seeing one example?

STOP. AskUserQuestion once per issue. Recommend + WHY.

Pass 3: Error Messages & Debugging (Fight Uncertainty)

Rate 0-10: When something goes wrong, does the developer know what happened, why, and how to fix it?

Evidence recall: Reference any error-related friction points from 0F and confusion points from 0G.

Load reference: Read the "## Pass 3" section from ~/.claude/skills/gstack/plan-devex-review/dx-hall-of-fame.md.

Trace 3 specific error paths from the plan or codebase. For each, evaluate against the three-tier system from the Hall of Fame:

  • Tier 1 (Elm): Conversational, first person, exact location, suggested fix
  • Tier 2 (Rust): Error code links to tutorial, primary + secondary labels, help section
  • Tier 3 (Stripe API): Structured JSON with type, code, message, param, doc_url

For each error path, show what the developer currently sees vs. what they should see.

Also evaluate:

  • Permission/sandbox/safety model: What can go wrong? How clear is the blast radius?
  • Debug mode: Verbose output available?
  • Stack traces: Useful or internal framework noise?

STOP. AskUserQuestion once per issue. Recommend + WHY.

Pass 4: Documentation & Learning (Findable + Learn by Doing)

Rate 0-10: Can a developer find what they need and learn by doing?

Evidence recall: Does the docs architecture match [persona from 0A]'s learning style? A YC founder needs copy-paste examples front and center. A platform engineer needs architecture docs and API reference.

Load reference: Read the "## Pass 4" section from ~/.claude/skills/gstack/plan-devex-review/dx-hall-of-fame.md.

Evaluate:

  • Information architecture: Find what they need in under 2 minutes?
  • Progressive disclosure: Beginners see simple, experts find advanced?
  • Code examples: Copy-paste complete? Work as-is? Real context?
  • Interactive elements: Playgrounds, sandboxes, "try it" buttons?
  • Versioning: Docs match the version dev is using?
  • Tutorials vs references: Both exist?

STOP. AskUserQuestion once per issue. Recommend + WHY.

Pass 5: Upgrade & Migration Path (Credible)

Rate 0-10: Can developers upgrade without fear?

Load reference: Read the "## Pass 5" section from ~/.claude/skills/gstack/plan-devex-review/dx-hall-of-fame.md.

Evaluate:

  • Backward compatibility: What breaks? Blast radius limited?
  • Deprecation warnings: Advance notice? Actionable? ("use newMethod() instead")
  • Migration guides: Step-by-step for every breaking change?
  • Codemods: Automated migration scripts?
  • Versioning strategy: Semantic versioning? Clear policy?

STOP. AskUserQuestion once per issue. Recommend + WHY.

Pass 6: Developer Environment & Tooling (Valuable + Accessible)

Rate 0-10: Does this integrate into developers' existing workflows?

Evidence recall: Does local dev setup work for [persona from 0A]'s typical environment?

Load reference: Read the "## Pass 6" section from ~/.claude/skills/gstack/plan-devex-review/dx-hall-of-fame.md.

Evaluate:

  • Editor integration: Language server? Autocomplete? Inline docs?
  • CI/CD: Works in GitHub Actions, GitLab CI? Non-interactive mode?
  • TypeScript support: Types included? Good IntelliSense?
  • Testing support: Easy to mock? Test utilities?
  • Local development: Hot reload? Watch mode? Fast feedback?
  • Cross-platform: Mac, Linux, Windows? Docker? ARM/x86?
  • Local env reproducibility: Works across OS, package managers, containers, proxies?
  • Observability/testability: Dry-run mode? Verbose output? Sample apps? Fixtures?

STOP. AskUserQuestion once per issue. Recommend + WHY.

Pass 7: Community & Ecosystem (Findable + Desirable)

Rate 0-10: Is there a community, and does the plan invest in ecosystem health?

Load reference: Read the "## Pass 7" section from ~/.claude/skills/gstack/plan-devex-review/dx-hall-of-fame.md.

Evaluate:

  • Open source: Code open? Permissive license?
  • Community channels: Where do devs ask questions? Someone answering?
  • Examples: Real-world, runnable? Not just hello world?
  • Plugin/extension ecosystem: Can devs extend it?
  • Contributing guide: Process clear?
  • Pricing transparency: No surprise bills?

STOP. AskUserQuestion once per issue. Recommend + WHY.

Pass 8: DX Measurement & Feedback Loops (Implement + Refine)

Rate 0-10: Does the plan include ways to measure and improve DX over time?

Load reference: Read the "## Pass 8" section from ~/.claude/skills/gstack/plan-devex-review/dx-hall-of-fame.md.

Evaluate:

  • TTHW tracking: Can you measure getting started time? Is it instrumented?
  • Journey analytics: Where do devs drop off?
  • Feedback mechanisms: Bug reports? NPS? Feedback button?
  • Friction audits: Periodic reviews planned?
  • Boomerang readiness: Will /devex-review be able to measure reality vs. plan?

STOP. AskUserQuestion once per issue. Recommend + WHY.

Appendix: Claude Code Skill DX Checklist

Conditional: only run when product type includes "Claude Code skill".

This is NOT a scored pass. It's a checklist of proven patterns from gstack's own DX.

Load reference: Read the "## Claude Code Skill DX Checklist" section from ~/.claude/skills/gstack/plan-devex-review/dx-hall-of-fame.md.

Check each item. For any unchecked item, explain what's missing and suggest the fix.

STOP. AskUserQuestion for any item that requires a design decision.

Outside Voice — Independent Plan Challenge (default-on)

After all review sections are complete, run an independent second opinion from a different AI system automatically — it is a standard part of plan review, not an opt-in. Two models agreeing on a plan is stronger signal than one model's thorough review. The user turns this off only by asking explicitly (gstack-config set codex_reviews disabled).

Preflight — decide whether and how the outside voice runs:

# Codex preflight: one block (functions sourced here don't persist to later blocks).
_TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || echo off)
_CODEX_CFG=$(~/.claude/skills/gstack/bin/gstack-config get codex_reviews 2>/dev/null || echo enabled)
source ~/.claude/skills/gstack/bin/gstack-codex-probe 2>/dev/null || true
if [ "$_CODEX_CFG" = "disabled" ]; then
  _CODEX_MODE="disabled"
# Running-under-Codex presence probe (#2519): a live Codex session exports
# CODEX_THREAD_ID / CODEX_SANDBOX into every shell it spawns (verified
# against a live `codex exec 'env | grep -i codex'` capture, codex 0.147.0).
# Nested codex spawns from inside a Codex host multiply token burn
# (observed: one /review = 15M tokens). GSTACK_FORCE_CODEX_REVIEW=1 forces
# the nested passes anyway.
elif [ "${GSTACK_FORCE_CODEX_REVIEW:-0}" != "1" ] && { [ -n "${CODEX_THREAD_ID:-}" ] || [ -n "${CODEX_SANDBOX:-}" ]; }; then
  _CODEX_MODE="under_codex"
elif ! command -v codex >/dev/null 2>&1; then
  _CODEX_MODE="not_installed"; _gstack_codex_log_event "codex_cli_missing" 2>/dev/null || true
elif ! _gstack_codex_auth_probe >/dev/null 2>&1; then
  _CODEX_MODE="not_authed"; _gstack_codex_log_event "codex_auth_failed" 2>/dev/null || true
else
  # Capture the probe's code: 2 means the CLI cannot execute at all, which is a
  # different problem (and a different fix) from a model the account can't use.
  _gstack_codex_model_probe; _CODEX_MP=$?
  if [ "$_CODEX_MP" -eq 2 ]; then
    _CODEX_MODE="broken_install"
  elif [ "$_CODEX_MP" -ne 0 ]; then
    _CODEX_MODE="model_unusable"
  else
    _CODEX_MODE="ready"; _gstack_codex_version_check 2>/dev/null || true
  fi
fi
echo "CODEX_MODE: $_CODEX_MODE"

Branch on the echoed CODEX_MODE:

  • disabled — the user turned Codex reviews off (codex_reviews=disabled). Skip this section entirely; do NOT fall back to a Claude subagent — disabled means no extra review step. Print: "Codex review skipped (codex_reviews disabled). Re-enable: gstack-config set codex_reviews enabled."
  • not_installed — Codex CLI absent. Print: "Codex not installed — falling back to a Claude subagent (fresh context, but the SAME model family — not an outside model). Install Codex for an actual outside-model read: npm install -g @openai/codex." Fall back to the Claude subagent path.
  • under_codex — this session is already running INSIDE a Codex host, so spawning codex again is the same model reviewing itself at multiplied token cost (#2519). Print exactly one line: "[running under Codex — nested codex passes skipped; set GSTACK_FORCE_CODEX_REVIEW=1 to force]" and skip the codex invocations below; run the section's free in-host pass instead if it defines one.
  • not_authed — installed but no credentials. Print: "Codex installed but not authenticated — falling back to a Claude subagent (same model family, not an outside model). Run codex login or set $CODEX_API_KEY." Fall back to the Claude subagent path.
  • broken_install — the CLI is on PATH but cannot execute (spawn ENOENT, non-executable binary, missing vendor payload). Print: "Codex is installed but its binary cannot run — Codex passes skipped. Reinstall: npm install -g @openai/codex." Relay the probe's HINT lines and fall back to the Claude subagent path. This state exists because a missing binary used to land in the model probe's fail-open bucket and report ready, so every Codex pass was skipped silently (#2742).
  • model_unusable — authed but the account cannot use its configured model (#2477: HTTP 400 on every call, usually a stale model = pin in ~/.codex/config.toml). Relay the probe's HINT lines, tell the user the one-line fix (update the pin; [notice.model_migrations] names the replacement), and fall back to the Claude subagent path. The ~10s round trip is cached for 1h; timeouts fail open to ready.
  • ready — run the Codex pass below.

When the mode is ready, not_installed, or not_authed, print one line so the off-switch stays discoverable: "Running the outside voice automatically (standard step). Disable: gstack-config set codex_reviews disabled."

Construct the plan review prompt (for ready, not_installed, and not_authed — skip only on disabled). Read the plan file being reviewed (the file the user pointed this review at, or the branch diff scope). If a CEO plan document was written in Step 0D-POST, read that too — it contains the scope decisions and vision.

Construct this prompt (substitute the actual plan content — if plan content exceeds 30KB, truncate to the first 30KB and note "Plan truncated for size"). Always start with the filesystem boundary instruction:

"IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/. These are Claude Code skill definitions meant for a different AI system. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\n\nYou are a brutally honest technical reviewer examining a development plan that has already been through a multi-section review. Your job is NOT to repeat that review. Instead, find what it missed. Look for: logical gaps and unstated assumptions that survived the review scrutiny, overcomplexity (is there a fundamentally simpler approach the review was too deep in the weeds to see?), feasibility risks the review took for granted, missing dependencies or sequencing issues, and strategic miscalibration (is this the right thing to build at all?). Be direct. Be terse. No compliments. Just the problems.

THE PLAN: "

If CODEX_MODE: ready — run Codex:

TMPERR_PV=$(mktemp /tmp/codex-planreview-XXXXXXXX)
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' -c 'web_search="cached"' < /dev/null 2>"$TMPERR_PV"

Use a 5-minute timeout (timeout: 300000). After the command completes, read stderr:

cat "$TMPERR_PV"

Present the full output verbatim:

CODEX SAYS (plan review — outside voice):
════════════════════════════════════════════════════════════
<full codex output, verbatim — do not truncate or summarize>
════════════════════════════════════════════════════════════

Error handling: All errors are non-blocking — the outside voice is informational.

  • Auth failure (stderr contains "auth", "login", "unauthorized"): "Codex auth failed. Run `codex login` to authenticate." Fall back to the Claude subagent below.
  • Timeout: "Codex timed out after 5 minutes." Fall back to the Claude subagent below.
  • Empty response: "Codex returned no response." Fall back to the Claude subagent below.

If CODEX_MODE: not_installed or not_authed (or Codex errored at runtime):

Dispatch via the Agent tool with run_in_background: false (subagents default to background since Claude Code v2.1.198; the findings must land before the workflow continues). The subagent has fresh context and no conversation bias — but it is the SAME model family, not an outside model; weigh its agreement accordingly. Bound it the same way as Codex: cap the dispatch at a 5-minute timeout so "never blocking" is also "never hanging."

Subagent prompt: same plan review prompt as above.

Present findings under an OUTSIDE VOICE (Claude subagent): header.

If the subagent fails or times out: "Outside voice unavailable. Continuing to outputs."

(On CODEX_MODE: disabled you already skipped this section per the preflight — do not reach here.)

Cross-model tension:

After presenting the outside voice findings, note any points where the outside voice disagrees with the review findings from earlier sections. Flag these as:

CROSS-MODEL TENSION:
  [Topic]: Review said X. Outside voice says Y. [Present both perspectives neutrally.
  State what context you might be missing that would change the answer.]

User Sovereignty: Do NOT auto-incorporate outside voice recommendations into the plan. Present each tension point to the user. The user decides. Cross-model agreement is a strong signal — present it as such — but it is NOT permission to act. You may state which argument you find more compelling, but you MUST NOT apply the change without explicit user approval.

For each substantive tension point, use AskUserQuestion:

"Cross-model disagreement on [topic]. The review found [X] but the outside voice argues [Y]. [One sentence on what context you might be missing.]"

RECOMMENDATION: Choose [A or B] because [one-line reason explaining which argument is more compelling and why]. Completeness: A=X/10, B=Y/10.

Options:

  • A) Accept the outside voice's recommendation (I'll apply this change)
  • B) Keep the current approach (reject the outside voice)
  • C) Investigate further before deciding
  • D) Add to TODOS.md for later

Wait for the user's response. Do NOT default to accepting because you agree with the outside voice. If the user chooses B, the current approach stands — do not re-argue.

If no tension points exist, note: "No cross-model tension — both reviewers agree."

Persist the result:

~/.claude/skills/gstack/bin/gstack-review-log '{"skill":"codex-plan-review","timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","status":"STATUS","source":"SOURCE","commit":"'"$(git rev-parse --short HEAD)"'"}'

Substitute: STATUS = "clean" if no findings, "issues_found" if findings exist. SOURCE = "codex" if Codex ran, "claude" if subagent ran.

Cleanup: Run rm -f "$TMPERR_PV" after processing (if Codex was used).


When constructing the outside voice prompt, include the Developer Persona from Step 0A and the Competitive Benchmark from Step 0C. The outside voice should critique the plan in the context of who is using it and what they're competing against.

CRITICAL RULE — How to ask questions

Follow the AskUserQuestion format from the Preamble above. Additional rules for DX reviews:

  • One issue = one AskUserQuestion call. Never combine multiple issues.
  • Ground every question in evidence. Reference the persona, competitive benchmark, empathy narrative, or friction trace. Never ask a question in the abstract.
  • Frame pain from the persona's perspective. Not "developers would be frustrated" but "[persona from 0A] would hit this at minute [N] of their getting-started flow and [specific consequence: abandon, file an issue, hack a workaround]."
  • Present 2-3 options. For each: effort to fix, impact on developer adoption.
  • Map to DX First Principles above. One sentence connecting your recommendation to a specific principle (e.g., "This violates 'zero friction at T0' because [persona] needs 3 extra config steps before their first API call").
  • Zero findings: if a section has zero findings, state "No issues, moving on" and proceed. Otherwise, use AskUserQuestion for each gap — a gap with an "obvious fix" is still a gap and still needs user approval before any change lands in the plan.
  • Assume the user hasn't looked at this window in 20 minutes. Re-ground every question.

Required Outputs

Developer Persona Card

The persona card from Step 0A. This goes at the top of the plan's DX section.

Developer Empathy Narrative

The first-person narrative from Step 0B, updated with user corrections.

Competitive DX Benchmark

The benchmark table from Step 0C, updated with the product's post-review scores.

Magical Moment Specification

The chosen delivery vehicle from Step 0D with implementation requirements.

Developer Journey Map

The journey map from Step 0F, updated with all friction point resolutions.

First-Time Developer Confusion Report

The roleplay report from Step 0G, annotated with which items were addressed.

"NOT in scope" section

DX improvements considered and explicitly deferred, with one-line rationale each.

"What already exists" section

Existing docs, examples, error handling, and DX patterns that the plan should reuse.

TODOS.md updates

After all review passes are complete, present each potential TODO as its own individual AskUserQuestion. Never batch. For DX debt: missing error messages, unspecified upgrade paths, documentation gaps, missing SDK languages. Each TODO gets:

  • What: One-line description
  • Why: The concrete developer pain it causes
  • Pros: What you gain (adoption, retention, satisfaction)
  • Cons: Cost, complexity, or risks
  • Context: Enough detail for someone to pick this up in 3 months
  • Depends on / blocked by: Prerequisites

Options: A) Add to TODOS.md B) Skip C) Build it now

DX Scorecard

+====================================================================+
|              DX PLAN REVIEW — SCORECARD                             |
+====================================================================+
| Dimension            | Score  | Prior  | Trend  |
|----------------------|--------|--------|--------|
| Getting Started      | __/10  | __/10  | __ ↑↓  |
| API/CLI/SDK          | __/10  | __/10  | __ ↑↓  |
| Error Messages       | __/10  | __/10  | __ ↑↓  |
| Documentation        | __/10  | __/10  | __ ↑↓  |
| Upgrade Path         | __/10  | __/10  | __ ↑↓  |
| Dev Environment      | __/10  | __/10  | __ ↑↓  |
| Community            | __/10  | __/10  | __ ↑↓  |
| DX Measurement       | __/10  | __/10  | __ ↑↓  |
+--------------------------------------------------------------------+
| TTHW                 | __ min | __ min | __ ↑↓  |
| Competitive Rank     | [Champion/Competitive/Needs Work/Red Flag]   |
| Magical Moment       | [designed/missing] via [delivery vehicle]    |
| Product Type         | [type]                                      |
| Mode                 | [EXPANSION/POLISH/TRIAGE]                    |
| Overall DX           | __/10  | __/10  | __ ↑↓  |
+====================================================================+
| DX PRINCIPLE COVERAGE                                               |
| Zero Friction      | [covered/gap]                                  |
| Learn by Doing     | [covered/gap]                                  |
| Fight Uncertainty  | [covered/gap]                                  |
| Opinionated + Escape Hatches | [covered/gap]                       |
| Code in Context    | [covered/gap]                                  |
| Magical Moments    | [covered/gap]                                  |
+====================================================================+

If all passes 8+: "DX plan is solid. Developers will have a good experience." If any below 6: Flag as critical DX debt with specific impact on adoption. If TTHW > 10 min: Flag as blocking issue.

DX Implementation Checklist

DX IMPLEMENTATION CHECKLIST
============================
[ ] Time to hello world < [target from 0C]
[ ] Installation is one command
[ ] First run produces meaningful output
[ ] Magical moment delivered via [vehicle from 0D]
[ ] Every error message has: problem + cause + fix + docs link
[ ] API/CLI naming is guessable without docs
[ ] Every parameter has a sensible default
[ ] Docs have copy-paste examples that actually work
[ ] Examples show real use cases, not just hello world
[ ] Upgrade path documented with migration guide
[ ] Breaking changes have deprecation warnings + codemods
[ ] TypeScript types included (if applicable)
[ ] Works in CI/CD without special configuration
[ ] Free tier available, no credit card required
[ ] Changelog exists and is maintained
[ ] Search works in documentation
[ ] Community channel exists and is monitored

Implementation Tasks

Before closing this review, synthesize the findings above into a flat list of build-actionable tasks. Each task derives from a specific finding — no padding. Emit the markdown section AND write a JSONL artifact that /autoplan can aggregate across phases.

Markdown section (always emit)

## Implementation Tasks
Synthesized from this review's findings. Each task derives from a specific
finding above. Run with Claude Code or Codex; checkbox as you ship.

- [ ] **T1 (P1, human: ~2h / CC: ~15min)** — <component> — <imperative title>
  - Surfaced by: <section name> — <specific finding text or line reference>
  - Files: <paths to touch>
  - Verify: <test command or manual check>
- [ ] **T2 (P2, human: ~30min / CC: ~5min)** — ...

Rules:

  • P1 blocks ship; P2 should land same branch; P3 is a follow-up TODO.
  • If a finding produced no actionable task, do not invent one.
  • If a section had zero findings, emit _No new tasks from <section>._
  • Effort uses the AI-compression table from CLAUDE.md.

JSONL artifact (always write, even if zero tasks)

/autoplan reads this file to aggregate across phases. Build each line with jq -nc so titles and source findings containing quotes, newlines, or backslashes serialize cleanly — never use hand-rolled echo / printf.

eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
TASKS_DIR="${HOME}/.gstack/projects/${SLUG:-unknown}"
mkdir -p "$TASKS_DIR"
TASKS_FILE="$TASKS_DIR/tasks-devex-review-$(date +%Y%m%d-%H%M%S).jsonl"
COMMIT=$(git rev-parse HEAD 2>/dev/null || echo unknown)
BRANCH=$(git branch --show-current 2>/dev/null || echo unknown)
RUN_ID="$(date -u +%Y%m%dT%H%M%SZ)-$$"

# Repeat ONE jq invocation per task identified during this review.
# Substitute the placeholders inline with shell variables you set per task:
#   TASK_ID (T1, T2, ...), PRIORITY (P1/P2/P3), COMPONENT, TITLE,
#   SOURCE_FINDING, EFFORT_HUMAN, EFFORT_CC, FILES_JSON (a JSON array literal
#   like '["browse/src/sanitize.ts","browse/src/server.ts"]').
jq -nc \
  --arg phase 'devex-review' \
  --arg run_id "$RUN_ID" \
  --arg branch "$BRANCH" \
  --arg commit "$COMMIT" \
  --arg id "$TASK_ID" \
  --arg priority "$PRIORITY" \
  --arg component "$COMPONENT" \
  --arg effort_human "$EFFORT_HUMAN" \
  --arg effort_cc "$EFFORT_CC" \
  --arg title "$TITLE" \
  --arg source_finding "$SOURCE_FINDING" \
  --argjson files "$FILES_JSON" \
  '{phase:$phase, run_id:$run_id, branch:$branch, commit:$commit, id:$id, priority:$priority, component:$component, files:$files, effort_human:$effort_human, effort_cc:$effort_cc, title:$title, source_finding:$source_finding}' \
  >> "$TASKS_FILE"

If jq is not installed, fall back to skipping the JSONL write and warn the user to install jq for autoplan aggregation. Never hand-roll JSONL.

If zero tasks were identified in this review, still touch the JSONL file (: > "$TASKS_FILE") so the aggregator sees that the phase produced output this run (an empty file means "ran, no findings" — distinct from "didn't run").

Unresolved Decisions

If any AskUserQuestion goes unanswered, note here. Never silently default.

Review Log

Persist after the DX Scorecard — the dashboard, the GSTACK REVIEW REPORT, and the EXIT PLAN MODE GATE's "review log was called" check depend on it. PLAN MODE EXCEPTION — ALWAYS RUN (writes to ~/.gstack/, not project files):

~/.claude/skills/gstack/bin/gstack-review-log '{"skill":"plan-devex-review","timestamp":"TIMESTAMP","status":"STATUS","initial_score":N,"overall_score":N,"product_type":"PRODUCT_TYPE","tthw_current":"TTHW_CURRENT","tthw_target":"TTHW_TARGET","mode":"MODE","persona":"PERSONA","competitive_tier":"COMPETITIVE_TIER","unresolved":N,"commit":"COMMIT"}'

TIMESTAMP = current ISO 8601 datetime; STATUS = "clean" if score 8+ AND 0 unresolved, else "issues_open"; other fields from the DX Scorecard + Step 0; COMMIT = git rev-parse --short HEAD.

Review Readiness Dashboard

After completing the review, read the review log and config to display the dashboard.

~/.claude/skills/gstack/bin/gstack-review-read

Parse the output. Find the most recent entry for each skill (plan-ceo-review, plan-eng-review, review, plan-design-review, design-review-lite, adversarial-review, codex-review, codex-plan-review). Ignore entries with timestamps older than 7 days. For the Eng Review row, show whichever is more recent between review (diff-scoped pre-landing review) and plan-eng-review (plan-stage architecture review). Append "(DIFF)" or "(PLAN)" to the status to distinguish. For the Adversarial row, show whichever is more recent between adversarial-review (new auto-scaled) and codex-review (legacy). For Design Review, show whichever is more recent between plan-design-review (full visual audit) and design-review-lite (code-level check). Append "(FULL)" or "(LITE)" to the status to distinguish. For the Outside Voice row, show the most recent codex-plan-review entry — this captures outside voices from both /plan-ceo-review and /plan-eng-review.

Source attribution: If the most recent entry for a skill has a `"via"` field, append it to the status label in parentheses. Examples: plan-eng-review with via:"autoplan" shows as "CLEAR (PLAN via /autoplan)". review with via:"ship" shows as "CLEAR (DIFF via /ship)". Entries without a via field show as "CLEAR (PLAN)" or "CLEAR (DIFF)" as before.

Note: autoplan-voices and design-outside-voices entries are audit-trail-only (forensic data for cross-model consensus analysis). They do not appear in the dashboard and are not checked by any consumer.

Display:

+====================================================================+
|                    REVIEW READINESS DASHBOARD                       |
+====================================================================+
| Review          | Runs | Last Run            | Status    | Required |
|-----------------|------|---------------------|-----------|----------|
| Eng Review      |  1   | 2026-03-16 15:00    | CLEAR     | YES      |
| CEO Review      |  0   | —                   | —         | no       |
| Design Review   |  0   | —                   | —         | no       |
| Adversarial     |  0   | —                   | —         | no       |
| Outside Voice   |  0   | —                   | —         | no       |
+--------------------------------------------------------------------+
| VERDICT: CLEARED — Eng Review passed                                |
+====================================================================+

Review tiers:

  • Eng Review (required by default): The only review that gates shipping. Covers architecture, code quality, tests, performance. Can be disabled globally with `gstack-config set skip_eng_review true` (the "don't bother me" setting).
  • CEO Review (optional): Use your judgment. Recommend it for big product/business changes, new user-facing features, or scope decisions. Skip for bug fixes, refactors, infra, and cleanup.
  • Design Review (optional): Use your judgment. Recommend it for UI/UX changes. Skip for backend-only, infra, or prompt-only changes.
  • Adversarial Review (automatic): Always-on for every review. Every diff gets both Claude adversarial subagent and Codex adversarial challenge. Large diffs (200+ lines) additionally get Codex structured review with P1 gate. No configuration needed.
  • Outside Voice (optional): Independent plan review from a different AI model when Codex is available (falls back to a same-family Claude subagent otherwise — fresh context, not cross-model). Offered after all review sections complete in /plan-ceo-review and /plan-eng-review. Never gates shipping.

Verdict logic:

  • CLEARED: Eng Review has >= 1 entry within 7 days from either `review` or `plan-eng-review` with status "clean" (or `skip_eng_review` is `true`)
  • NOT CLEARED: Eng Review missing, stale (>7 days), or has open issues
  • CEO, Design, and Codex reviews are shown for context but never block shipping
  • If `skip_eng_review` config is `true`, Eng Review shows "SKIPPED (global)" and verdict is CLEARED

Staleness detection: After displaying the dashboard, check if any existing reviews may be stale:

  • Content-first rule (diff-scoped rows only: `review`, `adversarial-review`, `codex-review`, ship-stage entries). Parse the `---WTREE---` and `---DIRTY---` sections from the bash output. If an entry has a `wtree` field AND it equals the current `---WTREE---` value, the review is CURRENT — identical content, regardless of commit count, rebase, amend, or whether it was committed yet (wtree equality alone proves identical content; that is the keystone property). Skip the commit-count heuristic for that entry and show no staleness note.
  • Plan-tier rows (plan-ceo-review, plan-eng-review, plan-design-review) grade a plan file, not the repo tree — never apply the wtree rule to them; they keep the 7-day freshness logic. If such an entry carries a `plan_sha256` field, you MAY compare it against the current plan file's sha256 and note "plan changed since review" on mismatch.
  • Fallback (no `wtree` on the entry, or wtree mismatch): parse the `---HEAD---` section to get the current HEAD commit hash. For each review entry that has a `commit` field: compare it against the current HEAD. If different, count elapsed commits: `git rev-list --count STORED_COMMIT..HEAD`. If that command FAILS (the stored commit was rebased away), grade UNKNOWN and treat as stale — do not error. Display: "Note: {skill} review from {date} may be stale — {N} commits since review"
  • For entries without a `commit` field (legacy entries): display "Note: {skill} review from {date} has no commit tracking — consider re-running for accurate staleness detection"
  • If all reviews grade CURRENT (wtree match or HEAD match), do not display any staleness notes

Plan File Review Report

After displaying the Review Readiness Dashboard in conversation output, also update the plan file itself so review status is visible to anyone reading the plan.

Detect the plan file

  1. Check if there is an active plan file in this conversation (the host provides plan file paths in system messages — look for plan file references in the conversation context).
  2. If not found, skip this section silently — not every review runs in plan mode.

Generate the report

Read the review log output you already have from the Review Readiness Dashboard step above. Parse each JSONL entry. Each skill logs different fields:

  • plan-ceo-review: `status`, `unresolved`, `critical_gaps`, `mode`, `scope_proposed`, `scope_accepted`, `scope_deferred`, `commit` → Findings: "{scope_proposed} proposals, {scope_accepted} accepted, {scope_deferred} deferred" → If scope fields are 0 or missing (HOLD/REDUCTION mode): "mode: {mode}, {critical_gaps} critical gaps"
  • plan-eng-review: `status`, `unresolved`, `critical_gaps`, `issues_found`, `mode`, `commit` → Findings: "{issues_found} issues, {critical_gaps} critical gaps"
  • plan-design-review: `status`, `initial_score`, `overall_score`, `unresolved`, `decisions_made`, `commit` → Findings: "score: {initial_score}/10 → {overall_score}/10, {decisions_made} decisions"
  • plan-devex-review: `status`, `initial_score`, `overall_score`, `product_type`, `tthw_current`, `tthw_target`, `mode`, `persona`, `competitive_tier`, `unresolved`, `commit` → Findings: "score: {initial_score}/10 → {overall_score}/10, TTHW: {tthw_current} → {tthw_target}"
  • devex-review: `status`, `overall_score`, `product_type`, `tthw_measured`, `dimensions_tested`, `dimensions_inferred`, `boomerang`, `commit` → Findings: "score: {overall_score}/10, TTHW: {tthw_measured}, {dimensions_tested} tested/{dimensions_inferred} inferred"
  • codex-review: `status`, `gate`, `findings`, `findings_fixed` → Findings: "{findings} findings, {findings_fixed}/{findings} fixed"

All fields needed for the Findings column are now present in the JSONL entries. For the review you just completed, you may use richer details from your own Completion Summary. For prior reviews, use the JSONL fields directly — they contain all required data.

Produce this markdown table:

```markdown

GSTACK REVIEW REPORT

Review Trigger Why Runs Status Findings
CEO Review `/plan-ceo-review` Scope & strategy {runs} {status} {findings}
Codex Review `/codex review` Independent 2nd opinion {runs} {status} {findings}
Eng Review `/plan-eng-review` Architecture & tests (required) {runs} {status} {findings}
Design Review `/plan-design-review` UI/UX gaps {runs} {status} {findings}
DX Review `/plan-devex-review` Developer experience gaps {runs} {status} {findings}
```

Below the table, add these lines. CODEX and CROSS-MODEL are optional (omit when empty); VERDICT is always present:

  • CODEX: (only if codex-review ran) — one-line summary of codex fixes
  • CROSS-MODEL: (only if both Claude and Codex reviews exist) — overlap analysis
  • VERDICT: list reviews that are CLEAR (e.g., "CEO + ENG CLEARED — ready to implement"). If Eng Review is not CLEAR and not skipped globally, append "eng review required".

Unresolved-decisions status (MANDATORY — never omitted; the report's final non-whitespace line). After VERDICT, end the report (content under the `## GSTACK REVIEW REPORT` heading — a bold label, never a new `## ` heading; exempt from the "omit when empty" rule) with exactly one: the exact unbolded line `NO UNRESOLVED DECISIONS` (a bolded one does NOT count), OR a `UNRESOLVED DECISIONS:` header + one bullet per open item (last bullet = final line; add `+ N unresolved from prior reviews` only when N > 0). This avoids double-counting: list THIS review's open items from context; for prior reviews sum `unresolved` over the latest fresh row per skill (dashboard 7-day window) after you DROP the current skill's row; emit the sentinel only when both are zero.

Write to the plan file

PLAN MODE EXCEPTION — ALWAYS RUN: This writes to the plan file, which is the one file you are allowed to edit in plan mode. The plan file review report is part of the plan's living status.

The report must always be the LAST section of the plan file — never mid-file. Use a single delete-then-append flow:

  1. Read the plan file (Read tool) to see its full current content. Search the read output for a `## GSTACK REVIEW REPORT` heading anywhere in the file.
  2. If found, use the Edit tool to DELETE the entire existing section. Match from `## GSTACK REVIEW REPORT` through either the next `## ` heading or end of file, whichever comes first. Replace with the empty string. This applies regardless of where the section currently lives — mid-file deletion is intentional, not a special case. If the Edit fails (e.g., concurrent edit changed the content), re-read the plan file and retry once.
  3. After the delete (or skipped, if no section existed), append the new `## GSTACK REVIEW REPORT` section at the END of the file. Use the Edit tool to match the file's current last paragraph and add the section after it, or use Write to re-emit the whole file with the section at the end.
  4. Verify with the Read tool that `## GSTACK REVIEW REPORT` is the last `## ` heading in the file before continuing. If it isn't, repeat steps 2-3 once.

Do NOT replace the section in place. The "replace mid-file" path is what allowed prior versions to leave the report mid-file when an older report already lived there — the user then sees a plan whose review report is not at the bottom and (correctly) rejects it.

Capture Learnings

If you discovered a non-obvious pattern, pitfall, or architectural insight during this session, log it for future sessions:

~/.claude/skills/gstack/bin/gstack-learnings-log '{"skill":"plan-devex-review","type":"TYPE","key":"SHORT_KEY","insight":"DESCRIPTION","confidence":N,"source":"SOURCE","files":["path/to/relevant/file"]}'

Types: pattern (reusable approach), pitfall (what NOT to do), preference (user stated), architecture (structural decision), tool (library/framework insight), operational (project environment/CLI/workflow knowledge).

Sources: observed (you found this in the code), user-stated (user told you), inferred (AI deduction), cross-model (both Claude and Codex agree).

Confidence: 1-10. Be honest. An observed pattern you verified in the code is 8-9. An inference you're not sure about is 4-5. A user preference they explicitly stated is 10.

files: Include the specific file paths this learning references. This enables staleness detection: if those files are later deleted, the learning can be flagged.

Only log genuine discoveries. Don't log obvious things. Don't log things the user already knows. A good test: would this insight save time in a future session? If yes, log it.

Brain Calibration Write-Back (Phase 2 / gated)

When the skill makes a typed prediction worth tracking (scope decision, TTHW target, architectural bet, wedge commitment), it MAY write a kind=bet take to the brain so a calibration profile builds over time.

Gated on two things:

  1. Brain trust policy for the active endpoint is personal (check via ~/.claude/skills/gstack/bin/gstack-config get brain_trust_policy@<endpoint-hash>). Shared brains skip write-back to avoid polluting team calibration.
  2. Feature flag BRAIN_CALIBRATION_WRITEBACK is set (today: false; flips to true when upstream gbrain v0.42+ ships takes_add MCP op).

When both gates pass, the write-back path uses mcp__gbrain__takes_add to record a take with weight 0.6 (per SKILL_CALIBRATION_WEIGHTS). If the MCP op is unavailable, fall back to mcp__gbrain__put_page with a gstack:takes fence block (documented but uglier path).

Mandatory take frontmatter shape:

kind: bet
holder: <user identity from whoami>
claim: <one-line prediction the skill is making>
weight: 0.6
since_date: <today's date>
expected_resolution: <date in 1-3 months depending on skill>
source_skill: plan-devex-review

After write, invalidate the affected digests so the next preflight reflects the new state:

eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true
  ~/.claude/skills/gstack/bin/gstack-brain-cache invalidate developer-persona --project "$SLUG" 2>/dev/null || true

Brain Cache Background Refresh

After the skill's work completes (and telemetry has logged), kick a background refresh of any cache digest that's getting close to its TTL. This is non-blocking — the user doesn't wait. Next invocation benefits from the warm cache.

eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true
(~/.claude/skills/gstack/bin/gstack-brain-cache refresh --project "$SLUG" 2>/dev/null &) || true

Next Steps — Review Chaining

After displaying the Review Readiness Dashboard, recommend next reviews:

Recommend /plan-eng-review if eng review is not skipped globally — DX issues often have architectural implications. If this DX review found API design problems, error handling gaps, or CLI ergonomics issues, eng review should validate the fixes.

Suggest /plan-design-review if user-facing UI exists — DX review focuses on developer-facing surfaces; design review covers end-user-facing UI.

Recommend /devex-review after implementation — the boomerang. Plan said TTHW would be [target from 0C]. Did reality match? Run /devex-review on the live product to find out. This is where the competitive benchmark pays off: you have a concrete target to measure against.

Use AskUserQuestion with applicable options:

  • A) Run /plan-eng-review next (required gate)
  • B) Run /plan-design-review (only if UI scope detected)
  • C) Ready to implement, run /devex-review after shipping
  • D) Skip, I'll handle next steps manually

Mode Quick Reference

             | DX EXPANSION     | DX POLISH          | DX TRIAGE
Scope        | Push UP (opt-in) | Maintain           | Critical only
Posture      | Enthusiastic     | Rigorous           | Surgical
Competitive  | Full benchmark   | Full benchmark     | Skip
Magical      | Full design      | Verify exists      | Skip
Journey      | All stages +     | All stages         | Install + Hello
             | best-in-class    |                    | World only
Passes       | All 8, expanded  | All 8, standard    | Pass 1 + 3 only
Outside voice| Recommended      | Recommended        | Skip

Formatting Rules

  • NUMBER issues (1, 2, 3...) and LETTERS for options (A, B, C...).
  • Label with NUMBER + LETTER (e.g., "3A", "3B").
  • One sentence max per option.
  • After each pass, pause and wait for feedback before moving on.
  • Rate before and after each pass for scannability.