- Add TestGroupFieldPopulated test to verify all ClassFreqConfigs have non-empty Group (GRP-01)
- Change TestHarmonicsNonEmpty threshold from < 2 to < 1 (accepts single-harmonic sine entries)
- Fix oscillator Advance() to use math.Abs(h.Amplitude) for normalization weight accumulation:
triangle wave uses alternating-sign amplitudes; signed sum underestimates totalWeight causing
output to exceed [-1,1] bounds when using WaveformPresetHarmonics (Rule 1 bug fix)
- Update TestStereoPan to use ClassSSH (pan=-0.7) instead of ClassDHCP (pan changed to 0.1)
- Update TestNewBankCustomConfigNoClip: passes after oscillator normalization fix
- Fix TestLoadPartialOverrideFrequency: derive expected WaveformType from defaults (not hardcoded 0)
- Fix TestAutoFreqSkipsBuiltins: derive expected HTTPS BaseHz from defaults (150.0 in Phase 9)
- Update TestAutoFreqAssignment bounds from [1200, 2350] to [2500, 4000]
- Update TestAutoFreqAssignment comment to reflect new range
- Update TestAutoFreqSkipsBuiltins HTTPS expected value from 175.0 to 150.0
(coordinated with Plan 01 frequency rebalancing)
- Change baseHz from 1200.0 to 2500.0
- Change numSteps from uint32(24) to uint32(31)
- Update function comment to reflect new range
- Prevents collision with built-in frequencies (max 2449 Hz)
- Add RawRule struct with Port *uint16, Protocol, Class fields
- Add LoadResult struct with FreqCfgs, UserRules, ConfigPath fields
- Change Load() signature to return LoadResult instead of bare map
- Add validateRules: checks protocol required, class required, valid protocols
- Add convertRules: converts RawRule slices to classify.Rule slices
- Add autoAssignFreq: FNV-32a deterministic Hz in [1200-2350] range
- Add addAutoFreqEntries: creates FreqConfig for new class names, skips built-ins
- Reorder ops: addAutoFreqEntries before merge so sounds overrides apply to user classes
- Update main.go call site to use LoadResult.FreqCfgs
- Update all 8 existing tests to use LoadResult return type
- Add 13 new tests covering rule parsing, validation, auto-freq, and LoadResult
- Load() discovers, parses, validates, and merges TOML config over defaults
- SoundOverride struct with *float64/*string pointer fields for partial-merge (CFG-04)
- parseFile uses BurntSushi/toml DecodeFile + Undecoded() for unknown-key errors (CFG-05)
- discoverPath probes ./netsynth.toml then ~/.config/netsynth/config.toml (CFG-02)
- merge applies non-nil overrides per class; warns on unknown class names (D-09)
- parseWaveform maps sine/square/sawtooth/triangle strings to WaveformType
- Harmonics regenerated via WaveformPresetHarmonics when waveform/frequency changes
- All 9 tests pass; go vet clean