You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A .bca-baseline.toml entry goes stale silently whenever a change lowers
a baselined metric. The filter suppresses a violation only while the live
value stays at or below the recorded one, so a decrease never reddens the
gate and nothing forces a refresh. The recorded value then describes a tree
that no longer exists, and the gate is looser than intended until someone
regenerates for an unrelated reason.
Traced to d4479cdf ("fix: address the PR #1446 review findings",
2026-09-13), which changed compute.rs after the last baseline refresh
(bcd62812). Because the value fell, make self-scan stayed green and the
baseline-refresh discipline in AGENTS.md was never triggered.
It surfaced only because #1407 on fix/batch-2026-09-13 refreshed the
baseline for an unrelated Kotlin entry, and make self-scan-write-baseline-headroom regenerates the file wholesale, so
the stale line came along as a rider.
Why it matters
The gap between recorded and live is headroom nobody chose. Between d4479cdf and the next regeneration, metrics_inner could have grown by
~2400 halstead.effort — a real regression — without the gate noticing,
because it would still have measured below the stale recorded value.
The looser the entry, the longer it stays stale, so the failure compounds
quietly.
Options
Fail the gate on a decrease too, requiring a refresh in the same
change. Strictest, and symmetrical with the increase rule already in AGENTS.md. Cost: a refresh commit on changes that improved a metric,
which is friction for the good case.
Warn on a decrease without failing, so the drift is visible in CI
output and someone can batch the refresh.
Periodic regeneration — a scheduled job that regenerates and opens a
PR when the file moves, the way the quarterly mutation and benchmark jobs
already work.
Option 2 is probably the cheapest honest fix; option 3 catches it without
any per-change friction.
AGENTS.md, "Baseline-refresh discipline" — currently written entirely in
terms of a metric moving past its recorded value.
Resolution
Option 2 — warn on a decrease, do not fail the gate. Implemented in aa649899 (branch fix/issue-1465).
filter_by_baseline folds every Coverage::Covered classification into
a baseline::StaleTally, which counts entries whose live value has
moved past the recorded one in the improving direction and emits one
aggregated stderr warning per run, naming the count and the worst entry
by relative drift. Direction is decided by breaches_limit(recorded, value, lower_is_worse) — the ratchet's own predicate with its arguments
swapped — so both polarities are handled and the two cannot disagree. An
entry sitting exactly on its record stays silent. Exit codes and the
kept violation set are unchanged.
The fix covers only half the class. A function that stopped
breaching its threshold entirely produces no Violation, so it never
reaches classify and its stale entry remains invisible to this code
path by construction. Option 3 — a scheduled make self-scan-write-baseline-headroom plus git diff --exit-code .bca-baseline.toml, opening a PR when the file moves — is what closes
the remainder, and is deliberately left for a follow-up issue.
Documented in CHANGELOG.md, in AGENTS.md's "Baseline-refresh
discipline" (which now carries the decrease direction), and in a new
"The stale-entry warning" section of recipes/baselines.md. The improved bucket description in that recipe no longer calls the state
"harmless".
Summary
A
.bca-baseline.tomlentry goes stale silently whenever a change lowersa baselined metric. The filter suppresses a violation only while the live
value stays at or below the recorded one, so a decrease never reddens the
gate and nothing forces a refresh. The recorded value then describes a tree
that no longer exists, and the gate is looser than intended until someone
regenerates for an unrelated reason.
Concrete instance
src/spaces/compute.rs::metrics_inner,halstead.effort:main:119147.7514530567116715.6053686016Traced to
d4479cdf("fix: address the PR #1446 review findings",2026-09-13), which changed
compute.rsafter the last baseline refresh(
bcd62812). Because the value fell,make self-scanstayed green and thebaseline-refresh discipline in
AGENTS.mdwas never triggered.It surfaced only because #1407 on
fix/batch-2026-09-13refreshed thebaseline for an unrelated Kotlin entry, and
make self-scan-write-baseline-headroomregenerates the file wholesale, sothe stale line came along as a rider.
Why it matters
The gap between recorded and live is headroom nobody chose. Between
d4479cdfand the next regeneration,metrics_innercould have grown by~2400
halstead.effort— a real regression — without the gate noticing,because it would still have measured below the stale recorded value.
The looser the entry, the longer it stays stale, so the failure compounds
quietly.
Options
change. Strictest, and symmetrical with the increase rule already in
AGENTS.md. Cost: a refresh commit on changes that improved a metric,which is friction for the good case.
output and someone can batch the refresh.
PR when the file moves, the way the quarterly mutation and benchmark jobs
already work.
Option 2 is probably the cheapest honest fix; option 3 catches it without
any per-change friction.
Related
ef20b2caand is named in that commit's message.
AGENTS.md, "Baseline-refresh discipline" — currently written entirely interms of a metric moving past its recorded value.
Resolution
Option 2 — warn on a decrease, do not fail the gate. Implemented in
aa649899(branchfix/issue-1465).filter_by_baselinefolds everyCoverage::Coveredclassification intoa
baseline::StaleTally, which counts entries whose live value hasmoved past the recorded one in the improving direction and emits one
aggregated stderr warning per run, naming the count and the worst entry
by relative drift. Direction is decided by
breaches_limit(recorded, value, lower_is_worse)— the ratchet's own predicate with its argumentsswapped — so both polarities are handled and the two cannot disagree. An
entry sitting exactly on its record stays silent. Exit codes and the
kept violation set are unchanged.
The fix covers only half the class. A function that stopped
breaching its threshold entirely produces no
Violation, so it neverreaches
classifyand its stale entry remains invisible to this codepath by construction. Option 3 — a scheduled
make self-scan-write-baseline-headroomplusgit diff --exit-code .bca-baseline.toml, opening a PR when the file moves — is what closesthe remainder, and is deliberately left for a follow-up issue.
Documented in
CHANGELOG.md, inAGENTS.md's "Baseline-refreshdiscipline" (which now carries the decrease direction), and in a new
"The stale-entry warning" section of
recipes/baselines.md. Theimprovedbucket description in that recipe no longer calls the state"harmless".