Files
msd-core/get-shit-done/workflows/health.md
Tom Boucher 5fdc950eb7 feat(#2792): namespace meta-skills + keyword-tag descriptions + context utilization guard (#2825)
* feat(#2792): namespace meta-skills retargeted at the post-#2790 surface

This branch is now based on #2790's HEAD (the consolidation PR) instead
of main, and every routing table targets the consolidated surface so a
user routed by a namespace meta-skill never lands at a deleted /
folded sub-skill.

Cross-PR inconsistencies the original PR #2825 carried (vs #2790):

  - ns-ideate routed to gsd-note / gsd-add-todo / gsd-add-backlog /
    gsd-plant-seed → all folded into gsd-capture by #2790. Now routes
    to gsd-capture (the parent picks the mode from the user's intent).
  - ns-context routed to gsd-scan and gsd-intel → folded into
    gsd-map-codebase --fast / --query by #2790. Now routes to those
    flag forms.
  - ns-manage routed all workspace intent to gsd-list-workspaces (a
    list-only entry) → CR also flagged the over-narrow target. #2790
    folds into gsd-workspace; routing now points there.
  - ns-workflow routed to gsd-research-phase → deleted outright by
    #2790. Removed.
  - ns-project routed to gsd-plan-milestone-gaps → deleted outright by
    #2790. Removed.
  - None of the namespaces previously surfaced #2790's new consolidated
    skills (gsd-capture, gsd-phase, gsd-config, gsd-workspace,
    gsd-progress). All five are now reachable through the routers.
  - extract_learnings → extract-learnings (canonicalized by #2858).

Defect fixes within the namespace skills:

  - Hyphen-form `name:` (gsd-workflow, …) per the canonical naming
    contract — the colon-form addressed CR's drift complaint.
  - `Skill` added to allowed-tools on every router. The body instructs
    "Invoke the matched skill directly using the Skill tool" — without
    Skill in the permission list the meta-skill cannot route at all.

New regression guard in tests/enh-2792-namespace-skills.test.cjs: every
gsd-* token in any namespace router's table column resolves to a
surviving commands/gsd/*.md file (or to a known consolidated parent for
flag-form targets like gsd-map-codebase --fast). This single test would
have caught every dead-end route the original PR shipped with.

Skill-count cap in tests/enh-2790-skill-consolidation.test.cjs now
filters out ns-*.md from its <= 63 cap. Namespace routers are
descriptor-only entries, not part of the consolidation surface that cap
is policing — they have their own contract in
tests/enh-2792-namespace-skills.test.cjs.

INVENTORY.md gains a "Namespace Meta-Skills" section with the 6 router
rows; INVENTORY-MANIFEST.json gains 6 entries; the headline count moves
59 → 65 to match.

Out of scope for this rebase: the gsd-health --context flag (PR #2825
advertised the contract but didn't implement it). That's a separate
feature concern and is left untouched here.

5908/5908 on `npm test`.

* feat(#2792): implement gsd-health --context utilization guard

The original PR #2825 advertised a `--context` flag on gsd-health with a
60%/70% utilization threshold table but never implemented the workflow
logic — CR caught it as a contract leak, the rebase deferred it. This
commit closes the gap with TDD red/green/refactor.

Math layer (pure):
  - get-shit-done/bin/lib/context-utilization.cjs
    classifyContextUtilization(tokensUsed, contextWindow) →
      { percent, state }
    State boundaries use the exact ratio:
      < 60% healthy / 60–70% warning / ≥ 70% critical (fracture point)
    Display percent rounded for humans. Throws TypeError on non-integer
    or out-of-range inputs.
  - STATES = Object.freeze({ HEALTHY, WARNING, CRITICAL }) exported
    so callers reference the names by symbol, not by literal string.

SDK CLI integration:
  - get-shit-done/bin/gsd-tools.cjs
    `validate context --tokens-used N --context-window M [--json]`
    routes to the classifier, owns the recommendation copy (the
    classifier intentionally does not — keeps the renderer free to
    evolve without touching the math layer or its tests), and uses
    core.output's rawValue path for the sync-flush guarantee.
  - sdk/src/query/validate.ts + sdk/src/query/index.ts
    TypeScript validateContext handler registered at 'validate.context'
    and 'validate context'. Mirrors the CJS classifier inline (15 lines
    of arithmetic; not worth a shared cross-language module).

User-facing wiring:
  - commands/gsd/health.md frontmatter advertises --context, body
    documents the three-state threshold table.
  - get-shit-done/workflows/health.md adds a `context_check` step
    that's reached only when --context is set. Step calls
    `gsd-sdk query validate.context` with self-reported tokensUsed and
    contextWindow, prints the SDK output verbatim, and ends. Includes
    a TEXT_MODE plain-text fallback for non-Claude runtimes per #2012.

Tests:
  - tests/context-utilization.test.cjs (17 tests) — pure-function
    contract: state thresholds at every boundary, percent rounding,
    input validation, return-shape (no recommendation field — that's
    the renderer's job).
  - tests/validate-context.test.cjs (9 tests) — SDK CLI plumbing:
    arg parsing errors, JSON vs human rendering, recommendation copy
    pinned per state.
  - tests/enh-2792-namespace-skills.test.cjs (4 new tests) — markdown
    contract: --context advertised in argument-hint, threshold table
    in command body, context_check step exists in workflow, step
    invokes gsd-sdk query validate.context with both flags.

Inventory bookkeeping:
  - docs/INVENTORY.md "CLI Modules" 31 → 32; new row for
    context-utilization.cjs.
  - docs/INVENTORY-MANIFEST.json mirror.

5939/5939 on `npm test`.
2026-04-30 01:04:41 -04:00

224 lines
7.2 KiB
Markdown

<purpose>
Validate `.planning/` directory integrity and report actionable issues. Checks for missing files, invalid configurations, inconsistent state, and orphaned plans. Optionally repairs auto-fixable issues.
</purpose>
<required_reading>
Read all files referenced by the invoking prompt's execution_context before starting.
</required_reading>
<process>
<step name="parse_args">
**Parse arguments:**
Check if `--repair`, `--backfill`, or `--context` flags are present in the command arguments.
```
REPAIR_FLAG=""
BACKFILL_FLAG=""
CONTEXT_MODE=""
if arguments contain "--repair"; then
REPAIR_FLAG="--repair"
fi
if arguments contain "--backfill"; then
BACKFILL_FLAG="--backfill"
fi
if arguments contain "--context"; then
CONTEXT_MODE="true"
fi
```
If `CONTEXT_MODE` is set, jump to the `context_check` step and skip the
integrity validation steps. The two modes are orthogonal — context utilization
has nothing to do with `.planning/` directory health.
</step>
<step name="context_check">
**Run only when `--context` is set.**
The model running this workflow self-reports the current session's
approximate `tokensUsed` and the active model's `contextWindow`. Use the values
visible in your runtime (Claude Code's `/context` slash command output, or the
model's own session telemetry). If the runtime exposes neither, prompt the user
once via AskUserQuestion for both numbers.
**TEXT_MODE fallback:** when `text_mode` is true (config or `--text` flag) the
runtime is non-Claude (Codex, Gemini, etc.) and `AskUserQuestion` is not
available — replace the prompt with a plain-text two-question sequence
("Approximate tokens used? Context window size?") and read the answers as
plain text from the user's response.
```bash
gsd-sdk query validate.context \
--tokens-used "$TOKENS_USED" \
--context-window "$CONTEXT_WINDOW"
```
The query prints a one-line status (`Context utilization: NN% (state)`) plus
a recommendation line for the warning and critical states. Print the SDK
output verbatim and end the workflow — do **not** mix in `.planning/`
health output, the two modes are independent diagnostics.
</step>
<step name="run_health_check">
**Run health validation:**
```bash
gsd-sdk query validate.health $REPAIR_FLAG $BACKFILL_FLAG
```
Parse JSON output:
- `status`: "healthy" | "degraded" | "broken"
- `errors[]`: Critical issues (code, message, fix, repairable)
- `warnings[]`: Non-critical issues
- `info[]`: Informational notes
- `repairable_count`: Number of auto-fixable issues
- `repairs_performed[]`: Actions taken if --repair was used
</step>
<step name="format_output">
**Format and display results:**
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GSD Health Check
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Status: HEALTHY | DEGRADED | BROKEN
Errors: N | Warnings: N | Info: N
```
**If repairs were performed:**
```
## Repairs Performed
- ✓ config.json: Created with defaults
- ✓ STATE.md: Regenerated from roadmap
```
**If errors exist:**
```
## Errors
- [E001] config.json: JSON parse error at line 5
Fix: Run /gsd-health --repair to reset to defaults
- [E002] PROJECT.md not found
Fix: Run /gsd-new-project to create
```
**If warnings exist:**
```
## Warnings
- [W002] STATE.md references phase 5, but only phases 1-3 exist
Fix: Review STATE.md manually before changing it; repair will not overwrite an existing STATE.md
- [W005] Phase directory "1-setup" doesn't follow NN-name format
Fix: Rename to match pattern (e.g., 01-setup)
```
**If info exists:**
```
## Info
- [I001] 02-implementation/02-01-PLAN.md has no SUMMARY.md
Note: May be in progress
```
**Footer (if repairable issues exist and --repair was NOT used):**
```
---
N issues can be auto-repaired. Run: /gsd-health --repair
```
</step>
<step name="offer_repair">
**If repairable issues exist and --repair was NOT used:**
Ask user if they want to run repairs:
```
Would you like to run /gsd-health --repair to fix N issues automatically?
```
If yes, re-run with --repair flag and display results.
</step>
<step name="verify_repairs">
**If repairs were performed:**
Re-run health check without --repair to confirm issues are resolved:
```bash
gsd-sdk query validate.health
```
Report final status.
</step>
</process>
<error_codes>
| Code | Severity | Description | Repairable |
|------|----------|-------------|------------|
| E001 | error | .planning/ directory not found | No |
| E002 | error | PROJECT.md not found | No |
| E003 | error | ROADMAP.md not found | No |
| E004 | error | STATE.md not found | Yes |
| E005 | error | config.json parse error | Yes |
| W001 | warning | PROJECT.md missing required section | No |
| W002 | warning | STATE.md references invalid phase | No |
| W003 | warning | config.json not found | Yes |
| W004 | warning | config.json invalid field value | No |
| W005 | warning | Phase directory naming mismatch | No |
| W006 | warning | Phase in ROADMAP but no directory | No |
| W007 | warning | Phase on disk but not in ROADMAP | No |
| W008 | warning | config.json: workflow.nyquist_validation absent (defaults to enabled but agents may skip) | Yes |
| W009 | warning | Phase has Validation Architecture in RESEARCH.md but no VALIDATION.md | No |
| W018 | warning | MILESTONES.md missing entry for archived milestone snapshot | Yes (`--backfill`) |
| W019 | warning | Unrecognized .planning/ root file — not a canonical GSD artifact | No |
| I001 | info | Plan without SUMMARY (may be in progress) | No |
</error_codes>
<repair_actions>
| Action | Effect | Risk |
|--------|--------|------|
| createConfig | Create config.json with defaults | None |
| resetConfig | Delete + recreate config.json | Loses custom settings |
| regenerateState | Create STATE.md from ROADMAP structure when it is missing | Loses session history |
| addNyquistKey | Add workflow.nyquist_validation: true to config.json | None — matches existing default |
| backfillMilestones | Synthesize missing MILESTONES.md entries from `.planning/milestones/vX.Y-ROADMAP.md` snapshots | None — additive only; triggered by `--backfill` flag |
**Not repairable (too risky):**
- PROJECT.md, ROADMAP.md content
- Phase directory renaming
- Orphaned plan cleanup
</repair_actions>
<stale_task_cleanup>
**Windows-specific:** Check for stale Claude Code task directories that accumulate on crash/freeze.
These are left behind when subagents are force-killed and consume disk space.
When `--repair` is active, detect and clean up:
```bash
# Check for stale task directories (older than 24 hours)
TASKS_DIR="$HOME/.claude/tasks"
if [ -d "$TASKS_DIR" ]; then
STALE_COUNT=$( (find "$TASKS_DIR" -maxdepth 1 -type d -mtime +1 2>/dev/null || true) | wc -l )
if [ "$STALE_COUNT" -gt 0 ]; then
echo "⚠️ Found $STALE_COUNT stale task directories in ~/.claude/tasks/"
echo " These are leftover from crashed subagent sessions."
echo " Run: rm -rf ~/.claude/tasks/* (safe — only affects dead sessions)"
fi
fi
```
Report as info diagnostic: `I002 | info | Stale subagent task directories found | Yes (--repair removes them)`
</stale_task_cleanup>