docs(01.1): create phase plan

Phase 01.1: Hexagonal Architecture
- 6 plans in 3 waves
- Wave 1: Event bus + tRPC foundation (parallel)
- Wave 2: Process, logging, server module refactors (parallel)
- Wave 3: CLI tRPC integration
- Ready for execution
This commit is contained in:
Lukas May
2026-01-30 13:49:07 +01:00
parent 1da8e4f795
commit 5c5632dccf
6 changed files with 784 additions and 0 deletions

View File

@@ -0,0 +1,155 @@
---
phase: 01.1-hexagonal-architecture
plan: 01
type: execute
wave: 1
depends_on: []
files_modified: [package.json, vitest.config.ts, src/events/types.ts, src/events/bus.ts, src/events/index.ts, src/events/bus.test.ts]
autonomous: true
---
<objective>
Create the event bus foundation - the backbone of the hexagonal architecture.
Purpose: Establish extensible event infrastructure that can be swapped for external systems (RabbitMQ, WebSocket forwarding) later. All modules will communicate through this event bus.
Output: Working event bus with typed domain events and full test coverage.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01.1-hexagonal-architecture/1.1-CONTEXT.md
@src/index.ts
@src/process/types.ts
@src/logging/types.ts
@src/server/types.ts
@package.json
</context>
<tasks>
<task type="auto">
<name>Task 1: Install and configure Vitest test framework</name>
<files>package.json, vitest.config.ts</files>
<action>
Install vitest as dev dependency: `npm install -D vitest`
Create vitest.config.ts with:
- TypeScript support (uses tsconfig.json automatically)
- Test globals enabled (describe, it, expect without imports)
- Coverage reporter (optional, for future use)
- Test file pattern: **/*.test.ts
Add npm scripts to package.json:
- "test": "vitest run"
- "test:watch": "vitest"
- "test:coverage": "vitest run --coverage"
</action>
<verify>npm test runs successfully (0 tests found is fine at this stage)</verify>
<done>Vitest configured, npm test works, test:watch works</done>
</task>
<task type="auto">
<name>Task 2: Create EventBus port interface and EventEmitter adapter</name>
<files>src/events/types.ts, src/events/bus.ts, src/events/index.ts</files>
<action>
Create src/events/types.ts:
- Define EventBus interface (the PORT):
```typescript
export interface EventBus {
emit<T extends DomainEvent>(event: T): void;
on<T extends DomainEvent>(eventType: T['type'], handler: (event: T) => void): void;
off<T extends DomainEvent>(eventType: T['type'], handler: (event: T) => void): void;
once<T extends DomainEvent>(eventType: T['type'], handler: (event: T) => void): void;
}
```
- Define base DomainEvent interface:
```typescript
export interface DomainEvent {
type: string;
timestamp: Date;
payload: unknown;
}
```
Create src/events/bus.ts:
- Implement EventEmitterBus class that implements EventBus interface
- Use Node.js EventEmitter internally
- Wrap EventEmitter methods to match EventBus interface
- Add type safety for event emission
Create src/events/index.ts:
- Export EventBus interface
- Export EventEmitterBus class
- Export DomainEvent type
- Export convenience function: createEventBus() that returns EventEmitterBus instance
Key design: EventBus interface is the PORT. EventEmitterBus is the ADAPTER.
This allows swapping to RabbitMQ/Kafka/WebSocket later without changing consumers.
</action>
<verify>TypeScript compiles: npm run build</verify>
<done>EventBus interface defined, EventEmitterBus implemented, exports working</done>
</task>
<task type="auto">
<name>Task 3: Define domain events with typed payloads and write tests</name>
<files>src/events/types.ts, src/events/bus.test.ts</files>
<action>
Add domain event types to src/events/types.ts:
Process events:
- ProcessSpawned: { processId: string, pid: number, command: string }
- ProcessStopped: { processId: string, pid: number, exitCode: number | null }
- ProcessCrashed: { processId: string, pid: number, signal: string | null }
Server events:
- ServerStarted: { port: number, host: string, pid: number }
- ServerStopped: { uptime: number }
Log events:
- LogEntry: { processId: string, stream: 'stdout' | 'stderr', data: string }
Create union type: DomainEventMap that maps event type strings to payload types.
This enables type-safe event handling.
Create src/events/bus.test.ts:
- Test emit/on pattern works
- Test once fires only once
- Test off removes handler
- Test multiple handlers for same event
- Test typed event payloads (TypeScript compilation = passing)
- Test events include timestamp automatically
</action>
<verify>npm test passes with all event bus tests green</verify>
<done>Domain events defined, all tests pass, type safety verified</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] npm run build succeeds
- [ ] npm test passes (event bus tests)
- [ ] EventBus interface is exported from src/events/index.ts
- [ ] EventEmitterBus implements EventBus correctly
- [ ] All domain events have typed payloads
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- Event bus is extensible (interface-based)
- Tests prove the pattern works
</success_criteria>
<output>
After completion, create `.planning/phases/01.1-hexagonal-architecture/01.1-01-SUMMARY.md`
</output>

View File

@@ -0,0 +1,123 @@
---
phase: 01.1-hexagonal-architecture
plan: 02
type: execute
wave: 1
depends_on: []
files_modified: [package.json, src/trpc/router.ts, src/trpc/context.ts, src/trpc/index.ts, src/trpc/router.test.ts]
autonomous: true
---
<objective>
Create tRPC foundation for type-safe CLI-server communication.
Purpose: Establish the tRPC contract that CLI and future WebUI will use to communicate with the server. Same interface for all clients.
Output: Working tRPC router with procedures matching existing HTTP endpoints, fully tested.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01.1-hexagonal-architecture/1.1-CONTEXT.md
@src/server/index.ts
@src/server/types.ts
@package.json
</context>
<tasks>
<task type="auto">
<name>Task 1: Install tRPC dependencies and create router foundation</name>
<files>package.json, src/trpc/router.ts, src/trpc/context.ts, src/trpc/index.ts</files>
<action>
Install dependencies:
- npm install @trpc/server zod
- npm install -D @trpc/client (for testing)
Create src/trpc/context.ts:
- Define TRPCContext type containing:
- eventBus: EventBus (from src/events - import the interface type)
- serverStartedAt: Date | null
- processCount: number
- Export createContext function that builds context
- Context will be injected into procedures
Create src/trpc/router.ts:
- Initialize tRPC with initTRPC.context<TRPCContext>().create()
- Create base router and procedure helpers
- Export router, publicProcedure
Create src/trpc/index.ts:
- Export router, context, types
- Export AppRouter type for client usage
Note: Don't import actual EventBus implementation yet - just the type.
This keeps tRPC foundation independent of event bus implementation.
</action>
<verify>npm run build succeeds, no import errors</verify>
<done>tRPC initialized, context defined, router foundation in place</done>
</task>
<task type="auto">
<name>Task 2: Define procedures matching HTTP endpoints with tests</name>
<files>src/trpc/router.ts, src/trpc/router.test.ts</files>
<action>
Add procedures to src/trpc/router.ts:
health procedure (query):
- Input: none
- Output: { status: 'ok', uptime: number, processCount: number }
- Uses context.serverStartedAt to calculate uptime
- Use Zod for output validation
status procedure (query):
- Input: none
- Output: { server: { startedAt: string, uptime: number, pid: number }, processes: Array }
- More detailed than health
- Use Zod for output validation
Create appRouter that combines procedures.
Export AppRouter type: typeof appRouter
Create src/trpc/router.test.ts:
- Use @trpc/server/adapters/standalone for testing (or direct caller)
- Test health procedure returns correct shape
- Test status procedure returns correct shape
- Test with mocked context (no real server needed)
- Verify Zod validation catches bad outputs
Testing approach: Use createCallerFactory from tRPC for direct procedure calls without HTTP.
</action>
<verify>npm test passes with tRPC router tests green</verify>
<done>Procedures defined, Zod validation in place, tests pass</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] npm run build succeeds
- [ ] npm test passes (tRPC router tests)
- [ ] AppRouter type is exported
- [ ] health and status procedures work
- [ ] Zod schemas validate outputs
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- tRPC router ready for HTTP adapter integration
- Type-safe contract established
</success_criteria>
<output>
After completion, create `.planning/phases/01.1-hexagonal-architecture/01.1-02-SUMMARY.md`
</output>

View File

@@ -0,0 +1,126 @@
---
phase: 01.1-hexagonal-architecture
plan: 03
type: execute
wave: 2
depends_on: ["01.1-01"]
files_modified: [src/process/registry.test.ts, src/process/manager.test.ts, src/process/manager.ts, src/process/index.ts]
autonomous: true
---
<objective>
Add test coverage and event emission to the Process module.
Purpose: ProcessManager is core to agent lifecycle. Events like ProcessSpawned/ProcessStopped will drive coordination logic in later phases.
Output: Fully tested process module that emits domain events via the event bus.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01.1-hexagonal-architecture/01.1-01-SUMMARY.md
@src/process/index.ts
@src/process/manager.ts
@src/process/registry.ts
@src/process/types.ts
@src/events/index.ts
</context>
<tasks>
<task type="auto">
<name>Task 1: Write unit tests for ProcessRegistry and ProcessManager</name>
<files>src/process/registry.test.ts, src/process/manager.test.ts</files>
<action>
Create src/process/registry.test.ts:
- Test register() adds process to registry
- Test get() retrieves registered process
- Test getAll() returns all processes
- Test updateStatus() changes status correctly
- Test unregister() removes process
- Test get() returns undefined for non-existent process
Create src/process/manager.test.ts:
- Test spawn() creates process and registers it
- Test spawn() throws if process ID already running
- Test stop() terminates process
- Test stop() throws if process not found
- Test stopAll() stops all running processes
- Test restart() stops and respawns process
- Test isRunning() returns correct state
Testing approach for ProcessManager:
- Mock the execa module to avoid spawning real processes
- Use vi.mock('execa') with mock subprocess that has controllable behavior
- Create mock registry for isolation
Focus on behavior, not implementation details.
</action>
<verify>npm test passes with process module tests green</verify>
<done>ProcessRegistry and ProcessManager have comprehensive test coverage</done>
</task>
<task type="auto">
<name>Task 2: Refactor ProcessManager to emit events via event bus</name>
<files>src/process/manager.ts, src/process/index.ts</files>
<action>
Modify ProcessManager constructor:
- Add optional eventBus parameter: EventBus | undefined
- Store as private readonly property
Add event emission in spawn():
- After successful spawn, emit ProcessSpawned event:
{ type: 'process.spawned', timestamp: new Date(), payload: { processId, pid, command } }
Add event emission in exit handler:
- On normal exit, emit ProcessStopped:
{ type: 'process.stopped', timestamp: new Date(), payload: { processId, pid, exitCode } }
- On crash (non-zero exit), emit ProcessCrashed:
{ type: 'process.crashed', timestamp: new Date(), payload: { processId, pid, signal } }
Add event emission in stop():
- After successful stop, ProcessStopped is already emitted by exit handler
- No additional event needed
Update src/process/index.ts exports if needed.
Update tests to verify events are emitted:
- Create mock EventBus
- Pass to ProcessManager
- Assert emit() called with correct event shape
Key: eventBus is optional for backwards compatibility. If not provided, no events emitted.
</action>
<verify>npm test passes, events emitted correctly in tests</verify>
<done>ProcessManager emits domain events, tests verify event emission</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] npm run build succeeds
- [ ] npm test passes (all process module tests)
- [ ] ProcessManager accepts optional eventBus
- [ ] ProcessSpawned event emitted on spawn
- [ ] ProcessStopped/ProcessCrashed events emitted on exit
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- Process lifecycle events flow through event bus
- Backwards compatible (eventBus is optional)
</success_criteria>
<output>
After completion, create `.planning/phases/01.1-hexagonal-architecture/01.1-03-SUMMARY.md`
</output>

View File

@@ -0,0 +1,124 @@
---
phase: 01.1-hexagonal-architecture
plan: 04
type: execute
wave: 2
depends_on: ["01.1-01"]
files_modified: [src/logging/manager.test.ts, src/logging/writer.test.ts, src/logging/writer.ts, src/logging/index.ts]
autonomous: true
---
<objective>
Add test coverage and event emission to the Logging module.
Purpose: Log events will be forwarded to UI/FSUI for real-time visibility into agent output. Critical for the file system UI in Phase 7.
Output: Fully tested logging module that emits LogEntry events via the event bus.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01.1-hexagonal-architecture/01.1-01-SUMMARY.md
@src/logging/index.ts
@src/logging/manager.ts
@src/logging/writer.ts
@src/logging/types.ts
@src/events/index.ts
</context>
<tasks>
<task type="auto">
<name>Task 1: Write unit tests for LogManager and ProcessLogWriter</name>
<files>src/logging/manager.test.ts, src/logging/writer.test.ts</files>
<action>
Create src/logging/manager.test.ts:
- Test ensureLogDir() creates directory
- Test ensureProcessDir() creates process-specific directory
- Test getProcessDir() returns correct path
- Test getLogPath() returns correct path for stdout/stderr
- Test listLogs() returns process IDs
- Test listLogs() returns empty array if dir doesn't exist
- Test cleanOldLogs() removes old directories
- Test getBaseDir() returns configured base
Use temporary directories for file system tests (use os.tmpdir()).
Clean up after each test.
Create src/logging/writer.test.ts:
- Read src/logging/writer.ts first to understand the API
- Test open() creates log files
- Test writeStdout() writes to stdout.log
- Test writeStderr() writes to stderr.log
- Test close() flushes and closes handles
- Test writing after close throws
Use temporary directories, clean up after tests.
</action>
<verify>npm test passes with logging module tests green</verify>
<done>LogManager and ProcessLogWriter have comprehensive test coverage</done>
</task>
<task type="auto">
<name>Task 2: Add event emission to ProcessLogWriter</name>
<files>src/logging/writer.ts, src/logging/index.ts</files>
<action>
Modify ProcessLogWriter constructor:
- Add optional eventBus parameter: EventBus | undefined
- Store as private readonly property
Add event emission in writeStdout():
- After writing to file, emit LogEntry event:
{ type: 'log.entry', timestamp: new Date(), payload: { processId, stream: 'stdout', data } }
Add event emission in writeStderr():
- After writing to file, emit LogEntry event:
{ type: 'log.entry', timestamp: new Date(), payload: { processId, stream: 'stderr', data } }
Update createLogger() convenience function:
- Add optional eventBus parameter
- Pass through to ProcessLogWriter
Update src/logging/index.ts exports if needed.
Update tests to verify events are emitted:
- Create mock EventBus
- Pass to ProcessLogWriter
- Assert emit() called with correct LogEntry shape
- Test that events are NOT emitted if eventBus not provided
Key: This enables real-time log streaming to UI/FSUI in later phases.
</action>
<verify>npm test passes, log events emitted correctly</verify>
<done>ProcessLogWriter emits LogEntry events, tests verify emission</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] npm run build succeeds
- [ ] npm test passes (all logging module tests)
- [ ] ProcessLogWriter accepts optional eventBus
- [ ] LogEntry events emitted on stdout/stderr writes
- [ ] Backwards compatible (eventBus is optional)
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- Log events flow through event bus
- Ready for real-time log streaming
</success_criteria>
<output>
After completion, create `.planning/phases/01.1-hexagonal-architecture/01.1-04-SUMMARY.md`
</output>

View File

@@ -0,0 +1,133 @@
---
phase: 01.1-hexagonal-architecture
plan: 05
type: execute
wave: 2
depends_on: ["01.1-01"]
files_modified: [src/server/index.test.ts, src/server/shutdown.test.ts, src/server/index.ts, src/server/shutdown.ts]
autonomous: true
---
<objective>
Add test coverage and event emission to the Server module.
Purpose: Server events (started/stopped) are critical for coordination. The shutdown handler orchestrates cleanup - events enable reactive patterns.
Output: Fully tested server module with event-driven lifecycle.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01.1-hexagonal-architecture/01.1-01-SUMMARY.md
@src/server/index.ts
@src/server/shutdown.ts
@src/server/types.ts
@src/events/index.ts
</context>
<tasks>
<task type="auto">
<name>Task 1: Write unit tests for CoordinationServer</name>
<files>src/server/index.test.ts</files>
<action>
Create src/server/index.test.ts:
Test server lifecycle:
- Test start() begins listening on configured port
- Test start() throws if already running
- Test start() throws if PID file exists (another server)
- Test stop() stops the server
- Test stop() is idempotent (no error if not running)
- Test isRunning() returns correct state
Test HTTP endpoints:
- Test GET /health returns status 'ok' with uptime
- Test GET /status returns server info
- Test GET /unknown returns 404
- Test POST /health returns 405 (method not allowed)
Test PID file:
- Test PID file created on start
- Test PID file removed on stop
- Test stale PID file is cleaned up
Testing approach:
- Create server with random high port to avoid conflicts
- Use temporary directory for PID file
- Create mock ProcessManager and LogManager (they're not used in tests)
- Use fetch() or http.request() to test endpoints
- Clean up after each test (stop server, remove temp files)
</action>
<verify>npm test passes with server tests green</verify>
<done>CoordinationServer has comprehensive test coverage</done>
</task>
<task type="auto">
<name>Task 2: Add event emission to server and shutdown handler</name>
<files>src/server/index.ts, src/server/shutdown.ts</files>
<action>
Modify CoordinationServer constructor:
- Add optional eventBus parameter: EventBus | undefined
- Store as private readonly property
Add event emission in start():
- After server starts listening, emit ServerStarted:
{ type: 'server.started', timestamp: new Date(), payload: { port, host, pid: process.pid } }
Add event emission in stop():
- Before stopping, emit ServerStopped:
{ type: 'server.stopped', timestamp: new Date(), payload: { uptime } }
Modify GracefulShutdown:
- Add optional eventBus parameter
- Emit events during shutdown sequence if event bus provided:
- Could emit ShutdownInitiated, ShutdownComplete events
- Or rely on ServerStopped from CoordinationServer (simpler)
- Keep it simple: just ensure CoordinationServer emits ServerStopped
Update tests:
- Add mock EventBus to server tests
- Verify ServerStarted emitted on start
- Verify ServerStopped emitted on stop
Create src/server/shutdown.test.ts:
- Test shutdown() calls server.stop()
- Test shutdown() calls processManager.stopAll()
- Test signal handlers are installed
- Mock dependencies (server, processManager, logManager)
</action>
<verify>npm test passes with all server module tests green</verify>
<done>Server emits lifecycle events, shutdown handler tested</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] npm run build succeeds
- [ ] npm test passes (all server module tests)
- [ ] CoordinationServer accepts optional eventBus
- [ ] ServerStarted event emitted on start
- [ ] ServerStopped event emitted on stop
- [ ] GracefulShutdown has test coverage
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- Server lifecycle events flow through event bus
- Shutdown sequence is tested
</success_criteria>
<output>
After completion, create `.planning/phases/01.1-hexagonal-architecture/01.1-05-SUMMARY.md`
</output>

View File

@@ -0,0 +1,123 @@
---
phase: 01.1-hexagonal-architecture
plan: 06
type: execute
wave: 3
depends_on: ["01.1-02", "01.1-05"]
files_modified: [src/cli/trpc-client.ts, src/cli/index.ts, src/server/trpc-adapter.ts, src/server/index.ts, tests/integration/cli-server.test.ts]
autonomous: true
---
<objective>
Integrate tRPC client into CLI and add HTTP adapter to server.
Purpose: Complete the tRPC contract by connecting CLI to server. This establishes the pattern for all future client-server communication.
Output: CLI communicates with server via tRPC, with integration tests proving the full flow.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01.1-hexagonal-architecture/01.1-02-SUMMARY.md
@.planning/phases/01.1-hexagonal-architecture/01.1-05-SUMMARY.md
@src/cli/index.ts
@src/server/index.ts
@src/trpc/index.ts
</context>
<tasks>
<task type="auto">
<name>Task 1: Add tRPC HTTP adapter to server and create CLI client</name>
<files>src/server/trpc-adapter.ts, src/server/index.ts, src/cli/trpc-client.ts, package.json</files>
<action>
Install additional dependency if needed:
- npm install @trpc/client (should already be dev dep, move to regular deps)
Create src/server/trpc-adapter.ts:
- Create function to handle tRPC requests via node:http
- Use @trpc/server/adapters/fetch or manual handling
- Route /trpc/* requests to tRPC router
- Create context with eventBus, serverStartedAt, processCount
Modify CoordinationServer:
- In handleRequest(), add routing for /trpc/* paths
- Delegate to tRPC adapter
- Keep existing /health and /status routes for backwards compatibility
- tRPC endpoints: /trpc/health, /trpc/status
Create src/cli/trpc-client.ts:
- Create tRPC client using @trpc/client
- Configure with httpBatchLink pointing to http://127.0.0.1:{port}/trpc
- Export typed client
- Export helper function: createTrpcClient(port: number)
Key: The existing HTTP endpoints (/health, /status) remain for curl/debugging.
CLI will use tRPC endpoints for type safety.
</action>
<verify>npm run build succeeds</verify>
<done>tRPC adapter integrated into server, CLI client created</done>
</task>
<task type="auto">
<name>Task 2: Update CLI status command and write integration tests</name>
<files>src/cli/index.ts, tests/integration/cli-server.test.ts</files>
<action>
Update src/cli/index.ts:
- Modify status command to use tRPC client instead of placeholder
- Create tRPC client with default port (3847) or env var
- Call health procedure and display result
- Handle connection errors gracefully (server not running)
Keep the placeholder message if tRPC call fails:
```
Server not running or unreachable. Start with: cw --server
```
Create tests/integration/cli-server.test.ts:
- Test full flow: start server, call tRPC from client, verify response
- Test health procedure returns correct data
- Test status procedure returns correct data
- Test client handles server not running
Integration test approach:
- Start CoordinationServer on random port in beforeAll
- Create tRPC client pointing to that port
- Run procedures, verify responses
- Stop server in afterAll
These are INTEGRATION tests, not unit tests - they test the full stack.
</action>
<verify>npm test passes with all tests including integration tests</verify>
<done>CLI uses tRPC for status, integration tests prove full flow works</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] npm run build succeeds
- [ ] npm test passes (all tests including integration)
- [ ] cw status connects to server via tRPC
- [ ] tRPC endpoints work alongside HTTP endpoints
- [ ] Full flow tested: server start → tRPC call → response
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- CLI communicates with server via type-safe tRPC
- Integration tests prove the architecture works end-to-end
</success_criteria>
<output>
After completion, create `.planning/phases/01.1-hexagonal-architecture/01.1-06-SUMMARY.md`
</output>