Files
flynn/docs/plans/phase1-pr2-command-registry-checklist.md
2026-02-12 22:47:28 -08:00

192 lines
5.8 KiB
Markdown

# Phase 1 PR #2 Checklist: Fast-Path Command Registry
Created: 2026-02-12
Owner: Flynn core
Status: ready to implement
## Goal
Add a deterministic `CommandRegistry` that handles simple slash commands before `AgentOrchestrator`, reducing latency/token usage and keeping fallback behavior fully intact.
Initial fast-path commands for this PR:
- `/help`
- `/status`
- `/usage`
- `/model`
- `/compact`
- `/reset`
## PR Boundary
In scope:
- New `CommandRegistry` abstraction with typed handlers
- Built-in deterministic command handlers
- Fast-path integration in channel routing and gateway agent handler
- Graceful fallback to existing orchestrator flow when unmatched
- Unit + integration tests
Out of scope:
- Runtime plugin command loading
- Command auth/ACL system beyond current session/channel constraints
- Command chaining/pipelines
- Per-command rate limiting
## File-by-File Diff Plan
1) `src/commands/types.ts` (new)
- Define command contracts.
```ts
export interface CommandContext {
channel: string;
senderId: string;
sessionId: string;
rawInput: string;
}
export interface CommandResult {
handled: boolean;
text: string;
}
export interface CommandDefinition {
name: string;
aliases?: string[];
description: string;
execute: (args: string[], ctx: CommandContext) => Promise<CommandResult>;
}
```
2) `src/commands/registry.ts` (new)
- Implement registration + parse + execute.
- Use `Map` for O(1) lookup.
Required methods:
- `register(def: CommandDefinition): void`
- `get(nameOrAlias: string): CommandDefinition | undefined`
- `list(): CommandDefinition[]`
- `isCommand(input: string): boolean`
- `parse(input: string): { name: string; args: string[] } | null`
- `execute(input: string, ctx: CommandContext): Promise<CommandResult>`
3) `src/commands/builtin/index.ts` (new)
- Export command factories to avoid circular deps and enable daemon wiring with dependencies.
Factories:
- `createHelpCommand(registry: CommandRegistry)`
- `createStatusCommand()`
- `createUsageCommand()`
- `createModelCommand(...)`
- `createCompactCommand(...)`
- `createResetCommand(...)`
4) `src/commands/index.ts` (new)
- Barrel exports.
5) `src/daemon/index.ts` (modify)
- Construct a `CommandRegistry` at daemon startup.
- Register built-in commands with required deps.
- Pass registry into both message routing and gateway handler dependency paths.
- Add to `DaemonContext` if needed for future introspection.
6) `src/daemon/routing.ts` (modify)
- In `handler`, before invoking `agent.process(...)`:
- detect slash command with registry
- execute command
- if `handled`, reply immediately and return
- if not handled, continue existing orchestrator path
Important:
- Preserve existing `msg.metadata?.isCommand` behavior.
- Do not break current `/model`, `/usage`, `/compact`, `/reset` logic while migrating; either reuse existing code in handlers or keep compatibility shim.
7) `src/gateway/handlers/agent.ts` (modify)
- Add equivalent fast-path before agent processing.
- Return command output through existing gateway response events.
- Maintain cancellation/queue behavior unchanged.
8) `src/gateway/handlers/index.ts` and related handler deps (modify as needed)
- Thread `commandRegistry` through constructor/dependency interfaces.
9) Tests
- `src/commands/registry.test.ts` (new)
- `src/daemon/routing.test.ts` (modify/add cases)
- `src/gateway/handlers/agent.test.ts` (modify/add cases)
## Implementation Steps
1. Create typed command contracts and registry.
2. Add core parse/lookup/execute behavior with strict handling for unknown commands.
3. Implement built-in command handlers as factories.
4. Wire registry in daemon bootstrap.
5. Add channel router fast-path.
6. Add gateway fast-path.
7. Add tests for success/fallback/error cases.
8. Run full validation suite.
## Validation Commands
```bash
pnpm typecheck
pnpm test:run src/commands/registry.test.ts
pnpm test:run src/daemon/routing.test.ts
pnpm test:run src/gateway/handlers/agent.test.ts
pnpm test:run
pnpm lint
pnpm build
```
## Acceptance Criteria
- `CommandRegistry` supports register/get/list/parse/execute.
- Known commands are handled without entering `AgentOrchestrator.process()`.
- Unknown commands cleanly fall through to existing orchestration flow.
- Existing slash command UX remains unchanged for users.
- `/model`, `/usage`, `/compact`, `/reset` still function correctly via fast-path.
- Gateway and channel routes both support fast-path execution.
- All tests pass; no regressions in existing command behavior.
## Quality Gates
- Backward compatibility:
- Existing command parsing and metadata behavior still works.
- Existing TUI/web command flows are not broken.
- Security:
- No shell execution from command names/args in registry core.
- Input length and basic parse validation enforced.
- Handler errors are caught and returned safely.
- Performance:
- Lookup uses O(1) maps.
- No measurable slowdown for non-command messages.
- Reliability:
- Registry failures do not crash daemon startup unexpectedly.
- Unknown command behavior deterministic and tested.
## Risks and Mitigations
- Risk: duplicate command logic between old path and new path.
- Mitigation: move existing command branches into reusable built-in handlers, keep thin compatibility shim.
- Risk: divergence between gateway and channel behavior.
- Mitigation: reuse same registry + handler functions; add parity tests for both entrypoints.
- Risk: hidden regressions in slash command edge-cases.
- Mitigation: preserve existing parse semantics and add regression tests.
## Suggested Commit Message
`feat(commands): add fast-path command registry before orchestrator`
## Follow-up PRs
1. Add command discovery/docs endpoint for UI autocomplete.
2. Add optional command-level permission profiles.
3. Add skill-registered commands (`//skill`) on top of registry.