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:
5
.changeset/nimble-seals-munch.md
Normal file
5
.changeset/nimble-seals-munch.md
Normal 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.
|
||||
41
CONTEXT.md
41
CONTEXT.md
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user