Archive roadmap and requirements to milestones/, update PROJECT.md
with shipped state, collapse ROADMAP.md, update retrospective.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add printConfig bool var and --print-config flag registration
- runPrintConfig() early-exit before interface-required check (CFG-06)
- runLiveMode and runPcapMode accept config.LoadResult; user rules prepend via append(result.UserRules, classify.DefaultRules...)
- Remove unused synth import from main.go
- Add TestPrintConfigFlagRegistered, TestPrintConfigNoInterface, TestPrintConfigWithConfigFile
- newTestCmd() wires --print-config and --config flags through PersistentPreRunE
- Add 07-01-SUMMARY.md
- Advance STATE.md to plan 2 of 2, progress 83%
- Update ROADMAP.md: phase 7 in progress (1/2 summaries)
- Mark RULE-01, RULE-02, RULE-03 complete in REQUIREMENTS.md
- 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
Research for Phase 7 custom rules and print-config. Covers TOML array-of-tables parsing behavior, LoadResult struct design, FNV-32a auto-frequency algorithm, and print-config implementation patterns. All verified against existing source code.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add configPath string var and --config flag to Cobra command
- Import config and synth packages in main.go
- Call config.Load(configPath) in run() before capture (D-11 fail fast)
- Change runLiveMode and runPcapMode signatures to accept freqCfgs map
- Pass freqCfgs to encode.RunSynthesis in both live and pcap modes
- Change RunSynthesis signature to accept injected config map (D-10)
- Replace hardcoded synth.ClassFreqConfigs with passed-in freqCfgs
- Update all three RunSynthesis calls in encode tests to pass synth.ClassFreqConfigs
- 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
- WaveformType int with five constants: WaveformCustom (0), WaveformSine,
WaveformSquare, WaveformSawtooth, WaveformTriangle
- WaveformPresetHarmonics generates bandlimited harmonic series for each type
- WaveformCustom returns nil to preserve existing hand-tuned harmonics
- All generated harmonics are below Nyquist (sampleRate/2)
- FreqConfig gains WaveformType field (zero value = WaveformCustom)
- ClassFreqConfigs converted to named fields (required for new struct field)
- All existing synth tests continue to pass (38 tests total)
- Add bpfFilter and readPath global vars with flag registration
- Add deriveOutputPath helper: capture.pcap -> capture.mp3 (D-05)
- run() validates mutual exclusion of --read and -i (D-03)
- run() pre-validates BPF filter via capture.ValidateBPFFilter before capture (CAPT-05)
- runLiveMode: extracts existing live pipeline, passes bpfFilter to StartCapture
- runPcapMode: reads pcap with ReadPcapFile, classifies with timestamps, AggregatePcap, encodes
- Bookend messages: 'Reading <file>...' at start, 'Saved...' at end (D-06)
- --verbose supported in pcap mode via onSnapshot callback (D-07)
- Empty pcap produces clear error with file path (Pitfall 4)
- Add AggregatePcap to aggregate/window.go: reads ClassifiedPackets from channel,
assigns to time windows using Timestamp field (D-01), fills gaps with empty
snapshots (D-02: silence), fires onSnapshot callback per window (D-07)
- Add 7 tests covering basic, multiple windows, gaps, empty input, WindowIndex,
class counts, and onSnapshot callback
- All tests pass
- Add Timestamp time.Time field to ClassifiedPacket (classify/types.go)
- Create capture/bpf.go: ValidateBPFFilter and CompileSoftwareBPF
- Create capture/pcap_reader.go: ReadPcapFile with optional software BPF filter
- Update OpenCapture and StartCapture to accept filter string param
- Update cmd/netsynth/main.go to pass empty filter to StartCapture
- All new tests pass; existing tests unaffected
- Add 03-01-SUMMARY.md with deviations and test results
- Update STATE.md: advance progress, record metrics and decisions
- Update ROADMAP.md: phase 3 plan progress
- Mark CLAS-02 requirement complete
- Replace ClassUnknown with ClassUnknown1-4 (unknown-1 through unknown-4)
- AllClasses() now returns 14 elements
- Add hashBucket() function routing unrecognized traffic deterministically
- Update synth/config.go: NumLayers=14, 4 dissonant unknown tone configs at 862-1047 Hz
- Add TestAllClassesCount and TestHashBucketDistribution tests
- Add TestClassFreqConfigsComplete and TestNumLayersMatchesAllClasses to config_test.go
- Update TestNewBankHas14Layers, TestMixerNoClip for 14 classes
- All classify and synth tests pass
- Add outputPath var and -o/--output Cobra flag (OUT-01)
- Default output path resolves to netsynth-<timestamp>.mp3 when -o not provided (D-15)
- Snapshot accumulator collects all WindowSnapshots for Phase 3 synthesis wiring
- TODO(phase-3) marker for encode.RunSynthesis integration
- EncodeMP3: writes stereo PCM frames to MP3 via go-lame (44100 Hz, 128 kbps, stereo)
- RunSynthesis: renders WindowSnapshots through OscillatorBank then encodes to MP3
- Zero-packet guard before file creation returns descriptive error (OUT-03)
- InitParams called after all Set* calls per go-lame requirements
- TestMP3Valid: synthetic snapshots through full pipeline, ffprobe validation
- TestZeroPacketError: empty slice and zero-count snapshots return error, no file created
- TestEncodeMP3DirectBytes: direct frame encoding validated by ffprobe
- Created 02-02-SUMMARY.md with plan results and 14 test descriptions
- Updated STATE.md: advanced to plan 3 of 3, recorded metrics and decisions
- Updated ROADMAP.md: phase 02 showing 2/3 summaries complete
- Marked SYNTH-03 complete in REQUIREMENTS.md
- NewBank creates 11 layers from ClassFreqConfigs, one per TrafficClass
- RenderWindow transforms WindowSnapshot into SamplesPerWindow stereo frames
- GainPerLayer (1/11) applied per layer guarantees no clipping with all layers at max
- PanGains applied per layer for constant-power stereo positioning
- EMA amplitude smoothing provides temporal convergence across windows
- 7 bank tests: layer count, output length, silence, non-zero, no-clip, stereo pan, EMA convergence
- synth/config.go: FreqConfig, HarmonicDef types; ClassFreqConfigs for all 11 traffic classes
with frequencies in 60-800 Hz using musical intervals; pan positions per D-12
- synth/oscillator.go: phase-accumulator oscillator with additive harmonic synthesis
and phase subtraction wrap (not math.Mod) per D-05
- synth/layer.go: EMA amplitude smoothing with tau-based alpha; whisper floor (0.03)
prevents silence once class has been seen; UpdateTarget/AdvanceSample interface
- All 12 tests pass (TestAllClassesHaveConfig, TestFrequenciesUnique, TestEMAAmplitudeRise, etc.)
- Add github.com/sjzar/go-lame v0.0.9 to go.mod for MP3 encoding
- gcc and ffprobe available via apt (gcc 13.3.0, ffprobe 6.1.1)
- CGO_ENABLED=1 build verified successful