diff --git a/.planning/phases/11-synthesis-and-config-layer/11-RESEARCH.md b/.planning/phases/11-synthesis-and-config-layer/11-RESEARCH.md
new file mode 100644
index 0000000..0849231
--- /dev/null
+++ b/.planning/phases/11-synthesis-and-config-layer/11-RESEARCH.md
@@ -0,0 +1,412 @@
+# Phase 11: Synthesis and Config Layer - Research
+
+**Researched:** 2026-03-27
+**Domain:** Go — synth config completion, PrintConfig group headers, TOML [groups] table
+**Confidence:** HIGH
+
+## Summary
+
+Phase 11 completes the synthesis layer for all 35 traffic classes and adds group-aware output to `PrintConfig`. The work is entirely within `synth/config.go`, `config/config.go`, and `classify/types.go` — no new packages, no external dependencies beyond what is already in go.mod. The frequency allocation table in `synth/config.go` lines 74–110 is the authoritative source of truth for all Hz, waveform, group, and pan values for the 18 table-designed classes. LDAP, Kerberos, and Syslog get their Hz values from `autoAssignFreq` (verified by running the FNV hash: LDAP=2950 Hz, Kerberos=3250 Hz, Syslog=3050 Hz) and use Triangle waveform / Infrastructure group, matching existing Infrastructure family members.
+
+The test suite is currently broken in `config` and `synth` packages because `ClassFreqConfigs` only has 14 entries while `AllClasses()` returns 32. After Phase 11, `AllClasses()` returns 35 and `ClassFreqConfigs` must match exactly. Multiple existing test hardcodes (`want 14`, `TestNewBankHas14Layers`, `TestAllClassesCount want 32`) need updating to 35.
+
+The `[groups]` TOML feature requires adding a `Groups map[string]string` field to `rawConfig`, wiring it through `Load()`, storing the reassignments in `LoadResult`, applying them in `PrintConfig`, and adding a warning for unknown class names. No existing config pipeline stages need structural changes — group reassignment is a post-merge overlay on FreqConfig.Group fields.
+
+**Primary recommendation:** Execute in three sequential sub-tasks: (1) add 21 ClassFreqConfigs entries + update AllClasses() + fix count tests, (2) refactor PrintConfig for group headers, (3) add [groups] TOML support + update tests.
+
+---
+
+
+## User Constraints (from CONTEXT.md)
+
+### Locked Decisions
+- **D-01:** Add ClassFreqConfigs entries for all 18 new classes currently in AllClasses(). Use Hz values, waveforms, pans, and groups from the Phase 9 frequency allocation table comment in `synth/config.go` lines 74-110. Use `WaveformPresetHarmonics()` for all new entries (not hand-tuned harmonics).
+- **D-02:** Add ClassLDAP, ClassKerberos, ClassSyslog to AllClasses() in `classify/types.go`. Create ClassFreqConfigs entries for them using `autoAssignFreq`-derived Hz values (FNV hash in [2500, 4000] Hz range). Their group is "Infrastructure", waveform is Triangle (matching the Infrastructure family pattern).
+- **D-03:** After D-01 and D-02, AllClasses() returns 35 entries (32 + 3). TestAllClassesCount updated to 35.
+- **D-04:** PrintConfig groups classes by their Group field value. Each group gets a comment header line: `# ` followed by a blank line, then all classes in that group. Groups are ordered: Infrastructure, Web, Mail, Remote Access, File Transfer, Database, Discovery, VoIP, Unknown.
+- **D-05:** Within each group, classes are ordered by ascending BaseHz (matching the frequency allocation table order).
+- **D-06:** User-defined classes (not in AllClasses but in FreqCfgs) are emitted after all built-in groups under a "# User-defined" section header.
+- **D-07:** Users define group reassignments in TOML with a `[groups]` table using simple key-value pairs: `IMAP = "Web"` reassigns IMAP from Mail to Web group. The key is the TrafficClass string value, the value is the target group name.
+- **D-08:** Group reassignment only affects `--print-config` output grouping and the Group field in FreqConfig. It does NOT change frequency, waveform, or pan — those stay as designed. PrintConfig reflects the reassignment.
+- **D-09:** Unknown group names in `[groups]` config are accepted (user can invent custom group names). Unknown class names produce a warning (same pattern as `[sounds.X]` with unknown class).
+
+### Claude's Discretion
+- Exact Hz values for LDAP, Kerberos, Syslog (computed from autoAssignFreq FNV hash)
+- Pan positions for LDAP, Kerberos, Syslog
+- Test structure for new ClassFreqConfigs entries and PrintConfig group output
+- Whether to add `[groups]` to rawConfig struct as `map[string]string` or a custom type
+- How to handle group reassignment in the merge/load pipeline
+
+### Deferred Ideas (OUT OF SCOPE)
+None — discussion stayed within phase scope.
+
+
+---
+
+
+## Phase Requirements
+
+| ID | Description | Research Support |
+|----|-------------|------------------|
+| GRP-02 | `--print-config` output organizes classes by group with section headers | PrintConfig refactor: iterate canonical group order, emit `# ` header + blank line, sort within group by BaseHz ascending; user-defined classes under `# User-defined` |
+| GRP-03 | Users can define `[groups]` in TOML config to reassign protocols to different sound families | Add `Groups map[string]string` to rawConfig; apply after merge in Load(); warn on unknown class name; store in LoadResult; PrintConfig reads Group field from FreqConfig |
+
+
+---
+
+## Standard Stack
+
+Phase 11 uses only packages already in go.mod. No new dependencies.
+
+### Core (already in go.mod)
+| Library | Version | Purpose | Why Standard |
+|---------|---------|---------|--------------|
+| `github.com/BurntSushi/toml` | existing | TOML decode — `map[string]string` for `[groups]` table | Already used; `md.Undecoded()` handles unknown key detection |
+| `github.com/netsynth/netsynth/classify` | local | TrafficClass constants, AllClasses() | Local package; phase modifies it |
+| `github.com/netsynth/netsynth/synth` | local | FreqConfig, ClassFreqConfigs, WaveformPresetHarmonics | Local package; phase adds 21 entries |
+
+**No installation needed.** `go test ./...` is the only verification command.
+
+---
+
+## Architecture Patterns
+
+### Pattern 1: ClassFreqConfigs Entry Structure
+**What:** Each entry in `ClassFreqConfigs` follows a strict pattern — BaseHz from the frequency table, WaveformType constant, Harmonics generated by `WaveformPresetHarmonics`, Pan from the table, Group string.
+**When to use:** For all 18 table-designed new entries (D-01) and 3 auto-assigned entries (D-02).
+**Example (from existing code):**
+```go
+classify.ClassIMAP: {
+ BaseHz: 241.0,
+ WaveformType: WaveformTriangle,
+ Harmonics: WaveformPresetHarmonics(WaveformTriangle, 241.0, SampleRate),
+ Pan: 0.3,
+ Group: "Mail",
+},
+```
+
+### Pattern 2: autoAssignFreq for LDAP/Kerberos/Syslog
+**What:** FNV-32a hash into [2500, 4000] Hz range. Values are deterministic — computed once and hardcoded in the entry.
+**Verified computed values:**
+- LDAP: 2950.0 Hz
+- Kerberos: 3250.0 Hz
+- Syslog: 3050.0 Hz
+
+Pan positions (Claude's discretion): assign spread within [-0.3, 0.3] range — LDAP=-0.2, Kerberos=0.0, Syslog=0.2 keeps them center-ish (Infrastructure family is already spread -0.3 to +0.3 at low frequencies; these are high-frequency so center-cluster is appropriate).
+
+### Pattern 3: AllClasses() Update (classify/types.go)
+**What:** Add ClassLDAP, ClassKerberos, ClassSyslog inside the Infrastructure block in AllClasses(). The comment guard `// D-01: no ClassFreqConfigs until Phase 11` is removed.
+**Ordering within Infrastructure:** Append after ClassSNMP (highest-Hz member at 118 Hz) since LDAP/Kerberos/Syslog are at 2950/3250/3050 Hz — technically they sort higher but within their family group they follow table order.
+
+### Pattern 4: PrintConfig Group-Ordered Iteration (GRP-02)
+**What:** Replace the current flat `classify.AllClasses()` iteration with a canonical-group-ordered iteration. Build a `groupOrder []string` slice with the 9 canonical groups; for each group collect classes from AllClasses() whose `FreqCfgs[cls].Group == group`, sort by BaseHz, emit group header + entries.
+
+**Canonical group order (D-04):**
+```
+Infrastructure, Web, Mail, Remote Access, File Transfer, Database, Discovery, VoIP, Unknown
+```
+
+**Implementation approach:**
+```go
+// groupOrder defines canonical display order for --print-config
+var groupOrder = []string{
+ "Infrastructure", "Web", "Mail", "Remote Access",
+ "File Transfer", "Database", "Discovery", "VoIP", "Unknown",
+}
+
+// Build group -> []TrafficClass index from AllClasses()
+// Sort each group slice by FreqCfgs[cls].BaseHz ascending (D-05)
+// For each group: emit "# \n\n" then entries
+// After all built-in groups: user-defined classes under "# User-defined\n\n"
+```
+
+**Critical detail:** The sort must use `result.FreqCfgs[cls].BaseHz` (the effective config, post-merge) not `synth.ClassFreqConfigs[cls].BaseHz`, so user frequency overrides are reflected in the sort order. This is unlikely to matter in practice but is correct.
+
+### Pattern 5: TOML [groups] Support (GRP-03)
+
+**rawConfig struct extension:**
+```go
+type rawConfig struct {
+ Sounds map[string]SoundOverride `toml:"sounds"`
+ Rules []RawRule `toml:"rules"`
+ Groups map[string]string `toml:"groups"` // key=TrafficClass string, val=group name
+}
+```
+
+Using `map[string]string` (Claude's discretion) is the simplest approach — TOML decodes `[groups]` as a string map naturally. No custom type needed.
+
+**LoadResult extension:**
+```go
+type LoadResult struct {
+ FreqCfgs map[classify.TrafficClass]synth.FreqConfig
+ UserRules []classify.Rule
+ ConfigPath string
+ AutoClasses map[classify.TrafficClass]bool
+ GroupOverrides map[classify.TrafficClass]string // NEW: class -> reassigned group name
+}
+```
+
+**applyGroupOverrides function (new):** After `merge()` in `Load()`, iterate `raw.Groups`; for each key=className, val=groupName: if className is known (exists in freqCfgs), update `cfg.Group = groupName` and store back; if unknown, emit warning to stderr (D-09). The warning uses the same pattern as unknown [sounds.X] classes.
+
+**Alternative approach:** Apply group reassignment directly inside PrintConfig by reading `result.GroupOverrides` map on-the-fly without mutating FreqConfig. This avoids touching LoadResult but makes PrintConfig depend on a new field anyway. Mutating FreqConfig.Group is cleaner because classAnnotation and other consumers see a consistent view.
+
+### Pattern 6: TOML md.Undecoded() and the [groups] map
+**Critical:** `BurntSushi/toml` decodes map fields without flagging individual map keys as "undecoded" — unknown class names in `[groups]` will NOT be caught by `md.Undecoded()`. This is the same behavior as `[sounds.*]` (unknown class names are silently accepted at decode time and validated in `merge()`). The warning for unknown class names in `[groups]` must be implemented in the new `applyGroupOverrides` function, not in `parseFile`. This matches D-09 exactly.
+
+### Anti-Patterns to Avoid
+- **Hand-computing harmonics for new entries:** Use `WaveformPresetHarmonics()` — don't write `[]HarmonicDef{{1, 1.0}, {2, 0.5}, ...}` manually for table-designed entries.
+- **Sorting AllClasses() output by BaseHz globally:** The table order in AllClasses() is the authoritative display order within families. The sort in PrintConfig must use group-then-BaseHz, not a flat sort.
+- **Adding [groups] validation to parseFile:** `md.Undecoded()` cannot catch unknown string map keys; validation must be in `applyGroupOverrides`.
+- **Mutating synth.ClassFreqConfigs:** copyDefaults() produces a working copy; all mutations go there, never to the package-level map.
+
+---
+
+## Don't Hand-Roll
+
+| Problem | Don't Build | Use Instead | Why |
+|---------|-------------|-------------|-----|
+| Harmonic series for new waveform entries | Manual `[]HarmonicDef` slices | `WaveformPresetHarmonics(wt, baseHz, SampleRate)` | Already computes bandlimited series; manual values would drift if SampleRate changes |
+| FNV hash for LDAP/Kerberos/Syslog Hz | Custom hash | `autoAssignFreq("LDAP")` output hardcoded as 2950.0 | Values are pre-computed; hardcoding eliminates runtime dependency on config package from synth package |
+| TOML group reassignment validation | Custom struct with Validate() | `map[string]string` + warning in applyGroupOverrides | Matches existing pattern for [sounds.*] unknown classes |
+
+---
+
+## Runtime State Inventory
+
+Step 2.5: SKIPPED — this is not a rename/refactor/migration phase.
+
+---
+
+## Environment Availability Audit
+
+Step 2.6: SKIPPED — phase is purely code changes within existing Go packages. No external tools, services, databases, or CLI utilities beyond the existing `go` toolchain are required. The Go toolchain is already confirmed working (tests ran above).
+
+---
+
+## Common Pitfalls
+
+### Pitfall 1: Stale hardcoded counts in tests
+**What goes wrong:** `TestNewBankHas14Layers` expects 14 layers; `TestLoadNoConfig`/`TestLoadUnknownClass`/`TestLoadAllDefaultsPresent`/`TestLoadNoConfigReturnsLoadResult` all assert `len(cfgs) == 14`; `TestAllClassesCount` asserts 32. After adding 21 ClassFreqConfigs entries and 3 AllClasses entries these fail.
+**Why it happens:** Tests were written against the Phase 9 baseline of 14 built-in entries and never updated.
+**How to avoid:** Update all hardcoded count assertions to 35 in a single pass after D-01 and D-02 are complete. The specific files and line numbers:
+- `synth/bank_test.go:12` — `want 14` → `want 35`
+- `synth/bank_test.go:13` — `len(b.layers) != 14` → `!= 35`
+- `config/config_test.go:120` — `want 14` → `want 35`
+- `config/config_test.go:149` — `want 14 (BOGUS should not appear)` → `want 35`
+- `config/config_test.go:185` — `want 14` → `want 35`
+- `config/config_test.go:617` — `want 14` → `want 35`
+- `classify/classifier_test.go:459` — `want 32` → `want 35`
+**Warning signs:** Any test that hardcodes a count of 14 or 32.
+
+### Pitfall 2: TestPrintConfigContainsAllClasses class list is stale
+**What goes wrong:** `config/config_test.go:407–416` lists exactly 14 class name strings. After Phase 11, AllClasses() has 35 entries; the test should either check all 35 or be replaced by a loop over `classify.AllClasses()`.
+**How to avoid:** Replace the hardcoded `classNames` slice with `for _, cls := range classify.AllClasses()` during PrintConfig test updates.
+
+### Pitfall 3: PrintConfig group-sort uses default Hz, not effective Hz
+**What goes wrong:** If group-sort code reads `synth.ClassFreqConfigs[cls].BaseHz` instead of `result.FreqCfgs[cls].BaseHz`, user frequency overrides don't affect sort position. The output is still correct for default configs but fails when the user overrides a frequency in [sounds.*].
+**How to avoid:** Always sort from `result.FreqCfgs[cls].BaseHz`.
+
+### Pitfall 4: [groups] map keys not caught by md.Undecoded()
+**What goes wrong:** `parseFile` uses `md.Undecoded()` to catch field typos. TOML map keys are all valid decode targets by definition — `md.Undecoded()` will be empty even if `[groups]` contains `IMAPtypo = "Web"`. The warning must come from `applyGroupOverrides`.
+**How to avoid:** Implement unknown-class warning in `applyGroupOverrides`, not in `parseFile` or `validate`.
+
+### Pitfall 5: TestClassFreqConfigsMatchAllClasses fails if counts diverge
+**What goes wrong:** `synth/config_test.go:57` checks `len(synth.ClassFreqConfigs) == len(classify.AllClasses())`. If the implementer adds all 21 ClassFreqConfigs entries but only adds 2 of the 3 new AllClasses entries, this test fails in a confusing way.
+**How to avoid:** D-01 and D-02 must be completed atomically — add all 21 ClassFreqConfigs entries and all 3 AllClasses entries in a single task.
+
+### Pitfall 6: "File Transfer" group name has a space
+**What goes wrong:** If the group sort map uses `"FileTransfer"` instead of `"File Transfer"` (matching the FreqConfig.Group string), Discovery and File Transfer classes end up under a catch-all or dropped entirely.
+**How to avoid:** All group strings must exactly match those in `FreqConfig.Group`. Canonical list: `"Infrastructure"`, `"Web"`, `"Mail"`, `"Remote Access"`, `"File Transfer"`, `"Database"`, `"Discovery"`, `"VoIP"`, `"Unknown"`.
+
+---
+
+## Code Examples
+
+### Complete frequency table for all 21 missing entries
+From the allocation table in `synth/config.go` lines 74–110 plus D-02 computed values:
+
+```
+// Infrastructure additions (Triangle, [93-118] Hz)
+mDNS: 93 Hz, Triangle, pan=+0.3
+SSDP: 105 Hz, Triangle, pan=-0.2
+SNMP: 118 Hz, Triangle, pan=+0.2
+// Web addition (Sawtooth)
+QUIC: 190 Hz, Sawtooth, pan=-0.2
+// Mail additions (Triangle)
+IMAP: 241 Hz, Triangle, pan=+0.3
+POP3: 271 Hz, Triangle, pan=+0.4
+SMTP-sub: 305 Hz, Triangle, pan=+0.5
+// Remote Access additions (Square)
+RDP: 385 Hz, Square, pan=-0.6
+Telnet: 432 Hz, Square, pan=-0.5
+VNC: 485 Hz, Square, pan=-0.4
+// File Transfer additions (Square)
+FTP: 545 Hz, Square, pan=+0.5
+SMB: 612 Hz, Square, pan=+0.6
+TFTP: 687 Hz, Square, pan=+0.7
+// Database additions (Sawtooth)
+MySQL: 1543 Hz, Sawtooth, pan=-0.4
+PostgreSQL: 1732 Hz, Sawtooth, pan=-0.2
+Redis: 1944 Hz, Sawtooth, pan=+0.2
+MongoDB: 2182 Hz, Sawtooth, pan=+0.4
+// VoIP (Sine)
+SIP: 2449 Hz, Sine, pan=0.0
+// Infrastructure auto-assigned (Triangle) — D-02
+LDAP: 2950 Hz, Triangle, pan=-0.2
+Kerberos: 3250 Hz, Triangle, pan=0.0
+Syslog: 3050 Hz, Triangle, pan=+0.2
+```
+
+### PrintConfig group header pattern (D-04)
+```go
+// groupOrder is the canonical display order for --print-config section headers.
+var groupOrder = []string{
+ "Infrastructure", "Web", "Mail", "Remote Access",
+ "File Transfer", "Database", "Discovery", "VoIP", "Unknown",
+}
+
+// In PrintConfig, replace the flat AllClasses() loop with:
+builtinByGroup := map[string][]classify.TrafficClass{}
+for _, cls := range classify.AllClasses() {
+ cfg := result.FreqCfgs[cls]
+ grp := cfg.Group
+ builtinByGroup[grp] = append(builtinByGroup[grp], cls)
+}
+// Sort each group slice by BaseHz ascending (D-05)
+for grp := range builtinByGroup {
+ sort.Slice(builtinByGroup[grp], func(i, j int) bool {
+ return result.FreqCfgs[builtinByGroup[grp][i]].BaseHz <
+ result.FreqCfgs[builtinByGroup[grp][j]].BaseHz
+ })
+}
+// Emit in canonical order
+for _, grp := range groupOrder {
+ classes, ok := builtinByGroup[grp]
+ if !ok || len(classes) == 0 {
+ continue
+ }
+ fmt.Fprintf(&sb, "# %s\n\n", grp)
+ for _, cls := range classes {
+ // ... existing per-class emit logic ...
+ }
+}
+```
+
+### [groups] TOML apply pattern (D-07/D-08/D-09)
+```go
+// applyGroupOverrides overlays [groups] reassignments onto freqCfgs.Group in-place.
+// Unknown class names produce a warning; unknown group names are silently accepted (D-09).
+func applyGroupOverrides(cfgs map[classify.TrafficClass]synth.FreqConfig, groups map[string]string) {
+ for className, groupName := range groups {
+ cls := classify.TrafficClass(className)
+ cfg, known := cfgs[cls]
+ if !known {
+ fmt.Fprintf(os.Stderr, "Warning: config: [groups]: unknown class %q (ignored)\n", className)
+ continue
+ }
+ cfg.Group = groupName
+ cfgs[cls] = cfg
+ }
+}
+```
+
+Call site in `Load()` — after `merge()`:
+```go
+addAutoFreqEntries(freqCfgs, userRules, autoClasses)
+merge(freqCfgs, raw.Sounds)
+applyGroupOverrides(freqCfgs, raw.Groups) // NEW
+```
+
+---
+
+## Validation Architecture
+
+nyquist_validation is enabled (not false in config.json).
+
+### Test Framework
+| Property | Value |
+|----------|-------|
+| Framework | Go testing (stdlib) |
+| Config file | none — `go test ./...` |
+| Quick run command | `go test ./classify/... ./synth/... ./config/...` |
+| Full suite command | `go test ./...` |
+
+### Current Test Failures (baseline — must fix)
+The following tests are currently failing and Phase 11 must make them green:
+
+| Test | Package | Failure Cause | Fix Required |
+|------|---------|---------------|--------------|
+| `TestAllClassesHaveConfig` | `synth` | 18 new classes in AllClasses() missing from ClassFreqConfigs | Add 18+3 entries |
+| `TestClassFreqConfigsMatchAllClasses` | `synth` | 14 != 32 | Add entries, update AllClasses |
+| `TestNewBankHas14Layers` | `synth` | 14 != 32 + missing layers | Add entries + update assert |
+| `TestLoadAllDefaultsPresent` | `config` | 18 classes missing from loaded map | Add ClassFreqConfigs entries |
+| `TestLoadNoConfig` | `config` | len==14 assert, now 35 | Update count |
+| `TestLoadUnknownClass` | `config` | len==14 assert | Update count |
+| `TestLoadNoConfigReturnsLoadResult` | `config` | len==14 assert | Update count |
+| `TestPrintConfigContainsAllClasses` | `config` | Hardcoded 14 class names | Expand to all 35 |
+| `TestAllClassesCount` | `classify` | want 32, still passes (no new classes yet) | Update to 35 after D-02 |
+
+### Phase Requirements to Test Map
+| Req ID | Behavior | Test Type | Automated Command | Test Exists? |
+|--------|----------|-----------|-------------------|-------------|
+| GRP-02 | PrintConfig shows group headers | unit | `go test ./config/... -run TestPrintConfig` | Partial — `TestPrintConfigContainsAllClasses` exists; new `TestPrintConfigGroupHeaders` needed |
+| GRP-02 | Each built-in class appears in correct group | unit | `go test ./config/... -run TestPrintConfigGroupHeaders` | No — Wave 0 |
+| GRP-03 | [groups] TOML reassigns class to different group | unit | `go test ./config/... -run TestLoadGroupOverride` | No — Wave 0 |
+| GRP-03 | Unknown class in [groups] produces warning | unit | `go test ./config/... -run TestLoadGroupUnknownClass` | No — Wave 0 |
+| GRP-03 | PrintConfig reflects group reassignment | unit | `go test ./config/... -run TestPrintConfigGroupReassignment` | No — Wave 0 |
+
+### Sampling Rate
+- **Per task commit:** `go test ./classify/... ./synth/... ./config/...`
+- **Per wave merge:** `go test ./...`
+- **Phase gate:** `go test ./...` fully green before `/gsd:verify-work`
+
+### Wave 0 Gaps
+- [ ] `config/config_test.go` — add `TestPrintConfigGroupHeaders` (asserts `# Infrastructure`, `# Mail`, etc. appear in output in correct order)
+- [ ] `config/config_test.go` — add `TestLoadGroupOverride` (TOML `[groups]\nIMAP = "Web"` → FreqCfgs[IMAP].Group == "Web")
+- [ ] `config/config_test.go` — add `TestLoadGroupUnknownClass` (TOML `[groups]\nBOGUS = "Web"` → no error, warning to stderr)
+- [ ] `config/config_test.go` — add `TestPrintConfigGroupReassignment` (LoadResult with group-reassigned IMAP shows IMAP under `# Web` not `# Mail`)
+- [ ] `config/config_test.go` — update `TestPrintConfigContainsAllClasses` classNames slice to cover all 35 (or replace with AllClasses() loop)
+
+---
+
+## Open Questions
+
+1. **Pan positions for LDAP, Kerberos, Syslog (Claude's discretion)**
+ - What we know: Infrastructure family uses [-0.3, +0.3] pan range for existing members (ICMP=-0.3, NTP=-0.1, DHCP=+0.1, DNS=0.0, mDNS=+0.3, SSDP=-0.2, SNMP=+0.2)
+ - Recommendation: LDAP=-0.2, Kerberos=0.0, Syslog=+0.2 — fills the same spread pattern without exact collision with existing values
+
+2. **TestNewBankHas14Layers function name**
+ - What we know: The CONTEXT.md calls out that the name is stale (should reflect 35 classes)
+ - Recommendation: Rename to `TestNewBankHasAllLayers` and update the assertion to `len(b.layers) != len(classify.AllClasses())` — this future-proofs it against further class additions
+
+3. **Discovery group — no existing built-in entries in AllClasses() have Group="Discovery"**
+ - What we know: mDNS, SSDP, SNMP are currently in AllClasses() but not in ClassFreqConfigs (they are 3 of the 18 missing entries); they belong to "Infrastructure" per the frequency allocation table (slots 3-5, Group=Infrastructure)
+ - Clarification needed: the frequency table marks them as Infrastructure, but the canonical group order in D-04 lists "Discovery" as a separate group. The table must take precedence — mDNS/SSDP/SNMP are Group="Infrastructure", not "Discovery". The "Discovery" slot in groupOrder may remain empty or be omitted from PrintConfig output (the `if !ok || len(classes) == 0 { continue }` guard handles this).
+
+---
+
+## Sources
+
+### Primary (HIGH confidence)
+- Direct code inspection of `/home/dev/workspace/yoloyolo/synth/config.go` — frequency allocation table lines 74-110 (authoritative Hz, waveform, pan, group values)
+- Direct code inspection of `/home/dev/workspace/yoloyolo/config/config.go` — PrintConfig, rawConfig struct, merge(), autoAssignFreq(), addAutoFreqEntries()
+- Direct code inspection of `/home/dev/workspace/yoloyolo/classify/types.go` — AllClasses(), TrafficClass constants
+- Computed autoAssignFreq values by running the FNV hash Go code — LDAP=2950, Kerberos=3250, Syslog=3050
+- `go test ./...` output — confirmed exact failing tests and error messages
+
+### Secondary (MEDIUM confidence)
+- CONTEXT.md decisions D-01 through D-09 — user decisions locked in prior /gsd:discuss-phase session
+- STATE.md accumulated decisions — confirms Phase 10 left AllClasses() at 32 intentionally pending Phase 11
+
+---
+
+## Metadata
+
+**Confidence breakdown:**
+- Standard stack: HIGH — no new dependencies; entire phase is code within existing packages
+- Architecture: HIGH — patterns derived directly from existing code and locked CONTEXT.md decisions
+- Pitfalls: HIGH — derived from actual failing test output and code inspection
+- autoAssignFreq Hz values: HIGH — computed by executing the actual FNV hash code
+
+**Research date:** 2026-03-27
+**Valid until:** 2026-04-27 (stable — no external packages change)