diff --git a/.claude/issue-traces/740/01-issue-body.md b/.claude/issue-traces/740/01-issue-body.md new file mode 100644 index 00000000..c9fe6cd3 --- /dev/null +++ b/.claude/issue-traces/740/01-issue-body.md @@ -0,0 +1,101 @@ +## Summary + +7 findings from the holistic council review of the Swarm Command Conflict Prevention implementation (3 phases, 14 tasks). These are non-blocking advisory items that should be addressed to close remaining gaps in test coverage, runtime safety, spec accuracy, and quality assurance. + +--- + +### Finding 1: Add FR-014 performance and idempotency tests (HIGH) + +**Location:** `src/hooks/cc-command-intercept.ts` / `src/hooks/cc-command-intercept.test.ts` + +**Issue:** The spec (FR-014, SC-004) requires automated verification that the runtime hook processes messages in under 5ms (for a 10,000-token message) and is idempotent (applying twice produces identical output to applying once). Neither test exists. + +**Acceptance criteria:** +- [ ] Performance test: generate a ~10,000-token message, pass through `createCcCommandInterceptHook({}).messagesTransform`, assert processing time < 5ms +- [ ] Idempotency test: apply hook to a bare `/plan` message, save result; apply hook again to the result; assert both outputs are identical +- [ ] Both tests run as part of `bun test src/hooks/cc-command-intercept.test.ts` + +--- + +### Finding 2: Add runtime protection for `/checkpoint` (MEDIUM) + +**Location:** `src/hooks/cc-command-intercept.ts` lines 165-204 + +**Issue:** `/checkpoint` is classified as **CRITICAL** in the conflict registry but the runtime hook does nothing when it encounters bare `/checkpoint`. It's neither hard-blocked (like `/reset`/`/clear`) nor soft-corrected (like `/plan`). + +**Proposed fix:** Add `/checkpoint` to the hard-block list or soft-correct it to `/swarm checkpoint `. + +**Acceptance criteria:** +- [ ] Bare `/checkpoint` in an agent message results in runtime intervention +- [ ] Existing adversarial tests still pass +- [ ] `/checkpoint` inside code blocks, URLs, comments, or `/swarm`-namespaced is unaffected + +--- + +### Finding 3: Write task gate evidence for Phase 3 tasks 3.2 and 3.3 (MEDIUM) + +**Location:** `.swarm/evidence/` + +**Issue:** Task 3.2 (adversarial tests) and Task 3.3 (hook registration) have deliverables on disk but no gate evidence files (`3.2.json`, `3.3.json`) in `.swarm/evidence/`. Phase 3 completion-verify only reports 1 of 3 tasks checked. + +**Acceptance criteria:** +- [ ] `.swarm/evidence/3.2.json` exists with reviewer + test_engineer gate results +- [ ] `.swarm/evidence/3.3.json` exists with reviewer + test_engineer gate results +- [ ] `completion-verify` for Phase 3 reports 3/3 tasks + +--- + +### Finding 4: Add prompt content regression tests (MEDIUM) + +**Location:** `tests/unit/agents/` + +**Issue:** COMMAND NAMESPACE blocks exist in all 4 agent prompts but no automated tests verify they persist. A prompt refactor could silently remove hardening. + +**Acceptance criteria:** +- [ ] Tests verify `## COMMAND NAMESPACE` presence in architect.ts, coder.ts, reviewer.ts, test-engineer.ts +- [ ] Specific command names flagged: `/plan`, `/reset`, `/checkpoint`, `/clear`, `/compact` +- [ ] Specific directive language checked: `NEVER`, `PROHIBITED`, `DO NOT INVOKE` +- [ ] Removing a COMMAND NAMESPACE block causes a test failure + +--- + +### Finding 5: Update FR-012 spec text to match implementation (LOW) + +**Location:** `.swarm/spec.md` FR-012 + +**Issue:** Spec says "throws a structured error" but implementation uses output mutation (replacing message content) — necessary because `composeHandlers` wraps in `safeHook` which catches thrown errors. + +**Fix:** Update FR-012 to say "replaces the message content via output mutation with a structured advisory." + +--- + +### Finding 6: Expand reviewer/test-engineer prompt coverage (LOW) + +**Location:** `src/agents/reviewer.ts`, `src/agents/test-engineer.ts` + +**Issue:** Prompts list 6 commands only. Missing: `/agents`, `/config`, `/export`, `/doctor`, `/history`. + +**Fix:** Evaluate whether to expand to include all 9 conflicts or document the scope decision. + +--- + +### Finding 7: Add config-option tests for the hook (LOW) + +**Location:** `src/hooks/cc-command-intercept.ts` + +**Issue:** All 45 tests use default config. No test exercises `blockDestructive: false`, `intercept: ['CRITICAL']`, or `logIntercepts: false`. + +**Acceptance criteria:** +- [ ] `blockDestructive: false` — `/reset` passes through unblocked +- [ ] `intercept: ['CRITICAL']` — `/status` (HIGH) is not intercepted +- [ ] `logIntercepts: false` — logger.warn is NOT called for HIGH commands + +--- + +## Priority + +1. **Finding 1** (perf/idempotency tests) — highest priority, spec-requirement gap +2. **Finding 2** (/checkpoint runtime gap) — CRITICAL classification, no runtime protection +3. **Finding 4** (prompt regression tests) — protects against silent regression +4. **Finding 3** (gate evidence) — process completeness +5. **Findings 5-7** — polish and spec accuracy diff --git a/.claude/session/swarm-mode.md b/.claude/session/swarm-mode.md index e4e8d5c9..7b732bb4 100644 --- a/.claude/session/swarm-mode.md +++ b/.claude/session/swarm-mode.md @@ -60,3 +60,23 @@ Ignore these thoughts: - "I should move on because this is taking too long" If any of those appear, slow down and return to the workflow. + +## Command Namespace + +All swarm commands use the /swarm form. + +The following bare slash commands share names with swarm subcommands and must never +be invoked in a swarm session: + +| Bare CC Command | Why Prohibited | Swarm Equivalent | +|---|---|---| +| `/plan` | Enters CC plan mode — blocks execution | `/swarm plan` | +| `/reset` | Wipes conversation context | `/swarm reset --confirm` | +| `/checkpoint` | Reverts conversation history | `/swarm checkpoint ` | +| `/clear` | Wipes conversation context | — | +| `/compact` | Corrupts task-critical context | — | +| `/status` | Shows CC version info | `/swarm status` | +| `/agents` | Manages CC subagent configs | `/swarm agents` | +| `/config` | Opens CC settings | `/swarm config` | +| `/export` | Exports conversation text | `/swarm export` | +| `/memory` | Edits CLAUDE.md | Use swarm knowledge tools | diff --git a/.claude/skills/swarm-implement/SKILL.md b/.claude/skills/swarm-implement/SKILL.md index 7f2e2709..024e4fd6 100644 --- a/.claude/skills/swarm-implement/SKILL.md +++ b/.claude/skills/swarm-implement/SKILL.md @@ -24,6 +24,18 @@ Use this execution ladder: This is not a slow full-swarm recreation. This is a speed-preserving, quality-maximizing workflow. +## Command Namespace + +Swarm commands: always /swarm — never bare subcommand names. + +CRITICAL — these CC built-ins share names with swarm commands and MUST be avoided: + /plan → use /swarm plan (reading) or do not invoke (entering plan mode) + /reset → NEVER invoke — wipes conversation context + /checkpoint → NEVER invoke bare — use /swarm checkpoint + +HIGH — these CC built-ins produce wrong output: + /status → use /swarm status (not CC /status) + ## Quality and speed policy - Quality and pre-ship defect detection are paramount. - Speed still matters. diff --git a/.claude/skills/swarm/SKILL.md b/.claude/skills/swarm/SKILL.md index 9adeae61..0797032f 100644 --- a/.claude/skills/swarm/SKILL.md +++ b/.claude/skills/swarm/SKILL.md @@ -15,9 +15,65 @@ Argument handling: - If the first word of `$ARGUMENTS` is a **known plugin subcommand** (see list below): do NOT treat it as a swarm task. Instead, tell the user to run it as a slash command directly (e.g., `/swarm close`, `/swarm handoff`). These are OpenCode plugin commands handled by the swarm plugin's command system, not tasks for the swarm workflow. Do NOT try to interpret or execute them yourself. - Otherwise: enable swarm mode, then treat `$ARGUMENTS` as the task to execute immediately. -Known plugin subcommands (do NOT interpret these as tasks): - -`status`, `plan`, `agents`, `history`, `config`, `evidence`, `handoff`, `archive`, `diagnose`, `diagnosis`, `preflight`, `sync-plan`, `benchmark`, `export`, `reset`, `rollback`, `retrieve`, `clarify`, `analyze`, `specify`, `brainstorm`, `qa-gates`, `dark-matter`, `knowledge`, `curate`, `turbo`, `full-auto`, `write-retro`, `reset-session`, `simulate`, `promote`, `issue`, `pr-review`, `checkpoint`, `close` +### SWARM-NAMESPACED subcommands — DO NOT confuse with Claude Code built-in commands + +These are invoked as `/swarm `, NOT as bare `/subcommand`: + +- `/swarm status` — show current swarm status +- `/swarm plan` — view or manage implementation plan +- `/swarm agents` — list available swarm agents +- `/swarm history` — view swarm execution history +- `/swarm config` — view swarm configuration +- `/swarm evidence` — view evidence files +- `/swarm handoff` — hand off to another agent +- `/swarm archive` — archive swarm sessions +- `/swarm diagnose` / `/swarm diagnosis` — diagnose swarm issues +- `/swarm preflight` — run preflight checks +- `/swarm sync-plan` — sync plan with repository +- `/swarm benchmark` — run benchmarks +- `/swarm export` — export swarm data +- `/swarm reset` — reset swarm state +- `/swarm rollback` — rollback to previous state +- `/swarm retrieve` — retrieve swarm data +- `/swarm clarify` — clarify swarm task +- `/swarm analyze` — analyze swarm execution +- `/swarm specify` — specify swarm requirements +- `/swarm brainstorm` — brainstorm swarm tasks +- `/swarm qa-gates` — manage QA gates +- `/swarm dark-matter` — detect hidden couplings +- `/swarm knowledge` — manage knowledge base +- `/swarm curate` — curate knowledge +- `/swarm turbo` — enable turbo mode +- `/swarm full-auto` — enable full auto mode +- `/swarm write-retro` — write retrospective +- `/swarm reset-session` — reset session +- `/swarm simulate` — simulate swarm execution +- `/swarm promote` — promote knowledge +- `/swarm issue` — create issue +- `/swarm pr-review` — review pull request +- `/swarm checkpoint` — checkpoint session state +- `/swarm close` — close swarm session + +### CRITICAL NAMING CONFLICTS + +These swarm subcommands share exact names with CC built-in commands. +Invoking the bare form instead of `/swarm ` causes irreversible damage: + +| Swarm Command | CC Built-in | Damage | +|---|---|---| +| `/swarm plan` | CC `/plan` | Enters CC plan mode — blocks execution | +| `/swarm reset` | CC `/reset` | Wipes entire conversation context | +| `/swarm checkpoint` | CC `/checkpoint` | Reverts conversation history | + +All swarm commands: `/swarm `. Never the bare name. + +### COMMAND INVOCATION RULE + +All commands in this list are invoked as `/swarm `. +Never invoke the bare subcommand as a standalone slash command. +`/plan`, `/status`, `/reset`, `/checkpoint`, `/agents`, `/config`, `/export`, `/doctor` +are Claude Code built-in commands with completely different behaviors. +The `/swarm` prefix is mandatory, not optional. Examples: - `/swarm` — enable swarm mode only diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65a901b4..f7c25e14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,17 +1,16 @@ name: CI on: - push: - branches: ["**"] pull_request: branches: ["**"] -# Cancel in-progress runs for the same branch when a new push arrives. -# Groups push and pull_request events for the same branch together so only -# one run proceeds at a time, preventing double-run dist-check interference. +# Serialize CI runs for the same branch/PR to prevent double-run dist-check +# interference. Old runs are NOT cancelled because cancelling leaves stale +# check statuses that persist on PRs and block merging even after a new +# successful run completes. concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }} - cancel-in-progress: true + cancel-in-progress: false permissions: contents: read diff --git a/CHANGELOG.md b/CHANGELOG.md index a6c675b9..8077ea41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,17 @@ # Changelog -## [7.3.7](https://github.com/zaxbysauce/opencode-swarm/compare/v7.3.6...v7.3.7) (2026-05-03) - - -### Bug Fixes - -* rename release docs from v7.3.6 to v7.3.7; improve deadlock reset assertion ([dd04c18](https://github.com/zaxbysauce/opencode-swarm/commit/dd04c1814b322569e42c3e4d6e40fdfb01b44bf0)) +## [Unreleased] -## [7.3.6](https://github.com/zaxbysauce/opencode-swarm/compare/v7.3.5...v7.3.6) (2026-05-03) +### Features +* **conflict-registry:** add pure-data module mapping 9 swarm commands to their CC built-in counterparts with severity ratings (CRITICAL/HIGH/MEDIUM); commands with conflicts display a ⚠️ warning in `/swarm help` output +* **ci-gate:** add `src/commands/conflict-registry.test.ts` CI gate that prevents new CRITICAL conflicts from being merged without explicit acknowledgment in the test allow-list +* **constants:** add `CLAUDE_CODE_NATIVE_COMMANDS` frozen set (115 CC built-in commands) to `src/config/constants.ts` for runtime intercept hook; includes `freezeSet()` helper that throws on mutation attempts -### Bug Fixes +### Changed -* **mutation:** bound generateMutants with timeout; fix git apply CRLF failure on Windows ([#739](https://github.com/zaxbysauce/opencode-swarm/issues/739)) ([de535a2](https://github.com/zaxbysauce/opencode-swarm/commit/de535a2ef37bd8d07b45c906ea499e9ddd065b92)) +* **registry:** add optional `clashesWithNativeCcCommand` field to `CommandEntry` type; populated on 9 commands (`plan`, `reset`, `checkpoint`, `status`, `agents`, `config`, `export`, `doctor`, `history`) +* **index:** `buildHelpText()` now renders a ⚠️ conflict warning line for every command that has `clashesWithNativeCcCommand` set ## [7.3.5](https://github.com/zaxbysauce/opencode-swarm/compare/v7.3.4...v7.3.5) (2026-05-03) diff --git a/README.md b/README.md index ade7b256..b543fe86 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,8 @@ All 41 subcommands at a glance: Use `/swarm help` to see all available commands categorized by function. Use `/swarm help ` for detailed usage information on a specific command. +Nine commands display a ⚠️ warning in help output because they share names with Claude Code built-in slash commands (e.g., `/plan`, `/reset`, `/status`). The warning reminds you to always use `/swarm ` — the bare CC command does something different and sometimes destructive. See [docs/commands.md#claude-code-command-conflicts](docs/commands.md#claude-code-command-conflicts) for the full conflict registry. + See [docs/commands.md](docs/commands.md) for the full reference (41 commands). ## Command Aliases diff --git a/dist/agents/prompt-namespace.test.d.ts b/dist/agents/prompt-namespace.test.d.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/dist/agents/prompt-namespace.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/cli/index.js b/dist/cli/index.js index 0f2bff9a..c8fbfa30 100755 --- a/dist/cli/index.js +++ b/dist/cli/index.js @@ -52,7 +52,7 @@ var package_default; var init_package = __esm(() => { package_default = { name: "opencode-swarm", - version: "7.3.5", + version: "7.3.7", description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review", main: "dist/index.js", types: "dist/index.d.ts", @@ -16011,7 +16011,40 @@ var init_tool_names = __esm(() => { }); // src/config/constants.ts -var QA_AGENTS, PIPELINE_AGENTS, ORCHESTRATOR_NAME = "architect", ALL_SUBAGENT_NAMES, ALL_AGENT_NAMES, OPENCODE_NATIVE_AGENTS, AGENT_TOOL_MAP, DEFAULT_AGENT_CONFIGS; +function freezeSet(items) { + const set2 = new Set(items); + const proxy = new Proxy(set2, { + get(target, prop) { + if (prop === "add" || prop === "delete" || prop === "clear") { + return () => { + throw new TypeError("CLAUDE_CODE_NATIVE_COMMANDS is readonly"); + }; + } + if (prop === "forEach") { + return (callback, thisArg) => { + const wrapped = (v, k) => callback.call(thisArg ?? undefined, v, k, proxy); + return set2.forEach(wrapped); + }; + } + const value = Reflect.get(target, prop); + return typeof value === "function" ? value.bind(target) : value; + }, + set() { + throw new TypeError("CLAUDE_CODE_NATIVE_COMMANDS is readonly"); + }, + deleteProperty() { + throw new TypeError("CLAUDE_CODE_NATIVE_COMMANDS is readonly"); + }, + defineProperty() { + throw new TypeError("CLAUDE_CODE_NATIVE_COMMANDS is readonly"); + }, + setPrototypeOf() { + throw new TypeError("CLAUDE_CODE_NATIVE_COMMANDS is readonly"); + } + }); + return proxy; +} +var QA_AGENTS, PIPELINE_AGENTS, ORCHESTRATOR_NAME = "architect", ALL_SUBAGENT_NAMES, ALL_AGENT_NAMES, OPENCODE_NATIVE_AGENTS, CLAUDE_CODE_NATIVE_COMMANDS, AGENT_TOOL_MAP, DEFAULT_AGENT_CONFIGS; var init_constants = __esm(() => { init_tool_names(); QA_AGENTS = ["reviewer", "critic", "critic_oversight"]; @@ -16044,6 +16077,113 @@ var init_constants = __esm(() => { "title", "summary" ]); + CLAUDE_CODE_NATIVE_COMMANDS = freezeSet([ + "clear", + "new", + "reset", + "resume", + "continue", + "exit", + "quit", + "compact", + "fork", + "branch", + "undo", + "checkpoint", + "rewind", + "rename", + "doctor", + "help", + "status", + "statusline", + "cost", + "usage", + "stats", + "context", + "debug", + "insights", + "recap", + "release-notes", + "heapdump", + "powerup", + "config", + "settings", + "model", + "effort", + "fast", + "theme", + "color", + "keybindings", + "privacy-settings", + "init", + "focus", + "sandbox", + "terminal-setup", + "permissions", + "allowed-tools", + "security-review", + "fewer-permission-prompts", + "plugin", + "reload-plugins", + "hooks", + "mcp", + "ide", + "chrome", + "desktop", + "app", + "mobile", + "ios", + "android", + "remote-control", + "rc", + "remote-env", + "login", + "logout", + "review", + "pr-comments", + "agents", + "batch", + "loop", + "proactive", + "claude-api", + "schedule", + "routines", + "autofix-pr", + "plan", + "diff", + "export", + "copy", + "feedback", + "bug", + "btw", + "add-dir", + "memory", + "skills", + "upgrade", + "vim", + "voice", + "extra-usage", + "install-github-app", + "install-slack-app", + "passes", + "setup-bedrock", + "install", + "tasks", + "history", + "term", + "teleport", + "ultrareview", + "ultraplan", + "web-setup", + "setup-vertex", + "tui", + "simplify", + "summary", + "stickers", + "tp", + "team-onboarding", + "bashes" + ]); AGENT_TOOL_MAP = { architect: [ "checkpoint", @@ -47081,6 +47221,9 @@ function buildHelpText() { for (const cmd of catLines) { const entry = COMMAND_REGISTRY[cmd]; lines.push(`- \`/swarm ${cmd}\` \u2014 ${entry.description}`); + if (entry.clashesWithNativeCcCommand) { + lines.push(` \u26A0\uFE0F Name conflicts with CC built-in \`${entry.clashesWithNativeCcCommand}\` \u2014 always use \`/swarm ${cmd}\``); + } if (entry.args) { lines.push(` Args: \`${entry.args}\``); } @@ -47110,6 +47253,9 @@ function buildHelpText() { if (entry.aliasOf || entry.subcommandOf) continue; lines.push(`- \`/swarm ${cmd}\` \u2014 ${entry.description}`); + if (entry.clashesWithNativeCcCommand) { + lines.push(` \u26A0\uFE0F Name conflicts with CC built-in \`${entry.clashesWithNativeCcCommand}\` \u2014 always use \`/swarm ${cmd}\``); + } if (entry.args) { lines.push(` Args: \`${entry.args}\``); } @@ -47121,6 +47267,10 @@ function buildHelpText() { lines.push("### Deprecated Commands", ""); for (const { name, aliasOf } of deprecatedAliases) { lines.push(`- \`/swarm ${name}\` \u2192 Use \`/swarm ${aliasOf}\``); + const aliasEntry = COMMAND_REGISTRY[name]; + if (aliasEntry?.clashesWithNativeCcCommand) { + lines.push(` \u26A0\uFE0F Name conflicts with CC built-in \`${aliasEntry.clashesWithNativeCcCommand}\` \u2014 always use \`/swarm ${aliasOf}\``); + } } } return lines.join(` @@ -47428,17 +47578,20 @@ var init_registry = __esm(() => { status: { handler: (ctx) => handleStatusCommand(ctx.directory, ctx.agents), description: "Show current swarm state", - category: "core" + category: "core", + clashesWithNativeCcCommand: "/status" }, plan: { handler: (ctx) => handlePlanCommand(ctx.directory, ctx.args), description: "Show plan (optionally filter by phase number)", - category: "core" + category: "core", + clashesWithNativeCcCommand: "/plan" }, agents: { handler: (ctx) => Promise.resolve(handleAgentsCommand(ctx.agents, undefined)), description: "List registered agents", - category: "core" + category: "core", + clashesWithNativeCcCommand: "/agents" }, help: { handler: (ctx) => handleHelpCommand(ctx), @@ -47450,12 +47603,14 @@ var init_registry = __esm(() => { history: { handler: (ctx) => handleHistoryCommand(ctx.directory, ctx.args), description: "Show completed phases summary", - category: "utility" + category: "utility", + clashesWithNativeCcCommand: "/history" }, config: { handler: (ctx) => handleConfigCommand(ctx.directory, ctx.args), description: "Show current resolved configuration", - category: "config" + category: "config", + clashesWithNativeCcCommand: "/config" }, "config doctor": { handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args), @@ -47510,7 +47665,8 @@ var init_registry = __esm(() => { description: "Export plan and context as JSON", args: "", details: "Exports the current plan and context as JSON to stdout. Useful for piping to external tools or debugging swarm state.", - category: "utility" + category: "utility", + clashesWithNativeCcCommand: "/export" }, evidence: { handler: (ctx) => handleEvidenceCommand(ctx.directory, ctx.args), @@ -47542,7 +47698,8 @@ var init_registry = __esm(() => { description: "Run config doctor checks", category: "diagnostics", aliasOf: "config doctor", - deprecated: true + deprecated: true, + clashesWithNativeCcCommand: "/doctor" }, info: { handler: (ctx) => handleStatusCommand(ctx.directory, ctx.agents), @@ -47676,7 +47833,8 @@ var init_registry = __esm(() => { description: "Clear swarm state files [--confirm]", details: "DELETES plan.md, context.md, and summaries/ directory from .swarm/. Stops background automation and clears in-memory queues. SAFETY: requires --confirm flag \u2014 without it, displays a warning and tips to export first.", args: "--confirm (required)", - category: "utility" + category: "utility", + clashesWithNativeCcCommand: "/reset" }, "reset-session": { handler: (ctx) => handleResetSessionCommand(ctx.directory, ctx.args), @@ -47761,7 +47919,8 @@ var init_registry = __esm(() => { description: "Manage project checkpoints [save|restore|delete|list]