Files
DeepSeek-TUI/docs/architecture/command-dispatch.md
Hmbown f2ba5d15e4 docs: make the tool-surface, subagent, and config references match the code
docs/TOOL_SURFACE.md carried four claims the runtime's own tests contradict:

1. "The default-active policy contains exactly these ten names" listing
   `update_plan`. `DEFAULT_ACTIVE_NATIVE_TOOLS`
   (crates/tui/src/core/engine/tool_catalog.rs:44-58) has eight entries and
   `update_plan` is not among them — it appears nowhere in tool_catalog.rs. The
   policy is nine (those eight plus synthetic `tool_search`), eight with memory
   disabled. `update_plan` is registered (crates/tui/src/tools/plan.rs:401) but
   reachable only through `tool_search`; the tool table now says so.
2. "A memory-disabled or Moraine-fallback runtime". There is no Moraine
   fallback — docs/MEMORY.md:11-13 records the removal, and
   crates/tui/src/prompts.rs:2445-2449 is a test asserting MEMORY_GUIDANCE must
   not contain the word.
3. A "Replay-only aliases" table promising "saved transcripts, sessions, and
   recorded automation replay without migration" for 23 names, 16 of which are
   asserted REMOVED at crates/tui/src/tools/registry.rs:2066-2088 ("{retired}
   must stay removed") and 6 more at :2290-2304 ("{alias} must be removed").
   Split into a "Removed spellings" section (with the registry.rs:313-316 note
   that resolve has no fuzzy step, so those calls fail rather than dispatch) and
   a "Replay-only aliases" section holding only what is still registered:
   apply_patch, task_*, github_*, automation_*, rlm_*, checklist_*/todo_*.
4. A "Release verification" block whose three cargo filters name tests that do
   not exist (`rg` finds those three strings only in that doc). `cargo test`
   exits 0 with "0 passed; N filtered out" on a filter that matches nothing, so
   a release engineer following it got three green checkmarks having verified
   nothing. Replaced with the real names —
   `shell_surface_contains_only_the_canonical_bash_tool` (registry.rs:2290) and
   `runtime_task_families_expose_only_canonical_tools` (registry.rs:2333) — plus
   the receipt test, and a warning about the silent-pass failure mode.

docs/RUNTIME_SIMPLIFICATION_DESIGN.md repeats errors 1 and 3 and is designated
authoritative by docs/TOOL_LIFECYCLE.md:3-7, but carries no status marker. Given
a status banner naming both divergences and pointing at TOOL_SURFACE.md; the
"Rejected alternatives" provenance is worth keeping, so not deleted.

docs/SUBAGENTS.md:
- "a bounded queue of up to 200 running plus queued sub-agents by default" —
  `MAX_SUBAGENT_ADMISSION` is 1024 (crates/tui/src/config/subagent_limits.rs:21),
  which is what docs/TOOL_SURFACE.md:182 already said. The 64/128 concurrency
  figures on the same page were correct and are untouched.
- The memory section described a `memory.md` that does not exist and omitted the
  `scope` parameter. crates/tui/src/tools/remember.rs:165 states the legacy
  single-file path was removed in v0.9.4; writes go through
  `NativeMemoryStore::remember(scope, workspace_id, note)` (remember.rs:77-108).

config.example.toml documented two key sets that do not exist. Neither struct has
`deny_unknown_fields`, so both were silently discarded rather than rejected:
- `[advisor] max_tool_pairs` / `system_prompt`. `AdvisorConfigToml`
  (crates/config/src/lib.rs:2369-2394) has enabled, max_tool_calls (default 10,
  clamped 1-50 — the doc said 8, max 32), rate_limit_secs, dedup_window_secs,
  and model. `model` was undocumented; now it is.
- `[fleet.profiles.*.permissions] allow_tools` / `deny_tools`.
  `FleetProfilePermissions` (lib.rs:1966-1977) has allow_shell, trust,
  approval_required. `rg 'allow_tools|deny_tools' crates/` finds nothing. The
  example value was `"exec_shell"`, itself a removed tool name.

docs/CONFIGURATION.md: deleted the "Parsed but currently unused" section. Its one
entry, `tools_file`, is not parsed by anything — the field was removed in
346bfe3b6 and the doc bullet was orphaned. Repo-wide `rg` finds the string only
in that section, and nothing links a #parsed-but-currently-unused anchor.

docs/TTC_DESIGN.md said implementation "is deferred beyond v0.9.0". The `verify`
tool shipped and is default-on (crates/tui/src/tools/verify.rs,
features.rs:262, registry.rs:1040-1041 with verify_tool_enabled defaulted true).
Retitled as landed-in-part; capability (B) is still genuinely deferred, so the
doc stays. Its interface line said `with_verify(critic)`; the real signature is
`with_verify_tool(client, model)` (registry.rs:886).

docs/skills/README.md advertised `gh-plan-issues`, deleted in 18de2ebc0, and
credited these skills to "the v0.8.61 release" at a 0.9.4 release.

docs/architecture/provider-model-settings-v091.md pinned
`provider_is_configured` to config.rs:8625-8669; it is at :10160 and that region
is now unrelated code. Replaced with the symbol name, since config.rs is under
active edit.

docs/architecture/command-dispatch.md:133 claimed EPIC-002 was "ready for PR".
The PR (#3706) merged and #2870 closed 2026-08-01. Line 145 was an empty
"Current Evidence (Draft)" heading with no content; removed.

.gitignore: `git check-ignore -v` attributes .claude/settings.json,
scheduled_tasks.lock, worktrees/, and *.local.* to the blanket `.claude/` at
line 126, not to the specific rules above them. Dropped the redundant ones and
annotated why the HANDOFF_/CODEMAP_ patterns are deliberately kept.
2026-08-03 22:51:59 -07:00

9.3 KiB

Command Dispatch Architecture

Target branch: main Related EPIC: #2870 Related issue: #2791 EPIC-002 (Command Single Responsibility Extraction): Layer 4.x (FEAT-006 through FEAT-008)

This document records the command-dispatch ownership model after the command-boundary replay landed on main, updated through EPIC-002 (command single responsibility extraction). It reflects the final layered ownership: top-level group registration, group-owned command registration, and command-level ownership of metadata and behavior. It is the public reference for the module boundaries, dispatch precedence, and permanent exceptions that remain after the command-boundary refactor.

Dispatch Flow

commands::execute() owns the slash-command dispatch gate. The order is intentional:

Step Source Behavior
0 $skill compatibility $name is resolved as /skill name before slash parsing.
1 User commands user_registry::try_dispatch() checks workspace and global markdown commands first, so user commands can shadow built-ins.
2 Permanent mode compatibility aliases /jihua and /zidong route through config mode dispatch so each selects its fixed legacy mode. They remain registered aliases for discovery, but bypass normal /mode execution.
3 Built-in registry CommandRegistry resolves group-owned built-in commands by canonical name or alias.
4 Legacy migration hints Retired commands such as /set and /deepseek return targeted replacement guidance.
5 Skills fallback If no command matches, a skill with the same name may run before unknown-command suggestions are shown.

Module Boundaries

Module Responsibility
crates/tui/src/commands/mod.rs Central dispatch gate, registry initialization, public command lookup helpers, and unknown-command suggestions.
crates/tui/src/commands/traits.rs Built-in command metadata, trait-backed command objects, command groups, and registry lookup.
crates/tui/src/commands/groups/ Group-owned built-in command areas. Each group owns its command metadata and handlers.
crates/tui/src/commands/user_registry.rs User-command registry boundary: markdown metadata, aliases, hidden entries, validation errors, dispatch state resets, and shadowing behavior.
crates/tui/src/commands/user_commands.rs Lower-level file scanning, frontmatter parsing, allowed-tools parsing, and template substitution used by the registry.
crates/tui/src/tui/command_palette.rs Palette entries for built-ins and visible user commands, with user commands shadowing built-ins.
crates/tui/src/tui/widgets/mod.rs Slash completion, user-command metadata display, and alias-shadowing behavior.

Built-In Command Groups

Group Scope
core Help, model/provider selection, queue, hooks, subagents, links, feedback, voice, and core navigation.
config Config, settings, status surfaces, mode, theme, trust, logout, and related settings commands.
debug Token/cost introspection, cache, system/context, diff/edit, undo, and retry.
memory Persistent memory and notes.
plugins Read-only bundle discovery/validation plus explicit trust, enable, disable, revoke, and reload lifecycle commands; legacy executable tools remain separate.
project Project initialization, sharing, LSP, and goal/hunt commands.
session Rename, save, fork/new/load sessions, compaction, purge, relay, and export.
skills Skills Manager (/skills), text inspect/remote/sync paths, activation (/skill), and managed install/update/uninstall/trust.
utility Attachments, tasks/jobs, MCP, and network.

User Commands

User commands are markdown files loaded from these locations in precedence order:

  1. <workspace>/.codewhale/commands/
  2. <workspace>/.deepseek/commands/
  3. <workspace>/.claude/commands/
  4. <workspace>/.cursor/commands/
  5. ~/.codewhale/commands/
  6. ~/.deepseek/commands/

Supported frontmatter fields:

Field Meaning
name Canonical slash-command name. It is normalized without a leading slash and replaces the filename-derived default.
description Work objective and UI description.
usage Preferred user-facing invocation syntax shown in the palette and slash completion.
arguments Argument synopsis and a signal that selection should leave the composer open for input. It does not impose runtime validation.
argument-hint Backward-compatible palette/completion hint for expected arguments. It remains the display fallback when usage is absent.
allowed-tools Restricts command execution tools. An explicit empty value blocks all tools.
pausable Marks the command as pause/resume capable.
alias / aliases Additional user-command names that can shadow built-in aliases.
hidden Hides the command from palette/completion while allowing direct dispatch.

The canonical name defaults to the normalized markdown filename. A valid frontmatter name replaces that default; the filename is not retained as an implicit alias, so a renamed command must list the old filename under alias or aliases if both spellings should dispatch. A configured name may include one leading slash for readability, but after normalization it must be one non-empty slash-command token with no whitespace or embedded /. An invalid configured name is a recoverable error attached to the filename-derived command, preventing silent fallthrough to a built-in.

Source precedence is resolved before frontmatter naming: a higher-precedence directory wins when the same filename exists in more than one location. Files inside each directory are ordered by normalized filename. If distinct files then resolve to the same frontmatter name, the first file in that stable directory-and-filename order wins and the losing file records a recoverable load error. Aliases cannot replace any canonical user-command name; duplicate aliases also use first-wins order. Errors on a losing duplicate never poison a valid winning definition.

Presentation metadata has an explicit fallback order: non-empty usage, then non-empty legacy argument-hint, then non-empty arguments. arguments and argument-hint also cause palette/menu selection to append a space; usage does so when it describes more than the bare command name. These fields do not parse, require, or reject invocation arguments. Runtime expansion remains backwards-compatible: $ARGUMENTS receives the complete argument tail and $1, $2, and so on receive whitespace-separated positional values.

Malformed files remain registered under their resolved name with a dispatch-time error, so they cannot silently fall through to a built-in. Their errors are isolated per file: valid siblings still load, appear, and dispatch. Hidden commands participate in shadowing and remain directly dispatchable, but are removed from palette and slash-completion discovery.

Explicit /help <name> topics and unknown-command typo suggestions resolve through the same user-command precedence as execution. When a user command owns a built-in name or alias, help shows the user command's metadata and typo suggestions point to its canonical name rather than the shadowed built-in.

Dispatch through user_registry resets stale command state before sending the new command body: hunt objective fields, token/time counters, continuation count, allowed tools, pause state, todos, and plan state.

Permanent Exceptions

Exception Rationale
/jihua, /zidong Backward-compatible mode aliases that predate the group-owned registry. They route through config mode dispatch to preserve their fixed mode selection.
/set and /deepseek migration hints Retired commands kept only as direct typed guidance. They are excluded from registry and autocomplete.
#[allow(clippy::module_inception)] in matching group modules Group directories intentionally contain same-named child modules such as core/core.rs.
user_commands.rs lower layer The registry owns runtime behavior, while this module remains the shared filesystem and parser layer.
#[cfg(test)] helpers in user_commands.rs Deferred test migration compatibility while registry-specific tests are added.

EPIC-002 Completion Status (closed)

EPIC-002 (Command Single Responsibility Extraction) extracted commands for all 9 command groups through Layer 4.x sublayers. Layer 4.2 (FEAT-008) landed as PR #3706 (merged) and issue #2870 closed on 2026-08-01.

Layer FEAT Title Status
4 FEAT-006 Core, Config, Session, and Debug Command Extraction Complete
4.1 FEAT-007 Project, Memory, Skills, Utility, and Plugins Extraction Complete
4.2 FEAT-008 Registry Cleanup, Documentation, and Full Validation Complete

Replay Status (EPIC-001)

FEAT-001's group-owned built-in command direction is represented on main by the newer trait-backed registry and nested group tree. FEAT-002 is replayed as the dedicated user-command registry boundary. FEAT-003 is replayed as public architecture and PR/issue evidence documentation, updated for the current main target instead of the old release/v0.8.60 branch.