Emit the anchor-bias heartbeat from the round loop with an explicit anchored: false reason, so a skipped round is distinguishable from a detector that never saw it
Source
Reader comment 3dn6k by vinhnguyenthanhdn on "A guard that has never fired and a guard that stopped running look identical on disk" (https://dev.to/pm25coder/a-guard-that-has-never-fired-and-a-guard-that-stopped-running-look-identical-on-disk-177f), 2026-08-30T05:53Z.
Reader's insight
The anchor-bias-heartbeat (added in #1073, consumed by #1076's calibration) is emitted per anchored round FROM INSIDE _detect_silent_anchor_drift — so anything that stops anchored rounds entirely yields zero heartbeat lines, byte-identical to the detector having stopped:
"that is the same ambiguity you removed one level down with anchor_loss, reappearing one level up at the call site."
Current state (master 981f037)
- Heartbeat logged at
emrg/server/daemon.py:3128-3135, inside _detect_silent_anchor_drift (L3094) — AFTER the early returns at L3114 (if not old or old[1] <= 0 or estimate <= 0: return) and L3119 (if old_bias <= 0: return). - Call site at L2594-2606:
if final_usage: → pt = final_usage.get("prompt_tokens") → if pt: → _detect_silent_anchor_drift(session, pt, estimate). The detector is only reached when the LLM response carries prompt_tokens AND a previous anchor exists. - Failure modes that go fully silent:
final_usage missing/empty (config flag, upstream change, stream edge case) → detector never called → no heartbeat;prompt_tokens missing/zero → same;- no anchor yet / anchor invalid (first round, post-compact, post-switch, provider stopped returning usage) → early return before the heartbeat.
_warn_missing_usage_anchor (L2998-3050) covers only the auto-compact projection gate, fires once per session, and does not emit the drift heartbeat.
Proposed change
Move the heartbeat to the round loop so every round emits exactly one anchor-bias-heartbeat line with an explicit state:
- Keep the existing inside-detector heartbeat as the
anchored: true case (it already logs bias_shift / threshold / old_bias / new_bias). - In the round loop at the usage-processing point (L2594-2606), log
anchored: false with a reason when the detector is skipped:
reason=no_usage — final_usage missing/empty;reason=no_prompt_tokens — final_usage present but prompt_tokens missing/zero;reason=no_anchor — no previous anchor (not old or old[1] <= 0);reason=invalid_estimate — estimate <= 0;reason=invalid_bias — old_bias <= 0 (kept as-is, reachable only via the detector's early return).
- Keep the greppable marker
anchor-bias-heartbeat and debug level unchanged so existing grep "anchor-bias-heartbeat" workflows keep working; only the reason field is new.
This preserves "log unconditionally, alert conditionally" one level higher: a skipped round is now a first-class, labeled observation instead of an absence.
Acceptance (self-verifiable)
- A round with no
final_usage emits anchor-bias-heartbeat ... anchored=false reason=no_usage. - A round with
final_usage but no prompt_tokens emits anchored=false reason=no_prompt_tokens. - A round with no previous anchor emits
anchored=false reason=no_anchor. - A normal anchored round still emits
anchored=true with bias_shift (existing behavior unchanged). - Tests assert each state (positive + negative per the repo's verification rules); CI test + test-windows green.
Emit the anchor-bias heartbeat from the round loop with an explicit
anchored: falsereason, so a skipped round is distinguishable from a detector that never saw itSource
Reader comment 3dn6k by vinhnguyenthanhdn on "A guard that has never fired and a guard that stopped running look identical on disk" (https://dev.to/pm25coder/a-guard-that-has-never-fired-and-a-guard-that-stopped-running-look-identical-on-disk-177f), 2026-08-30T05:53Z.
Reader's insight
The
anchor-bias-heartbeat(added in #1073, consumed by #1076's calibration) is emitted per anchored round FROM INSIDE_detect_silent_anchor_drift— so anything that stops anchored rounds entirely yields zero heartbeat lines, byte-identical to the detector having stopped:Current state (master 981f037)
emrg/server/daemon.py:3128-3135, inside_detect_silent_anchor_drift(L3094) — AFTER the early returns at L3114 (if not old or old[1] <= 0 or estimate <= 0: return) and L3119 (if old_bias <= 0: return).if final_usage:→pt = final_usage.get("prompt_tokens")→if pt:→_detect_silent_anchor_drift(session, pt, estimate). The detector is only reached when the LLM response carries prompt_tokens AND a previous anchor exists.final_usagemissing/empty (config flag, upstream change, stream edge case) → detector never called → no heartbeat;prompt_tokensmissing/zero → same;_warn_missing_usage_anchor(L2998-3050) covers only the auto-compact projection gate, fires once per session, and does not emit the drift heartbeat.Proposed change
Move the heartbeat to the round loop so every round emits exactly one
anchor-bias-heartbeatline with an explicit state:anchored: truecase (it already logsbias_shift/threshold/old_bias/new_bias).anchored: falsewith a reason when the detector is skipped:reason=no_usage—final_usagemissing/empty;reason=no_prompt_tokens—final_usagepresent butprompt_tokensmissing/zero;reason=no_anchor— no previous anchor (not old or old[1] <= 0);reason=invalid_estimate—estimate <= 0;reason=invalid_bias—old_bias <= 0(kept as-is, reachable only via the detector's early return).anchor-bias-heartbeatand debug level unchanged so existinggrep "anchor-bias-heartbeat"workflows keep working; only thereasonfield is new.This preserves "log unconditionally, alert conditionally" one level higher: a skipped round is now a first-class, labeled observation instead of an absence.
Acceptance (self-verifiable)
final_usageemitsanchor-bias-heartbeat ... anchored=false reason=no_usage.final_usagebut noprompt_tokensemitsanchored=false reason=no_prompt_tokens.anchored=false reason=no_anchor.anchored=truewithbias_shift(existing behavior unchanged).