fix(telemetry): a floor that moved inside the window makes the band check a comparison of two populations (#4225)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / integration (push) Successful in 55s
CI & Build / TypeScript typecheck (push) Successful in 1m0s
CI & Build / Python tests (push) Successful in 1m34s
CI & Build / Build & push image (push) Successful in 33s

`retrieval_telemetry(days=30)` reported, for write_path_rule:

  "the weakest tenth of what this arm returns scores 0.6984, only -0.0216
   above its floor of 0.72"

A negative distance above something. The tenth percentile of what an arm
RETURNED cannot sit below the floor that gates what it may return — not
inside one population.

MEASURED CAUSE. write_path_rule's floor was 0.68 until 2026-09-02, when
2385100 (#3318) raised the shipped default to 0.72. The window opened
2026-08-22, so six days of it are calls made under the old bar; top_score.min
for the surface is exactly 0.68, the old bar still in the sample.

AND THE CHANGE LEFT NO TRACE THE READOUT COULD SEE. retrieval_tuning_events
records dial turns — a person or a model choosing a number. It was silent
about the other way a floor moves: somebody edits floor_default and ships it.
retrieval_tuning_history returned {"events": []} and retrieval_surfaces said
last_change: {}, source: "shipped". All true, and all of it silent about a
floor that had in fact moved.

THE RAISE ANNOUNCED ITSELF. A LOWERED FLOOR WOULD NOT: the gap comes out
comfortably positive and reads as a clean bill of health on a sample that
half predates the bar being judged. Both directions are now pinned.

So the check is SUSPENDED, not softened. band_hugs_floor asks whether the
scores are piled on the bar; that needs the scores and the bar to come from
the same regime. Where they do not, the honest answer is that this sample
cannot say, plus the date after which one can — floor_moved_mid_window
replaces band_hugs_floor for that arm and never accompanies it. A reader told
a number is unavailable goes and gets one; a reader handed a qualified number
uses it.

NO MIGRATION. `actor` is Text with no CHECK precisely so a new kind of actor
is not one — the model's own comment says so, and this is the case it
anticipated. "release" joins "model" and "human". user_id is already
nullable, which is right: no user did this, a release acts on every account
that has not overridden the dial, and a row per user would both multiply and
misattribute it. Both readers now take the newest of (this user's change, the
release's).

THE FIRST SIGHTING IS A BASELINE, written with old_value NULL. Nothing moved;
the row exists so the next release has a predecessor. That null is
load-bearing: floor_moves_since asks for old_value IS NOT NULL, so a fresh
install's baseline does not silently retire the check on every new install.

UI: the tuning history rendered actor as `human ? 'you' : 'Claude'`, so a
release row would have told the operator that Claude moved a floor it never
touched — the one failure the actor column exists to prevent. Three-way now,
with an unknown value printing itself rather than guessing.

Recorded at startup, inline and awaited. What #4181 cost three hours was
concurrency — a background task racing the hook for the same pool. Sequential
creates no contention, and this is twelve single-row reads. It must finish
before serving because a readout served before the change was recorded is the
exact answer this exists to stop giving.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
This commit is contained in:
2026-09-21 01:17:37 -04:00
co-authored by Claude Opus 5
parent 36b54bff1f
commit 512d0326a0
7 changed files with 499 additions and 9 deletions
+28 -2
View File
@@ -253,6 +253,21 @@ async function loadTuningHistory() {
}
}
/** Who moved a dial. Three answers, not two (#4225).
*
* `release` means the shipped default itself changed between versions — no
* user turned anything. Folding that into the `human ? 'you' : 'Claude'`
* fallback labelled it "Claude", which tells the operator the session moved a
* floor it never touched. Misattribution is the one failure the `actor`
* column exists to prevent, so an unknown value says so rather than guessing.
*/
function actorLabel(actor: string): string {
if (actor === "human") return "you";
if (actor === "release") return "this release";
if (actor === "model") return "Claude";
return actor;
}
async function saveKbInject() {
const t = Math.min(1, Math.max(0, Number(kbInjectThreshold.value) || 0));
const k = Math.min(10, Math.max(1, Math.floor(Number(kbInjectTopK.value) || 1)));
@@ -1925,8 +1940,11 @@ async function deleteUser(userId: number) {
<template v-else>set to</template>
{{ ev.new_value }}
</span>
<span class="tuning-actor" :class="{ 'is-human': ev.actor === 'human' }">
{{ ev.actor === 'human' ? 'you' : 'Claude' }}
<span
class="tuning-actor"
:class="{ 'is-human': ev.actor === 'human', 'is-release': ev.actor === 'release' }"
>
{{ actorLabel(ev.actor) }}
</span>
<span v-if="ev.created_at" class="tuning-when">{{ fmtDate(ev.created_at) }}</span>
</div>
@@ -4236,6 +4254,14 @@ async function deleteUser(userId: number) {
color: var(--fs-accent);
border-color: var(--fs-accent);
}
/* A shipped default that moved between releases (#4225). Muted rather than
accented: it is the answer to "did I do this?" being NO for both of the
other two, and it wants to be legible without competing with the changes
somebody actually made. */
.tuning-actor.is-release {
color: var(--fs-text-secondary);
border-style: dashed;
}
.tuning-when { margin-left: auto; color: var(--fs-text-tertiary); }
.tuning-reason {
margin: var(--fs-space-2) 0 0;