chore: invalidate bug-2543 outdated namespace invariant + document /gsd-<cmd> migration in CONTEXT.md (#164)

* test(meta): invalidate bug-2543 outdated /gsd:<cmd> namespace invariant

The "no /gsd-<cmd> hyphen form in source files" scan (test 2 of the
describe block) is skipped via test.skip. Motivation:

- Bug-3584 (2026-05-15) introduced runtime-slash.cjs, which intentionally
  emits `/gsd-${token}` for skills-based runtimes. This is the correct
  canonical form for runtime-persisted strings (ROADMAP.md, STATE.md,
  recommended_actions, fix hints).
- Bug-2543 was last updated 2026-05-12, three days BEFORE bug-3584 landed.
  The "no hyphen form" invariant was never updated to reflect the new
  two-tier model.
- PR #154 first-pass: an agent misread bug-2543 and reverted the correct
  `/gsd-plan-phase` to `/gsd:plan-phase` in phase-lifecycle-policy.ts:156,
  breaking tests/bug-3584-runtime-slash-emitters.test.cjs. A 2nd-pass agent
  reverted.

The canonical active invariant for runtime-emitter context is:
  tests/bug-3584-runtime-slash-emitters.test.cjs

The remaining 4 tests in this file (commands/gsd/ existence, command
filename slug format, transformer behavior, non-command identifier safety)
are still valid and remain active — only test 2 is skipped. This avoids
vacuous-truth (a full describe-block skip would let all assertions pass
trivially).

See CONTEXT.md § "Slash-command form: /gsd-<cmd> vs /gsd:<cmd>" (added
in the companion commit) for the full two-tier model documentation.

User directive: 2026-05-23 session authorizing this invalidation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(context): document /gsd-<cmd> slash-command migration for AI agents

Adds CONTEXT.md § "Slash-command form: /gsd-<cmd> (current) vs /gsd:<cmd>
(legacy)" to give future agents an unambiguous reference for which form
to use in which context.

Covers:
- The two-tier model (source text → colon; runtime-emitted strings → hyphen)
- The runtime-emitter authority: get-shit-done/bin/lib/runtime-slash.cjs
- The canonical invariant test: bug-3584-runtime-slash-emitters.test.cjs
- The PR #154 incident: how an agent misread bug-2543 and applied the wrong form
- An explicit "Context for AI agents" block: stop and re-read if bug-2543 is
  influencing a patch
- A note that DEFECT.AGENT-RETIRED-SLASH-SYNTAX-DRIFT.* predicates (written
  pre-two-tier) are stale for runtime-emitter contexts

Also updates the stale DEFECT predicates at the end of CONTEXT.md with a
clarifying note (the predicates remain for historical reference; the new §
supersedes their fix-forward guidance for runtime-emitter contexts).

Motivation: PR #154 first-pass incident (2026-05-23 session).
Canonical runtime contract: tests/bug-3584-runtime-slash-emitters.test.cjs.
Companion: test(meta) commit invalidating bug-2543's scan.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(meta): re-activate bug-2543 scan as scoped invariant, exclude runtime-emitter contexts

Codex adversarial review of PR #164 [high finding]: the single content-scanning
test in bug-2543 was test.skip, leaving a vacuous test surface. The transformer/
filename unit tests remained active but no live source surface was guarded.

Fix: replace test.skip with an active scoped invariant.

Scope change:
- REMOVED get-shit-done/bin/lib/ from SEARCH_DIRS entirely. That directory is
  runtime-emitter territory (runtime-slash.cjs, *.generated.cjs,
  phase-lifecycle-policy.ts) and intentionally uses /gsd-<cmd> (hyphen) per
  bug-3584's contract. Scanning it causes false positives that led to PR #154
  first-pass incident.
- Added RUNTIME_EMITTER_EXCLUDES set documenting exactly why each file is exempt.
- Remaining SEARCH_DIRS (workflows/, references/, templates/, commands/gsd/,
  agents/, hooks/) are Claude-facing source — colon form is correct there.

Verified: 9/9 tests pass, 0 skipped.
Canonical runtime-emitter contract: tests/bug-3584-runtime-slash-emitters.test.cjs
bug-2543's scoped scope: excludes bin/lib/ (runtime-emitter contexts).

* docs(context): rewrite slash-command section as directory-level matrix

Codex adversarial review of PR #164 [high finding]: the previous CONTEXT.md
section was internally contradictory — line 616 claimed /gsd-<cmd> was globally
canonical while lines 634/640 correctly stated Claude-facing source text uses
/gsd:<cmd>. Same document; opposite claims.

Root cause: the first push of this section (2026-05-23) overstated the hyphen
form as universal, when the project has always had a two-tier model.

Fix: replace the section with an unambiguous directory-level matrix.

New structure:
- Single table mapping each directory/surface to its correct form and enforcement.
- "How to choose" decision tree (4 steps, replaces ambiguous prose).
- "What was WRONG previously" retains historical motivation and adds the
  PR #164 contradiction incident to the record.
- "Context for AI agents" updated: ban mass-rewrites based on single test failure,
  cite the two-tier model explicitly.

Two-tier model (unchanged from reality, now clearly documented):
- Claude-facing source (commands/, agents/, workflows/, etc.): /gsd:<cmd> colon.
- Runtime-emitter contexts (runtime-slash.cjs, *.generated.cjs, ROADMAP.md
  persistence): /gsd-<cmd> hyphen per bug-3584 invariant.

Canonical authorities: bug-2543 (colon contract), bug-3584 (hyphen contract).

* fix(workflows): replace dead /gsd-* tokens with live registry forms

Codex adversarial review of PR #164 [medium finding]: stale slash-command
references in user-facing workflow content. Registry-backed sweep confirmed
the following tokens are not in commands/gsd/ registry.

Tokens removed/updated (dead token → live registry form):

1. /gsd-remove-workspace → /gsd:workspace --remove <name>
   File: get-shit-done/workflows/list-workspaces.md:58
   Reason: no commands/gsd/remove-workspace.md; remove-workspace is a subcommand
   of /gsd:workspace (commands/gsd/workspace.md, --remove flag).

2. /gsd-list-workspaces → /gsd:workspace --list
   File: get-shit-done/workflows/remove-workspace.md:35
   Reason: no commands/gsd/list-workspaces.md; list-workspaces is a subcommand
   of /gsd:workspace (commands/gsd/workspace.md, --list flag).

3. /gsd-list-phase-assumptions <phase> → /gsd:discuss-phase <phase> --assumptions
   File: get-shit-done/workflows/list-phase-assumptions.md:17-18 (usage block)
   Reason: no commands/gsd/list-phase-assumptions.md; the workflow is invoked
   via commands/gsd/discuss-phase.md with --assumptions flag.

4. /gsd-list-phase-assumptions 2 → /gsd:discuss-phase 2 --assumptions
   File: get-shit-done/references/continuation-format.md:59
   Reason: same as #3.

Sweep scope: user-facing markdown only (workflows/, references/). Runtime-emitter
hyphen-form references in bin/lib/ are guarded by bug-3584 and were not touched.

* chore(164): add changeset fragment for adversarial-review fixes

The 3 commits addressing Codex review touched user-facing surfaces
(get-shit-done/workflows/, get-shit-done/references/, CONTEXT.md,
bug-2543 test). Add fragment to satisfy changeset-lint.

Refs #164

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-05-23 18:42:47 -04:00
committed by GitHub
parent 63396dbb16
commit 85c67b4b21
7 changed files with 101 additions and 13 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 164
---
Re-activate bug-2543 scan as scoped invariant (excludes runtime-emitter contexts), rewrite CONTEXT.md slash-command guidance as directory-level matrix, replace dead /gsd-* tokens in workflow and reference docs with live registry forms.

View File

@@ -617,4 +617,45 @@ Migration plan: Phase 1 (#3465) seam additions complete; Phase 2 (#3466) targets
`PROC.TRIAGE.routing-incoming=stale-bug-already-fixed to close as duplicate of originating issue + cite fix PR + first stable tag; release-publish-or-backport to ready-for-human; reporter-can-self-test to awaiting-retest`
`PROC.TRIAGE.comment-shape=lead with "duplicate of #NNNN, fixed by PR #MMMM, in v1.X.Y"; show current code snippet proving bug-surface gone; give @latest and @next upgrade commands; close`
---
## Slash-command form: directory-level matrix
There is NO single global canonical form. The form depends on the directory and runtime context. Use this table:
| Surface | Form | Source of truth | Enforced by |
|---|---|---|---|
| `agents/*.md` (Claude Code agent definitions) | `/gsd:<cmd>` (colon) | Claude Code agent file convention | (Claude Code runtime) |
| `commands/gsd/*.md` (Claude Code slash commands) | `/gsd:<cmd>` (colon) | Claude Code slash-command convention | (Claude Code runtime) |
| `get-shit-done/workflows/**/*.md` (workflow docs read by Claude Code) | `/gsd:<cmd>` (colon, when invoking commands) | Claude Code slash-command convention | Live command registry (`tests/helpers/live-command-registry.cjs`) |
| `get-shit-done/bin/lib/runtime-slash.cjs` and runtime-emitter contexts | `/gsd-<cmd>` (hyphen) | bug-3584 invariant | `tests/bug-3584-runtime-slash-emitters.test.cjs` |
| ROADMAP.md / STATE.md persistence strings | `/gsd-<cmd>` (hyphen) | bug-3584 invariant | `tests/bug-3584-runtime-slash-emitters.test.cjs` |
| `*.generated.cjs` files | matches the TS source's emitted form | Generator | Freshness checks per ADR-3524 |
| CHANGELOG.md / .changeset / ADRs | whichever form was used at the time | Historical record | (no enforcement) |
### How to choose
1. Are you writing a slash-command reference that a Claude Code session will execute? → `/gsd:<cmd>` (colon).
2. Are you writing a string that will be persisted to ROADMAP.md or returned by a runtime emitter (recommended_actions, fix hints)? → `/gsd-<cmd>` (hyphen).
3. Is it a generated CJS file? Don't hand-edit; fix the generator's source.
4. Unsure? Check both `tests/bug-2543-gsd-slash-namespace.test.cjs` (Claude-facing colon contract) and `tests/bug-3584-runtime-slash-emitters.test.cjs` (runtime-emitter hyphen contract).
### What was WRONG previously
- `tests/bug-2543-gsd-slash-namespace.test.cjs` originally enforced the OPPOSITE invariant ("no `/gsd-<cmd>` hyphen form anywhere in source files"), which was OUTDATED — it codified the pre-migration state (commit `73c1af51`, 2026-05-12).
- Bug-3584 introduced `runtime-slash.cjs` on 2026-05-15 (three days AFTER bug-2543 was last updated), creating a two-tier model. Bug-2543 was never updated to reflect the new model.
- An agent fix on PR #154 misread bug-2543 and changed `/gsd-plan-phase` → `/gsd:plan-phase` in `sdk/src/query/phase-lifecycle-policy.ts:156`, breaking bug-3584's runtime contract. A 2nd-pass agent reverted.
- The first version of this CONTEXT.md section (2026-05-23 first push) claimed `/gsd-<cmd>` was globally canonical. That was incorrect — the project has a two-tier model. Codex adversarial review of PR #164 surfaced the contradiction; this rewrite corrects it.
- Bug-2543's content scan was `test.skip` from 2026-05-23 to avoid the PR #154 incident. The Codex adversarial review of PR #164 flagged that skip as leaving a vacuous test surface. The scan was re-activated as a scoped invariant (excluding `get-shit-done/bin/lib/` entirely) in the PR #164 follow-up commits.
### Context for AI agents
If you see a slash-command reference and aren't sure which form is right:
- Default to the table above.
- If the context isn't covered, ask before changing it.
- DO NOT mass-rewrite slash forms based on a single test failure — both bug-2543 and bug-3584 are active and they enforce opposite rules in opposite contexts.
- PR #154 first-pass incident illustrates the failure mode of getting this wrong.
The DEFECT predicates below (`DEFECT.AGENT-RETIRED-SLASH-SYNTAX-DRIFT.*`) were written before the two-tier model and refer to bug-2543 as the authoritative invariant test. That predicate is now STALE for runtime-emitter contexts. The "fix-forward" in those predicates applies only to Claude-facing source text drift, not to `runtime-slash.cjs` or other emitter modules.
`PROC.TRIAGE.no-duplicate-label=this repo has no duplicate label; framing lives in comment text + closing the issue`

View File

@@ -56,7 +56,7 @@ Standard format for presenting next steps after completing a command or workflow
**Also available:**
- Review plan before executing
- `/gsd-list-phase-assumptions 2` — check assumptions
- `/gsd:discuss-phase 2 --assumptions` — check assumptions
---
```

View File

@@ -14,8 +14,8 @@ Phase number: $ARGUMENTS (required)
```
Error: Phase number required.
Usage: /gsd-list-phase-assumptions [phase-number]
Example: /gsd-list-phase-assumptions 3
Usage: /gsd:discuss-phase <phase-number> --assumptions
Example: /gsd:discuss-phase 3 --assumptions
```
Exit workflow.

View File

@@ -55,7 +55,7 @@ GSD Workspaces (~/gsd-workspaces/)
Manage:
cd ~/gsd-workspaces/<name> # Enter a workspace
/gsd-remove-workspace <name> # Remove a workspace
/gsd:workspace --remove <name> # Remove a workspace
```
For each workspace, show:

View File

@@ -32,7 +32,7 @@ Parse JSON for: `workspace_name`, `workspace_path`, `has_manifest`, `strategy`,
**If no workspace name provided:**
First run `/gsd-list-workspaces` to show available workspaces, then ask:
First run `/gsd:workspace --list` to show available workspaces, then ask:
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.

View File

@@ -3,7 +3,7 @@
// allow-test-rule: structural-regression-guard
/**
* Slash-command namespace invariant (#3443).
* Slash-command namespace invariant (#3443) — SCOPED ACTIVE VARIANT.
*
* History:
* #3443 re-establishes `/gsd:<cmd>` as canonical in Claude-facing source text.
@@ -11,12 +11,27 @@
* `.claude/commands/gsd/` (namespaced slash commands), while non-Claude runtimes
* perform install-time conversion (for example `/gsd:<cmd>` -> `/gsd-<cmd>`).
*
* Invariant enforced here:
* No `/gsd-<cmd>` pattern in Claude-facing source text.
* Two-tier model (current — see CONTEXT.md § "Slash-command form: directory-level matrix"):
* • Claude-facing SOURCE TEXT (commands/, agents/, workflows/, references/,
* templates/, hooks/, .clinerules): uses `/gsd:<cmd>` (colon).
* THIS test enforces the colon invariant over those directories.
* • Runtime-emitter contexts (runtime-slash.cjs, phase-lifecycle-policy.ts,
* *.generated.cjs, bug-3584 test file): use `/gsd-<cmd>` (hyphen) per
* bug-3584's contract. Those files are EXCLUDED from this scan.
*
* Exceptions:
* - CHANGELOG.md: historical entries document commands under their original names.
* - gsd-sdk / gsd-tools identifiers: never rewritten (not slash commands).
* Scoped invariant enforced here:
* No `/gsd-<cmd>` pattern in Claude-facing source files, EXCLUDING the
* runtime-emitter contexts listed in RUNTIME_EMITTER_EXCLUDES below.
*
* Canonical reference for the runtime-emitter (hyphen-form) contract:
* tests/bug-3584-runtime-slash-emitters.test.cjs
*
* DO NOT expand RUNTIME_EMITTER_EXCLUDES without also updating the bug-3584
* test and CONTEXT.md § "Slash-command form: directory-level matrix".
*
* See also: PR #154 first-pass incident (agent applied outdated invariant,
* broke bug-3584 contract); PR #164 Codex adversarial review (surfaced the
* need to re-activate this test with explicit exclusions).
*/
const { test, describe } = require('node:test');
@@ -27,8 +42,27 @@ const path = require('node:path');
const ROOT = path.join(__dirname, '..');
const COMMANDS_DIR = path.join(ROOT, 'commands', 'gsd');
// Runtime-emitter contexts: these files intentionally emit `/gsd-<cmd>` (hyphen)
// as part of the bug-3584 runtime contract. They must NOT be scanned by this
// invariant — doing so caused PR #154 first-pass to revert correct hyphen form
// to colon form, breaking bug-3584-runtime-slash-emitters.test.cjs.
//
// Expand this list only if a new runtime-emitter module is introduced AND the
// bug-3584 test is updated to cover it.
const RUNTIME_EMITTER_EXCLUDES = new Set([
// Primary runtime-slash emitter (bug-3584 canonical contract):
path.join(ROOT, 'get-shit-done', 'bin', 'lib', 'runtime-slash.cjs'),
// phase-lifecycle-policy.ts emits runtime-persisted slash references (bug-3584):
path.join(ROOT, 'get-shit-done', 'bin', 'lib', 'phase-lifecycle-policy.ts'),
// Generated CJS files match the TS source's emitted form — never hand-edited:
// (matched below by .generated.cjs extension — see collectFiles exclusion)
]);
const SEARCH_DIRS = [
path.join(ROOT, 'get-shit-done', 'bin', 'lib'),
// NOTE: get-shit-done/bin/lib is intentionally EXCLUDED from SEARCH_DIRS.
// runtime-slash.cjs and *.generated.cjs live there and use the hyphen form
// per bug-3584's runtime-emitter contract. The full bin/lib tree is
// runtime-emitter territory — scanning it would cause false positives.
path.join(ROOT, 'get-shit-done', 'workflows'),
path.join(ROOT, 'get-shit-done', 'references'),
path.join(ROOT, 'get-shit-done', 'templates'),
@@ -81,7 +115,15 @@ describe('slash-command namespace invariant (#3443)', () => {
assert.ok(cmdNames.includes('execute-phase'), 'execute-phase must be a known command');
});
test('no /gsd-<cmd> retired syntax in Claude-facing source files', () => {
// SCOPED ACTIVE INVARIANT (2026-05-23 re-activation after Codex adversarial review of PR #164).
//
// Scan is scoped to Claude-facing source directories only (SEARCH_DIRS above).
// get-shit-done/bin/lib/ is excluded entirely — runtime-slash.cjs and
// *.generated.cjs there use hyphen form per bug-3584's runtime-emitter contract.
//
// If this test fails: check CONTEXT.md § "Slash-command form: directory-level matrix"
// before deciding whether to update the file or add to RUNTIME_EMITTER_EXCLUDES.
test('no /gsd-<cmd> retired syntax in Claude-facing source files (scoped — excludes runtime-emitter contexts)', () => {
const violations = [];
for (const file of allUserFacingFiles) {
const src = fs.readFileSync(file, 'utf-8');