docs(02-02): complete registry integration plan

- Created 02-02-SUMMARY.md with implementation details
- Updated STATE.md: Plan 2 of 3 complete, 50% progress
- Added decisions: onContextSwitched callback, SSH context ID format
- Updated metrics: 3 plans completed, 5 min average
- No deviations from plan
This commit is contained in:
matt 2026-02-12 01:06:45 +00:00
parent 24051acac8
commit d10c56adb7
2 changed files with 273 additions and 12 deletions

View file

@ -10,29 +10,29 @@ See: .planning/PROJECT.md (updated 2026-02-12)
## Current Position
Phase: 2 of 4 (Service Infrastructure)
Plan: 1 of 3
Status: Plan 02-01 complete
Last activity: 2026-02-12 - Completed 02-01 (ServiceContext infrastructure)
Plan: 2 of 3
Status: Plan 02-02 complete
Last activity: 2026-02-12 - Completed 02-02 (Registry integration)
Progress: [███░░░░░░░] 37.5% (1.5/4 phases)
Progress: [████░░░░░░] 50.0% (2.0/4 phases)
## Performance Metrics
**Velocity:**
- Total plans completed: 2
- Average duration: 4 min
- Total execution time: 0.13 hours
- Total plans completed: 3
- Average duration: 5 min
- Total execution time: 0.23 hours
**By Phase:**
| Phase | Plans | Total | Avg/Plan |
|-------|-------|-------|----------|
| 01 Provider Plumbing | 1 | 4 min | 4 min |
| 02 Service Infrastructure | 1 | 4 min | 4 min |
| 02 Service Infrastructure | 2 | 10 min | 5 min |
**Recent Trend:**
- Last 5 plans: 4, 4
- Trend: Consistent velocity
- Last 5 plans: 4, 4, 6
- Trend: Slight increase (registry integration more complex)
*Updated after each plan completion*
@ -53,6 +53,10 @@ Recent decisions affecting current work:
- ServiceContext bundles all session-data services for single workspace isolation (02-01)
- dispose() separate from stop() - stop pauses (reversible), dispose destroys (permanent) (02-01)
- removeAllListeners() called LAST in dispose() to prevent events during cleanup (02-01)
- File watcher event rewiring via exported onContextSwitched callback from index.ts (02-02)
- SSH handler dynamically imports onContextSwitched to avoid circular dependencies (02-02)
- Context ID for SSH uses simple format: ssh-{host} (02-02)
- Destroy existing SSH context on reconnection to same host (02-02)
### Pending Todos
@ -78,9 +82,9 @@ None yet.
## Session Continuity
Last session: 2026-02-12
Stopped at: Completed 02-01 (ServiceContext and ServiceContextRegistry) — ready for 02-02
Stopped at: Completed 02-02 (Registry integration) — ready for 02-03
Resume file: None
---
*Created: 2026-02-12*
*Last updated: 2026-02-12 after completing 02-01-PLAN.md*
*Last updated: 2026-02-12 after completing 02-02-PLAN.md*

View file

@ -0,0 +1,257 @@
---
phase: 02-service-infrastructure
plan: 02
subsystem: main-process
tags: [registry-integration, ipc-refactoring, ssh-context-management]
dependency-graph:
requires: [02-01]
provides: [registry-based-service-routing]
affects: [all-ipc-handlers, ssh-connect-flow]
tech-stack:
added: []
patterns: [registry-routing, dynamic-service-resolution, context-switching]
key-files:
created: []
modified:
- src/main/index.ts
- src/main/ipc/handlers.ts
- src/main/ipc/projects.ts
- src/main/ipc/sessions.ts
- src/main/ipc/search.ts
- src/main/ipc/subagents.ts
- src/main/ipc/ssh.ts
decisions:
- "File watcher event rewiring handled by exported onContextSwitched callback from index.ts"
- "SSH handler imports onContextSwitched dynamically to avoid circular dependencies"
- "Context ID for SSH uses simple format: ssh-{host}"
- "Destroy existing SSH context on reconnection to same host"
metrics:
duration: 6
completed: 2026-02-12
---
# Phase 02 Plan 02: Registry Integration Summary
**Registry-based service routing with multi-context SSH support**
## Objective Achieved
Replaced global service variables in main/index.ts with ServiceContextRegistry, updated all IPC handlers to route through registry.getActive(), and implemented SSH context creation/destruction flow.
## Tasks Completed
### Task 1: Refactor main/index.ts to use ServiceContextRegistry
**Changed:**
- Removed global service variables (projectScanner, sessionParser, subagentResolver, chunkBuilder, dataCache, fileWatcher, cleanupInterval)
- Added `contextRegistry: ServiceContextRegistry`
- Removed `handleModeSwitch` callback entirely
- Created `wireFileWatcherEvents(context)` helper to manage event listener cleanup
- Created `onContextSwitched(context)` export for SSH handler to call
**Implementation:**
```typescript
// Create ServiceContextRegistry
contextRegistry = new ServiceContextRegistry();
// Create local context
const localContext = new ServiceContext({
id: 'local',
type: 'local',
fsProvider: new LocalFileSystemProvider(),
});
// Register and start
contextRegistry.registerContext(localContext);
localContext.start();
// Wire file watcher events
wireFileWatcherEvents(localContext);
// Initialize IPC handlers with registry
initializeIpcHandlers(contextRegistry, updaterService, sshConnectionManager);
```
**handlers.ts changes:**
- Changed `initializeIpcHandlers` to accept `ServiceContextRegistry` instead of individual services
- Removed `reinitializeServiceHandlers` entirely (no longer needed)
- Updated all domain handler initialize calls to pass registry
**Files modified:**
- src/main/index.ts (111 insertions, 161 deletions)
- src/main/ipc/handlers.ts
**Commit:** 5bf41c6
### Task 2: Update domain IPC handlers to route via registry
**Pattern applied to all handlers:**
```typescript
let registry: ServiceContextRegistry;
export function initialize{Domain}Handlers(contextRegistry: ServiceContextRegistry): void {
registry = contextRegistry;
}
async function handleXxx(...): Promise<...> {
const { projectScanner, sessionParser, ... } = registry.getActive();
// Use services
}
```
**projects.ts:**
- Changed signature to `initializeProjectHandlers(registry: ServiceContextRegistry)`
- Resolve `projectScanner` via `registry.getActive()` in each handler
- 3 handlers updated
**sessions.ts:**
- Changed signature to `initializeSessionHandlers(registry: ServiceContextRegistry)`
- Destructure all services from `registry.getActive()` at invocation time
- 6 handlers updated
**search.ts:**
- Changed signature to `initializeSearchHandlers(registry: ServiceContextRegistry)`
- Resolve `projectScanner` via `registry.getActive()`
- 1 handler updated
**subagents.ts:**
- Changed signature to `initializeSubagentHandlers(registry: ServiceContextRegistry)`
- Resolve all services from `registry.getActive()`
- Still passes fsProvider and projectsDir to buildSubagentDetail as before
- 1 handler updated
**ssh.ts:**
- Changed signature to `initializeSshHandlers(manager, registry)`
- Removed `onModeSwitch` callback parameter
- **SSH_CONNECT handler:**
- Creates new `ServiceContext` with SSH provider
- Registers in registry
- Starts context
- Switches registry to new context
- Dynamically imports `onContextSwitched` from index.ts and calls it
- **SSH_DISCONNECT handler:**
- Switches registry back to 'local'
- Destroys SSH context
- Calls `onContextSwitched` with local context
- All other SSH handlers unchanged (test, getConfigHosts, etc.)
**Files modified:**
- src/main/ipc/projects.ts
- src/main/ipc/sessions.ts
- src/main/ipc/search.ts
- src/main/ipc/subagents.ts
- src/main/ipc/ssh.ts (100 insertions, 70 deletions)
**Commit:** 24051ac
## Deviations from Plan
None - plan executed exactly as written.
## Verification Results
**Type checking:** ✅ Pass
```
pnpm typecheck — zero errors
```
**Tests:** ✅ Pass
```
Test Files: 31 passed
Tests: 494 passed
Duration: 4.88s
```
**Code inspection:** ✅ Pass
- main/index.ts has `contextRegistry` instead of individual service globals
- `reinitializeServiceHandlers` no longer exists in handlers.ts
- All IPC handlers use `registry.getActive()` pattern (12 occurrences)
- SSH connect creates ServiceContext and registers it
- SSH disconnect destroys SSH context and switches to local
- File watcher events rewired via onContextSwitched callback
## Architecture Impact
**Before (single-context):**
```
main/index.ts:
let projectScanner: ProjectScanner
let sessionParser: SessionParser
...
IPC handlers:
let projectScanner = /* set at init */
function handleXxx() {
projectScanner.scan() // always uses same instance
}
SSH connect:
recreate all services with new provider
call reinitializeServiceHandlers()
```
**After (multi-context):**
```
main/index.ts:
let contextRegistry: ServiceContextRegistry
contextRegistry.registerContext(localContext)
IPC handlers:
let registry: ServiceContextRegistry
function handleXxx() {
const { projectScanner } = registry.getActive()
projectScanner.scan() // dynamically resolved
}
SSH connect:
create new ServiceContext
registry.registerContext(sshContext)
registry.switch('ssh-host')
// local context stays alive in background
```
**Key improvements:**
1. **No service recreation** - contexts are isolated, switching is instant
2. **Local context persists** - can switch back without re-initialization
3. **No reinitializeServiceHandlers** - dynamic routing eliminates the need
4. **Clean separation** - SSH connection management vs. service context management
5. **File watcher stays alive** - only pause/resume on switch
## Next Steps
**Phase 02 Plan 03 (SSH notification manager integration):**
- Set NotificationManager on SSH context's FileWatcher
- Ensure error detection works for SSH sessions
- Wire up SSH context file watcher to NotificationManager
**Phase 03 (Renderer state management):**
- Snapshot/restore Zustand state on context switch
- Validate tab restoration against current context
- Update workspace indicators in sidebar
## Self-Check
**Created files exist:** N/A (no new files created)
**Modified files verified:**
```
✓ src/main/index.ts — uses contextRegistry
✓ src/main/ipc/handlers.ts — accepts registry, no reinitialize function
✓ src/main/ipc/projects.ts — routes via registry.getActive()
✓ src/main/ipc/sessions.ts — routes via registry.getActive()
✓ src/main/ipc/search.ts — routes via registry.getActive()
✓ src/main/ipc/subagents.ts — routes via registry.getActive()
✓ src/main/ipc/ssh.ts — creates/destroys ServiceContext
```
**Commits exist:**
```
✓ 5bf41c6 — refactor(02-02): main/index.ts to use ServiceContextRegistry
✓ 24051ac — refactor(02-02): IPC handlers route via ServiceContextRegistry
```
**Self-Check: PASSED**
---
*Completed: 2026-02-12*
*Duration: 6 minutes*