fix(telemetry): silence is only an answer where the ledger was watching (#4268)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 59s
CI & Build / Python tests (push) Successful in 1m44s
CI & Build / Build & push image (push) Successful in 31s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 59s
CI & Build / Python tests (push) Successful in 1m44s
CI & Build / Build & push image (push) Successful in 31s
`band_hugs_floor` compares an arm's weakest tenth against its floor, and #4225 made it SUSPEND rather than soften when the floor moved inside the window — because across a change the scores and the bar come from two different populations. It reads `floor_moves_since`, which answers "did this arm's floor move", and it read an empty answer as "no, it held steady". Those are the same answer only where the ledger was watching. Before an arm's first floor row there is nothing to move, nothing to report, and no way to tell a steady floor from an unrecorded one. The suspension was reading absence of evidence as evidence of absence, and the symptom is the one #4225 documented: a band "-0.0208 above its floor" — an impossible negative distance, printed with the suspension silent. Every install passes through this. The ledger's first row for an arm is written when that install first boots the release that records baselines, so any window longer than the install is old reaches back past it. The lowered-floor direction hides: the gap comes out comfortably positive and reads as a clean bill of health. `floor_history_gaps(since)` answers the question its companion cannot: which arms' floor history does not REACH the start of the window. Arms come from `surface_names()`, not from the ledger — an arm the ledger has never heard of is exactly the one at risk, so it cannot be the ledger that decides which arms get asked about. Baselines count here, which is the one place the two deliberately disagree. A baseline records a default without changing it, so it is not a move and `floor_moves_since` filters it out. It IS the ledger beginning to observe the arm, and from that moment silence genuinely means the floor held — filtering it out here would suspend the band check forever on every install that has never tuned. `floor_history_unknown` sits between the known move and the band, so an arm with a date to give gives it. Two sentences, because the remedies differ: a date says ask again with a smaller `days`; no history at all says there is nothing to wait for, and names what starts the record. Why now: #4261 measures the work-log change by reading these warnings, and every window for the next month opens before this ledger's first row. Measuring against an instrument that prints a number it cannot support is the #4225 trap one level up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
This commit is contained in:
@@ -340,3 +340,135 @@ async def test_a_baseline_is_not_reported_as_a_floor_move():
|
||||
stmt = str(session.execute.call_args.args[0])
|
||||
assert "old_value IS NOT NULL" in stmt
|
||||
assert "dial" in stmt
|
||||
|
||||
|
||||
# ── floor_history_gaps: "no move recorded" is not "no move" ───────────────
|
||||
#
|
||||
# THE HOLE ITS COMPANION LEFT. `floor_moves_since` answers "did this arm's
|
||||
# floor move inside the window", and the band check read an empty answer as
|
||||
# "no, it held steady". Those are the same answer only where the ledger was
|
||||
# WATCHING. Before an arm's first row there is nothing to move, nothing to
|
||||
# report, and no way to tell a steady floor from an unrecorded change.
|
||||
#
|
||||
# Measured the day the ledger was seeded on this instance: its earliest event
|
||||
# of any kind is 2026-09-17, its `write_path_rule` floor rows begin at
|
||||
# 2026-09-21, and a 30-day window opening 2026-08-22 reported that arm's band
|
||||
# sitting "-0.0208 above" its floor. That negative distance is the documented
|
||||
# tell of a raise inside the window — announced by the check the suspension
|
||||
# was built to stop, because the suspension had nothing to read.
|
||||
|
||||
|
||||
def _gap_rows(rows):
|
||||
"""Patch `async_session` so the reader sees `rows` as the whole ledger.
|
||||
|
||||
`rows` maps surface -> the datetime of its earliest floor event. A surface
|
||||
left out has no floor history at all, which is the harder of the two
|
||||
cases: absent from the ledger AND present in the registry.
|
||||
"""
|
||||
session = make_mock_session()
|
||||
result = MagicMock()
|
||||
result.all.return_value = list(rows.items())
|
||||
session.execute = AsyncMock(return_value=result)
|
||||
return session
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_an_arm_recorded_before_the_window_opens_has_no_gap():
|
||||
"""The ordinary case, and the one that must stay silent: a ledger that
|
||||
covers the window means silence from `floor_moves_since` is trustworthy,
|
||||
and the band check should go ahead and judge."""
|
||||
from datetime import datetime, timedelta, timezone
|
||||
since = datetime(2026, 8, 22, tzinfo=timezone.utc)
|
||||
covered = {n: since - timedelta(days=1) for n in rt.surface_names()}
|
||||
session = _gap_rows(covered)
|
||||
with patch.object(rt, "async_session", MagicMock(return_value=session)):
|
||||
out = await rt.floor_history_gaps(since)
|
||||
assert out == {}, "a covered arm is absent from the result, not present-and-false"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_an_arm_first_recorded_inside_the_window_reports_when_it_clears():
|
||||
"""The symptom case. A finding with no remedy is a complaint, so the value
|
||||
IS the date from which the floor becomes knowable."""
|
||||
from datetime import datetime, timezone
|
||||
since = datetime(2026, 8, 22, tzinfo=timezone.utc)
|
||||
first = datetime(2026, 9, 21, 11, 58, tzinfo=timezone.utc)
|
||||
name = rt.surface_names()[0]
|
||||
rows = {n: since for n in rt.surface_names()}
|
||||
rows[name] = first
|
||||
session = _gap_rows(rows)
|
||||
with patch.object(rt, "async_session", MagicMock(return_value=session)):
|
||||
out = await rt.floor_history_gaps(since)
|
||||
assert set(out) == {name}
|
||||
assert out[name] == first.isoformat()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_an_arm_with_no_floor_history_at_all_is_reported_as_unknowable():
|
||||
"""Distinct from "recorded too late", and it has a different remedy: there
|
||||
is no date to wait for, so the caller is told to start the record. Mapping
|
||||
it to None rather than leaving it out is the point — leaving it out is what
|
||||
the band check was already doing wrong."""
|
||||
from datetime import datetime, timezone
|
||||
since = datetime(2026, 8, 22, tzinfo=timezone.utc)
|
||||
names = rt.surface_names()
|
||||
session = _gap_rows({n: since for n in names[1:]})
|
||||
with patch.object(rt, "async_session", MagicMock(return_value=session)):
|
||||
out = await rt.floor_history_gaps(since)
|
||||
assert out == {names[0]: None}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_baseline_counts_here_though_it_is_not_a_move():
|
||||
"""The one place this and `floor_moves_since` deliberately disagree.
|
||||
|
||||
A baseline records a default without changing it, so it is not a move and
|
||||
that function filters it out. It IS the ledger beginning to watch the arm,
|
||||
and from that moment silence genuinely means the floor held — so filtering
|
||||
it out here would suspend the band check forever on an install whose only
|
||||
floor rows are baselines, which is every install that has never tuned.
|
||||
"""
|
||||
from datetime import datetime, timezone
|
||||
session = _gap_rows({n: datetime(2026, 1, 1, tzinfo=timezone.utc)
|
||||
for n in rt.surface_names()})
|
||||
with patch.object(rt, "async_session", MagicMock(return_value=session)):
|
||||
out = await rt.floor_history_gaps(datetime(2026, 8, 22, tzinfo=timezone.utc))
|
||||
assert out == {}
|
||||
stmt = str(session.execute.call_args.args[0])
|
||||
assert "old_value IS NOT NULL" not in stmt, (
|
||||
"a baseline is not a move, but it is the ledger observing the arm"
|
||||
)
|
||||
assert "dial" in stmt and "min" in stmt.lower()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_the_registry_supplies_the_arms_not_the_ledger():
|
||||
"""An arm the ledger has never heard of is exactly the one at risk, so it
|
||||
cannot be the ledger that decides which arms get asked about."""
|
||||
from datetime import datetime, timezone
|
||||
session = _gap_rows({})
|
||||
with patch.object(rt, "async_session", MagicMock(return_value=session)):
|
||||
out = await rt.floor_history_gaps(datetime(2026, 8, 22, tzinfo=timezone.utc))
|
||||
assert set(out) == set(rt.surface_names())
|
||||
assert all(v is None for v in out.values())
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_row_written_exactly_as_the_window_opens_covers_it():
|
||||
"""The boundary, from both sides. `since` is the first instant the window
|
||||
contains, so a floor recorded AT it leaves nothing earlier unaccounted
|
||||
for — and one microsecond later does. Asserted because an off-by-one here
|
||||
fails in the expensive direction: a covered arm reported as unknowable
|
||||
retires a working check on the strength of a rounding."""
|
||||
from datetime import datetime, timedelta, timezone
|
||||
since = datetime(2026, 8, 22, tzinfo=timezone.utc)
|
||||
names = rt.surface_names()
|
||||
|
||||
session = _gap_rows({n: since for n in names})
|
||||
with patch.object(rt, "async_session", MagicMock(return_value=session)):
|
||||
assert await rt.floor_history_gaps(since) == {}
|
||||
|
||||
later = since + timedelta(microseconds=1)
|
||||
session = _gap_rows({n: later for n in names})
|
||||
with patch.object(rt, "async_session", MagicMock(return_value=session)):
|
||||
assert set(await rt.floor_history_gaps(since)) == set(names)
|
||||
|
||||
Reference in New Issue
Block a user