--- phase: 06-config-package-and-sound-overrides plan: 01 subsystem: config tags: [toml, BurntSushi/toml, config-loading, partial-merge, validation] # Dependency graph requires: - phase: 05-waveform-types-and-bank-decoupling provides: WaveformType enum, WaveformPresetHarmonics, FreqConfig.WaveformType field - phase: 01-capture-and-classification provides: classify.TrafficClass, classify.AllClasses, 14 class constants provides: - config.Load(configPath string) returns map[classify.TrafficClass]synth.FreqConfig - SoundOverride struct with pointer fields for partial-merge semantics - TOML file parsing with unknown-key detection via BurntSushi/toml Undecoded() - Auto-discovery of ./netsynth.toml and ~/.config/netsynth/config.toml - Per-field overlay merge preserving unspecified defaults - Waveform string validation before merge (fail fast) affects: - 06-02 (CLI flag wiring: --config flag passes configPath to config.Load) - encode package (RunSynthesis will accept merged config map from config.Load) # Tech tracking tech-stack: added: ["github.com/BurntSushi/toml v1.6.0 — TOML parsing with MetaData.Undecoded() for unknown-key detection"] patterns: - "Pointer fields (*float64, *string) in decode struct for partial-override semantics (nil = not set)" - "parseFile → validate → merge pipeline for fail-fast config loading (D-11)" - "Dedicated config package for testable isolation from Cobra/CLI concerns" key-files: created: - config/config.go - config/config_test.go modified: - go.mod - go.sum key-decisions: - "Used BurntSushi/toml v1.6.0 over pelletier/go-toml v2 — Undecoded() returns structured []Key (not formatted string), easier to extract key name for error messages" - "Shallow copy in copyDefaults() is safe because merge reconstructs Harmonics via WaveformPresetHarmonics rather than mutating the original slice" - "Unknown class names produce stderr warning (not error) per D-09, preparing for Phase 7 user-defined classes" patterns-established: - "Config package is independent of cmd/ — no Cobra imports, fully unit-testable" - "Merge functions take defaults map by value and modify in place, returning it" requirements-completed: [CFG-01, CFG-02, CFG-04, CFG-05] # Metrics duration: 3min completed: 2026-03-26 --- # Phase 6 Plan 01: Config Package Summary **TOML-based config loader with pointer-field partial merge, BurntSushi/toml Undecoded() unknown-key detection, and XDG auto-discovery at ./netsynth.toml and ~/.config/netsynth/config.toml** ## Performance - **Duration:** ~3 min - **Started:** 2026-03-26T19:55:08Z - **Completed:** 2026-03-26T19:57:45Z - **Tasks:** 1 (TDD: red → green) - **Files modified:** 4 (config/config.go, config/config_test.go, go.mod, go.sum) ## Accomplishments - Created `config` package with single `Load(configPath string)` public API - Implemented pointer-field partial merge: only non-nil fields override defaults (CFG-04) - Added BurntSushi/toml Undecoded() for field-level typo detection (CFG-05) - Auto-discovery of netsynth.toml in working dir and ~/.config/netsynth/config.toml (CFG-02) - Explicit file missing returns clear error; auto-discovery missing is silent (CFG-02/CFG-03) - Harmonics regenerated via WaveformPresetHarmonics when waveform or frequency is overridden - All 9 tests pass; go vet clean ## Task Commits Each task committed atomically via TDD: 1. **RED - Failing tests** - `b9ec05a` (test): 9 test functions for CFG-01 through CFG-05 2. **GREEN - Full implementation** - `1f877e7` (feat): config.Load, merge, validate, discover _Note: TDD task has two commits (RED test stub → GREEN implementation)_ ## Files Created/Modified - `config/config.go` - Load(), SoundOverride, rawConfig, merge, validate, discoverPath - `config/config_test.go` - 9 test functions covering all CFG requirements - `go.mod` - Added github.com/BurntSushi/toml v1.6.0 - `go.sum` - Updated checksum for new dependency ## Decisions Made - **BurntSushi/toml over pelletier/go-toml v2**: Undecoded() returns `[]toml.Key` ([]string slices) — structured, allowing exact key name extraction for error messages. pelletier's DisallowUnknownFields returns a formatted string (harder to extract just the key name). - **Shallow copy in copyDefaults()**: Safe because merge code always replaces `Harmonics` with a freshly generated slice from WaveformPresetHarmonics rather than mutating the original. Documented with comment for future maintainers. - **Unknown class warning (not error)**: Following D-09 to emit `fmt.Fprintf(os.Stderr, "Warning: ...")` for unknown class names. Phase 7 user-defined classes will be valid, so this is by design. ## Deviations from Plan None - plan executed exactly as written. The worktree needed a rebase onto master to include phase 05 code (WaveformType, WaveformPresetHarmonics) before starting — this was a prerequisite resolution, not a deviation. ## Issues Encountered - Worktree was based on remote origin/master (pre-phase-05). Rebased onto local master to get WaveformType and WaveformPresetHarmonics before implementation. No code conflicts. ## User Setup Required None - no external service configuration required. BurntSushi/toml is fetched automatically via `go get`. ## Next Phase Readiness - `config.Load()` is ready for wiring into `cmd/netsynth/main.go` via `--config` flag (Plan 06-02) - `encode.RunSynthesis` signature change (accept `freqCfgs map[classify.TrafficClass]synth.FreqConfig`) is needed in Plan 06-02 - All 14 default classes always present in result map — safe to pass directly to `synth.NewBank()` ## Self-Check: PASSED - FOUND: config/config.go - FOUND: config/config_test.go - FOUND: .planning/phases/06-config-package-and-sound-overrides/06-01-SUMMARY.md - FOUND: b9ec05a (RED commit — failing tests) - FOUND: 1f877e7 (GREEN commit — full implementation) --- *Phase: 06-config-package-and-sound-overrides* *Completed: 2026-03-26*