devlog/categorical-resonance-judge

Pond · Engineering devlog

Categorical classification for the artefact-resonance judge

An LLM judge that scored every candidate connection on a freehand 0–1 scale, all in one call, collapsed into relative ranking and dropped the oblique cross-domain links the product exists to surface. Asking it to classify the kind of relationship instead — from a fixed vocabulary mapped to fixed scores — made the verdict stable under batching without changing anything downstream.

Project: Pond Window: Jun 2026 Recorded: 26 Jun 2026
Status: decided, implemented & live behind a flag

At a glance

The problem

Pond surfaces resonances: when you add something to your library, it points out which of your existing threads that artefact would enrich — especially the oblique, cross-domain connections you wouldn’t go looking for.

The mechanism is an LLM judge. For a newly imported artefact it looks at the candidate threads and decides which are worth surfacing, keeping anything above a fixed score floor. The judge’s whole job is to tell a real, enriching traversal apart from an incidental keyword overlap.

The trigger was a concrete miss. A newly imported image fragment surfaced a healthy set of resonances but dropped one thread that was exactly the kind of oblique cross-domain link the product is built to find. The judge had scored it 0.28, under the 0.4 floor, so it never showed. Evidence The question was why a connection a human reader would call apt scored as near-noise.

Decision map

Causal map from the miss to the categorical judge A dropped oblique link leads to the hypothesis that the 0–1 magnitude is unreliable when all candidates are scored in one call. An isolated-versus-batched measurement confirms it. Three probes — capping the batch, scoring in isolation, and prompt engineering — each fail to fix the number. The constraint is that a freehand magnitude collapses into relative ranking under batching. That leads to the decision: classify the kind of relationship and map each kind to a fixed score, which leaves everything downstream unchanged and defers the question of how many links to show. Oblique link dropped scored 0.28, below the 0.4 floor Hypothesis: magnitude is unreliable when batched Measure: isolated vs batched 0.72 alone → 0.227 in the batch Probe · cap the batch stays below floor at every cap ✗ Probe · score in isolation recovers it but floods — no discrimination ✗ Probe · prompt-engineer prose right, number still unstable ✗ Decision: classify the KIND → map to a fixed score stable under batching · downstream unchanged “how many to show” deferred to the surface
Causal map. The measurement (0.72 alone vs 0.227 batched) is the hinge; the three probes confirm the magnitude itself can’t be salvaged cheaply. Text alternative in the diagram description.

Timeline

  1. Jun 2026 · the trigger
    An oblique cross-domain link is dropped Evidence

    A newly imported image fragment surfaced many resonances but dropped a thread that a reader would call an apt, oblique connection. The judge had scored it 0.28, under the 0.4 keep floor.

  2. Jun 2026 · root cause
    Isolated vs batched measurement Evidence

    Scored on its own against the artefact, the thread got 0.72 — the judge clearly sees the link. Scored in the batch of ~33 candidates (the production path), 0.227, matching production’s 0.28. A freehand 0–1 magnitude asked for all candidates at once collapses into relative ranking: the oblique link loses to on-topic competitors in the same call.

  3. Jun 2026 · ruled out
    Cap the batch Evidence

    Tried caps of 33, 25, 20, 15, 12, 10. The thread stayed below 0.4 at every cap. Capping drops tail candidates, but the threads that suppress it are the high-similarity on-topic ones — exactly the ones a cap keeps.

  4. Jun 2026 · ruled out
    Score each thread in isolation Evidence

    Recovers the oblique thread (0.72) but floods the surface: unrelated threads also score 0.57–0.72 on their own and clear the floor. Removing the batch removes the discrimination too.

  5. Jun 2026 · partial
    Prompt variants Evidence

    Reasoning-first, tension-crediting, and “movement” framings got the judge’s prose right — for the oblique thread it wrote a precise account of the counter-pressure. But the number stayed unreliable (mean 0.38 across runs, high variance). The reasoning was sound; the magnitude was the weak link.

  6. Jun 2026 · the pivot
    Classify the kind, map to a fixed score Evidence

    Replace the freehand number with a fixed vocabulary of relationship kinds, each mapped to a fixed score. A classification question doesn’t depend on the other threads in the call, so it survives batching. Two kinds land below the floor, three above it, so nothing downstream changes.

  7. Jun 2026 · verification
    Three-run eval, then ship behind a flag Evidence

    An offline eval ran the production judge over real recent artefacts, three runs each. The scheme read each relationship per-artefact rather than blanket-generously, with high label stability. Shipped code-only behind a feature flag, now on in production.

Hypotheses and experiments

Each hypothesis with the probe and conclusion. Results are point-in-time eval outputs on a handful of artefacts — see Method and limitations.
HypothesisProbeExpected signalObservedVerdict
The 0–1 magnitude is unreliable under batching Score one thread alone vs in the full batch Same link, very different score 0.72 alone vs 0.227 batched Supported
A smaller batch restores the score Cap the batch at 33 → 10 Score rises above the floor as competitors drop Below floor at every cap Rejected
Scoring in isolation fixes it Score each thread alone Good links recovered, weak links stay low Recovered, but weak links also score 0.57–0.72 Rejected
Better prompting stabilises the number Reasoning-first / tension / “movement” prompts Apt links score consistently above the floor Prose improved; number mean 0.38, high variance Weakened
Classifying the kind is stable under batching Fixed vocabulary → fixed score, judged per-thread Stable labels; apt links land above the floor Per-artefact discrimination; high label stability Supported

Inference

The deeper reason a magnitude fails and a category holds: “rate 0–1 across all of these” invites the model to spread the candidates relative to each other, so a strong-but-oblique link is graded on a curve against on-topic ones. A category names an absolute property of the single pair, with no reference to the rest of the call. Inference

The decision

The judge now classifies each thread’s relationship to the artefact from a fixed vocabulary, and a lookup maps the kind to the same score column the surface already used:

dead_end             0.00   drop    no shared stake; incidental vocabulary
echo                 0.20   drop    only restates what the thread already holds
direct_development   0.60   keep    pushes the thread’s own question forward
complication         0.85   keep    tensions / complicates / opposes the thesis
cross_domain_ground  0.90   keep    a mechanism or frame from another domain

The two non-resonant kinds map below the 0.4 floor and the three good-neighbour kinds above it, so the score column, the floor, the write/rollback path and the surface are all untouched — the verdict’s shape changed, not the plumbing. The prompt tells the judge explicitly that a complication or a cross-domain grounding is a prime neighbour, not a weak one, and to judge each thread on its own rather than rank them against each other.

A second, smaller change rode along: the surfaced reasons were long and led with a restatement of the thread (“This thread explores X…”). The prompt now caps each clause to a short span and tells the judge to state the move, not the topic. A response-wide token cap can’t do this — it would truncate the tail rather than tighten each reason — so the lever is the prompt.

Deliberately not solved

How many links to show. When an artefact genuinely connects to many threads, that’s a surface question — rank the cross-domain and complication kinds above plain development (the 0.6 / 0.85 / 0.9 spread already supports that) and paginate — not a reason to make the judge cut good links. Raising the floor to drop plain development was evaluated and rejected: it hid the most-connected hub threads and cut unevenly across artefacts. Inference

What the eval showed

Three runs per artefact; modal label. Artefacts are described generically. “Kept” is links above the floor out of candidates judged.
ArtefactKeptNotable
Image fragment (the production miss) 28 / 32 The dropped oblique thread is recovered as a complication
A philosophy-of-mathematics book 14 / 17 Recovers 3 the old judge dropped (incl. an anthropology thread as cross_domain_ground); drops the same oblique thread as a dead_end
A tightly-scoped philosophy-of-language PDF 11 / 21 Cut 10 stable dead_ends; ~86% label stability across runs

The bellwether is that one oblique thread across the first two artefacts: the image judge keeps it (complication), the book judge drops it (dead_end), both stable and both defensible — the image genuinely complicates the thread’s thesis, while the book is adjacent but doesn’t engage its actual stake. The scheme reads the real relationship per artefact rather than being blanket-generous. Agreement with the old scalar judge is high wherever the scalar was confident; the divergences sit in the scalar’s 0.25–0.38 mush, where the categorical judge pulls up real groundings the float threshold had buried. Evidence

Evidence and implementation trail

Sources are generalised for public release — internal file names, identifiers, thread and artefact titles are omitted or described. Each item is referenced inline above by its label.

Source ledger
  • S-1 Production observation · Jun 2026. The imported image fragment whose resonance set dropped an apt oblique thread at score 0.28. Evidence
  • S-2 Investigation notes · Jun 2026. The isolated-vs-batched measurement (0.72 → 0.227), the cap sweep, the isolation flood, and the prompt-variant runs (mean 0.38). Evidence
  • S-3 Version history · Jun 2026. The change replacing the scalar judge with the categorical vocabulary, mapping kinds to the existing score column, and tightening reason length — code-only, no schema change. Evidence
  • S-4 Offline eval + working-tree check · Jun 2026. The three-run eval over real recent artefacts (the table above), run through the exact production candidate-selection path; verified the judge is live behind a flag that is on in production. Evidence

Outcome, open questions, revisit triggers

Outcome

The categorical judge is live in production behind its flag, as a drop-in replacement for the scalar judge — same score column, same floor, same surface. The production miss that started this is recovered. Evidence

Open questions

Keep density. Keep rates ran 88% / 82% / 52% across the test artefacts — they track how genuinely connected an artefact is. When many links are real, showing all of them at once is a surface problem, to be handled by ranking and pagination, not by the judge dropping good links. Two density levers were tried and rejected: raising the floor (hides the most-connected hub threads, cuts unevenly) and tightening the pre-judge similarity filter (strictly recall-negative, since the judge already drops dead ends). Evidence

Label stability. A few artefacts show non-unanimous modal labels across runs, all at category boundaries (complication ↔ cross-domain ground, development ↔ complication). It doesn’t change keep/drop — both sides keep — but it’s worth watching. Unresolved

The score values are knobs. The mapping (0.6 / 0.85 / 0.9 above the floor, 0.0 / 0.2 below) is tunable; the eval is the place to re-tune it against more libraries. Unresolved

Revisit triggers

Method and limitations

This devlog was reconstructed by Claude from a maintained record of the work, version history, and a check against the current code. The judge was verified live behind its flag at the time of writing.