Skip to content

context-guard: zone-crossing hooks (advisory/enforcing) + window-class token bands #1475

Description

@kyle-sexton

Goal

Automate context-zone awareness: hooks that silently watch the plugin's own snapshot seam and inject routing guidance when a session crosses into a worse zone — plus rework zone bands so defaults are meaningful on 1M-class windows. Today the plugin is observability-only ("consumers decide what to do"); this adds the first shipped consumer.

Locked decisions (interview 2026-07-25/26; sweep-audited 2026-07-26)

  • Ownership: context-guard owns the hooks (owns the data seam). Injected message carries a minimal generic continuation tree + presence-gated pointer to session-flow:workflow's router (session-flow/workflow: end-of-phase continuation router (continue / clear / handoff / subagent / compact) #1476); plugin stays independently useful per PLUGIN-PHILOSOPHY.md.
  • Cadence: injection only on transition into a worse zone; silent otherwise; advisory by default.
  • Posture: config mode: advisory | blocking (renamed from enforcing to match the repo's shipped gate-posture enum; the advisory-vs-blocking and fail-open axes are owned by docs/conventions/hook-observability + docs/conventions/hook-precision — conform there or record divergence in those docs' terms). Blocking = PreToolUse gate active only on a fresh dumb-zone snapshot; fail-open on unknown; handoff-writing operations exempt (no deadlock); small grace budget.
  • PostCompact hook marks the session evidence-degraded — closes the reader contract's documented gap. VERIFIED against code.claude.com/docs/en/hooks (2026-07-26): PostCompact exists, manual/auto matchers, side-effect-only with no decision control — never gate on it.

Reopened by discipline sweep (2026-07-26) — design-stage inputs, not locked

  • Event selection. The original pick (UserPromptSubmit + PostToolUse + a bespoke per-session dedupe state file) was made from recall; the live hooks doc lists ~30 events. Survey the field at design time. Leading candidates: PostToolBatch (fires once per parallel tool batch, before the next model call — likely replaces PostToolUse AND dissolves the dedupe file), UserPromptSubmit, PreCompact (blocking-capable — the point-of-no-return intervention seam), SessionStart (the unknown-at-start state). PostToolUseFailure exists; note it. SubagentStart/SubagentStop evaluation belongs to context-guard: research non-interactive observability (headless + subagent context usage) #1478.
  • Hook config delivery. Bands stay in zones.json (cross-plugin data seam). Hook enable/events/mode config: re-derive against the owner docs the interview missed — docs/conventions/hook-config-delivery/README.md (exec-form argv substitution reaches plugin hooks; CLAUDE_PLUGIN_OPTION_<KEY> env export; direct settings read) and docs/conventions/config-cascade/README.md (the fleet's three-layer cascade dissolves — not defers — the per-project override tier).

Bands (amended by sweep)

  • Window-class-aware absolute-token bands; resolver classifies from the snapshot's context_window.context_window_size.
  • Occupancy definition first. Upstream used_percentage counts input tokens only (input_tokens + cache_creation_input_tokens + cache_read_input_tokens, no output — statusline doc). State ONE definition for token bands, including output-token treatment; never equate percentage and token shapes without normalizing (the interview's 50% ≙ 100k continuity argument compared unlike units).
  • Combination rule. Conservative-min across both shapes was the interview's pick, but what a consumer inlines and computes when the two shapes disagree must be an explicit acceptance criterion. Open design question, choose deliberately and record rationale in the reader contract: continuous measure (tokens + derived zone words as convenience view) vs enum-primitive.
  • Version floor. total_input_tokens/total_output_tokens mean current context occupancy only since CLI 2.1.132 (before that: cumulative session totals — bands would misfire badly). Design specified against 2.1.220; ship a minimum-version floor or runtime guard.
  • Defaults — declared judgment with named anchors, never presented as benchmark-derived:
Class smart acceptable dumb Anchor
1M ≤ 200k ≤ 400k above Thariq Shihipar range (sole named origin; see provenance)
200k ≤ 100k ≤ 160k above Declared judgment below Chroma's confounded ~113k signal and Pocock's uncited 125k floor

Numeric agreement of the 200k row with the shipped 50/75 percentage defaults is coincidence, not validation — those encode compaction headroom, not quality. Both rows carry equally low confidence.

  • Local anchor: the producing machine's transcript history shows a session at 308k input tokens uncompacted on a 1M-class window (reader-contract observation). Bounds compaction behavior only — says nothing about quality; a testable prediction against future local sessions.
  • Percentage keys retained only with (a) the recorded rationale in the reader contract — percentage answers distance to compaction, tokens answer distance to quality — and (b) a retirement trigger (e.g. removed once no shipped consumer inlines the percentage floor). Back-compat alone is how a second vocabulary becomes permanent.

Provenance (condensed claim table)

Claim Origin Tier Note
~300–400k context rot Thariq Shihipar (Anthropic CC team), x.com/trq212/status/2044548257058328723, 2026-04 Named staff, informal Self-hedged "highly task-dependent, not a fast rule"; sole origin
CLAUDE_CODE_AUTO_COMPACT_WINDOW=400000 howborisusesclaudecode.com Curation Setting value invented by the site; absent from Thariq's article
"Use ≤40% of 1M" Unlocatable Folklore Arithmetic on Thariq's number
Dumb zone at 125–150k Pocock, aihero.dev smart-zone entry Practitioner Uncited
Official threshold None exists Official Framing: "performance gradient, not hard cliff"
Degradation tracks absolute tokens, not window % Chroma context-rot report; Anthropic system-card fixed-point evals Primary research + official High confidence
Sonnet 5 auto-compacts ~967k code.claude.com/docs/en/model-config Official These bands are product opinion, not Anthropic-endorsed

Generation drift is real (Mythos 5 markedly better than Opus 4.8 at 1M in same-table GraphWalks) — bands may already be conservative. No public benchmark measures agentic instruction-following decay, so onset numbers likely under-predict the practical dumb zone. Bands are meaningful against total occupancy including baseline overhead (system prompt, skills, MCP schemas). zones.json is the correction path.

Constraints

  • Statusline single-slot contention (verified 2026-07-26): on the reference machine ~/.claude/context-guard/ does not exist (setup never ran) and the statusline slot is owned by rate-limit-guard's tee — every session there resolves unknown today, interactive included. Tee coexistence/chaining in the one statusline slot is a named design constraint; context-guard: research non-interactive observability (headless + subagent context usage) #1478 owns the observability survey.
  • Consumer-agnostic per PLUGIN-PHILOSOPHY.md.

Verify at implementation (never from memory)

  • Chosen hook events' injection/blocking semantics vs the current hooks doc (PostCompact already verified 2026-07-26).
  • Whether subagent tool calls traverse parent-session hooks — undocumented upstream; needs an empirical test.
  • Statusline snapshot refresh timing vs tool-side hook reads (staleness interplay).
  • Anthropic auto-compact defaults (Sonnet 5 ~967k et al.) — recheck.

Acceptance criteria

  • Crossing into a worse zone injects once per transition; silent while zone unchanged or unknown.
  • Advisory default; blocking mode per spec — never blocks on unknown, never blocks handoff writing.
  • PostCompact marks evidence-degraded per contract language.
  • zones.json accepts the new band shape; malformed file falls back with visible notice (existing rule).
  • Both known inliners migrate: plugin-quality audit's zones gate (currently rejects a token-shape zones.json and silently falls back to its inlined 50/75 table — the split-brain to prevent) and context-guard setup's seeder. Reader-contract inline floor updated with drift-check lane; the disagreement/combination rule consumers inline is stated verbatim in the contract.
  • Version floor or runtime guard for pre-2.1.132 token-field semantics.
  • Config delivered per the re-derived channel (see reopened section); consumer-agnostic.

Related

Activity

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

    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.priority: mediumReal value, no hard deadline; normal backlog flow.work-class: structuralRefactors, migrations, contract changes; cross-cutting and hard to reverse.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions