fix: config reload — watch nested deps, fix Ctrl+C, propagate to runner#364
fix: config reload — watch nested deps, fix Ctrl+C, propagate to runner#364
Conversation
Three bugs in the dynamic config reloading feature: 1. ConfigWatcher only watched the main config file. Changes to files referenced via extends/include/imports (workflows, skills, parent configs) were silently ignored. Now collects all local dependency paths by recursively parsing YAML and watches every resolved file. After each successful reload the watch list is refreshed to pick up newly added or removed dependencies. 2. The SIGINT/SIGTERM handler ran cleanup but never re-raised the signal, preventing process exit (Ctrl+C printed "[ConfigWatcher] Stopped" but the process kept running). Now removes itself and re-raises so the default handler terminates the process. 3. The onSwap callback only updated a local variable in cli-main.ts but SlackSocketRunner kept its own private config copy. Future requests after a reload still used the old config. Added runner.updateConfig() method and call it from onSwap. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PR Overview: Config Reload Bug FixesSummaryThis PR fixes three critical bugs in the dynamic config reloading feature introduced in #358:
Files Changed
Architecture & Impact AssessmentWhat This PR AccomplishesThe PR transforms ConfigWatcher from a single-file watcher into a sophisticated multi-file dependency tracking system: Before: After: Key Technical Changes
Affected System Components
Component Relationshipsgraph TD
A[ConfigWatcher.start] --> B[collectLocalConfigDeps]
B --> C[Parse YAML]
C --> D[Find extends/include/imports]
D --> E[Resolve Paths]
E --> F[Recursive Collection]
F --> G[watchFile for each dep]
G --> H[fs.watch with persistent:false]
H --> I[File Change Detected]
I --> J[debouncedReload]
J --> K[ConfigReloader.reload]
K --> L{Success?}
L -->|Yes| M[refreshWatches]
L -->|No| N[Log Error]
M --> O[Re-collect dependencies]
O --> P[Add new watchers]
P --> Q[Remove stale watchers]
K --> R[onSwap callback]
R --> S[runner.updateConfig]
T[SIGINT/SIGTERM] --> U[onSignal handler]
U --> V[watcher.stop]
V --> W[store.shutdown]
W --> X[Remove listeners]
X --> Y[process.kill pid, sig]
Scope Discovery & Context ExpansionDirect Impact
Related Modules (Inferred)Based on the changes, these related areas should be considered:
Potential Edge Cases
Testing CoverageThe PR includes comprehensive testing: Unit Tests (21):
Integration Tests (7):
Review Recommendations
Labels
The PR is well-structured with extensive test coverage and clear separation of concerns. The dependency collection logic is robust with proper handling of edge cases like circular references and remote URLs. Metadata
Powered by Visor from Probelabs Last updated: 2026-02-15T19:25:05.934Z | Triggered by: pr_opened | Commit: 95592a0 💡 TIP: You can chat with Visor using |
Security Issues (1)
Architecture Issues (1)
Performance Issues (1)
Quality Issues (15)
Powered by Visor from Probelabs Last updated: 2026-02-15T19:25:10.465Z | Triggered by: pr_opened | Commit: 95592a0 💡 TIP: You can chat with Visor using |
Summary
Fixes three bugs in the dynamic config reloading feature (#358):
Nested dependency watching:
ConfigWatcheronly watched the main config file. Changes to files referenced viaextends/include/imports(workflows, skills, parent configs) were silently ignored. Now recursively collects all local dependency paths by parsing YAML and watches every resolved file. After each successful reload the watch list is refreshed to track newly added/removed dependencies.Ctrl+C doesn't exit: The SIGINT/SIGTERM handler ran cleanup but never re-raised the signal, so the process kept running (printing
[ConfigWatcher] Stoppedon every Ctrl+C). Now removes itself and re-raises so the default handler terminates the process.Config not propagated to runner: The
onSwapcallback only updated a local variable incli-main.tsbutSlackSocketRunnerkept its own private config copy. Future requests after a reload still used the old config. Addedrunner.updateConfig()method and call it fromonSwap.Test plan
ConfigWatcher+collectLocalConfigDeps(extends, include, imports, transitive, circular, workflow config refs)ConfigManager, real YAML parsing, realfs.watch:updateConfig()visor --slack --config .visor.yaml --watch+ edit skill file → verify reload🤖 Generated with Claude Code