Files
yoloyolo/.planning/milestones/v1.2-phases/08-test-and-constant-cleanup/08-01-PLAN.md
T

215 lines
9.4 KiB
Markdown
Raw Normal View History

2026-03-27 08:50:37 +01:00
---
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\\)"
---
<objective>
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.
</objective>
<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
<interfaces>
<!-- Key constants and types the executor needs from synth/config.go -->
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
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Delete stale NumLayers and GainPerLayer constants from synth/config.go</name>
<files>synth/config.go</files>
<read_first>
- synth/config.go (see current constant block at lines 5-12)
- synth/bank.go (line 21 confirms gainPerLayer is computed dynamically)
</read_first>
<action>
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.
</action>
<verify>
<automated>cd /home/dev/workspace/yoloyolo && grep -n "NumLayers\|GainPerLayer" synth/config.go; echo "EXIT:$?"</automated>
</verify>
<acceptance_criteria>
- `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)
</acceptance_criteria>
<done>NumLayers and GainPerLayer constants no longer exist in synth/config.go. The constant block contains only SampleRate, WindowMs, SamplesPerWindow, and WhisperFloor. Code compiles cleanly.</done>
</task>
<task type="auto">
<name>Task 2: Update synth/config_test.go — future-proof frequency bounds, rename test, remove duplicate</name>
<files>synth/config_test.go</files>
<read_first>
- synth/config_test.go (full file — see all 6 test functions)
- synth/config.go (after Task 1 edits — confirm SampleRate is exported)
</read_first>
<action>
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.
</action>
<verify>
<automated>cd /home/dev/workspace/yoloyolo && go test ./synth/... -v -count=1 2>&1 | head -40</automated>
</verify>
<acceptance_criteria>
- `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)
</acceptance_criteria>
<done>TestFrequenciesInRange uses Nyquist-based validation (no hardcoded upper bound). TestNumLayersMatchesAllClasses renamed to TestClassFreqConfigsMatchAllClasses. Duplicate TestClassFreqConfigsComplete removed. Full test suite passes.</done>
</task>
</tasks>
<verification>
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)
</verification>
<success_criteria>
- 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
</success_criteria>
<output>
After completion, create `.planning/phases/08-test-and-constant-cleanup/08-01-SUMMARY.md`
</output>