[FEATURE] Persistent governance context to address context dilution

Status Open
Maintainer reply None cached
Activity 10 comments · opened Apr 7, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

Developer-editable instructions (CLAUDE.md, hook output) share the context window with working content. As sessions grow, instructions dilute. At 15KB of instructions in 500KB of conversation, they occupy 3% of context and sit furthest from the model's recency bias. Rules followed at session start stop being followed after sustained work; not by decision, but by distance.

Compaction discards instruction text alongside working content. On 1M context windows, compaction may never fire, removing the only hook-triggerable re-orientation boundary.

I maintain a structured instruction set (~15KB) loaded via hooks. Instructions govern coding standards, file routing, session state, and style rules. These instructions must apply consistently throughout a session, not only at the start.

Proposed Solution

A persistent governance context: a small, developer-defined region the model processes every turn, separate from the conversation context.

A designated file (~/.claude/GOVERNANCE.md or a project-level equivalent) whose contents the system injects into a privileged position on every turn, alongside the system prompt, but developer-controlled. Not conversation context. Not subject to compaction or summarisation.

Required properties:

| Property | Requirement |
|---|---|
| Fixed-size | Bounded (~15KB); cannot grow with conversation |
| Always-visible | Processed every turn, not only at session start |
| Non-dilutable | Maintains priority regardless of conversation length |
| Refreshable | Updatable mid-session via hooks or commands |
| Separate from context | Outside the message stream; not subject to compaction or summarisation |

Contents (developer-defined): the subset of instructions that must govern every turn. Examples: coding standards, style rules, forbidden patterns; routing rules (what files to read before starting work); session state (current task, active plan); diagnostic markers that detect instruction-following failure.

Alternative Solutions

Current mitigations, all of which delay dilution but cannot prevent it:

  • SessionStart hook injects core instructions at session open
  • PostCompact hook re-injects the same instructions after compaction
  • Manual /compact as a deliberate boundary between tasks
  • Planned refresh points at task boundaries re-orient the model to its instructions

Re-injected instructions enter the context window and begin diluting again immediately. The model cannot discard context; only a person or the system (at limits) can remove it.

Why existing mechanisms fall short:

| Mechanism | Limitation |
|---|---|
| System prompt | Not developer-editable; Anthropic-controlled |
| CLAUDE.md | Loaded as context; dilutes with growth; sometimes skipped (#44329) |
| SessionStart hook | Output enters context; dilutes over time |
| PostCompact hook | Re-injects after compaction; same dilution applies |
| Extended thinking | Generated fresh each turn; no persistence; no privileged input |
| MCP servers | Demand-driven (model calls them); not supply-driven governance |

Priority

Critical. This is the structural limitation that all other mitigations work around. Every workaround adds context volume, which accelerates the problem it tries to solve.

Priority

Critical - Blocking my work

Feature Category

API and model interactions

Use Case Example

  1. Session starts. SessionStart hook injects ~8KB of instructions: coding standards, file routing table, active task state, style rules.
  2. Work proceeds. After ~100K tokens of conversation (reading files, writing code, discussing decisions), the instructions occupy less than 8% of context and sit at maximum distance from the current turn.
  3. Style rules stop being followed. The model writes "user" in documentation where the style guide requires "person". Code patterns drift from documented conventions. File routing is skipped.
  4. I notice and trigger a manual refresh. The model re-reads instruction files, adding another ~8KB to context. Compliance recovers briefly.
  5. Work continues. The refresh text itself begins to dilute. By ~300K tokens, even recent refreshes lose priority. I must choose between compacting (losing working context) or accepting degraded instruction-following.
  6. At ~400K tokens, instruction-following fray is consistently observable. I trigger /compact, losing working context, to restore compliance via PostCompact hook re-injection.

With a persistent governance context, steps 3–6 do not occur. The 8-15KB of instructions maintain consistent priority regardless of conversation length.

Additional Context

Related issues:

  • #35309 (instructions disregarded mid-session despite confirmation)
  • #19471 (CLAUDE.md instructions ignored after context compaction)
  • #44329 (CLAUDE.md sometimes skipped at session start)
  • #44465, #44461, #44431 (instructions ignored under context pressure)
  • #42796 (thinking depth regression; read-to-edit ratio collapse in long sessions)

Calibration data: first observed consistent instruction-following fray at ~398K tokens (1M context window, Opus model, ~15KB instruction set). Fray manifests as style guide violations, skipped file reads, and routing table non-compliance.

Platform optimisations compound the problem: the Read tool caches previously read files and refuses to re-read them if it considers the content unchanged. The cache persists across compaction; it tracks the entire process lifetime, not the context window. A developer who re-reads an instruction file for re-orientation (bringing it back to the recency window after dilution) gets a stub response instead of the file contents. The optimisation treats re-orientation and change detection as identical, suppressing the one that matters most under context pressure. This is the same structural pattern: the system enforces efficiency at a layer below where the developer can intervene, and the enforcement undermines the developer's ability to maintain instruction priority.

Design constraint: the governance context must be fixed-size to prevent developers from recreating the dilution problem by stuffing unbounded content into the privileged region.

View original on GitHub ↗

9 Comments

github-actions[bot] · 4 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/19471
  2. https://github.com/anthropics/claude-code/issues/22421
  3. https://github.com/anthropics/claude-code/issues/39502

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

turukawa · 4 months ago

These issues describe the same symptom (instructions lose priority as sessions grow) but propose different solutions. #19471 and #39502 are bugs requesting that existing mechanisms work reliably. #22421 proposes periodic re-injection into the context window.

My feature request proposes something structurally different: a region outside the context window that the model processes every turn. Re-injection (the solution in the linked issues) is one of my current mitigations; my feature request documents why it is insufficient. Re-injected instructions enter the context window and begin diluting immediately. On 1M context windows where compaction may never fire, no amount of re-injection solves the recency bias problem.

The linked issues are related (and cited in the Related Issues section), but the proposed mechanism is distinct.

turukawa · 4 months ago

Update: proof-of-concept persistent governance injection

I've built a workaround to not having an external governance context by using the hook system. A UserPromptSubmit hook injects a compressed GOVERNANCE.md (~1.7KB) into context on every submitted message. The doc contains a highly-summarised version of the main conventions I use for writing code and documentation.

Findings from a full session (936K tokens, no compaction):

  • ~425 tokens per injection, ~50 messages = ~21K tokens total (~2% of 1M context)
  • No perceptible latency impact on message submission
  • The injected rules are interpretable and actionable when the agent receives them cold
  • Agent self-reports correct governance adherence throughout the session

At the end of today's session, some fray noted on my hard-rule no "user" term when referring to me. Overall, though, today has been much more consistent despite fairly complex trouble-shooting.

Implementation:

  • Hook: UserPromptSubmit with empty matcher in settings.json
  • Script: reads a markdown file and outputs it to stdout (injected as system-reminder)
  • The governance file is human-editable but written in compressed notation optimised for LLM interpretation, not human readability

This is a workaround, not a solution. The underlying issue — that compaction discards instruction context with no mechanism for the developer to mark content as compaction-resistant — remains. But for projects with long sessions and complex convention
systems, this keeps the most critical rules in every context window at minimal cost.

sgroy10 · 4 months ago

@turukawa — this is the most carefully specified governance-context proposal I've seen on this tracker. The framing of "distance" rather than just "dilution" is the right one — your point that "Rules followed at session start stop being followed after sustained work; not by decision, but by distance" names the failure mode more precisely than I've seen elsewhere. The 8KB-of-instructions-at-3%-of-context calculation, and the recency-bias compounding from re-injection, make the structural argument airtight.

Disclosure: I'm the creator of SpecLock. I'm replying because your spec describes about 70% of what SpecLock already ships — and I want to be honest about the 30% gap, because your design is genuinely better in places where mine compromises.

Mapping your required properties against SpecLock:

| Your property | SpecLock equivalent |
|---|---|
| Fixed-size, bounded | SpecLock rules live in .speclock/ files; no in-context budget at all (0 bytes of context cost) |
| Always-visible | Re-read by the pre-commit hook on every commit, so always-current at the enforcement layer |
| Non-dilutable | Zero dilution because rules never enter the context window |
| Refreshable | Hot-edit the rules file; next commit picks it up |
| Separate from context | Lives entirely outside the model — the model never sees the rules |
| Survives compaction | Compaction is irrelevant; the rules aren't in context to be compacted |

Where SpecLock matches your spec: the "separate from context" property is the one that matters most, and SpecLock satisfies it natively because enforcement is a git pre-commit hook reading semantic locks from .speclock/rules.yml. Your 1.7KB UserPromptSubmit hook workaround is doing the inverse — injecting the rules into every context — which SpecLock avoids entirely by moving enforcement out of the model loop.

Where SpecLock falls short of your governance vision (and you should know):

  1. Pre-edit vs post-edit. SpecLock fires at commit time, which means the model may write violating code mid-session and only get caught at the gate. A native in-harness governance context would influence the decision to write the file, not just the decision to commit it. Yours is a steering wheel; mine is a safety net. They're complementary, not equivalent.
  1. Behavioral rules ("don't use 'user', use 'person'"). SpecLock catches this in commit messages and code comments, but it can't catch it in the model's narration text inside a session. Your governance context would, because it stays in attention every turn.
  1. Read tool truncation/cache. The pathological caching you described (Read tool refusing to re-read instruction files) is exactly the sort of optimization that breaks any in-context approach. SpecLock sidesteps it by never relying on the model to re-read anything.
  1. Cowork/Desktop. SpecLock works wherever git works, so it covers the CLI well. It doesn't help with in-session behavior in Cowork/Desktop where there's no commit boundary.

For your specific calibration data (398K tokens, 15KB instructions, ~8% context occupancy at fray onset): SpecLock would let you cut the in-context instruction budget to ~0KB by moving the enforceable subset (style rules, file routing, forbidden patterns, version-bump checks) into the rules file. Whatever can't be moved (the genuinely behavioral subset) stays in CLAUDE.md but is now small enough to live in the recency window. That's not the same as your governance context, but it's a complementary approach you can deploy today.

One command:

npx speclock protect

Reads CLAUDE.md, extracts rules, installs the hook. Default mode is WARN (loud warnings, no blocks) so you can validate against your real workflow before turning on speclock enforce hard. v5.5.7, 1009 tests passing, MIT licensed, on the Official MCP Registry.

Genuine ask: I'd love your gap analysis on which of your 15KB rule set converts cleanly to commit-time semantic locks vs which needs in-session enforcement. The cases where in-session beats commit-time are the cases SpecLock should document as limitations — and probably the cases where the architecture should evolve in the next major version. Your spec is the kind of design input that makes the tool better.

Sandeep (@sgroy10)

turukawa · 4 months ago

Hey @sgroy10, thanks for engaging and introducing me to your project.

I'd like to take a step back on this, because what you call the "30% gap" is really about two entirely different approaches to coding. We could differentiate it as "interpreted" vs "compiled". For me, though, this feature request is about 10% of what I'm trying to define. It is only necessary as a monkey-patch to an architectural decision Anthropic made, and which both your SpecLock approach, and my GOVERNANCE.md response are attempting to address retrospectively.

Let me approach this formally, for two reasons: 1) I'm writing this up with the intention to publish a prototype knowledge framework in the next few weeks, and 2) when what we're describing is a non-deterministic natural language interpreter it is critical to be as coherent, concise and unambiguous as possible.

Problem: a traditional programming language is a deterministic, structured interpreter (or compiler). A developer "talks" to it using a fixed grammar; it replies with structured output or specific error messages. The cognitive overhead is remembering the grammar and interpreting the responses. That overhead is bounded and predictable. For me, that load slows things down, and I develop fewer features to reduce this complexity.

An LLM is a non-deterministic, natural language interpreter. Both sides communicate in natural language. There is a vast chasm of non-determinism which translates as ambiguity: developers may be ambiguous in what they say; the model may be ambiguous in how it responds. That ambiguity produces incorrect results before context drift or fray even emerge.

Yelling at the LLM that "it's _always_ forkin yellow!" with the response "I'm sorry. You're right. I won't do it again." ... Well, what sort of response did you expect? It's a non-deterministic natural language interpreter where "memory" is stored in an enormous muddy-pool of ever-growing flat text. An "apology" doesn't fix the underlying problem. Convention drift is inevitable where there is no unambiguous, immutable governor context.

What knowledge framework conventions solve: conventions are a grammar for the non-deterministic interpreter. Style guides, development methods, UI, structured planning ... each constrains the space of valid outputs the same way a type system constrains a compiler. The difference: a compiled language enforces its grammar at parse time. Conventions must be enforced through attention and repetition, because the interpreter has no built-in enforcement layer.

And _that_ is the architectural problem.

Alternative: commit-time validation. A commit-time validation tool (e.g. SpecLock) adds a compile step to the interpreter. The model's output is checked against a specification at commit time; violations produce error messages and the model revises. This is effective for mechanical rules (spelling, file naming, forbidden patterns) but has three limitations:

  1. Steering vs catching. Conventions that influence decisions during generation (expertise calibration, escalation triggers, design specifications) need to be in the model's attention while it works. A commit-time check catches violations after the fact but cannot prevent the model from reasoning down the wrong path. The distinction is constitutive rules (shape what the model considers valid before generating) vs regulative rules (check output afterwards).
  1. Non-deterministic error correction. A compiled language's error messages are deterministic: "missing semicolon on line 42" is unambiguous. Commit-time violation messages go back into the non-deterministic interpreter for correction. The model might fix the violation while introducing a new ambiguity, misinterpret what the rule meant, or fix it in a way that technically passes but misses the intent. The compile step does not make the interpreter deterministic; it adds a gate that the non-deterministic system must pass through.
  1. Iteration cost. The reason people use interpreters is the fast feedback loop: write, run, see the result, adjust. A compile-then-fix cycle trades one cost (fray) for another (iteration delay). For interactive, human-in-the-loop work this trade is unfavourable.

Two working modes. The choice between conventions and commit-time validation tracks two modes of LLM use:

| Mode | Human role | Governance mechanism | Trade-off |
|---|---|---|---|
| Mostly automated | Sets goals at start; reviews at end | Commit-time validation (compile step) | Catches violations but cannot steer; iteration delay on failure |
| Mostly interactive | Engaged throughout | In-session conventions (parse-time grammar) | Requires attention and repetition; developer takes responsibility for output quality |

A convention knowledge framework targets the interactive mode. Commit-time validation is a useful complement for the mechanical subset of rules, freeing context budget for the behavioural rules that must remain in attention. The two approaches compose but do not substitute.

Architectural gap. The missing capability is a persistent, non-dilutable instruction register that the model attends to at generation time, not just at prompt time. The GOVERNANCE.md injection via UserPromptSubmit hook is the closest available approximation to a constitutive mechanism, but it is a workaround for the absence of this architectural feature.

Everything I'm doing in mitigation of this feature request, and which you're doing with SpecLock, is a bandaid until Anthropic addresses the fundamental problem that conventions do not belong in the context workspace.

mahdikayvan · 3 months ago

@turukawa Hey — I felt this issue immediately, and it is very close to the exact pain I’m trying to solve.

I’m building AgaveCore, and I’d like to let a few early people use it for free while I shape it into something stronger.

It helps when a coding agent starts to drift, loses important context, or gives you an output you do not fully trust.

In practice, it works through two habits:

  • tell the agent to remember-progress or remember-this when something important should not be lost
  • use consult-brain when the current task needs more grounded feedback on what to do next

Over time, as useful journals, decisions, and failures pile up, the product should help the agent make sharper decisions and take better actions with less drift.

If this sounds relevant, reply here and I’ll send the exact steps. You can also use the email or Telegram on my profile.

Cheers,
Mads.

hipvlady · 3 months ago

This is the broader context-dilution problem that manifests as instruction drift across long sessions. As conversation length grows, instructions get pushed back in the context by working material, and recency bias causes them to lose priority.

Partial fit for agent-coherence plugin: The plugin directly solves the post-checkpoint variant (instructions lost after compaction). The general session-length dilution (without compaction) is a different architectural problem at the Claude Code level, but the plugin's version-tracking approach helps surface when drift has occurred.

If you're hitting this in long sessions with compaction, the coordinator's stale-read detection will surface when your CLAUDE.md version is stale, triggering a re-read. If it's happening purely due to context length (no compaction), that's a different issue — but filing an issue with "instruction drift after N tokens without compaction" would be valuable data.

ferhimedamine · 2 months ago

The dilution problem is real and measurable — instructions at 3% of a 500KB context window are functionally invisible to the model's recency bias.

The core issue is treating governance instructions the same as conversational content. They have fundamentally different persistence requirements: instructions should be immortal (always present, never diluted), while conversation context should decay naturally (recent turns matter more than old ones).

The pattern that works: externalize governance instructions as high-importance procedural memories in a persistent memory service. At each turn start, the agent recalls its procedural memories (importance ≥ 0.9, type = procedural) and re-injects them at the TOP of context — not mixed in with conversation history. Because they're stored externally with importance 0.95+, they survive any number of compactions and are always re-injected at the same position in context.

This is different from CLAUDE.md because: (1) no 200-line cap, (2) re-injection happens per-turn not per-session, so instructions are always at maximum recency, and (3) the agent can self-update its procedural memories during the session (\"I learned that this project uses tabs not spaces\") without waiting for a human to edit a config file.

Memory type separation (procedural vs episodic vs semantic) with per-type decay curves: https://github.com/Dakera-AI/dakera-py/blob/main/examples/basic_usage.py — procedural memories get near-zero decay, episodic memories decay aggressively.

ajdelaguila · 1 month ago

Full disclosure: I maintain Data Olympus, a git-native governance knowledge base plus MCP server for coding agents: https://github.com/knaisoma/data-olympus

This feature request matches the boundary we ended up drawing: governance context should not live in the same lifecycle as ordinary conversation context.

In our implementation, rules and decisions are stored as markdown files in git with frontmatter that makes their authority explicit: stable id, controlled type, status, tier, plus supersedes and superseded_by chains. The MCP server exposes compact search/get tools and supports an in_force mode so the agent can ask for currently governing guidance only. Superseded guidance remains auditable, but it is not allowed to control a new implementation decision by accident.

That is the part I would want Claude Code to support natively or via a provider hook: not just "persistent text that gets recalled," but a persistent governance surface with lifecycle semantics.

A few product requirements fall out of that:

  • governance context should be reloaded at session start and after compaction, not merely summarized;
  • query-time retrieval should be possible when a task enters a specific domain, since loading every rule every turn does not scale;
  • stale/superseded rules should be excluded before ranking, not just ranked lower;
  • agent-discovered updates should enter a proposal queue rather than silently modifying authoritative project rules;
  • the user should be able to inspect what governed the current answer or edit.

This would make CLAUDE.md, hooks, MCP memory, and project knowledge less competitive with each other. CLAUDE.md can stay as bootstrap instructions, while governed project knowledge can live in a reviewed, queryable store that survives context dilution without being treated as unquestioned model memory.

Showing cached comments. Read the full discussion on GitHub ↗