Skip to content

Commit 92afa83

Browse files
dreamrecclaude
andcommitted
fix(listening): the shared-reference guard closed a quarter of its defect
Third audit round, aimed at the second round's own fixes. Two findings, the second of which explains how the first got shipped. 1. EXACT-MATCH MERGING ONLY CATCHES A RE-USED FILE. capture_audio records live playback, so a producer who re-captures their reference each session gets a NEW file every time -- same material, never byte-identical. Measured at production geometry, 6 sessions x 2 pairs: re-recorded anchor cosine 0.98 0.95 0.90 0.85 0.80 0.70 certified as real taste 100% 100% 100% 100% 100% 100% captures merged 0 0 0 0 0 0 (honest control, own reference per session: 1%) A cosine threshold cannot fix this: at anchor cosine 0.70 the two anchors are less alike than two independent captures from one project, so any cutoff strict enough to catch it merges every real corpus. Added _anchor_asymmetry -- mean cross-session cosine among rejected captures minus the same among preferred. Relative, so it needs no calibration constant: anchored 0.70 .. 1.00 +0.47 .. +1.00 (sd ~0.006) honest, own reference/session -0.000 (sd 0.008) honest + a real quality signal +0.002 (sd 0.011) reference on the PREFERRED side -0.810 (sd 0.005) A genuine preference moves both sides apart symmetrically, which is why it does not trip. After: 0% certified at every cosine, control untouched, and a real signal still certifies at 100%. 2. THE MEASUREMENTS WERE TAKEN IN A REGIME THE CODE NEVER RUNS IN. Real CLAP embeddings are L2-normalised, so difference vectors have norm 0.3-1.4. Every number quoted for the earlier guards was measured on raw gaussians at norm ~30, where _fit_weights returns ~1e-8 and every corpus scores at chance. That is how a guard closing a quarter of its defect passed a full green suite with a docstring claiming it was measured. Test helpers now generate unit-norm captures at production dimension, and every documented number was re-measured. The session-level false-positive residual is 5.8-12.5% (previously documented ~9%), degrading as sessions get thinner. Power unchanged: 100% at >=5 sessions, 0% at 4. Also documents _fit_weights for what it is: at _LR=0.5 and l2=1.0 the memory coefficient (1 - _LR*2*l2) is exactly 0, so it is a fixed-point iteration, not gradient descent. Correct in the deployed regime, fragile to any change in those two constants. Corrects the previous commit's claim of "After the fix: 0 of 40", which held only for byte-identical captures. Suite 4784 passed, 2 skipped. Tool count unchanged at 472. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 02c9231 commit 92afa83

5 files changed

Lines changed: 297 additions & 59 deletions

File tree

CHANGELOG.md

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ looking. Both defects are measured, not argued.
6767
of 40 runs, for a head scoring 50.7% on fresh pairs. The identical corpus without a
6868
shared anchor: 3 of 40. Groups sharing a capture are now merged before scoring, which
6969
routes them to the existing OPTIMISTIC path, withholds the p-value and makes
70-
`taste_rank` warn. After the fix: 0 of 40. Reported as `groups_merged`.
70+
`taste_rank` warn. Reported as `groups_merged`. **Superseded below — that fix closed
71+
only the re-USED-file case, roughly a quarter of the defect.**
7172
- **Significance counted pairs as independent trials.** Grouping the CV split left the
7273
test itself ungrouped, but every pair in a held-out session is scored by one fitted `w`
7374
against correlated material, so a session is one observation rather than k. False
@@ -86,7 +87,41 @@ looking. Both defects are measured, not argued.
8687
regardless of corpus size). A test that cannot certify anything is not conservative, it
8788
is broken — and it would have looked more rigorous than what it replaced. The reasoning
8889
is recorded in `_group_sign_test` so it is not re-attempted blind.
89-
- 5 new regression tests pin the two defects; the suite is 4780 passed / 2 skipped.
90+
- 5 new regression tests pin the two defects.
91+
92+
### Fixed — the shared-reference guard closed a quarter of its defect, and the measurements said otherwise
93+
A third audit round, aimed at the previous round's own fixes.
94+
95+
- **Exact-match merging only catches a re-USED file.** `capture_audio` records live
96+
playback, so a producer who re-captures their reference each session produces a new
97+
file every time — same material, never byte-identical. Measured at production geometry,
98+
6 sessions x 2 pairs: a re-recorded anchor at cosine 0.98 / 0.95 / 0.90 / 0.85 / 0.80 /
99+
0.70 certified as real taste in **100%** of runs at every one, merging **nothing**,
100+
against 1% for a control with an independent reference per session. A cosine threshold
101+
cannot fix it: at 0.70 the two anchors are less alike than two independent captures from
102+
one project, so any cutoff strict enough would merge every real corpus.
103+
- Added `_anchor_asymmetry` — mean cross-session cosine among rejected captures minus the
104+
same among preferred. It needs no calibration constant because it is relative: anchored
105+
corpora measure **+0.47 to +1.00** (sd ~0.006), honest ones **-0.000** (sd 0.008), and a
106+
corpus carrying a genuine quality signal **+0.002** (sd 0.011) — a real preference moves
107+
both sides apart symmetrically, so it does not trip. Symmetric in which side is anchored
108+
(a shared preferred capture measures -0.81). After: 0% certified at every cosine, control
109+
untouched, genuine signal still certifies at 100%. Reported as `anchor_asymmetry` and
110+
`shared_reference_detected`.
111+
- **The measurements themselves were the deeper defect.** Real CLAP embeddings are
112+
L2-normalised, so difference vectors have norm 0.3-1.4. Every number quoted for the
113+
earlier guards was measured on raw gaussians at norm ~30, where `_fit_weights` returns
114+
~1e-8 and every corpus scores at chance. That regime is why a guard closing a quarter of
115+
its defect passed a full green suite with a docstring claiming it was measured. Test
116+
helpers now generate unit-norm captures at production dimension, and every documented
117+
number was re-measured: the session-level false-positive residual is **5.8-12.5%**
118+
(previously documented as ~9%), degrading as sessions get thinner; power is unchanged at
119+
100% for >=5 sessions.
120+
- `_fit_weights` is documented for what it is. At `_LR=0.5` and `l2=1.0` the memory
121+
coefficient `1 - _LR*2*l2` is exactly 0, so it is a fixed-point iteration rather than
122+
gradient descent. Correct in the deployed regime, fragile to any change in those two
123+
constants.
124+
- 4 new regression tests; the suite is 4784 passed / 2 skipped.
90125

91126
## v1.28.0 — 2026-07-31
92127

livepilot/skills/livepilot-core/references/perception.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,15 @@ itself — every item below was added after it was caught doing exactly that:
8787
- Significance counts **sessions, not pairs** — five or more separate sessions,
8888
or nothing can be certified however good the accuracy looks. Counting pairs
8989
put the false-positive rate at ~25% against a nominal 5%.
90-
- **Do not A/B every candidate against one fixed baseline.** Pairs sharing a
91-
capture are one piece of evidence, not several; they are merged before
92-
scoring, and `groups_merged` in the report says when that happened. Before
93-
this guard existed, 6 such pairs across 3 labelled sessions reported "a real
94-
preference signal" in 40 of 40 runs — for a head scoring 50.7% on fresh pairs.
90+
- **Do not A/B every candidate against one fixed baseline** — not even a
91+
re-captured one. Pairs sharing a reference are one piece of evidence, not
92+
several. Two guards catch it: `groups_merged` for a reused file,
93+
`shared_reference_detected` / `anchor_asymmetry` for a re-recorded one (which
94+
is what `capture_audio` produces — it records live playback, so the file
95+
differs every time even when the material does not). Before these, a
96+
re-recorded reference certified as real taste in **100%** of runs at every
97+
anchor similarity from 0.70 to 0.98, against 1% for an honest control.
98+
Compare candidates against **each other**.
9599

96100
`significant: null` means "could not be tested" (too few sessions, or captures
97101
shared). Treat it exactly as `false`.

mcp_server/listening/taste_head.py

Lines changed: 124 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
read ~100% whether or not the head learned anything real.
2929
3030
So this module never reports training accuracy. It reports cross-validated
31-
accuracy, a p-value against chance, and a verdict in words. Four corrections
31+
accuracy, a p-value against chance, and a verdict in words. Five corrections
3232
make that honest, each added after it was caught misreporting:
3333
3434
1. GROUPED cross-validation. A session yields several pairs from the same
@@ -43,21 +43,38 @@
4343
learnable signal at the same n. `significant: false` means the number
4444
carries no information however high it looks.
4545
46-
3. SHARED-CAPTURE merging, before the split. Grouping trusts a label, and no
47-
label can say "these pairs share a baseline". Keep one reference render and
48-
A/B candidates against it — the most natural way anyone compares anything —
49-
and every pair carries the same anchor, so every fold learns "not the
50-
anchor" and scores perfectly. Measured: 6 pairs over 3 labelled sessions
51-
sharing one anchor reported 100% / p=0.016 / significant in 40 of 40 runs,
52-
for a head that scores 0.507 on fresh pairs. See
53-
`_merge_groups_sharing_a_capture`.
46+
3. SHARED-REFERENCE detection, before the split. Grouping trusts a label, and
47+
no label can say "these pairs share a baseline". Keep one reference render
48+
and A/B candidates against it — the most natural way anyone compares
49+
anything — and every pair carries the same anchor, so every fold learns "not
50+
the anchor" and scores perfectly. Two guards, because the obvious one closes
51+
only a quarter of it: `_merge_groups_sharing_a_capture` catches a re-USED
52+
file, and `_anchor_asymmetry` catches a re-RECORDED one, which is what
53+
`capture_audio` actually produces. Measured at production geometry, 6
54+
sessions x 2 pairs, before the second guard existed:
55+
56+
re-recorded anchor cosine 0.98 0.95 0.90 0.85 0.80 0.70
57+
certified as real taste 100% 100% 100% 100% 100% 100%
58+
captures merged 0 0 0 0 0 0
59+
60+
(honest control, own reference per session: 1%.) After: 0% certified at
61+
every cosine, control untouched, and a genuine quality signal still
62+
certifies at 100%.
5463
5564
4. Significance over SESSIONS. Correction 1 grouped the cross-validation split
5665
but left the test counting pairs as independent trials. False-positive rate
5766
on corpora with no transferable signal, nominal 5%: 25% counting pairs, ~9%
5867
counting sessions. See `_group_sign_test`, which also records why the
5968
textbook permutation fix was built and rejected.
6069
70+
5. Measuring any of this in the geometry the code ACTUALLY RUNS IN. Real CLAP
71+
embeddings are L2-normalised, so a difference vector has norm 0.3-1.4. The
72+
first two rounds of measurement used raw gaussians at norm ~30, where
73+
`_fit_weights` returns near-zero and every corpus looks clean. That is how a
74+
guard closing a quarter of its defect passed a full green suite with a
75+
docstring claiming it was measured. The test helpers now generate unit-norm
76+
captures at production dimension; see `_unit` in the test module.
77+
6178
The cost of 3 and 4 is a higher bar: significance needs decisions from
6279
MIN_GROUPS_FOR_SIGNIFICANCE separate sessions with no shared captures, and below
6380
that nothing can be certified however good it looks. That bar is derived from
@@ -153,8 +170,21 @@ def _update(data: dict) -> dict:
153170
def _fit_weights(diffs: np.ndarray, l2: float) -> np.ndarray:
154171
"""Logistic regression on difference vectors, all labels positive.
155172
156-
Minimises -sum(log sigmoid(w.d)) + l2*||w||^2 by gradient descent.
157-
Convex, so the optimum is unique and plain GD is sufficient at this size.
173+
Targets -sum(log sigmoid(w.d)) + l2*||w||^2, which is convex.
174+
175+
Note what the iteration actually is at the default settings. The update
176+
w -= _LR * (-mean(d*p_wrong) + 2*l2*w) has memory coefficient
177+
(1 - _LR*2*l2), which is EXACTLY 0 at _LR=0.5 and l2=1.0 — so w is
178+
discarded and recomputed each step rather than accumulated. It is a fixed
179+
point iteration, not gradient descent, and it converges in one step for
180+
well-scaled inputs.
181+
182+
That is fine in the deployed regime (unit-norm CLAP embeddings, ||d|| in
183+
0.3-1.4, giving ||w|| ~ 0.03-0.26 and clean margins) but it is fragile: at
184+
||d|| ~ 30 the same code returns ~1e-8 and every corpus scores at chance.
185+
Anything that changes _LR or DEFAULT_L2 changes the character of this
186+
function, not just its speed. Measure in production geometry — see
187+
correction 5 in the module docstring for what happens when you do not.
158188
"""
159189
w = np.zeros(diffs.shape[1], dtype=np.float64)
160190
n = len(diffs)
@@ -281,8 +311,12 @@ def _group_sign_test(per_group: list[tuple[int, int]]) -> Optional[float]:
281311
A session counts once, as a win or a loss depending on whether the head beat
282312
chance on it. Ties carry no directional evidence and are dropped.
283313
284-
KNOWN RESIDUAL, measured not assumed: ~9% against a nominal 5%, because the
285-
CV folds share training data so sessions are not fully independent either.
314+
KNOWN RESIDUAL, re-measured at production geometry (unit-norm CLAP, 512-d):
315+
5.8% at 6 sessions x 4 pairs, 6.7% at 5 x 4, and 12.5% at 8 x 3 — against a
316+
nominal 5%. It degrades as sessions get thinner, because the CV folds share
317+
training data so sessions are not fully independent either. Power is
318+
unaffected: 100% on a genuine shared direction at >= 5 sessions, 0% at 4
319+
(structurally impossible, not a failure).
286320
The textbook fix — a group sign-flip permutation test — was built and
287321
REJECTED: `_fit_weights` is deliberately robust (`p_wrong` downweights
288322
already-correct points), so on the bimodal +/-d data that sign-flipping
@@ -304,13 +338,23 @@ def _group_sign_test(per_group: list[tuple[int, int]]) -> Optional[float]:
304338

305339
def _verdict(loo: Optional[float], n_pairs: int, scheme: str = "",
306340
n_groups: int = 0, p: Optional[float] = None,
307-
n_decisive: int = 0, merged: int = 0) -> str:
341+
n_decisive: int = 0, merged: int = 0, anchored: bool = False,
342+
asymmetry: float = 0.0) -> str:
308343
if loo is None:
309344
return (f"Only {n_pairs} pair(s). Need at least {MIN_PAIRS} before "
310345
"cross-validation says anything; the head is not usable yet.")
311346

312347
prefix = ""
313-
if merged:
348+
if anchored:
349+
side = "rejected" if asymmetry > 0 else "preferred"
350+
prefix = (f"Every session's {side} captures are far more alike than "
351+
f"chance (asymmetry {asymmetry:+.2f} against ~0.00 for "
352+
"independent material), which is the signature of one shared "
353+
"reference compared against over and over. Those decisions are "
354+
"one piece of evidence, not several, so they were scored as a "
355+
"single session. Compare candidates against EACH OTHER, not "
356+
"against a fixed baseline. ")
357+
elif merged:
314358
prefix = (f"{merged + 1} of the recorded groups share a capture — a "
315359
"reused baseline makes those pairs one piece of evidence, not "
316360
"several — so they were merged before scoring. ")
@@ -411,6 +455,59 @@ def find(x: str) -> str:
411455
return [find(g) for g in groups], merged
412456

413457

458+
# Honest corpora measure 0.000 +/- 0.008 (max 0.030 over 60 runs, including
459+
# corpora carrying a genuine quality signal). The weakest anchoring that still
460+
# certified at 100% measures 0.473. This sits >13 sd above honest and far below
461+
# the weakest real offender — a gap wide enough that the exact value is not
462+
# load-bearing.
463+
ANCHOR_ASYMMETRY_LIMIT = 0.15
464+
465+
466+
def _anchor_asymmetry(pairs: list[dict], groups: list[str]) -> float:
467+
"""How much more alike the REJECTED captures are than the preferred ones,
468+
measured only ACROSS sessions, using the groups AS RECORDED. Positive means a shared reference on the
469+
rejected side; negative means one on the preferred side.
470+
471+
Guard 4 keyed on exact vector equality, which turned out to close a far
472+
narrower hole than it claimed. `capture_audio` records live playback, so a
473+
producer who re-captures their reference each session produces a NEW file
474+
every time — never byte-identical, always the same material. Measured at
475+
realistic CLAP geometry, 6 sessions x 2 pairs, no genuine quality signal:
476+
477+
re-recorded anchor cosine 0.98 0.95 0.90 0.85 0.80 0.70
478+
certified as real taste 100% 100% 100% 100% 100% 100%
479+
captures merged by guard 4 0 0 0 0 0 0
480+
(honest control, own anchor per session: 1%)
481+
482+
A cosine threshold cannot fix that: at anchor cosine 0.70 the two anchors are
483+
less alike (0.49) than two independent captures from one project, so any
484+
cutoff strict enough to catch it merges every real corpus. This statistic is
485+
relative instead, so it needs no notion of "how similar is one project":
486+
487+
anchored 0.70 .. 1.00 +0.473 .. +1.000 (sd ~0.006)
488+
honest, own anchor per session -0.000 (sd 0.008)
489+
honest + a real quality signal +0.002 (sd 0.011)
490+
anchor on the PREFERRED side -0.810 (sd 0.005)
491+
492+
A real preference direction moves preferred and rejected captures APART
493+
symmetrically, leaving this near zero — which is why a genuine signal does
494+
not trip it.
495+
"""
496+
def mean_cross(key: str) -> float:
497+
vs = np.array([p[key] for p in pairs], dtype=np.float64)
498+
norms = np.linalg.norm(vs, axis=1, keepdims=True)
499+
norms[norms == 0] = 1.0
500+
vs = vs / norms
501+
sims = []
502+
for i in range(len(vs)):
503+
for j in range(i + 1, len(vs)):
504+
if groups[i] != groups[j]:
505+
sims.append(float(vs[i] @ vs[j]))
506+
return float(np.mean(sims)) if sims else 0.0
507+
508+
return mean_cross("rejected") - mean_cross("preferred")
509+
510+
414511
def train(store: TasteHeadStore, l2: float = DEFAULT_L2) -> dict:
415512
"""Fit the head and report cross-validated skill (never training fit)."""
416513
data = store.get_all()
@@ -428,6 +525,15 @@ def train(store: TasteHeadStore, l2: float = DEFAULT_L2) -> dict:
428525
# Guard #4 runs BEFORE the split: a label cannot express "these pairs share
429526
# a baseline capture", and grouped CV is blind to it.
430527
groups, merged = _merge_groups_sharing_a_capture(pairs, raw_groups)
528+
# ...and exact equality only catches a re-USED file, not a re-RECORDED one,
529+
# which is what capture_audio actually produces. This catches the rest.
530+
# Measured against the groups as RECORDED, not the merged ones: the merge
531+
# can already have collapsed everything into a single group, leaving no
532+
# cross-group pairs and a statistic of 0.0 that would read as "clean".
533+
asymmetry = _anchor_asymmetry(pairs, raw_groups)
534+
anchored = abs(asymmetry) > ANCHOR_ASYMMETRY_LIMIT
535+
if anchored:
536+
groups = ["_shared_reference"] * n
431537
n_groups = len(set(groups))
432538
weights = _fit_weights(diffs, l2)
433539
loo, scheme, per_group = _cv_accuracy(diffs, groups, l2)
@@ -454,14 +560,16 @@ def train(store: TasteHeadStore, l2: float = DEFAULT_L2) -> dict:
454560
"n_groups": n_groups,
455561
"n_groups_recorded": len({g for g in raw_groups if g}),
456562
"groups_merged": merged,
563+
"anchor_asymmetry": round(asymmetry, 4),
564+
"shared_reference_detected": bool(anchored),
457565
"n_decisive_groups": n_decisive,
458566
"sessions_needed_for_significance": MIN_GROUPS_FOR_SIGNIFICANCE,
459567
"baseline_accuracy": 0.5,
460568
"p_value": None if not certifiable else round(p_value, 4),
461569
"significant": (None if not certifiable
462570
else bool(p_value <= SIGNIFICANCE_ALPHA)),
463571
"verdict": _verdict(loo, n, scheme, n_groups, p_value, n_decisive,
464-
merged),
572+
merged, anchored, asymmetry),
465573
"trained_at": int(time.time()),
466574
}
467575
# Training accuracy is deliberately absent: with n << d it is ~100%

mcp_server/listening/taste_tools.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,15 @@ def taste_record_pair(
116116
reflect the true grouping.
117117
118118
Two things about HOW you compare, which matter more than how many pairs you
119-
record. Do not A/B every candidate against one fixed baseline: pairs that
120-
share a capture are one piece of evidence, not several, and they are merged
121-
before scoring (the head cannot be certified on them at all). And record
122-
from at least ``taste_head.MIN_GROUPS_FOR_SIGNIFICANCE`` separate sessions —
123-
significance counts sessions, not pairs, so more pairs from the sessions you
124-
already have will not move it.
119+
record. Do not A/B every candidate against one fixed baseline — not even a
120+
re-captured one, since ``capture_audio`` records live playback and produces
121+
a different file each time for the same material. Pairs sharing a reference
122+
are one piece of evidence, not several, and a corpus built that way cannot
123+
be certified at all (see ``anchor_asymmetry`` in the train report). Compare
124+
candidates against EACH OTHER. And record from at least
125+
``taste_head.MIN_GROUPS_FOR_SIGNIFICANCE`` separate sessions — significance
126+
counts sessions, not pairs, so more pairs from the sessions you already have
127+
will not move it.
125128
126129
Recording invalidates any previously fitted head — rerun ``taste_train``.
127130
"""
@@ -180,9 +183,11 @@ def taste_train(l2: float = taste_head.DEFAULT_L2) -> dict[str, Any]:
180183
turned out to share captures. Treat ``null`` exactly as you would
181184
``false``: as no evidence.
182185
183-
``groups_merged`` is worth reading. It is non-zero when sessions you
184-
recorded separately shared a capture and were collapsed, which is the
185-
single most common way a corpus looks bigger than the evidence in it.
186+
``groups_merged`` and ``shared_reference_detected`` are worth reading. They
187+
fire when sessions you recorded separately turn out to share a reference —
188+
byte-identical for the first, merely the same material for the second —
189+
which is the single most common way a corpus looks bigger than the evidence
190+
in it.
186191
187192
``l2`` raises or lowers regularisation. The default is strong on purpose —
188193
with far more dimensions than pairs, the penalty is what stops the head

0 commit comments

Comments
 (0)