From 19287c4cadbb00c925d8c00f09071c7966401129 Mon Sep 17 00:00:00 2001 From: William Valentin Date: Mon, 9 Feb 2026 20:58:52 -0800 Subject: [PATCH] docs(02-01): complete core overlay merge plan - SUMMARY.md with 2 task commits, 10 new tests, 3 min duration - STATE.md updated: Phase 2 in_progress, 02-01 done - ROADMAP.md updated: 02-01 checked off --- .planning/ROADMAP.md | 4 +- .planning/STATE.md | 23 ++-- .../02-config-overlays/02-01-SUMMARY.md | 113 ++++++++++++++++++ 3 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 .planning/phases/02-config-overlays/02-01-SUMMARY.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 11bebb6..56876aa 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -43,7 +43,7 @@ Three phases, each delivering one complete capability. Phase 1 decomposes the mo **Plans:** 2 plans in 2 waves Plans: -- [ ] 02-01-PLAN.md — Core overlay merge (deepMerge + overlay-aware loadConfig + FLYNN_ENV resolution + tests) +- [x] 02-01-PLAN.md — Core overlay merge (deepMerge + overlay-aware loadConfig + FLYNN_ENV resolution + tests) - [ ] 02-02-PLAN.md — Doctor overlay validation (checkOverlayExists check) | Plan | Wave | Objective | Tasks | @@ -75,7 +75,7 @@ Plans: | Phase | Status | Requirements | |-------|--------|--------------| | 1 — Daemon Decomposition | **complete** | DECO-01..08 (8) — 3 plans, 2 waves | -| 2 — Config Overlays | **planned** | CONF-01..03 (3) — 2 plans, 2 waves | +| 2 — Config Overlays | **in_progress** | CONF-01..03 (3) — 2 plans, 2 waves | | 3 — Live Ops Dashboard | not_started | DASH-01..05 (5) | **Coverage:** 16/16 v1 requirements mapped ✓ diff --git a/.planning/STATE.md b/.planning/STATE.md index 4d3fef7..d773dce 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -8,17 +8,17 @@ ## Current Position -**Phase:** 1 — Daemon Decomposition -**Plan:** 3 of 3 complete (01-01, 01-02, 01-03 done) -**Status:** phase_complete -**Progress:** ███░░░░░░░ 1/3 phases complete +**Phase:** 2 — Config Overlays +**Plan:** 1 of 2 complete (02-01 done) +**Status:** in_progress +**Progress:** █████░░░░░ 1/3 phases complete (02-01 of Phase 2 done) ## Phase Status | Phase | Status | Plans | |-------|--------|-------| | 1 — Daemon Decomposition | **complete** | 3/3 plans complete | -| 2 — Config Overlays | not_started | — | +| 2 — Config Overlays | **in_progress** | 1/2 plans complete | | 3 — Live Ops Dashboard | not_started | — | ## Performance Metrics @@ -34,6 +34,8 @@ | Plan 01-02 tasks | 3/3 | | Plan 01-03 duration | ~8 min | | Plan 01-03 tasks | 2/2 | +| Plan 02-01 duration | 3 min | +| Plan 02-01 tasks | 2/2 | ## Accumulated Context @@ -51,6 +53,9 @@ - Grouped skills/MCP/pairing/gateway/startup into services.ts rather than multiple tiny modules - getChannelAgents callback for late-binding channel agents into gateway token usage reporting - Type-only imports in index.ts for values only referenced in DaemonContext type definition +- Overlay merge before env expansion and Zod validation — overlays don't need required fields +- Arrays in overlays replace (not concat) — explicit override semantics +- resolveOverlayPath returns path without checking existence — callers decide error handling ### Technical Notes - daemon/index.ts now 140 lines — thin composition root: imports → init calls → wire → return DaemonContext @@ -59,6 +64,8 @@ - Tier 1 agent tools (session, agent list, message send, cron) remain in index.ts — they need deps from multiple init functions - Web dashboard is vanilla JS SPA at src/gateway/ui/ - Config loader at src/config/loader.ts, schema at src/config/schema.ts (409 lines) +- deepMerge + overlay-aware loadConfig in loader.ts; resolveOverlayPath + overlay-aware loadConfigSafe in cli/shared.ts +- FLYNN_ENV maps to {configDir}/{env}.yaml sibling file; no env = no overlay (backward compatible) ### TODOs _(none)_ @@ -68,9 +75,9 @@ _(none)_ ## Session Continuity -**Last session:** Plan 01-03 (composition root finalization) completed — Phase 1 done -**Stopped at:** Completed 01-03-PLAN.md (final plan in Phase 1) -**Next action:** Plan Phase 2 (Config Overlays) — run `/gsd-plan-phase` for Phase 2 +**Last session:** Plan 02-01 (core overlay merge) completed +**Stopped at:** Completed 02-01-PLAN.md +**Next action:** Execute Plan 02-02 (doctor overlay validation check) --- *State initialized: 2026-02-09* diff --git a/.planning/phases/02-config-overlays/02-01-SUMMARY.md b/.planning/phases/02-config-overlays/02-01-SUMMARY.md new file mode 100644 index 0000000..a3728e8 --- /dev/null +++ b/.planning/phases/02-config-overlays/02-01-SUMMARY.md @@ -0,0 +1,113 @@ +--- +phase: 02-config-overlays +plan: 01 +subsystem: config +tags: [yaml, deep-merge, overlay, environment-config, FLYNN_ENV] + +# Dependency graph +requires: + - phase: 01-daemon-decomposition + provides: decomposed daemon modules with config loader at src/config/loader.ts +provides: + - deepMerge utility for recursive object merging + - overlay-aware loadConfig (optional overlayPath parameter) + - resolveOverlayPath for FLYNN_ENV → overlay file resolution + - overlay-aware loadConfigSafe for all CLI commands +affects: [02-config-overlays, 03-live-ops-dashboard] + +# Tech tracking +tech-stack: + added: [] + patterns: [deep-merge-before-validation, optional-overlay-parameter, env-to-file-resolution] + +key-files: + created: [] + modified: + - src/config/loader.ts + - src/config/loader.test.ts + - src/config/index.ts + - src/cli/shared.ts + +key-decisions: + - "Merge happens before env var expansion and Zod validation — overlays don't need required fields" + - "Arrays in overlays replace base arrays (not concatenated) — explicit override semantics" + - "resolveOverlayPath does NOT check file existence — callers decide error handling (loadConfig throws, doctor reports)" + - "Env var expansion test uses string field (model name) not number field (port) since YAML env vars expand to strings" + +patterns-established: + - "Optional overlay parameter: loadConfig(base, overlay?) — backward compatible by default" + - "Environment resolution: FLYNN_ENV maps to {configDir}/{env}.yaml sibling file" + +# Metrics +duration: 3min +completed: 2026-02-10 +--- + +# Phase 2 Plan 1: Core Overlay Merge Summary + +**deepMerge utility + overlay-aware loadConfig + FLYNN_ENV resolution with 10 new tests proving merge behavior and backward compatibility** + +## Performance + +- **Duration:** 3 min +- **Started:** 2026-02-10T04:54:19Z +- **Completed:** 2026-02-10T04:57:21Z +- **Tasks:** 2 +- **Files modified:** 4 + +## Accomplishments +- deepMerge function with recursive object merging (arrays replace, null overwrites, 3+ level nesting) +- loadConfig accepts optional overlayPath — merges before env expansion and Zod validation +- resolveOverlayPath maps FLYNN_ENV to sibling YAML file path +- loadConfigSafe automatically passes overlay to loadConfig — all CLI commands get overlay support +- 10 new tests (6 deepMerge unit + 4 overlay integration), all 1087 tests still pass + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Implement deepMerge and overlay-aware loadConfig with tests** - `c2cc052` (feat) +2. **Task 2: Wire FLYNN_ENV resolution into shared.ts and update loadConfigSafe** - `29bc185` (feat) + +## Files Created/Modified +- `src/config/loader.ts` - Added deepMerge export + overlay-aware loadConfig with optional overlayPath +- `src/config/loader.test.ts` - Added 10 new tests: 6 for deepMerge, 4 for overlay integration +- `src/config/index.ts` - Re-exports deepMerge +- `src/cli/shared.ts` - Added resolveOverlayPath + updated loadConfigSafe to pass overlay + +## Decisions Made +- Merge before validation: overlay files don't need to repeat required base fields (telegram, models) +- Arrays replace (not concat): `allowed_chat_ids: [999]` in overlay replaces base `[123]`, not appends +- resolveOverlayPath returns path even if file doesn't exist — loadConfig throws on missing file, doctor can report gracefully +- Used string field (model name) for env var expansion test since YAML `${VAR}` produces strings, not numbers + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] Fixed env var expansion test using number field** +- **Found during:** Task 1 (GREEN phase) +- **Issue:** Plan specified testing `server.port: ${TEST_OVERLAY_PORT}` but env vars expand to strings, and Zod `z.number()` rejects string "7777" +- **Fix:** Changed test to use `models.default.model: ${TEST_OVERLAY_MODEL}` (string field) instead of port (number field) +- **Files modified:** src/config/loader.test.ts +- **Verification:** All 13 tests in loader.test.ts pass +- **Committed in:** c2cc052 (Task 1 commit) + +--- + +**Total deviations:** 1 auto-fixed (1 bug in test spec) +**Impact on plan:** Minor test adjustment. The env var expansion in overlay is still fully tested, just using a string-typed field instead of a number-typed field. + +## Issues Encountered +None + +## User Setup Required +None - no external service configuration required. + +## Next Phase Readiness +- Core overlay merge complete, ready for Plan 02-02 (doctor overlay validation check) +- All existing CLI commands automatically get overlay support via loadConfigSafe + +--- +*Phase: 02-config-overlays* +*Completed: 2026-02-10*