feat(#1787): add /gsd:next smart entry workflow (#1798)

* docs: design spec for /gsd smart-entry command

Hybrid approach porting gsd-pi's smart-entry wizard to gsd-core:
deterministic classifier (gsd-tools smart-entry --json) + markdown
command/workflow with AskUserQuestion + --text fallback. Routing-first
('what now?' menu), 10 situations redesigned for gsd-core's phase loop.

* feat: add /gsd-start smart-entry command

State-aware front door adapted from gsd-pi's smart-entry wizard,
redesigned for gsd-core's markdown-first, multi-runtime architecture.

- src/smart-entry.cts: deterministic situation classifier (no-project,
  paused, blocked, verify-failed, needs-first-phase, planning, executing,
  verify-pending, idle-stranded, complete, unknown). Reads STATE.md,
  ROADMAP.md, git, and verify signals; emits JSON the workflow consumes.
- gsd-tools.cjs: wire  case + help listing.
- commands/gsd/start.md + gsd-core/workflows/gsd.md: thin markdown
  dispatcher presenting an AskUserQuestion menu (with --text fallback for
  non-Claude runtimes) and dispatching to existing commands. Falls back
  to /gsd:progress if detection is unavailable.
- help.md: document /gsd:start (parity with bug-2954).
- tests: smart-entry.unit.test.cjs (classifier behavior across all
  situations + priority + JSON shape) and gsd-workflow.structure.test.cjs
  (markdown-layer invariants + every emitted command resolves to a real
  slash command).

Spec: docs/superpowers/specs/2026-06-27-gsd-smart-entry-design.md
Note: command-contract (ADR-0002) requires a gsd:* prefix, so the bare
/gsd from the spec surfaces as /gsd-start.

* refactor: rename smart-entry command to /gsd:next

Rename the command from /gsd:start to /gsd:next per feedback. The
command file is now commands/gsd/next.md (name: gsd:next) and the
backing workflow is gsd-core/workflows/smart-entry.md (named for the
smart-entry classifier and gsd-tools smart-entry subcommand; does not
collide with the existing workflows/next.md, which is the progress
--next sub-workflow). help.md and the spec updated to match.

All affected tests (188) pass; lint:ci clean.

* fix: smart-entry reads real STATE.md schema (nested progress YAML + body Phase field)

Codex review found the classifier misread this repo's own STATE.md: it
looked only for scalar current_phase/total_phases frontmatter and body
fields named 'Current Phase'/'Total Phases', but real STATE.md stores
the phase as body 'Phase: N' and total_phases/percent under a nested
'progress:' YAML object. Both came back null, so active projects
(e.g. this repo at Phase 3 / verifying) wrongly classified as
needs-first-phase.

- detectSignals now reads total_phases + percent from nested progress{}
  first, then scalar fm, then body; current_phase falls back to the
  body 'Phase:' field (parseProsePhaseField lineage).
- Add regression tests against the real schema (nested progress YAML +
  body Phase field) covering verify-pending + executing situations.

Verified against this repo: now classifies verify-pending (was
needs-first-phase). Coverage 93.25% lines / 86.99% branches.

* fix(workflow): tiered fallback when gsd-tools is broken (not just smart-entry)

Live test exposed a self-defeating fallback: when smart-entry --json
failed because gsd-tools itself was broken (missing
markdown-sectionizer.cjs), the workflow fell back to /gsd:progress —
which also depends on gsd-tools and would dead-end too.

Replace the single /gsd:progress fallback with a tiered recovery:
1. Probe gsd_run state-snapshot. If it ALSO errors, the whole tool
   layer is down — read .planning/STATE.md directly with the Read tool
   and synthesize a minimal situation + actions menu so /gsd:next stays
   useful. Surface a rebuild hint.
2. Only if smart-entry alone is missing (older gsd-core), fall back to
   /gsd:progress as before.

Matches the direct-read resilience the live agent already did by hand.

* docs: add gsd-next skill surface

* chore: trigger no-mistakes validation

* no-mistakes(review): Fix smart-entry phase ordering

* no-mistakes(review): Fix decimal smart-entry phase ordering

* no-mistakes(test): Fix smart-entry next test contracts

* no-mistakes(document): Docs synced for smart entry

* chore: add changeset fragment for #1798 (/gsd:next smart-entry workflow)

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>

* fix: shorten next.md description and update golden install parity fixtures

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>

* fix: update /gsd-next refs to /gsd:next in docs and add Smart Entry topic alias

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>

* chore: trigger no-mistakes validation

* fix: regenerate INVENTORY-MANIFEST.json for new /gsd-next files

Full CI caught that adding commands/gsd/next.md + gsd-core/workflows/smart-entry.md
left docs/INVENTORY-MANIFEST.json stale (not in the affected-test scope that
no-mistakes' test gate runs, so it surfaced in CI). Regenerated via
node scripts/gen-inventory-manifest.cjs --write; inventory-manifest-sync
test now passes.

* fix: add 'next' to core_loop cluster, update INVENTORY-MANIFEST, fix gates.md ref

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>

* fix: regenerate golden install parity fixtures for /gsd:next

Full CI (shard 3/3) caught that adding commands/gsd/next.md + the
smart-entry workflow/lib made the per-runtime golden install parity
fixtures stale across all 16 runtimes. Regenerated via
UPDATE_GOLDEN=1 node --test tests/golden-install-parity.test.cjs.
All 16 fixtures + inventory-manifest-sync now pass.

* Fix smart-entry verify-failed phase scoping and empty resolve shim step

Scope detectVerifyFailed to STATE.md's current phase so leftover higher
phase directories cannot force verify-failed routing. Move the gsd_run
shim resolver into the workflow resolve step so agents define gsd_run
before the detect step runs smart-entry.

* fix: recapture golden fixtures with updated gates.md hash (/gsd:next)

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>

* fix: recapture all 16 golden fixtures with updated smart-entry.md hash

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>

* chore: regenerate fixtures + inventory manifest after rebase onto next

Rebased onto next which adopted #1837 (package-version normalization to
<VERSION> in golden-install-parity hashes). Recaptured the golden fixture
that needed it (hermes), re-sorted INVENTORY-MANIFEST.json, and regenerated
the gsd-next / ns-workflow skill descriptions to match the command surface.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>

* refactor(#1787): delegate /gsd:next in-project advancement to gated /gsd:progress --next

Reconciles the /gsd:next smart-entry front door with the existing
/gsd:progress --next engine (davesienkowski review on PR #1798). The
classifier previously recommended /gsd:execute-phase directly for the
`executing` situation, bypassing workflows/next.md Route 0
(resume-incomplete-phase invariant, #160) and Gates 1-3 — reproducing the
duplication that got the old flat /gsd-next removed (#3054), plus a
correctness hazard (executing the recorded current phase while an earlier
phase is silently incomplete).

Now planning/executing/verify-pending recommend `/gsd:progress --next`
(single gated engine); the specific command stays an explicit secondary.
Off-path states (no-project, paused, blocked, verify-failed,
idle-stranded, complete) keep direct recommendations — smart-entry's
distinct value over --next. Adds docs/adr/1787-gsd-next-smart-entry.md and
a regression test locking the delegation contract.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#1787): avoid literal /gsd-next token in ADR (bug-3054 guard)

The repo-invariants #3054 guard bans the removed /gsd-next slash form in
docs surfaces. Refer to the removed command as `gsd-next` (prose) — the
historical reference is unchanged, just the banned token is dropped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: gitignore compiled host-integration-sdk + handshake-serialized .cjs

Pre-existing gap from #1683: these two src/*.cts modules compile to
gsd-core/bin/lib/*.cjs but were omitted from the per-file ignore list, so
`npm run build`/`npm test` left them as untracked build artifacts (dirty
tree + accidental-commit footgun). Adds them alongside their siblings
(host-integration.cjs, mcp-server.cjs, …). Found while finishing #1798.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(#1787): lock per-situation action invariants for all 11 situations + ADR typo

Adversarial-review follow-ups:
- Add a test asserting every situation's action set has exactly one
  recommended action, 1-4 unique-id /gsd:* actions (previously the
  one-recommended/1-4 invariant was only sampled for 6 of 11 situations).
- Fix ADR typo: /gsd-progress → /gsd:progress.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#1798): split oversized test chunks so a slow shard can't trip the per-chunk timeout

Root-cause of the intermittent `full test (windows-latest, 22, shard 1/3)`
failure. It was NOT a leaked handle (the runner's kill message guesses that,
but --test-force-exit already exits leaks cleanly). Diagnosis:

- Ran every shard-1/3 file WITHOUT --test-force-exit + a 45s kill-timer:
  zero hangs, zero leaks — every file self-exits. So no leaked handle / hang.
- CI activity profile: output kept flowing (slowly) right up to the 600.0s
  kill — a dead hang would go silent. => pure slowness.
- Per-file timing: install-minimal-hooks.test.cjs is a 4987-line / 250-case
  consolidation file doing dozens of real installs — 41s even on a fast Mac
  (much worse on the slow Windows I/O path), plus an install-heavy cluster.

Mechanism: MAX_FILES_PER_CHUNK=180 packed the whole ~171-file shard into ONE
`node --test` chunk, so the entire shard's wall-clock ran against a single
600s per-chunk backstop. On slow Windows runners that single chunk crossed
600s and was killed mid-run — an intermittent false-negative gate that also
hits `next` directly.

Fix: lower MAX_FILES_PER_CHUNK 180 -> 90 so each shard splits into ~2 chunks,
each with its own fresh 600s budget and a fresh node process (also relieves
per-process memory pressure). Verified locally: shard 1/3 now runs as
chunk 1/2 (90 files) + chunk 2/2 (81 files), 5323 tests, 0 fail. Also made the
timeout kill-message name slowness as a cause instead of asserting a leak, so
the next debugger isn't sent hunting a nonexistent handle leak.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jeremy McSpadden
2026-07-03 11:18:25 -05:00
committed by GitHub
parent b42a8f45de
commit e5ef323b15
44 changed files with 2025 additions and 93 deletions

View File

@@ -93,6 +93,17 @@ node gsd-tools.cjs state-snapshot
Returns JSON with: current position, phase, plan, status, decisions, blockers, metrics, last activity.
### Smart Entry
Read-only situation classifier used by `/gsd:next`.
```bash
node gsd-tools.cjs smart-entry # Human summary + recommended route
node gsd-tools.cjs smart-entry --json # Machine-readable result for workflows
```
The JSON result contains `situation`, `recommended`, `summary`, `signals`, and ordered `actions[]`. Detection reads `.planning/STATE.md`, `ROADMAP.md`, latest verification/summary artifacts, and git status; it does not write files or dispatch commands.
---
## Phase Commands

View File

@@ -28,7 +28,7 @@ Six namespace routers ship as the first-stage entry points in v1.40. They keep t
| Command | Routes to |
|---------|-----------|
| `/gsd-workflow` | Phase pipeline — discuss / plan / execute / verify / phase / progress |
| `/gsd-workflow` | Phase pipeline — discuss / plan / execute / verify / phase / progress / next |
| `/gsd-project` | Project lifecycle — milestones, audits, summary |
| `/gsd-quality` | Quality gates — code review, debug, audit, security, eval, ui |
| `/gsd-context` | Codebase intelligence — map, graphify, docs, learnings |
@@ -578,9 +578,21 @@ node gsd-tools.cjs phase uat-passed 3 --raw # Machine-readable
## Navigation Commands
### `/gsd:next`
Open the state-aware smart-entry launcher. It reads `.planning/STATE.md`, `ROADMAP.md`, verification artifacts, and git status, classifies the current situation, shows a short menu, then dispatches exactly one existing GSD command.
This is a launcher/router only — it never performs project work directly. Detection is handled by `gsd-tools smart-entry --json`; the markdown workflow presents the menu with `AskUserQuestion` or a numbered `--text` fallback.
**Situations detected:** no project, paused work, blockers, failed verification, first-phase setup, planning, executing, pending verification, idle stranded work, complete milestone, or unknown state.
```bash
/gsd:next # Detect state and route to the best next action
```
### `/gsd-progress`
Show status, next steps, and automatically advance to the next logical workflow step. Reads project state and determines the appropriate action.
Show status, next steps, and automatically advance to the next logical workflow step. Reads project state and determines the appropriate action. Use `/gsd:next` when you want an interactive smart-entry menu before dispatch; use `/gsd-progress --next` when you want GSD to advance directly.
| Flag | Description |
|------|-------------|
@@ -1419,6 +1431,7 @@ Reviewers are prompted to verify the plan's claims against the actual repository
**Default reviewer behavior (no flags):**
- If `review.default_reviewers` is **unset**, `/gsd-review` runs all detected reviewers (current default behavior).
- If `review.default_reviewers` is **set**, `/gsd-review` runs only that subset (for example `["gemini","codex"]`).
- `review.default_reviewers` may include names from `review.reviewer_instances`; each instance runs as its own reviewer identity using its configured adapter/model. Instance names are not CLI flags.
- `--all` always overrides config and runs the full detected set.
- Explicit flags (for example `--cursor`) override both `--all` and config defaults for that run.

View File

@@ -82,6 +82,7 @@ GSD stores project settings in `.planning/config.json`. Created during `/gsd-new
},
"review": {
"default_reviewers": null,
"reviewer_instances": {},
"models": {}
},
"parallelization": {
@@ -220,7 +221,7 @@ Use `review.default_reviewers` to scope the no-flag `/gsd-review` run to a subse
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `review.default_reviewers` | string[] \| null | `null` (all detected reviewers) | Optional default subset for no-flag `/gsd-review`, e.g. `["gemini","codex"]`. Precedence is: explicit reviewer flags > `--all` > `review.default_reviewers` > all detected. Unknown slugs are ignored with a warning; known-but-undetected slugs are ignored with an info note; empty arrays are rejected by `config-set`. |
| `review.default_reviewers` | string[] \| null | `null` (all detected reviewers) | Optional default subset for no-flag `/gsd-review`, e.g. `["gemini","codex"]`. Entries may be built-in reviewer slugs or configured `review.reviewer_instances` names. Precedence is: explicit reviewer flags > `--all` > `review.default_reviewers` > all detected. Unknown slugs are ignored with a warning when no instances are configured; with `review.reviewer_instances` present, unknown entries are hard errors to catch typoed instance names. Known-but-undetected slugs are ignored with an info note; empty arrays are rejected by `config-set`. |
Example:
@@ -972,7 +973,7 @@ Configure per-CLI model selection for `/gsd-review`. When set, overrides the CLI
| `review.models.ollama` | string | (server default) | Model name passed to Ollama when `--ollama` reviewer is invoked. If unset, the first available model reported by the server is used (e.g. `llama3`). Set to a specific tag: `gsd config-set review.models.ollama codellama` |
| `review.models.lm_studio` | string | (server default) | Model name passed to LM Studio when `--lm-studio` reviewer is invoked. If unset, the first available model reported by the server is used. |
| `review.models.llama_cpp` | string | (server default) | Model name passed to llama.cpp when `--llama-cpp` reviewer is invoked. If unset, the first model reported by `/v1/models` is used. |
| `review.default_reviewers` | string[] \| null | (all detected reviewers) | Default reviewer subset for no-flag `/gsd-review`. Example: `["gemini","codex"]`. Explicit flags and `--all` override this setting. |
| `review.default_reviewers` | string[] \| null | (all detected reviewers) | Default reviewer subset for no-flag `/gsd-review`. Example: `["gemini","codex"]`. May include configured `review.reviewer_instances` names. Explicit flags and `--all` override this setting. |
| `review.max_prompt_tokens` | number\|null | null | Default maximum estimated tokens for the assembled review prompt. When set, the prompt is deterministically trimmed before being sent to each reviewer. Per-reviewer overrides via `review.max_prompt_tokens_per_reviewer` take precedence. null = no trim (current behavior). |
| `review.max_prompt_tokens_per_reviewer` | object | {} | Per-reviewer token budget overrides. Keys are reviewer slugs (ollama, llama_cpp, lm_studio, gemini, claude, codex, opencode, qwen, cursor). Values override `review.max_prompt_tokens` for that reviewer. Recommended for local model servers. |
| `review.ollama_host` | string | `http://localhost:11434` | Base URL of the Ollama server. Override when running Ollama on a non-default port or remote host: `gsd config-set review.ollama_host http://192.168.1.10:11434` |

View File

@@ -1223,6 +1223,7 @@ When verification returns `human_needed`, items are persisted as a trackable HUM
**User configuration note:**
- Set `review.default_reviewers` in `.planning/config.json` (or via `gsd config-set`) to control no-flag `/gsd-review` fan-out.
- `review.default_reviewers` may include configured `review.reviewer_instances` names; each instance runs as an independent reviewer identity backed by its configured adapter/model. Instance names are not CLI flags.
- Use `--all` for a full pre-merge sweep without changing project defaults.
- For local model servers with small context windows, set `review.max_prompt_tokens_per_reviewer` to auto-trim prompts per reviewer — see [Prompt budgets for small-context reviewers](../docs/CONFIGURATION.md#prompt-budgets-for-small-context-reviewers) in CONFIGURATION.md.
@@ -2638,7 +2639,7 @@ Users who run a memory / knowledge-base MCP server (for example, ExoCortex-style
### 122. Skill Surface Consolidation
**Purpose:** Cut the eager skill-listing overhead by folding 31 micro-skills into 4 new grouped parents and 6 existing parents that absorb sub-operations as flags. Zero functional loss — every removed micro-skill's behavior survives via a flag on a consolidated parent. After consolidation, `commands/gsd/*.md` ships 59 sub-skills (plus 6 namespace meta-skills, see #123).
**Purpose:** Cut the eager skill-listing overhead by folding 31 micro-skills into 4 new grouped parents and 6 existing parents that absorb sub-operations as flags. Zero functional loss — every removed micro-skill's behavior survives via a flag on a consolidated parent. After consolidation, `commands/gsd/*.md` ships 60 sub-skills (plus 6 namespace meta-skills, see #123).
**Requirements:**
- REQ-CONSOLIDATE-01: Four new grouped skills replace clusters of micro-skills:
@@ -2647,8 +2648,9 @@ Users who run a memory / knowledge-base MCP server (for example, ExoCortex-style
- `/gsd-config` — folds settings-advanced (`--advanced`), settings-integrations (`--integrations`), set-profile (`--profile`)
- `/gsd-workspace` — folds new-workspace (`--new`), list-workspaces (`--list`), remove-workspace (`--remove`)
- REQ-CONSOLIDATE-02: Six existing parents absorb wrap-up / sub-operations as flags: `/gsd-update --sync`, `/gsd-update --reapply`, `/gsd-sketch --wrap-up`, `/gsd-spike --wrap-up`, `/gsd-map-codebase --fast`, `/gsd-map-codebase --query`, `/gsd-code-review --fix`, `/gsd-progress --do`, `/gsd-progress --next`.
- REQ-CONSOLIDATE-03: Deleted micro-skill slash forms (the bare `gsd-add-todo`, `gsd-add-backlog`, `gsd-plant-seed`, `gsd-check-todos`, `gsd-add-phase`, `gsd-insert-phase`, `gsd-remove-phase`, `gsd-edit-phase`, `gsd-new-workspace`, `gsd-list-workspaces`, `gsd-remove-workspace`, `gsd-settings-advanced`, `gsd-settings-integrations`, `gsd-set-profile`, `gsd-sketch-wrap-up`, `gsd-spike-wrap-up`, `gsd-reapply-patches`, `gsd-code-review-fix`, …) MUST resolve to "Unknown command" — no shadow stubs.
- REQ-CONSOLIDATE-04: `autonomous.md` invokes `/gsd-code-review --fix` (was previously calling the deleted `gsd-code-review-fix`).
- REQ-CONSOLIDATE-03: `/gsd:next` is not the retired workflow-advance command; it is reserved for the state-aware smart-entry launcher. Workflow advancement remains under `/gsd-progress --next`.
- REQ-CONSOLIDATE-04: Deleted micro-skill slash forms (the bare `gsd-add-todo`, `gsd-add-backlog`, `gsd-plant-seed`, `gsd-check-todos`, `gsd-add-phase`, `gsd-insert-phase`, `gsd-remove-phase`, `gsd-edit-phase`, `gsd-new-workspace`, `gsd-list-workspaces`, `gsd-remove-workspace`, `gsd-settings-advanced`, `gsd-settings-integrations`, `gsd-set-profile`, `gsd-sketch-wrap-up`, `gsd-spike-wrap-up`, `gsd-reapply-patches`, `gsd-code-review-fix`, …) MUST resolve to "Unknown command" — no shadow stubs.
- REQ-CONSOLIDATE-05: `autonomous.md` invokes `/gsd-code-review --fix` (was previously calling the deleted `gsd-code-review-fix`).
**Reference issue:** [#2790](https://github.com/open-gsd/gsd-core/issues/2790)
@@ -2659,7 +2661,7 @@ Users who run a memory / knowledge-base MCP server (for example, ExoCortex-style
**Purpose:** Replace the flat eager skill listing with a two-stage hierarchical routing layer. The model sees 6 namespace routers instead of 86 entries, selects a namespace, then routes to the sub-skill. Descriptions use pipe-separated keyword tags (≤ 60 chars) for routing density.
**Commands:**
- `/gsd-workflow` — phase pipeline router (discuss / plan / execute / verify / phase / progress)
- `/gsd-workflow` — phase pipeline router (discuss / plan / execute / verify / phase / progress / next)
- `/gsd-project` — project lifecycle (milestones, audits, summary)
- `/gsd-quality` — quality gates (code review, debug, audit, security, eval, ui)
- `/gsd-context` — codebase intelligence (map, graphify, docs, learnings)
@@ -2677,11 +2679,13 @@ Users who run a memory / knowledge-base MCP server (for example, ExoCortex-style
- REQ-NS-01: Six `commands/gsd/ns-*.md` namespace routers ship with pipe-separated keyword-tag descriptions (≤ 60 chars).
- REQ-NS-02: Existing sub-skills are unchanged and still invocable directly — namespace skills are additive, not a replacement for direct slash forms.
- REQ-NS-03: The body of each namespace router contains a routing table that maps user intent to the correct concrete sub-skill on the post-#2790 consolidated surface.
- REQ-NS-04: Tests validate namespace files exist, include matching command `requires`, and reference only existing sub-skill files.
**Reference issue:** [#2792](https://github.com/open-gsd/gsd-core/issues/2792)
---
### 124. Context-Window Utilization Guard
**Command:** `/gsd-health --context`
@@ -3192,6 +3196,7 @@ The load-bearing wire is the `plan-phase` lift into `must_haves.prohibitions`, s
**Reference:** [Prohibition Probe](../gsd-core/references/prohibition-probe.md)
### 147. Capability Management Command
**Command:** `gsd capability install | update | remove | list | outdated | disable | enable`
@@ -3209,3 +3214,23 @@ The load-bearing wire is the `plan-phase` lift into `must_haves.prohibitions`, s
**Trust boundary:** install never executes capability code (copy-only staging); executable surfaces require explicit consent; sources are gated by the **project-scoped** `capabilities.strict_known_registries` policy (fail-closed on a malformed/unparseable value); every shared-config write/delete is realpath-confined to the scope root, and a name collision with a user's `mcpServers` entry is never clobbered.
**Reference:** [`gsd capability` command reference](reference/gsd-capability-command.md) · [ADR-1244](adr/1244-capability-ecosystem.md)
### 148. Smart Entry Launcher
**Command:** `/gsd:next`
**Tool:** `gsd-tools smart-entry [--json]`
**Purpose:** Provide a state-aware front door that reads project/workflow state, classifies the user's situation, presents a short menu, and dispatches exactly one existing GSD command.
**Requirements:**
- REQ-SMART-ENTRY-01: Detection MUST be read-only and deterministic; classification lives in `gsd-tools smart-entry`.
- REQ-SMART-ENTRY-02: The launcher MUST never perform project work directly; it only displays a menu and dispatches one command.
- REQ-SMART-ENTRY-03: The workflow MUST fall back to `/gsd-progress` if detection fails.
- REQ-SMART-ENTRY-04: Each classified situation MUST provide exactly one recommended action and valid slash commands.
- REQ-SMART-ENTRY-05: Text-mode runtimes MUST receive a numbered-list fallback instead of being stranded by interactive UI assumptions.
**Situations:** no project, paused, blocked, verify failed, needs first phase, planning, executing, verify pending, idle stranded, complete, unknown.
**Reference:** [Smart Entry Design](superpowers/specs/2026-06-27-gsd-smart-entry-design.md)
---

View File

@@ -71,6 +71,7 @@
"/gsd-mvp-phase",
"/gsd-new-milestone",
"/gsd-new-project",
"/gsd-next",
"/gsd-ns-context",
"/gsd-ns-ideate",
"/gsd-ns-manage",
@@ -182,6 +183,7 @@
"ship.md",
"sketch-wrap-up.md",
"sketch.md",
"smart-entry.md",
"spec-phase.md",
"spike-wrap-up.md",
"spike.md",
@@ -340,8 +342,8 @@
"gsd2-import.cjs",
"handshake-serialized.cjs",
"hook-bus.cjs",
"host-integration.cjs",
"host-integration-sdk.cjs",
"host-integration.cjs",
"init-command-router.cjs",
"init.cjs",
"install-engine.cjs",
@@ -402,6 +404,7 @@
"security.cjs",
"semver-compare.cjs",
"shell-command-projection.cjs",
"smart-entry.cjs",
"stale-bake-guard.cjs",
"state-command-router.cjs",
"state-document.cjs",

View File

@@ -67,7 +67,7 @@ These six routers are descriptor-only entries that the model picks first; the bo
| Command | Role | Source |
|---------|------|--------|
| `/gsd-workflow` | Phase pipeline router — discuss / plan / execute / verify / phase / progress. | [commands/gsd/ns-workflow.md](../commands/gsd/ns-workflow.md) |
| `/gsd-workflow` | Phase pipeline router — discuss / plan / execute / verify / phase / progress / next. | [commands/gsd/ns-workflow.md](../commands/gsd/ns-workflow.md) |
| `/gsd-project` | Project lifecycle router — milestones, audits, summary. | [commands/gsd/ns-project.md](../commands/gsd/ns-project.md) |
| `/gsd-quality` | Quality-gate router — code review, debug, audit, security, eval, ui. | [commands/gsd/ns-review.md](../commands/gsd/ns-review.md) |
| `/gsd-context` | Codebase-intelligence router — map, graphify, docs, learnings. | [commands/gsd/ns-context.md](../commands/gsd/ns-context.md) |
@@ -123,6 +123,7 @@ These six routers are descriptor-only entries that the model picks first; the bo
| Command | Role | Source |
|---------|------|--------|
| `/gsd:next` | State-aware smart-entry launcher — reads project state, shows a contextual menu, and dispatches one existing GSD command. | [commands/gsd/next.md](../commands/gsd/next.md) |
| `/gsd-progress` | Check project progress, show context, and route to next action; use `--next` to advance automatically or `--do` to run a freeform task. | [commands/gsd/progress.md](../commands/gsd/progress.md) |
| `/gsd-capture` | Capture ideas, tasks, notes, and seeds — todo (default), `--note`, `--backlog`, `--seed`, or `--list` pending todos. | [commands/gsd/capture.md](../commands/gsd/capture.md) |
| `/gsd-stats` | Display project statistics — phases, plans, requirements, git metrics, timeline. | [commands/gsd/stats.md](../commands/gsd/stats.md) |

View File

@@ -0,0 +1,123 @@
# ADR 1787: `/gsd:next` smart-entry front door delegates advancement to `/gsd:progress --next`
- **Status:** Accepted
- **Date:** 2026-07-03
- **Issue:** #1787
- **Implementation:** PR #1798 (`feat(#1787): add /gsd:next smart entry workflow`)
- **Supersedes context:** the removal of the flat `gsd-next` command (#3054)
## Context
gsd-core has no state-aware front door. A user must already know whether to
reach for `/gsd:progress`, `/gsd:plan-phase`, `/gsd:execute-phase`, `/gsd:quick`,
or `/gsd:new-project`. gsd-pi ships a `/gsd` smart-entry wizard (a state-aware
menu with one recommended action) that users run first; gsd-core wants the same
"run one command, get told what to do next" feel without gsd-pi's TUI, since
gsd-core is a markdown prompt framework installed into AI agents, not a Node app.
Two facts constrain the design:
1. **A `gsd-next` command already existed and was deliberately removed (#3054),**
with `/gsd:progress --next` established as the canonical "advance to the next
logical step" engine. `tests/bug-3054-stale-gsd-next-references.test.cjs`
guards against user-facing surfaces re-referencing the removed flat command.
Re-introducing a `next` entry point re-opens a settled question: it must not
recreate the duplication that justified the removal.
2. **`/gsd:progress` is the "unified GSD situational command."** Its `--next`
mode (`gsd-core/workflows/next.md`) is a *gated* advancement engine:
- **Route 0** — the resume-incomplete-phase invariant (#160): if a session
died mid-execution and `STATE.md`'s `current_phase` advanced past a phase
that still has `PLAN.md` files without matching `SUMMARY.md`, `--next`
resumes the *incomplete earlier* phase rather than the recorded current one.
- **Gates 1–3** — unresolved checkpoint, error/failed state, and unchecked
verification failures each hard-stop advancement.
The initial implementation of the new `smart-entry` classifier
(`src/smart-entry.cts`) re-derived in-project forward routing itself. For the
`executing` situation it recommended dispatching `/gsd:execute-phase` **directly**,
bypassing Route 0 and Gates 1–3. That reproduced exactly the duplication that got
`gsd-next` removed — two front doors that can route the *same* in-project state
to *different* phases — and introduced a correctness hazard (executing the
recorded current phase while an earlier phase is silently incomplete). A
maintainer (davesienkowski) flagged the overlap on PR #1798.
## Decision
Ship `/gsd:next` as a **menu front door only**, with a hard boundary against
re-implementing advancement:
1. **Detection + classification** live in Node as `gsd-tools smart-entry [--json]`
(`src/smart-entry.cts`): a pure, unit-tested classifier over `.planning/STATE.md`,
`ROADMAP.md`, and read-only git signals, producing one of 11 situations with a
recommended action and an action menu. **Presentation + dispatch** live in the
markdown layer (`commands/gsd/next.md` → `gsd-core/workflows/smart-entry.md`),
using `AskUserQuestion` with a `--text` numbered-list fallback for non-Claude
runtimes. The command carries no `requires` field so it works pre-project.
2. **In-project forward motion delegates to the single gated engine.** For the
`planning`, `executing`, and `verify-pending` situations, the *recommended*
action is `/gsd:progress --next`. smart-entry never re-derives forward routing;
it hands linear advancement to `workflows/next.md` so Route 0 and Gates 1–3 are
always honored. The specific command (`/gsd:plan-phase`, `/gsd:execute-phase`,
`/gsd:verify-work`) remains available as an explicit secondary menu option for a
user who deliberately wants to bypass advancement gating.
3. **smart-entry's distinct value is the states `--next` cannot reach.** For
situations *off* the linear advance path it keeps direct recommendations, since
these are precisely what `/gsd:progress --next` does not (or cannot, given its
`requires: [phase]`) handle: `no-project` → `/gsd:new-project`, `paused` →
`/gsd:resume-work`, `blocked` → `/gsd:debug`, `verify-failed` →
`/gsd:verify-work`, `needs-first-phase` → `/gsd:discuss-phase`, `idle-stranded`
→ `/gsd:ship`, `complete` → `/gsd:new-milestone`, `unknown` → `/gsd:progress`.
This makes the spec's stated decision #4 ("complementary, not redundant") true in
the implementation, not just the prose: there is exactly one advancement engine,
and `/gsd:next` is a menu over it plus the off-path states.
## Consequences
### Positive
- **One advancement engine.** `/gsd:next` and `/gsd:progress --next` can never
disagree about the next in-project step, and Route 0 / Gates 1–3 cannot be
bypassed through the new front door. The #3054 duplication does not return.
- **Genuine new value, no overlap.** The front door adds pre-project, remediation,
and lifecycle-exit routing that `--next` structurally cannot serve.
- **Testable boundary.** `tests/smart-entry.unit.test.cjs` asserts that every
forward-motion situation recommends `/gsd:progress --next` and every off-path
situation keeps its direct recommendation — the delegation is a regression-locked
contract, not a convention.
### Negative / trade-offs
- One extra indirection hop for the common "just continue" case (`/gsd:next` →
`/gsd:progress --next` → dispatched command) versus dispatching the phase command
directly. Accepted: the hop is what buys gate-safety and single-engine behavior.
- The classifier reads STATE.md via shared primitives (`frontmatter.cjs`,
`state-document.cjs`, `phase-id.cjs`) rather than through `workflows/next.md`'s
own detection, so detection logic exists in two places. Accepted: they share
parsing primitives and only the *routing decision* is centralized (in `--next`),
which is where divergence would actually harm the user.
## Alternatives considered
1. **Keep smart-entry as an independent in-project router (as first implemented).**
Rejected: reproduces the #3054 duplication and the Route 0 / Gates 1–3 bypass
hazard.
2. **Fold everything into `/gsd:progress` (add a menu mode) and ship no new
command.** Rejected: `/gsd:progress` carries `requires: [phase]` and cannot
serve the pre-project `no-project` front-door case, which is a primary goal.
3. **Replace `workflows/next.md`'s inline detection with the new classifier so
there is one detection *and* routing engine.** Rejected for this PR: `--next`
couples detection to safety gates and convergence flags the classifier does not
model; swapping its detection wholesale would risk regressing those invariants.
Left as possible future consolidation.
## References
- Spec: `docs/superpowers/specs/2026-06-27-gsd-smart-entry-design.md`
- Removed flat command guard: `tests/bug-3054-stale-gsd-next-references.test.cjs`
- Gated engine: `gsd-core/workflows/next.md` (Route 0 = resume-incomplete-phase, #160)
- Classifier: `src/smart-entry.cts` → `gsd-core/bin/lib/smart-entry.cjs`
- Command contract naming (`gsd:*`): ADR-0002

View File

@@ -45,6 +45,8 @@ gsd config-set review.default_reviewers '["gemini","codex"]'
For the full integration settings schema (API keys, model overrides per reviewer, local server host addresses), see [Configuration](../CONFIGURATION.md).
If you need multiple independent reviewer voices from the same adapter, configure `review.reviewer_instances` and add those instance names to `review.default_reviewers`. Instance names run only through `review.default_reviewers`; they are not valid `/gsd-review` flags. See [Reviewer instances](../CONFIGURATION.md#reviewer-instances) for the schema.
---
## Run a review

View File

@@ -0,0 +1,310 @@
# Design: /gsd Smart Entry
**Date:** 2026-06-27
**Status:** Approved — ready for implementation planning
**Origin:** Adapted from the `/gsd` smart-entry wizard in `open-gsd/gsd-pi`, redesigned for gsd-core's markdown-first, multi-runtime architecture.
---
## Summary
A `/gsd:next` command that acts as gsd-core's **state-aware front door**. It reads project + workflow state, classifies the user's situation, and presents a small menu of the right next actions — then dispatches to an existing command. The "smart" part is deterministic detection living in Node (a new `gsd-tools smart-entry` subcommand); the presentation is an idiomatic markdown command + workflow using `AskUserQuestion` with a `--text` fallback for non-Claude runtimes.
This is a **launcher / router**, not an executor. It never does the work itself.
> **Implementation note (command name):** the command-contract (ADR-0002) requires `name:` to be `gsd:*` or `gsd-*` prefixed; a bare `/gsd` is not expressible. The command is therefore `gsd:next` → `/gsd:next` (file `commands/gsd/next.md`, backed by `gsd-core/workflows/smart-entry.md`). The "smart entry" concept and behavior are unchanged; only the surfaced name differs from the original `/gsd` sketch.
---
## Motivation
gsd-pi ships a `/gsd` smart-entry wizard — a state-aware menu that branches on detected project state (phase loop, blockers, stranded work) and surfaces one well-chosen set of options with a single recommended action. It is the command users run first. gsd-core has no equivalent front door: users must already know whether to reach for `progress`, `plan-phase`, `execute-phase`, `quick`, or `new-project`.
We want the same daily-driver feel — "run `/gsd`, get told what to do next" — without fighting gsd-core's nature. gsd-core is a **markdown prompt framework installed into AI agents**, not a Node CLI app. So gsd-pi's full-screen TUI and its imperative TypeScript branch tree do not port directly. What ports is the **behavior**: detect state → classify situation → offer contextual options → dispatch.
---
## Resolved design decisions
These were chosen during brainstorming and are fixed inputs to this spec:
1. **Approach: Hybrid.** Detection + classification as a new `gsd-tools smart-entry --json` subcommand (deterministic, unit-tested in Node); presentation + dispatch as a markdown command + workflow that shells out to it via the existing `gsd_run` shim. This mirrors how `gsd-core/workflows/do.md` already drives tooling. Rationale: code-driven detection is reliable and testable; the markdown surface keeps multi-runtime reach (Codex, Gemini, Copilot) and adds zero dependencies.
2. **Priority: workflow routing ("what now?").** The wizard optimizes for the ongoing-work menu (gsd-pi's `showSmartEntry`), not first-run onboarding (gsd-pi's `showProjectInit`). Onboarding routes to the existing `/gsd-new-project`, which already handles project detection and setup. We are **not** building an init wizard.
3. **Richness: phase + smart signals.** The classifier branches on gsd-core's phase loop **and** richer gsd-pi-style signals (blocked/recover, idle/stranded, paused, complete). All 10 situations below are in scope.
4. **Relationship to `/gsd-progress`: complementary, not redundant.** `/gsd:next` is the **front door / launcher** — a state-aware *menu* the user picks the next action from. `/gsd-progress` remains the **detailed situational report + auto-advance** (`--next` chaining). `/gsd:next` will frequently recommend `/gsd:progress`; it does not replace or deprecate it.
---
## Non-goals
- **No init/onboarding wizard.** `/gsd-new-project` already owns first-run project setup. `/gsd:next` routes to it.
- **No new prompt/TUI library.** `AskUserQuestion` (Claude) + `--text` numbered-list fallback (other runtimes) — matching repo convention. No inquirer/clack/ink.
- **No copy of gsd-pi's branch tree.** gsd-pi's milestone/slice/task model does not exist here. The situation table is **redesigned for gsd-core's phase loop** (`.planning/`).
- **No execution.** Pure launcher. Picked action dispatches to an existing command and stops.
- **No new state storage.** Reads existing artifacts (`.planning/STATE.md`, `ROADMAP.md`, git). Writes nothing.
---
## Architecture
```text
/gsd:next (commands/gsd/next.md — thin markdown dispatcher)
│
▼
workflow: gsd-core/workflows/smart-entry.md ◄── presentation + dispatch
│ step 1: resolve gsd_run shim
│ step 2: gsd_run smart-entry --json
│ step 3: render AskUserQuestion (or TEXT_MODE list)
│ step 4: show GSD ► ROUTING banner
│ step 5: dispatch + stop
▼
gsd-tools smart-entry --json ◄── NEW deterministic detection
│ reads: state-snapshot (.planning/STATE.md)
│ + .planning/ existence
│ + git status / branch / unpushed
│ + verify signals
│ emits: { situation, recommended, summary, actions[] }
▼
(existing command: progress / plan-phase / execute-phase / quick / ship …)
```
Two artifacts, one contract — the JSON shape in §"JSON contract". The workflow is thin because all branching logic lives in Node.
---
## The classifier — `gsd-tools smart-entry`
New `src/smart-entry.cts` → compiled (build-at-publish, ADR-457) to `gsd-core/bin/lib/smart-entry.cjs`. Registered in `gsd-tools.cjs` as `case 'smart-entry':` (≈2 lines, delegating to `smartEntry.run(cwd, { json: true }, raw)`).
### Contract
- **Pure detection → classification.** No side effects. No writes. No `process.exit()` (throw `ExitError` per repo convention; the `runMain` wrapper translates exit codes).
- **Output modes:** `--json` (machine, used by the workflow) and default (human-readable summary line, for `gsd-tools` users / debugging).
- **Idempotent, fast, no network.** Read-only filesystem + `git` calls only.
- **Never throws in `--json` mode when `.planning/` is absent** — it returns `situation: "no-project"` so the workflow always has a menu to render.
### Inputs
| input | source | what we read |
|---|---|---|
| workflow state | `gsd_run query state.load` / `cmdStateSnapshot` | `current_phase`, `total_phases`, `current_plan`, `total_plans_in_phase`, `status`, `progress`, `blockers[]`, `paused_at`, `last_activity`, session |
| planning dir | filesystem at cwd | existence of `.planning/`, `.planning/STATE.md`, `.planning/ROADMAP.md` |
| git signals | `git status --porcelain`, `git branch`, `git log @{u}..` (guarded) | dirty tree, branch, unpushed commits |
| verify signals | filesystem | latest phase's verify report presence; `STATUS:` marker on most recent summary |
Git calls are **guarded** — any git error (not a repo, no upstream, detached HEAD) is swallowed and treated as "no git signal," never fatal.
### Situations (priority order — first match wins)
This is the gsd-core analog of gsd-pi's phase enum. Evaluated top-down; the first matching row is the situation.
| # | situation | when (predicate over inputs) | recommended action |
|---|---|---|---|
| 1 | `no-project` | `.planning/` absent | `new-project` |
| 2 | `paused` | `paused_at` set (non-empty) | `resume-work` |
| 3 | `blocked` | `blockers[]` non-empty | `debug` |
| 4 | `verify-failed` | latest verify report `STATUS:` indicates failure/blocked | `verify-work` |
| 5 | `needs-first-phase` | STATE exists but `total_phases` ≤ 0 or no `ROADMAP.md` | `discuss-phase` |
| 6 | `planning` | `status` = planning (phase has no plan yet) | `plan-phase` |
| 7 | `executing` | `status` = executing / active | `execute-phase` |
| 8 | `verify-pending` | `status` = needs-verify / review-pending | `verify-work` |
| 9 | `idle-stranded` | clean tree + unpushed/stranded commits OR stale `last_activity` with committed-but-unshipped work | `ship` |
| 10 | `complete` | `total_phases` > 0 and current phase ≥ total and `status` = complete | `new-milestone` |
| — | `unknown` | fallback (no predicate matched) | `progress` |
**Note on `idle-stranded`:** this is the richest heuristic and the most likely to need tuning. Predicates: working tree clean **AND** (`git log @{u}..` non-empty **OR** `last_activity` older than threshold with non-complete `status`). Threshold: 72h (configurable later via config; hardcoded for v1). If this proves brittle in testing it is the first situation to relax — but it is in scope per the richness decision.
### Action set per situation
Each situation produces an ordered `actions[]` array. The recommended action is always first and carries `recommended: true`; the workflow shows the top 4 (`AskUserQuestion` cap). Every situation **always** includes `progress` ("Show progress") and `quick` ("Quick task") as escape hatches, and `help` is appended when room remains.
```
no-project → new-project*, map-codebase, quick, help
paused → resume-work*, progress, quick, help
blocked → debug*, verify-work, capture, progress
verify-failed → verify-work*, debug, code-review, progress
needs-first-phase→ discuss-phase*, plan-phase, quick, progress
planning → plan-phase*, discuss-phase, quick, progress
executing → execute-phase*, "progress --next", quick, code-review
verify-pending → verify-work*, code-review, "ship", progress
idle-stranded → ship*, complete-milestone, progress, capture
complete → new-milestone*, extract-learnings, quick, progress
unknown → progress*, "progress --next", quick, help
(* = recommended)
```
### JSON contract (machine output, `--json`)
```json
{
"situation": "executing",
"recommended": "execute-phase",
"summary": "Phase 2 of 5 · plan 1/3 · 60% · active",
"signals": {
"current_phase": 2,
"total_phases": 5,
"status": "executing",
"progress": 60,
"has_planning": true,
"git_dirty": false,
"paused": false,
"blockers": []
},
"actions": [
{ "id": "execute-phase", "label": "Continue executing phase 2", "command": "/gsd:execute-phase", "recommended": true },
{ "id": "progress-next", "label": "Advance to the next step", "command": "/gsd:progress --next", "recommended": false },
{ "id": "quick", "label": "Quick task", "command": "/gsd:quick", "recommended": false },
{ "id": "code-review", "label": "Review recent work", "command": "/gsd:code-review", "recommended": false }
]
}
```
- `situation`, `recommended`, `actions[]` are the contract the workflow depends on.
- `signals` is informational (shown in the summary banner); the workflow does not branch on it.
- `summary` is a one-line human string; the workflow may show it verbatim or reformat.
- `actions[].command` is the full slash command string the workflow dispatches, including flags (e.g. `/gsd:progress --next`).
---
## The markdown layer
### Command — `commands/gsd/next.md` (NEW)
Thin dispatcher, modeled on `commands/gsd/progress.md` and `commands/gsd/help.md`. Backed by `gsd-core/workflows/smart-entry.md` (named for the `smart-entry` classifier + `gsd-tools smart-entry` subcommand; does not collide with the existing `workflows/next.md`, which is the progress `--next` sub-workflow).
Frontmatter:
- `name: gsd:next` (surfaces as `/gsd:next`; the command-contract requires a `gsd:*`/`gsd-*` prefix — a bare `/gsd` is not expressible, see ADR-0002)
- `description:` "GSD smart entry — the state-aware front door. Reads your project state and routes you to the right next action."
- `argument-hint: ""` (no args for v1; reserved)
- `effort: low`
- `allowed-tools:` `Read, Bash, Glob, SlashCommand, AskUserQuestion`
- **No `requires: [phase]`** (unlike `progress`) — must work pre-project.
- `<execution_context>` → `@~/.claude/gsd-core/workflows/smart-entry.md` + `@~/.claude/gsd-core/references/ui-brand.md`
Body: a short `<objective>` stating this is a state-aware launcher, then `<process>` delegating entirely to the workflow. No inline logic.
### Workflow — `gsd-core/workflows/smart-entry.md` (NEW)
Five steps. **Must stay under 32 KiB (NEW_FILE_CAP)** — lean, because all branching is in Node.
**Step 1 — `resolve` (resolve the gsd_run shim):** Copy the `check_project`-style shim-resolution block verbatim from `gsd-core/workflows/do.md:29` (the long `_GSD_SHIM_NAME` resolver). This finds `gsd-tools.cjs` across all supported runtime homes. It is a proven, required block; do not paraphrase.
**Step 2 — `detect` (run the classifier):**
```bash
SNAPSHOT=$(gsd_run smart-entry --json 2>/dev/null)
```
Parse `SNAPSHOT` as JSON. If missing or unparseable → fall back to `/gsd:progress` (Step 5, with a one-line note "smart-entry unavailable — showing progress"). The agent never gets stuck.
**Step 3 — `present` (render the menu):**
TEXT_MODE handling copied verbatim from `do.md:15` (set `TEXT_MODE=true` when `--text` in `$ARGUMENTS` or `text_mode` from init JSON is true; replace every `AskUserQuestion` with a numbered list).
Present via `AskUserQuestion`:
- `header`: derived from `situation` (e.g. `executing` → "Continue work").
- `question`: the `summary` line + "What next?"
- `options`: the first 4 of `actions[]`, label = action `label`, recommended first. (AskUserQuestion shows the first option as recommended.)
- Always allow the user to type a custom command ("Other" is provided automatically by the tool).
In TEXT_MODE: print `summary`, then a numbered list of all `actions[]` (not capped — text has no 4-option limit), ask the user to type a number.
**Step 4 — `display` (routing banner):** Copy the `display` step from `do.md:77-89` verbatim — the `GSD ► ROUTING` banner showing input / routing-to / reason. Input here is the chosen `label`; routing-to is the chosen `command`.
**Step 5 — `dispatch`:** Invoke the chosen `command`. Pass `$ARGUMENTS` through if the user typed a custom command. Then **stop** — the dispatched command owns everything from here. (Same contract as `do.md:91-99`.)
### TEXT_MODE / multi-runtime
The `--text` fallback is mandatory and is the reason we keep menus small and logic in Node. The fallback is copied from `do.md`, not reinvented.
---
## Error handling
| failure | behavior |
|---|---|
| `gsd_run` shim not found | the shim block itself errors with the standard install hint (from `do.md:29`); not our concern |
| `smart-entry` command missing (older gsd-core) | workflow sees empty/unparseable output → falls back to `/gsd:progress` with a note |
| `smart-entry` throws | same: caught by the `2>/dev/null` + parse check → fallback to `/gsd:progress` |
| `.planning/` absent | `smart-entry` returns `situation: "no-project"` → menu offers `new-project` |
| git unavailable / not a repo | classifier swallows git errors; works without git signals |
| `AskUserQuestion` unavailable (non-Claude) | TEXT_MODE numbered list |
**Invariant:** `/gsd` always produces *some* actionable menu and never strands the user. The ultimate fallback is `/gsd:progress`, which is always safe and always exists.
---
## Testing
Per CONTRIBUTING: `node:test` + `node:assert/strict`, behavior assertions only, no source-grep tests.
### `tests/smart-entry.unit.test.cjs` (NEW)
Fixture-driven: create temp dirs with crafted `.planning/STATE.md` + optional git repo, run the classifier, assert situation + recommended + action set. Cases (one per situation at minimum):
- `no-project` — empty cwd → situation `no-project`, recommended `new-project`, actions include `map-codebase`.
- `paused` — STATE.md with `paused_at` set → situation `paused`, recommended `resume-work`.
- `blocked` — STATE.md with blockers → situation `blocked`, recommended `debug`.
- `verify-failed` — latest summary `STATUS: blocked` → situation `verify-failed`.
- `needs-first-phase` — STATE.md present, `total_phases: 0` → situation `needs-first-phase`.
- `planning` / `executing` / `verify-pending` — respective `status` values.
- `idle-stranded` — clean tree + unpushed commits → situation `idle-stranded`, recommended `ship`.
- `complete` — current ≥ total, status complete → situation `complete`, recommended `new-milestone`.
- `unknown` — malformed state → situation `unknown`, recommended `progress`.
- **Priority ordering** — a STATE.md that is both paused AND blocked resolves to `paused` (earlier row wins).
- **JSON shape** — `actions[].command` always starts with `/gsd:`; exactly one action has `recommended: true`.
### `tests/gsd-workflow.structure.test.cjs` (NEW)
Invariants over the markdown layer (these are structural/format assertions on shipped artifacts, not source-grep of logic — permitted since they test the *contract* the workflow exposes):
- `commands/gsd/next.md` exists with frontmatter `name: gsd:next`, no `requires` field, `allowed-tools` includes `AskUserQuestion`.
- `gsd-core/workflows/smart-entry.md` exists and is **under 32 KiB** (NEW_FILE_CAP).
- Every `command` string referenced by the classifier's action table resolves to a real existing slash command file in `commands/gsd/` (guard against dead routes).
- The workflow contains the TEXT_MODE fallback clause and the shim-resolution block (contract assertions).
- The workflow dispatches exactly one command and then stops (no inline execution).
### Coverage & baseline
- The new `.cjs` enters the `c8` coverage gate (`--lines 70 --branches 60`).
- After adding `workflows/smart-entry.md`, run `npm run size:baseline` to update `tests/workflow-size-baseline.json`; justify the new entry in the PR.
---
## File changes
| file | change | size budget |
|---|---|---|
| `src/smart-entry.cts` | NEW — detection + classifier; `--json` + human output | — |
| `gsd-core/bin/lib/smart-entry.cjs` | generated by `build:lib` (gitignored) | — |
| `gsd-core/bin/gsd-tools.cjs` | add `case 'smart-entry':` (~2 lines) | — |
| `commands/gsd/next.md` | NEW — thin dispatcher command (`gsd:next` → `/gsd:next`) | small |
| `gsd-core/workflows/smart-entry.md` | NEW — presentation + dispatch | < 32 KiB |
| `tests/smart-entry.unit.test.cjs` | NEW — classifier behavior | — |
| `tests/gsd-workflow.structure.test.cjs` | NEW — markdown-layer invariants | — |
| `tests/workflow-size-baseline.json` | regenerate via `npm run size:baseline` | — |
| `docs/superpowers/specs/2026-06-27-gsd-smart-entry-design.md` | this document | — |
No existing command or workflow is modified. No new npm dependencies.
---
## Open questions for implementation
None blocking. Two noted for the implementer's judgment (not spec-level):
1. **`idle-stranded` threshold** — 72h hardcoded for v1. If brittle in practice, relax to "unpushed commits only" (drop the staleness clause).
2. **Action label wording** — exact strings are an implementation/tuning detail; the contract is `id` + `command`.
---
## Success criteria
- [ ] `gsd-tools smart-entry --json` classifies all 10 situations + `unknown` correctly from fixtures.
- [ ] `/gsd` in a real project shows a situation-appropriate menu and dispatches the chosen command.
- [ ] `/gsd` pre-project offers `new-project`.
- [ ] `/gsd` works under TEXT_MODE (no `AskUserQuestion`).
- [ ] Any `smart-entry` failure falls back to `/gsd:progress` without erroring.
- [ ] New workflow under 32 KiB; `size:baseline` updated; coverage gate passes.
- [ ] No new dependencies; no existing command modified.