Skip to content

emrg: invalidate usage anchor on mid-session model/provider switch - #1001

Closed
argszero wants to merge 1 commit into
masterfrom
feature/fix-usage-anchor-invalidate
Closed

emrg: invalidate usage anchor on mid-session model/provider switch#1001
argszero wants to merge 1 commit into
masterfrom
feature/fix-usage-anchor-invalidate

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fixes#1000 — the usage anchor is not invalidated on a mid-session model/provider switch.

Root cause

The auto-compact usage anchor (emrg/server/daemon.py) is a per-session (real_prompt_tokens, local_estimate) pair, refreshed after every round from the provider's real prompt_tokens. The auto-compact gate projects:

projected = real_anchor + (estimate_now − estimate_anchor)

so the systematic local-estimator undercount (observed 148K est vs 222K real, #946) never accumulates.

The anchor is deliberately invalidated on auto-compact and manual compact — but not in _handle_set_model. After a mid-session /model switch, the anchor still holds the old model's real prompt_tokens, so the projection carries a stale tokenizer baseline into the new model's rounds — the first post-switch round can underestimate badly and the gate may not fire when it should (the exact #946 failure mode the anchor was built to prevent).

Fix

In _handle_set_model, when the API model actually changes:

  • drop all usage anchors (the model is global daemon state — every session's anchor is stale),
  • mark the drops deliberate (mirror of the post-compact drop) so the fail-LOUD "anchor missing" warning does not false-positive,
  • the next round re-anchors from the new provider's real prompt_tokens.

A no-op switch (same API model re-selected) leaves anchors untouched.

Verification

  • 2 new unit tests (test_model_switch_invalidates_usage_anchor, test_model_switch_same_model_keeps_anchor); all 12 usage-anchor/model-switch tests pass
  • Full suite: 1103 passed, 1 skipped; import check + emrg --help OK
  • Agent.md Python test count synced 1102 → 1104 (doc-count guard)
  • No daemon-protocol or client changes

Fix#1000: _handle_set_model switched llm.config.model but left the
per-session usage anchors holding the OLD model's real prompt_tokens.
The auto-compact projection (real_anchor + est_delta) then carried a
stale tokenizer baseline into the new model's rounds, re-introducing
the #946 underestimation (148K est vs 222K real) on the first
post-switch round. Now: when the API model actually changes, drop all
anchors and mark the drops deliberate (mirror of the post-compact drop)
so the fail-LOUD anchor-loss warning does not false-positive; the next
round re-anchors from the new provider. +2 tests, Agent.md count 1102->1104.
@argszero

Copy link
Copy Markdown
OwnerAuthor

Closing as superseded by #1003 (same fix for issue #1000, submitted ~5 min later by the issue author).

Why #1003 wins the consolidation:

  • It covers everything this PR does (drop all anchors + mark deliberate drops on real API-model change, no-op switches untouched)
  • Plus it clears pending drift windows (_missing_anchor_est) — my version would let a pre-switch anchor-loss window measure old-model-est vs new-model-real and pollute the countable anchor_drift metric
  • Cleaner separate marker set (_usage_anchor_dropped_by_switch) + helper method + 4 tests (vs 2 here)

Keeping all three duplicates open would split LGTM votes across PRs and force messy Agent.md doc-count conflict resolution on any double merge. #1003 is fully verified (CI double-green, full suite 1105 passed + 1 skipped, import OK) and has my LGTM. Merging #1003 closes #1000.

@argszero

Copy link
Copy Markdown
OwnerAuthor

Superseded by #1003 — see comment above. Fix tracked via #1003.

argszero added a commit that referenced this pull request Aug 26, 2026
…#1004)
The doc-count line was a single contention point: python-test PRs bump the
python count on line 1 while renderer-test PRs bump the renderer count
embedded in the GUI line — any two in-flight PRs conflict on Agent.md
(observed 3x this week: #997/#998/#999/#1001/#1002/#1003).
Split into three lines (Python / GUI / Renderer) so each suite's PR only
touches its own line. Side effect: the renderer breakdown was previously
unguarded (test_doc_counts._gui_breakdowns only parses the first (N: ...)
per line, which was the GUI count); on its own line it is now validated
by the existing guard (format (168: ...) so all parts parse).
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Usage anchor not invalidated on mid-session model/provider switch

1 participant

@argszero