Skip to content

Hot-context managed block uses Claude-only @import in AGENTS.md, dropping hot-tier content for non-Claude tools #1208

Description

@amrmelsayed

Bug

Since #1119, the generated CODEV HOT CONTEXT block (written by codev init / codev update into both root docs) replaced the inlined contents of arch-critical.md / lessons-critical.md with two Claude Code @import lines:

@codev/resources/arch-critical.md
@codev/resources/lessons-critical.md

renderHotContextBlock() in packages/codev/src/lib/managed-block.ts renders one block and syncHotContextBlock() writes it to both ROOT_DOCS (CLAUDE.md and AGENTS.md, deliberately byte-identical).

For CLAUDE.md this is an improvement: Claude Code expands @import at session launch, the hot files stay the single source of truth, no copy to drift (exactly #1119's rationale).

For AGENTS.md it is a regression for most non-Claude consumers: the agents.md standard defines no import mechanism ("just standard Markdown"; import support is open request agentsmd/agents.md#11), so @-expansion is a per-tool extension. Verified consumer matrix (2026-07-19, methods in the comments below):

Consumer @import expanded? Evidence
Claude Code yes product feature
Copilot CLI yes (relative in-repo paths) GitHub docs
Codex CLI no empirical (codex-cli 0.142.5) + official docs (discovery/concat only, no in-file imports)
Antigravity CLI (agy) no empirical (inline fact reached context, @import fact did not)
OpenCode no open feature request anomalyco/opencode#2225
Cursor (AGENTS.md path) no documented support @file refs documented only for .cursor/rules/*.mdc
Copilot coding agent / VS Code undocumented no expansion claim in docs

Sharpest consequence: consult's own non-Claude lanes (codex and agy) are empirically confirmed to lose the hot tier. Every CMAP review run in an updated project has its codex and gemini lenses reasoning without the always-on facts and lessons. This is self-inflicted on our consultation stack, not just a third-party-adopter concern. Also observed in an adopting project after its latest codev update: ~65 lines of inlined hot content in AGENTS.md replaced by the two literal import lines.

Porch phase prompts are unaffected (they consume the hot files at runtime via readHotTierFiles()).

Why per-tool native reference mechanisms are NOT a substitute

Several tools do offer their own way to pull extra files into always-on context, so "reference instead of inline" was evaluated per tool:

Tool Native mechanism Usable for the hot tier?
OpenCode opencode.jsoninstructions array (repo-committed, glob-capable) yes, cleanly
Cursor .cursor/rules/*.mdc with alwaysApply: true + @file refs yes
Copilot CLI @relative/path in AGENTS.md itself already works today
Copilot VS Code .github/copilot-instructions.md / *.instructions.md (docs: file refs NOT expanded in .instructions.md) only by inlining there too
Codex effectively none for this case: repo-level .codex/config.toml exists (trusted projects), but project_doc_fallback_filenames is consulted only when AGENTS.md is ABSENT at that directory level — it cannot add files alongside an existing AGENTS.md; and discovery is a root→cwd directory walk, so codev/resources/*.md is never on the path. Content must physically be in AGENTS.md (or AGENTS.override.md) no
Antigravity (agy) GEMINI.md override file; no documented import/include mechanism no

Two conclusions:

  1. The per-tool route still doesn't reach codex or agy — the two consult lanes, the empirically confirmed losers. For them the content has to be in AGENTS.md itself, which forces inlining regardless of what else is done.
  2. It's an N×format maintenance matrix: each tool has its own config file, syntax, and clobber semantics, and adopters may already own those files (codev would be merging into opencode.json / .cursor/rules/ it didn't create). One regenerated Markdown block has zero per-tool knowledge and covers every present and future consumer.

A later polish issue could add native wiring for OpenCode/Cursor to slim AGENTS.md, but it would be additive, not a substitute.

Suggested fix

Render the block per consumer at generation time:

Side effects of inlining on the edit workflow (in scope, not afterthoughts)

Inlining moves a sync burden onto whoever edits the hot files (architects in MAINTAIN cycles, builders routing lessons at review time). Consequences the implementation must address:

  1. A regeneration step enters the hot-file edit loop. Only codev init/update calls syncHotContextBlock() today, and there is no lightweight command that refreshes just the block (codev update does far more). Ship a small regeneration affordance (e.g. a codev subcommand that re-syncs the root docs' managed block) and wire it into the places that edit hot files: the update-arch-docs skill, the MAINTAIN protocol, and review-phase prompts.
  2. Builder PRs gain a second file to stage. A lesson-routing PR must touch the hot file AND the regenerated AGENTS.md. With explicit staging (no git add -A), forgetting the second file is easy; the symptom is a surprise AGENTS.md working-tree diff at the next codev update (exactly the confusion that produced this report).
  3. Duplicated merge-conflict surface. Two concurrent builders adding lessons currently conflict only inside the small hot file. Inlined, they also both rewrite the same generated block, and conflicts in generated content are the worst kind (correct resolution is "regenerate", which git cannot express).
  4. Freshness needs enforcement. In this repo: extend governance-sweep.test.ts to assert AGENTS.md's block equals renderHotContextBlock()'s current output, so a PR that edits hot files without regenerating fails CI. In adopter repos: Surface codev doctor results (incl. scaffold drift) in the VS Code extension #1207's doctor drift check (block-vs-hot-files is a local byte-compare, no version info needed).

Unaffected either way: porch phase prompts (runtime injection via readHotTierFiles()), and CLAUDE.md under option 1 (session-launch expansion spares the majority harness all four costs).

Impact on non-Claude users under option 1

Non-Claude consumers become strictly better than today but freshness-second-class unless regeneration is enforced:

  • Baseline: post-Replace hot-context managed block in CLAUDE.md/AGENTS.md with @import lines (single source of truth, kills drift footgun) #1119 they get NO hot tier at all. Bounded staleness is a strict improvement.
  • The gap: Claude sessions expand @import at launch (always current); non-Claude sessions read the inlined block (current as of last regeneration). If regeneration lags, agents on the same repo diverge: the Claude lane knows a newly added invariant, the codex/agy lanes don't. In a 3-way CMAP this means reviewers disagreeing because they hold different governance snapshots, not different perspectives; in mixed human teams, a Cursor user's agent can violate an invariant a Claude user's agent already respects — the exact failure class the hot tier exists to prevent.
  • With the enforcement above (regeneration wired into the edit loop + CI freshness check), the staleness window collapses from release cadence to the life of the editing PR — effectively zero for everyone consuming main. The workflow burden in the previous section is the price of making non-Claude consumers first-class rather than absent.

Design tension to resolve (the actual decision)

The hot-tier invariant (in arch-critical.md, echoed in managed-block.ts's ROOT_DOCS comment) says CLAUDE.md and AGENTS.md MUST stay byte-identical. Per-consumer rendering breaks byte-identity. Options:

  1. Amend the invariant to: identical outside the managed block; the generated hot-context block is rendered per-ecosystem (import for Claude, inline for the standard). Recommended: the invariant's intent is equal capability across tool ecosystems, and the current byte-identity literalism is what produced unequal capability. Per-consumer rendering from a single source still delivers semantically identical content to both files.
  2. Inline in both files (full revert of Replace hot-context managed block in CLAUDE.md/AGENTS.md with @import lines (single source of truth, kills drift footgun) #1119): keeps byte-identity literal and uniform, but reintroduces the drift footgun for CLAUDE.md that Replace hot-context managed block in CLAUDE.md/AGENTS.md with @import lines (single source of truth, kills drift footgun) #1119 existed to remove — and the drift is not hypothetical: hot files are user-evolved content edited routinely (MAINTAIN cycles, review-time lesson routing), while the inlined block refreshes only at codev update (package-upgrade cadence). Inline-both makes staleness uniform across all consumers instead of keeping the majority harness (Claude Code, which runs porch builders and MAINTAIN itself) always fresh via session-launch expansion.

Whichever lands, the equality test in governance-sweep.test.ts and any byte-compare between the two docs needs updating to compare content outside the managed block (option 1) or is unchanged (option 2).

Affected surfaces

  • packages/codev/src/lib/managed-block.ts (render + sync)
  • codev init / adopt / update call sites in scaffold.ts and commands
  • Byte-identity checks/tests, and the invariant wording in arch-critical.md (both trees: codev/ and codev-skeleton/)

Evidence trail

  • Comment 1: spec + Copilot CLI + Cursor documentation check (corrects the original blanket "non-Claude tools render as literal text" claim)
  • Comment 2: agy empirical test method + OpenCode documentation check
  • Comment 3: codex empirical test + final matrix

Sources: agents.md spec · agentsmd/agents.md#11 · GitHub Copilot CLI custom-instructions docs · learn.chatgpt.com Codex AGENTS.md guide + config reference · opencode.ai/docs/rules · anomalyco/opencode#2225

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/scaffoldArea: Install path — codev init/adopt/update/doctor, codev-skeleton, four-tier resolver

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions