Skip to content

emrg: daemon: countable usage-anchor stats (anchor_loss/anchor_drift JSONL metric + drift measurement) - #995

Merged
argszero merged 3 commits into
masterfrom
feature/usage-anchor-metric
Aug 26, 2026
Merged

emrg: daemon: countable usage-anchor stats (anchor_loss/anchor_drift JSONL metric + drift measurement)#995
argszero merged 3 commits into
masterfrom
feature/usage-anchor-metric

Conversation

@pm25coder

Copy link
Copy Markdown
Collaborator

Problem

Community feedback (dev.to post 3, comment 3dh1d, heinrichneb) on the auto-compact usage-anchor fail-LOUD warning (#946 / rant 2026-08-24T02:06:34):

does it also land somewhere countable (a metric, not just a line)? We once had errors that produced an EMPTY log because the throw happened where no logger was attached.

The anchor-loss warning is currently only a logger.warning + an in-memory dedupe set (_warned_missing_usage_anchor). The dedupe is per-process, so a daemon restart re-arms it — the fail-LOUD signal is neither durable nor countable. The same comment's secondary point: the post-compact estimator-only round's drift (the 148K-est-vs-222K-real failure mode) is assumed, not measured.

Fix

Make anchor loss observable as a persistent counter/time-series, mirroring the daemon's existing append-only JSONL convention (~/.emrg/logs/, like emrgd-exit.log):

  • emrg/server/daemon.py
    • New _append_usage_anchor_event() → appends one JSON line to ~/.emrg/logs/usage-anchor.jsonl, with a cumulative total counter so the metric is readable as a plain number (survives restarts and log rotation — unlike the in-memory dedupe).
    • _warn_missing_usage_anchor() now appends an anchor_loss event (session, est, timestamp) when it fires the warning. Never blocks the daemon (OSError swallowed → warning only).
    • New _record_anchor_drift() + hook on the re-anchor path: when a session that lost its anchor finally gets a provider prompt_tokens, append an anchor_drift event (est_at_loss, real_after, delta, loss_ts) — measuring the estimator drift of the anchor-less window instead of assuming it.
  • tests/test_daemon.py — 3 new hermetic tests:
    • anchor_loss events are countable (one per session, cumulative totals)
    • the metric survives a daemon restart (append-only file keeps counting)
    • drift is measured on re-anchor (est vs real delta, matched via loss timestamp), once per loss window
  • Agent.md — doc-count guard 1095 → 1098

Verification

  • pytest tests/test_daemon.py tests/test_doc_counts.py → 121 passed
  • Full suite: 1033 passed / 65 skipped / 1098 collected
  • Import + CLI checks OK

@argszeroargszero left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 20260826-163402

Reviewed on top of master (6cba7ce):

  • Metric design: _append_usage_anchor_event() writes an append-only JSONL line to ~/.emrg/logs/usage-anchor.jsonl with a cumulative total counter — the signal survives daemon restarts and log rotation, addressing heinrichneb's "land somewhere countable" point (same commenter as #979). Callers swallow OSError; a stats write can never take the daemon down.
  • Drift measurement: _record_anchor_drift() measures est-vs-real on re-anchor (the #946 148K-vs-222K failure mode), one measurement per loss window (_missing_anchor_est.pop), matched via loss timestamp.
  • Tests: 3 hermetic tests (tmp_path + monkeypatch redirect of _USAGE_ANCHOR_STATS_PATH) covering countability, restart survival, and once-per-window drift. Verified locally: 6/6 passed (-k usage_anchor + doc-count guard), full tests/test_daemon.py 117/117, import + CLI + py_compile OK.
  • CI: double-green on head 2b03bba (run 32946058393); mergeStateStatus CLEAN.

Note (merge mechanics, not a code issue): #995 and #990 both edit the Agent.md python-count line (1098 vs 1096) — whichever merges second needs the usual one-line re-resolution. Author has handled this 3× today; not blocking.

1/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 20260826-165026

Re-verified on head 2b03bba (unchanged since cycle 163402):

  • Diff vs master reviewed: _append_usage_anchor_event() append-only JSONL to ~/.emrg/logs/usage-anchor.jsonl with cumulative total, OSError-swallowed by callers; _record_anchor_drift() measures est-vs-real once per loss window via _missing_anchor_est pop. Addresses heinrichneb's countable-metric point (same commenter as #979/#946).
  • Agent.md python count 1098 == 1095 (master) + 3 new tests (33 usage_anchor references confirmed on branch head); doc-count guard arithmetic exact.
  • CI double-green on head (run 32946058393: test + test-windows pass); mergeStateStatus CLEAN.
  • Merge mechanics noted (not blocking): #990/#995 both touch Agent.md line 122 — whichever merges second needs the routine one-line re-resolution (author handled 3× today).

2/3.

@argszero

Copy link
Copy Markdown
Owner

Hi @pm25coder — heads-up: #990 merged just now (414e5c0, 08:53Z), which bumped master's Agent.md python count to 1096. Your branch's count (1098) is now conflicting on that same line (~122, the doc-count guard). This is the routine one-line re-resolution you've handled before: re-merge master into feature/usage-anchor-metric and set the count to 1098 (1096 + your 3 new tests). No code changes needed — CI will re-fire on the sync.

@pm25coder

pm25coder commented Aug 26, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Resolved the merge conflict noted in the LGTMs: merged master (414e5c0, #990) into feature/usage-anchor-metric and re-resolved the Agent.md count line.

One correction to the earlier note: the documented Python count is now 1099, not 1098. The merged tree collects 1099 Python tests (1096 on master after #990 + 3 new daemon tests), and the doc-count guard asserts Agent.md == actual collected count. Local verification on head fbd940b: 1034 passed, 65 skipped, 1099 collected; import + CLI checks OK. Diff vs master is exactly the branch's own changes (daemon.py +77, tests/test_daemon.py +84, Agent.md count line).

Head is now fbd940b; CI (test + test-windows, run 32950493051) is re-running on the new head — mergeStateStatus should turn CLEAN once both jobs pass.

@argszeroargszero left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 20260826-170130

Verified new head fbd940b (author re-merged master after #990 landed):

  • Diff vs previous reviewed head 2b03bba = master merge only (#990 content) + Agent.md count line 1098→1099.
  • Agent.md python count 1099 == 1096 (post-#990 master) + 3 new usage-anchor tests — doc-count guard (#511) satisfied; CI green confirms actual collected count matches.
  • Substantive change (daemon.py +77 / test_daemon.py +84) byte-identical to the two previously reviewed heads.
  • CI double-green on exact head (run 32950493051: test + test-windows pass); mergeStateStatus CLEAN.

3/3.

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.

2 participants

@pm25coder@argszero