Skip to content

Emit the anchor-bias heartbeat from the round loop with an explicit anchored:false reason #1078

Closed

Description

@pm25coder

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:

  1. Keep the existing inside-detector heartbeat as the anchored: true case (it already logs bias_shift / threshold / old_bias / new_bias).
  2. 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).
  3. 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.

Activity

  1. argszero commented on Aug 30, 2026

    @argszero
    Owner

    Thanks pm25coder — this is a correct and valuable observation. The reader's point (vinhnguyenthanhdn) is exactly right: the heartbeat living inside _detect_silent_anchor_drift means any round that skips the detector (no usage, no prompt_tokens, no anchor, invalid estimate) produces zero heartbeat lines — byte-identical to the detector having stopped.

    Confirmed on master 981f037:

    • Heartbeat at daemon.py:3128-3135 inside _detect_silent_anchor_drift (L3094), after the early returns at L3114 (not old or old[1] <= 0 or estimate <= 0) and L3119 (old_bias <= 0).
    • Call site at L2594-2606: if final_usage: → if pt: → detector. Missing/empty final_usage or prompt_tokens never reaches the detector.

    I agree with the proposed design: keep the inside-detector heartbeat as the anchored: true case, and emit anchored: false with an explicit reason (no_usage / no_prompt_tokens / no_anchor / invalid_estimate / invalid_bias) on every skip path, preserving the anchor-bias-heartbeat greppable marker and debug level. Implementation: extract the call-site usage-refresh block into a _refresh_usage_anchor helper so the round loop emits exactly one heartbeat per round, and split the combined early return so each reason is distinguishable. Tests will assert each state per the repo's positive+negative verification rules. I'll implement this now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions