Skip to content

Adaptive context hygiene, AGENTS.md, cache hardening, GLM usage fix#8

Merged
bajajra merged 8 commits into
mainfrom
bun-migration
Jul 20, 2026
Merged

Adaptive context hygiene, AGENTS.md, cache hardening, GLM usage fix#8
bajajra merged 8 commits into
mainfrom
bun-migration

Conversation

@bajajra

@bajajra bajajra commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Summary

Eight commits: a self-tuning context-hygiene layer driven by /lens telemetry, AGENTS.md support with cache-safe semantics, prompt-cache hardening per the official docs, and a GLM usage-parsing fix verified on the wire.

Adaptive context hygiene (029ab71, df83a00, 8ee6fd8, 94ddff8)

  • Supersession dedup: a newer read of the same file evicts older reads — semantically correct eviction (the old bytes may be wrong after edits), not just age-based. Whole-file reads supersede everything for that path; identical inputs dedupe; different ranges never.
  • Adaptive governor: the engine reads its own lens feedback (observed cache hits, avg requests/turn, idle vs cache TTL) and runs the cost break-even before any discretionary cache bust. Stale-clear defers when the bust costs more than it saves; supersession picks a depth cut (bust cost is set by the earliest cleared block) — deep candidates defer to the next cold-cache window or compaction.
  • Type-aware: observation files (logs, run output, jsonl streams) are exempt — each read is a time sample, and the error trace from before a fix is evidence a clean re-read must not evict. MIST_SUPERSEDE_SKIP extends the exempt list; MIST_ADAPTIVE_HYGIENE=0 restores eager clearing.
  • Every decision is recorded in TurnLens.hygiene and shown in /lens — adaptation stays explainable.

AGENTS.md + /init (099bb5a)

  • Codex-style discovery: ~/.mist/AGENTS.md → git root → cwd, broad-first so deeper files override; MIST.md wins per level; 32 KiB budget; .git dir or worktree-file marker.
  • Cache contract: docs load once and freeze into the system prompt. Mid-session edits append a tail supersede message — the wire test asserts the system block stays byte-identical across an edit.
  • /init is prompt-sugar: submits a canned prompt; the model explores the actual repo and writes the file with ordinary tools (hooks apply). 200–400-word spec, never overwrites an existing file.

Prompt-cache hardening (2ec1f4c)

  • Sliding breakpoint window: last block of each of the last three messages (+ system = the 4-breakpoint max), so hits never depend on the server's 20-block lookback — which a single large tool batch could outrun, silently re-writing the whole conversation.
  • MIST_CACHE_TTL=1h: hold the tools+system prefix an hour; human think-time routinely exceeds the 5-minute default TTL.

GLM usage fix (49f7396)

Wire-probed z.ai: it sends zeroed usage in message_start and the real numbers in message_delta — the opposite of Anthropic. GLM sessions logged 0 input · context now 0, and the real-token compaction trigger silently fell back to estimates. Fixed with positive-reading-wins parsing; live-verified: /lens now shows real input and 96% cache-read on GLM — z.ai honors our breakpoints.

/lens resume persistence (577ee4f)

One {"kind":"lens"} JSONL line per turn in the session store; resume rehydrates the ledger — /lens no longer answers "no turns recorded" after a restart, and adaptive-hygiene signals survive resumes.

Test plan

  • cd ts && bun test — 123 tests, 0 fail (wire-format tests for breakpoint window/TTL/frozen AGENTS.md prefix; depth-cut economics; observation-file exemptions; store roundtrip)
  • Live-verified against GLM (real usage + cache numbers) and mock (resume → /lens, AGENTS.md injection)

🤖 Generated with Claude Code

bajajra and others added 8 commits July 18, 2026 21:12
Age-based clearing treats a 5-minute-old read of the file being edited
the same as a stale build log. Supersession is the semantically correct
trigger: after a re-read, the older bytes aren't just bulky — they may
be WRONG (the file changed). dedupeSupersededReads evicts them
regardless of age:

- a whole-file read supersedes every earlier read of that path
- an identical-input read supersedes earlier identical reads
- different ranges of the same path never supersede each other

Cache discipline matches clearStaleToolResults: deterministic stub
(bytes depend only on the path), monotonic (one transition, then
byte-stable forever), minChars guard so small results aren't churned.
Runs at turn start before age-based clearing, and inside compact().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dback

The stale-result sweep now decides AT RUNTIME whether it pays, using
the harness's own telemetry instead of assumptions:

- cacheLive: did recent turns show real cache reads? If the endpoint
  ignores caching, there is nothing to protect — clear eagerly.
- cacheLikelyCold: idle past the ~5min provider TTL (or fresh session)
  means the prefix is cold and the bust is free — clear eagerly.
- otherwise run the break-even: 0.1 × savedTokens × observed avg
  requests/turn vs 1.25 × tail re-cache cost. Clear only when it pays.

clearStaleToolResults gains dryRun planning (savedChars + tailChars);
staleClearWorthIt is the pure, tested decision function. Supersession
dedup stays always-on — it's correctness, not economics.

The decision itself is explainable: TurnLens.hygiene records what was
evicted and WHY (or why deferred), shown in /lens and the HTML report.
MIST_ADAPTIVE_HYGIENE=0 restores unconditional clearing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… message_delta

Wire-probed the z.ai endpoint: it sends ZEROED usage in message_start
and the real numbers (input_tokens, cache_read_input_tokens) in
message_delta at stream end — the opposite of Anthropic's API. We only
read input from message_start, so every GLM request logged 0 input,
/lens showed 'context now 0', and the real-token compaction trigger
never saw a real reading (fell back to the estimate).

message_delta now also accepts positive input/cache readings;
Anthropic's API omits those fields there, so its values are never
overwritten. Regression test uses the captured GLM wire shape.

Live-verified on GLM: /lens now shows real input — and real cache
activity (96% of input served from cache on the probe turn), so z.ai
honors our cache_control breakpoints.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The lens ledger lived only in engine memory, so a resumed session
answered /lens with 'no turns recorded yet' — the explainability
module forgot exactly the history you resumed to look at.

Sessions now persist one {"kind":"lens","turn":…} JSONL line per
completed turn (seq-guarded so errored turns that recorded nothing
don't duplicate), and resume rehydrates the engine ledger (capped at
the same 50 turns). rename() already preserves non-meta lines, so
titles and ledgers coexist. Verified live: run → quit → --continue →
/lens shows the pre-restart turn, cache numbers intact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mist now reads repository guidelines the way the ecosystem converged:

Discovery (agents_md.ts): ~/.mist/AGENTS.md (user-global) → git-root →
cwd, broad-first so deeper files override by recency; MIST.md wins over
AGENTS.md per level; walk stops at the .git marker (dir OR worktree
file); 32 KiB total budget — this text is resent every request forever,
so the cap is the prefix budget.

Cache contract: docs load ONCE and freeze into the system prompt. A
mid-session edit is detected at turn start and appended at the TAIL as
'[project-docs update] These AGENTS.md instructions replace ALL
previously provided…' — the stale copy stays cached in the prefix,
recency + the notice neutralize it, compaction garbage-collects later.
Wire test asserts the system block stays byte-identical across the edit
and the supersede message carries the new text.

/init is prompt-sugar, not harness logic: submits a canned prompt and
the model explores the actual repo and writes AGENTS.md with ordinary
tools (hooks apply). Spec: 200-400 words, real commands, conventions
inferred from git history, never overwrite an existing file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sting warm cache

Supersession dedup was unconditional; clearing a read buried 200k
tokens deep re-caches the entire tail to save a few k of stale text.
The correctness argument doesn't require eagerness: the NEWER read is
in context and recency outranks the stale copy, so clearing can wait.

chooseSupersededToClear picks a DEPTH CUT, not all-or-nothing: bust
cost is set by the earliest cleared block, so it evaluates every
suffix of the candidate list and clears the best positive-net suffix.
Deep candidates are deferred — the next cold cache (idle past TTL),
inert endpoint, or compaction clears them for free. Same lens-driven
signals as the stale-clear governor; decision recorded in
TurnLens.hygiene (dedupedDeferred) and shown in /lens.
MIST_ADAPTIVE_HYGIENE=0 restores unconditional clearing;
compact() still dedupes everything (prefix dies anyway).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Supersession assumes the file IS its latest content (state semantics).
That's true for source/config/docs — and false for logs and run output,
where each read is a TIME SAMPLE of a changing stream: the error trace
read before a fix is unique evidence that a clean re-read afterwards
must not evict.

isObservationFile exempts *.log/out/err/trace, log dirs, and
*.jsonl/ndjson event streams from supersession ('log' inside a source
filename doesn't match; lock files are state and still supersede).
MIST_SUPERSEDE_SKIP adds a user regex to the exempt list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two changes from auditing the official prompt-caching rules:

1. Sliding breakpoint window. We marked only system + the final block,
   so hits relied on the server's 20-block lookback finding the
   previous request's entry — a single large tool batch appends >20
   blocks and outruns it, silently re-writing the whole conversation.
   Now the last block of each of the last THREE messages is marked
   (+ system = the 4-breakpoint API max). One request cycle appends 2
   messages, so the previous request's breakpoint is always still
   explicitly marked: exact-hash hit, lookback never needed.

2. MIST_CACHE_TTL=1h — hold the tools+system prefix for an hour (2x
   write once, same 0.1x reads). Human think-time between turns
   routinely exceeds the 5-minute default TTL and cold-started every
   turn. Ordering constraint (1h before 5m) is satisfied by
   construction: system is the first breakpoint.

Wire test asserts the window shape (last 3 marked, older unmarked,
exactly 4 breakpoints) and TTL placement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bajajra
bajajra merged commit 8c22689 into main Jul 20, 2026
3 checks passed
Sign up for free to 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.

1 participant