--- phase: 08-test-and-constant-cleanup plan: 01 type: execute wave: 1 depends_on: [] files_modified: - synth/config.go - synth/config_test.go autonomous: true requirements: - CLEAN-01 must_haves: truths: - "NumLayers and GainPerLayer constants do not exist in the synth package" - "go test ./... passes with zero failures after all edits" - "TestFrequenciesInRange accepts any BaseHz in (0, Nyquist) without manual test surgery" - "TestNumLayersMatchesAllClasses is renamed to TestClassFreqConfigsMatchAllClasses" artifacts: - path: "synth/config.go" provides: "Cleaned constant block without NumLayers or GainPerLayer" contains: "WhisperFloor" - path: "synth/config_test.go" provides: "Future-proof test assertions" contains: "TestClassFreqConfigsMatchAllClasses" key_links: - from: "synth/bank.go" to: "synth/config.go" via: "gainPerLayer computed dynamically in NewBank — no longer any static constant to conflict with" pattern: "1\\.0 / float64\\(len\\(cfgs\\)\\)" - from: "synth/config_test.go" to: "synth/config.go" via: "TestFrequenciesInRange validates BaseHz against SampleRate-derived Nyquist" pattern: "float64\\(synth\\.SampleRate\\)" --- Remove stale exported constants and hardcoded test bounds from the synth package so that subsequent v1.2 phases can add new traffic classes and frequencies without triggering false CI failures. Purpose: Phase 8 is the gatekeeper for all v1.2 work. NumLayers=14 is dead code (NewBank computes gain dynamically), and TestFrequenciesInRange's [60, 1100] bound will reject any new class above 1100 Hz. Both must be cleaned up before Phase 9-11 proceed. Output: Two edited files (synth/config.go, synth/config_test.go) with all tests green. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/08-test-and-constant-cleanup/08-CONTEXT.md @.planning/phases/08-test-and-constant-cleanup/08-RESEARCH.md From synth/config.go (current constant block, lines 5-12): ```go const ( SampleRate = 44100 // D-13: CD quality WindowMs = 500 // matches aggregate.DefaultWindowMs SamplesPerWindow = SampleRate * WindowMs / 1000 // 22050 NumLayers = 14 // <-- DELETE per D-01 GainPerLayer = 1.0 / float64(NumLayers) // D-10: ~0.0714 <-- DELETE per D-01 WhisperFloor = 0.03 // D-08/D-09: 3% of max amplitude ) ``` From synth/bank.go (line 21 — confirms constants are dead code): ```go gainPerLayer: 1.0 / float64(len(cfgs)), // dynamic computation, no constant needed ``` From synth/config_test.go (current test functions to modify): ```go // Lines 18-25: TestFrequenciesInRange — hardcoded [60, 1100] to replace // Lines 53-59: TestClassFreqConfigsComplete — duplicate of TestAllClassesHaveConfig // Lines 61-66: TestNumLayersMatchesAllClasses — rename to TestClassFreqConfigsMatchAllClasses ``` Task 1: Delete stale NumLayers and GainPerLayer constants from synth/config.go synth/config.go - synth/config.go (see current constant block at lines 5-12) - synth/bank.go (line 21 confirms gainPerLayer is computed dynamically) Per D-01: Delete lines 9-10 from synth/config.go — the `NumLayers = 14` and `GainPerLayer = 1.0 / float64(NumLayers)` constants. The resulting constant block must be: ```go const ( SampleRate = 44100 // D-13: CD quality WindowMs = 500 // matches aggregate.DefaultWindowMs SamplesPerWindow = SampleRate * WindowMs / 1000 // 22050 WhisperFloor = 0.03 // D-08/D-09: 3% of max amplitude ) ``` Do NOT modify any other lines in config.go. Do NOT touch bank.go — NewBank's dynamic `1.0 / float64(len(cfgs))` is already correct. Optionally, add a traceability comment to synth/bank.go line 21: `gainPerLayer: 1.0 / float64(len(cfgs)), // D-10: 1/N so all layers at full amplitude sum to 1.0` This is low-priority — skip if it feels like noise. cd /home/dev/workspace/yoloyolo && grep -n "NumLayers\|GainPerLayer" synth/config.go; echo "EXIT:$?" - `grep -c "NumLayers" synth/config.go` returns 0 - `grep -c "GainPerLayer" synth/config.go` returns 0 - `synth/config.go` still contains `SampleRate = 44100` - `synth/config.go` still contains `WhisperFloor = 0.03` - `go build ./synth/...` exits 0 (no compilation errors from removing the constants) NumLayers and GainPerLayer constants no longer exist in synth/config.go. The constant block contains only SampleRate, WindowMs, SamplesPerWindow, and WhisperFloor. Code compiles cleanly. Task 2: Update synth/config_test.go — future-proof frequency bounds, rename test, remove duplicate synth/config_test.go - synth/config_test.go (full file — see all 6 test functions) - synth/config.go (after Task 1 edits — confirm SampleRate is exported) Three changes to synth/config_test.go: **Change 1 (per D-02):** Replace the body of `TestFrequenciesInRange` (lines 18-25) with Nyquist-based validation. The new function body: ```go func TestFrequenciesInRange(t *testing.T) { const nyquist = float64(synth.SampleRate) / 2.0 // 22050 Hz for class, cfg := range synth.ClassFreqConfigs { if cfg.BaseHz <= 0 { t.Errorf("class %q BaseHz=%.1f must be positive", class, cfg.BaseHz) } if cfg.BaseHz >= nyquist { t.Errorf("class %q BaseHz=%.1f exceeds Nyquist (%.1f Hz)", class, cfg.BaseHz, nyquist) } } } ``` This accepts any BaseHz in (0, 22050) — no manual edit needed when Phase 10 adds classes above 1100 Hz. **Change 2 (per D-03):** Rename `TestNumLayersMatchesAllClasses` (line 61) to `TestClassFreqConfigsMatchAllClasses`. Keep the function body identical: ```go func TestClassFreqConfigsMatchAllClasses(t *testing.T) { if len(synth.ClassFreqConfigs) != len(classify.AllClasses()) { t.Errorf("ClassFreqConfigs has %d entries but AllClasses() has %d entries", len(synth.ClassFreqConfigs), len(classify.AllClasses())) } } ``` **Change 3 (Claude's discretion — consolidation):** Delete `TestClassFreqConfigsComplete` (lines 53-59) entirely. It is a semantic duplicate of `TestAllClassesHaveConfig` (lines 10-16) — both iterate `AllClasses()` and check for a map entry. Removing it leaves three non-overlapping tests: - `TestAllClassesHaveConfig` — every AllClasses() member has a map entry - `TestClassFreqConfigsMatchAllClasses` — count parity (catches extra entries) - `TestFrequenciesInRange` — all BaseHz positive and below Nyquist The remaining tests (`TestFrequenciesUnique`, `TestHarmonicsNonEmpty`, `TestPanPositionsInRange`) are untouched. cd /home/dev/workspace/yoloyolo && go test ./synth/... -v -count=1 2>&1 | head -40 - `grep -c "TestNumLayersMatchesAllClasses" synth/config_test.go` returns 0 - `grep -c "TestClassFreqConfigsMatchAllClasses" synth/config_test.go` returns 1 - `grep -c "TestClassFreqConfigsComplete" synth/config_test.go` returns 0 - `grep "nyquist" synth/config_test.go` returns at least one match - `grep "1100" synth/config_test.go` returns 0 matches (hardcoded bound removed) - `go test ./synth/... -run TestFrequenciesInRange` exits 0 - `go test ./synth/... -run TestClassFreqConfigsMatchAllClasses` exits 0 - `go test ./...` exits 0 (full suite green) TestFrequenciesInRange uses Nyquist-based validation (no hardcoded upper bound). TestNumLayersMatchesAllClasses renamed to TestClassFreqConfigsMatchAllClasses. Duplicate TestClassFreqConfigsComplete removed. Full test suite passes. After both tasks complete: 1. `grep -rn "NumLayers\|GainPerLayer" synth/*.go` — zero matches in production and test code 2. `grep -n "1100" synth/config_test.go` — zero matches (hardcoded bound gone) 3. `go test ./...` — all packages pass with zero failures 4. `go test ./synth/... -run TestFrequenciesInRange` — passes 5. `go test ./synth/... -run TestClassFreqConfigsMatchAllClasses` — passes 6. `go test ./synth/... -run TestClassFreqConfigsComplete` — no such test (removed) - NumLayers and GainPerLayer constants deleted from synth/config.go - TestFrequenciesInRange validates against Nyquist (22050 Hz), not hardcoded 1100 - TestNumLayersMatchesAllClasses renamed to TestClassFreqConfigsMatchAllClasses - Duplicate TestClassFreqConfigsComplete removed - `go test ./...` passes with zero failures - Adding a new class with BaseHz=2000 in a future phase would NOT require editing any test After completion, create `.planning/phases/08-test-and-constant-cleanup/08-01-SUMMARY.md`