docs(03): capture phase context
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
# Phase 3: Pipeline Integration and MVP - Context
|
||||
|
||||
**Gathered:** 2026-03-26
|
||||
**Status:** Ready for planning
|
||||
|
||||
<domain>
|
||||
## Phase Boundary
|
||||
|
||||
Wire the capture-classify-aggregate pipeline into the audio synthesis and MP3 encoding engine. Deliver the complete v1 MVP: `netsynth -i eth0 -o out.mp3` → capture → Ctrl+C → valid MP3. Replace the single "unknown" traffic class with 4 hash-bucketed sub-clusters, each producing a distinct dissonant drone tone.
|
||||
|
||||
</domain>
|
||||
|
||||
<decisions>
|
||||
## Implementation Decisions
|
||||
|
||||
### Auto-clustering
|
||||
- **D-01:** Hash-bucketing approach — deterministic hash of (dst_port, protocol) into 4 fixed buckets. No k-means, no warmup period, no external dependency.
|
||||
- **D-02:** 4 buckets for unknown traffic. Total layer count becomes 14 (10 known + 4 unknown buckets).
|
||||
- **D-03:** Hash-bucketing happens inside the existing classifier — classifier returns `unknown-1` through `unknown-4` instead of plain `unknown`. New TrafficClass constants added.
|
||||
- **D-04:** Replace `ClassUnknown` entirely with `ClassUnknown1`-`ClassUnknown4`. `AllClasses()` returns 14 classes. Clean break, no fallback.
|
||||
|
||||
### Cluster tone assignment
|
||||
- **D-05:** All 4 unknown buckets live in a dedicated 850-1100 Hz dissonant range, above the known protocol range (60-800 Hz). Slightly detuned intervals between them.
|
||||
- **D-06:** Same dissonant harmonic character for all 4 buckets (matching Phase 2's D-04/D-06 for the original "unknown" tone). Differentiated by pitch only, preserving the "something foreign" sonic identity.
|
||||
|
||||
### Shutdown & encoding feedback
|
||||
- **D-07:** Status line during encoding: print `Encoding N windows to <path>...` then `Saved <path> (Xs, N KB, encoded in Xs)` on completion. Minimal but confirms encoding is happening.
|
||||
- **D-08:** Protocol summary prints BEFORE encoding. Ctrl+C → protocol summary → "Encoding..." → "Saved". User sees capture stats immediately.
|
||||
|
||||
### Post-run output
|
||||
- **D-09:** Saved line includes path, audio duration, file size, and encoding time. e.g., `Saved out.mp3 (12.5s, 198 KB, encoded in 0.3s)`.
|
||||
- **D-10:** Unknown buckets appear in the protocol summary naturally as `unknown-1: 42, unknown-2: 17`, etc. Falls out of existing summary logic since they're full TrafficClass values.
|
||||
|
||||
### Claude's Discretion
|
||||
- Exact hash function for port/protocol → bucket mapping (within the 4-bucket constraint)
|
||||
- Exact Hz values for the 4 unknown bucket tones (within 850-1100 Hz range, detuned intervals)
|
||||
- GainPerLayer recalculation for 14 layers (was 1/11 for 11 layers)
|
||||
- Stereo panning positions for the 4 unknown buckets
|
||||
- Encoding time measurement implementation
|
||||
|
||||
</decisions>
|
||||
|
||||
<canonical_refs>
|
||||
## Canonical References
|
||||
|
||||
**Downstream agents MUST read these before planning or implementing.**
|
||||
|
||||
### Project context
|
||||
- `.planning/PROJECT.md` — Core value, constraints (Go, MP3 output, non-interactive)
|
||||
- `.planning/REQUIREMENTS.md` — CAPT-03 (graceful flush), CLAS-02 (auto-clustering)
|
||||
|
||||
### Prior phase context
|
||||
- `.planning/phases/01-capture-and-classification/01-CONTEXT.md` — Phase 1 decisions; classifier design, pipeline architecture
|
||||
- `.planning/phases/02-audio-synthesis-engine/02-CONTEXT.md` — Phase 2 decisions; tone mapping, mixing, encoding
|
||||
|
||||
### Key source files (integration points)
|
||||
- `cmd/netsynth/main.go` — CLI wiring with TODO(phase-3) at line 99; accumulator loop at lines 92-97
|
||||
- `encode/mp3.go` — `RunSynthesis()` and `EncodeMP3()` — the synthesis entry point
|
||||
- `synth/bank.go` — `OscillatorBank` and `RenderWindow()` — renders audio from WindowSnapshots
|
||||
- `synth/config.go` — `ClassFreqConfigs` map, `NumLayers`, `GainPerLayer` constants
|
||||
- `classify/types.go` — `TrafficClass` constants, `AllClasses()`, `WindowSnapshot` struct
|
||||
- `classify/rules.go` — Classification rules (first-match-wins ordered slice)
|
||||
- `classify/classifier.go` — `Classify()` method where hash-bucketing will be added
|
||||
- `aggregate/summary.go` — `PrintSummary()` and `PrintWindowLine()` for exit output
|
||||
|
||||
### Stack decisions
|
||||
- `CLAUDE.md` — Technology stack section; `muesli/kmeans` listed but NOT needed (hash-bucketing chosen instead)
|
||||
|
||||
</canonical_refs>
|
||||
|
||||
<code_context>
|
||||
## Existing Code Insights
|
||||
|
||||
### Reusable Assets
|
||||
- `encode.RunSynthesis([]WindowSnapshot, string) error` — already takes snapshots and produces MP3; main.go just needs to call it
|
||||
- `synth.NewBank(tau)` / `bank.RenderWindow(snap)` — synthesis engine already works with WindowSnapshot
|
||||
- `classify.NewClassifier(rules)` / `classifier.Classify(pkt)` — classifier where hash-bucketing logic will be added
|
||||
- `aggregate.PrintSummary(io.Writer, map[TrafficClass]int64)` — exit summary; will naturally show unknown-1 through unknown-4
|
||||
|
||||
### Established Patterns
|
||||
- Channel-based pipeline: capture → classify → aggregate → (synthesis goes here)
|
||||
- `done <-chan struct{}` for shutdown signaling via `signal.NotifyContext`
|
||||
- Buffered channels for stage decoupling (512-buffer capture, 1024-buffer classified, 8-buffer aggregate)
|
||||
- `io.Writer` injection for testable output (aggregate/summary.go)
|
||||
- Config-driven classifier with ordered `[]Rule` slice, first-match-wins
|
||||
|
||||
### Integration Points
|
||||
- `main.go:99` — Replace `_ = collectedSnapshots` with `encode.RunSynthesis(collectedSnapshots, outputPath)` call
|
||||
- `main.go:92-97` — Snapshot accumulator loop already collects snapshots; wiring is mechanical
|
||||
- `synth/config.go` — `ClassFreqConfigs` map needs 4 new entries for unknown buckets; `NumLayers` and `GainPerLayer` need updating from 11 to 14
|
||||
- `classify/types.go` — Remove `ClassUnknown`, add `ClassUnknown1`-`ClassUnknown4`, update `AllClasses()`
|
||||
|
||||
</code_context>
|
||||
|
||||
<specifics>
|
||||
## Specific Ideas
|
||||
|
||||
- The end-to-end flow should feel like tcpdump: start capturing, see activity, Ctrl+C, get your results
|
||||
- Unknown bucket tones should be immediately recognizable as "something foreign" — the dissonant character is the sonic signal, pitch differentiates which bucket
|
||||
- The protocol summary + saved line should give the user everything they need at a glance without running ffprobe
|
||||
|
||||
</specifics>
|
||||
|
||||
<deferred>
|
||||
## Deferred Ideas
|
||||
|
||||
None — discussion stayed within phase scope
|
||||
|
||||
</deferred>
|
||||
|
||||
---
|
||||
|
||||
*Phase: 03-pipeline-integration-and-mvp*
|
||||
*Context gathered: 2026-03-26*
|
||||
Reference in New Issue
Block a user