From d9a1cd366b413de6588e6be4974e7ed02c32dadd Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Thu, 1 Oct 2026 21:36:07 -0400 Subject: [PATCH 1/6] feat(instruction-placement): decide CLAUDE.md shim removal against the built-in AGENTS.md loader migrate now recommends dropping the @AGENTS.md shim only when four conditions hold: nothing else takes precedence, the Project instructions mode reads AGENTS.md without a CLAUDE.md, the built-in agents-md plugin is present and not disabled (read at run time), and the operator confirms no unsupported session type matters. Nested shims stay wherever the memory page does not say when a subdirectory AGENTS.md loads. remove-shims and its --confirm gate remain the only removal path. Closes #5736 Co-Authored-By: Claude Opus 5.5 --- .../.claude-plugin/plugin.json | 2 +- plugins/instruction-placement/CHANGELOG.md | 15 +++++ .../skills/migrate/SKILL.md | 45 ++++++++++--- .../skills/migrate/reference/sources.md | 63 ++++++++++++++----- 4 files changed, 100 insertions(+), 25 deletions(-) diff --git a/plugins/instruction-placement/.claude-plugin/plugin.json b/plugins/instruction-placement/.claude-plugin/plugin.json index d3c0e13837..6b3d764f08 100644 --- a/plugins/instruction-placement/.claude-plugin/plugin.json +++ b/plugins/instruction-placement/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "instruction-placement", - "version": "0.16.11", + "version": "0.17.0", "description": "Routes agent-instruction content to the surface that loads it at the right moment. The audit skill sweeps a repository's instruction layer and its ordinary markdown for content whose scope is narrower than the surface carrying it, meaning conventions keyed to one file type or one subtree sitting in an always-loaded CLAUDE.md or AGENTS.md, and for normative conventions stranded in documentation Claude never loads at all, then classifies each against a routing rubric and proposes a destination whose `paths:` glob is machine-validated before it is ever offered. Safety-class content (irreversible actions, secrets, data integrity, external publication, compliance, agent authority) is hard-denied from demotion and reported as held back rather than proposed, because demotion trades guaranteed presence for conditional presence: a deferred surface is absent until a read matches it, absent after a compaction until that trigger recurs, and never inherited by a subagent, which re-acquires it only by reading a covered path itself. Every accepted move regenerates an always-loaded index of deferred surfaces, which is what keeps a demoted rule discoverable from any context that has not happened to touch a path it covers. The audit is read-only and emits a diffable findings artifact; realignment is a separate skill gated per item with no blanket-approve path; a deterministic check skill gates that every rule glob still resolves and the index is current; a migrate skill moves a repository to AGENTS.md as the content home and keeps a CLAUDE.md shim while one is needed; and a setup skill verifies the one thing no other gate can see: that nothing in the repository stops Claude Code reading the index target, since a CLAUDE.md in the working directory or above it is read instead of the AGENTS.md beside it.", "author": { "name": "Melodic Software", diff --git a/plugins/instruction-placement/CHANGELOG.md b/plugins/instruction-placement/CHANGELOG.md index 68d467caa7..97a11a6c66 100644 --- a/plugins/instruction-placement/CHANGELOG.md +++ b/plugins/instruction-placement/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to the `instruction-placement` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.17.0] - 2026-10-01 + +### Added + +- **`/instruction-placement:migrate` decides shim removal against the built-in `AGENTS.md` + loader.** A new step before `remove-shims` recommends dropping the `@AGENTS.md` shim only when + four conditions hold: no other `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` takes + precedence; the **Project instructions** mode reads `AGENTS.md` without a `CLAUDE.md`; the + built-in `agents-md` plugin is present and not disabled, read at run time from + `/claude-ops:inventory` and settings; and the operator confirms no unsupported session type + matters to the repository's users. Any condition failed or unknown keeps the shim and is named. + Nested shims stay under every mode where the memory page does not say when a subdirectory's + `AGENTS.md` loads. Nothing is removed automatically; the dated quotes are in + `reference/sources.md`. + ## [0.16.11] - 2026-10-01 ### Added diff --git a/plugins/instruction-placement/skills/migrate/SKILL.md b/plugins/instruction-placement/skills/migrate/SKILL.md index dcd89c1fa4..42566717a5 100644 --- a/plugins/instruction-placement/skills/migrate/SKILL.md +++ b/plugins/instruction-placement/skills/migrate/SKILL.md @@ -269,6 +269,38 @@ there locates a path the same way. Two things are out of the scan and the condit markdown, which locates no path, and the acknowledgement list itself, whose every line quotes a detector by design. +### Is the shim droppable here? Decide before `remove-shims` + +`cutover-check` grades the build and the fleet. Whether this repository's users lose instructions +without the shim depends on the built-in `agents-md` loader, so recommend removal only when all four +hold. Report each as held, failed or unknown with its evidence; one failed or unknown keeps the +recommendation at "keep the shim" and names it. This recommends, never removes: `remove-shims` and +its `--confirm` gate stay the only path. + +| # | Holds when | Read it from | +|---|---|---| +| A | Nothing else takes precedence: no `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` at or above the working directory other than the shims going | `DIR` and `SUPPRESS` rows, a Glob for `.claude/CLAUDE.md` and `CLAUDE.local.md`, and the operator for the uncommitted `CLAUDE.local.md` contributors keep | +| B | **Project instructions** reads `AGENTS.md` with no `CLAUDE.md`: `claude-md-or-agents-md` (default) or `claude-md-and-agents-md` | `pluginConfigs["agents-md@builtin"].options.instructionFiles` in user, managed and any `--settings` file; absent everywhere is the default. Project and local settings are ignored for it. `claude-md` or `managed-only` fails | +| C | The loader is present and not disabled | `/claude-ops:inventory --bundled`, `builtin_plugins.cc-plugin-agents-md` (`in_loader`, `load`, `gated`, `gate_flags`), and no `enabledPlugins` entry set `false` in any scope for `agents-md@builtin` or the `id` the lane prints. Inventory absent, the lane `broken` or the entry missing is unknown | +| D | No session type without support matters to the repository's users | Ask the operator, listing: a CLI before v2.1.277; the plugin disabled in `/plugin`; in some cases the first session after upgrading from v2.1.276 or earlier; before v2.1.281, Amazon Bedrock and telemetry-disabled sessions; `--add-dir` directories under `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD`, whose `AGENTS.md` does not load. In those sessions **Project instructions** is missing from `/config` | + +B and C read this machine; the setting is per user and no repository can ship it, so the operator +answers D for everyone else's machines too. + +**Nested `AGENTS.md`, per mode**, as the memory page states it: + +- `claude-md-or-agents-md`: a subdirectory's `AGENTS.md` loads "when Claude opens a file there with + the Read tool and that subdirectory has none of the three `CLAUDE.md` files of its own". Nested + shims follow the root verdict and leave with it. +- `claude-md-and-agents-md`: "each directory's `CLAUDE.md` files first and its `AGENTS.md` after + them"; the page does not say when a subdirectory's file loads. A repository with nested + `AGENTS.md` keeps every shim under this mode, since `remove-shims` takes them together. +- `claude-md`, `managed-only`: no `AGENTS.md` loads. Under `managed-only` a subdirectory's + `CLAUDE.md` still loads on Read, and the page does not say whether its import expands. Keep them. + +Quotes, dates and triggers: [`reference/sources.md`](reference/sources.md), "The built-in +agents-md plugin". + ### `remove-shims`, one repository per run ```bash @@ -341,13 +373,9 @@ directly at all. - **Claim**: Claude Code reads `AGENTS.md` as the project instructions only where there is no `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` in the working directory or above it, and attaches a subdirectory's `AGENTS.md` when a Read opens a file there and that subdirectory has - none of those three names of its own. Reading it directly needs v2.1.277 or later. The memory - page's "When AGENTS.md support is unavailable" list is: a CLI before v2.1.277, the built-in - `agents-md` plugin disabled in `/plugin`, and in some cases the first session after an upgrade - from v2.1.276 or earlier. The same page says that before v2.1.281, some sessions, such as those - on Amazon Bedrock or with telemetry disabled, read `CLAUDE.md` files only, and that on those - versions you update Claude Code. A `CLAUDE.md` containing `@AGENTS.md` never makes Claude read - the file twice. + none of those three names of its own. Reading it directly needs v2.1.277 or later; the sessions + that cannot are condition D's list above. A `CLAUDE.md` containing `@AGENTS.md` never makes + Claude read the file twice. - **Basis**: [memory](https://code.claude.com/docs/en/memory), fetched 2026-09-29 (49,601 bytes; slug in `llms.txt`; first heading "How Claude remembers your project"), sections "AGENTS.md", "When Claude Code reads AGENTS.md", "When AGENTS.md support is unavailable", and "Remove an @@ -401,7 +429,8 @@ One native Claude Code surface works on the same file, and the two are easy to c **Routing.** The plugin is the loading mechanism this skill's shim decisions are judged against, not a replacement for the migration. Where the plugin is enabled in this session, the shim still carries `AGENTS.md` into the sessions the plugin does not reach; never treat the plugin's -presence on one machine as proof every session reads `AGENTS.md`. Verdict `complementary`, +presence on one machine as proof every session reads `AGENTS.md`; the droppability decision above +is where its state is read. Verdict `complementary`, integration `route`; the four-part record is in [`reference/sources.md`](reference/sources.md), "The built-in agents-md plugin". diff --git a/plugins/instruction-placement/skills/migrate/reference/sources.md b/plugins/instruction-placement/skills/migrate/reference/sources.md index f4411e9c76..fd8f3df666 100644 --- a/plugins/instruction-placement/skills/migrate/reference/sources.md +++ b/plugins/instruction-placement/skills/migrate/reference/sources.md @@ -308,22 +308,47 @@ The 2026-09-20 measurement above was not repeated on this pass. ## The built-in agents-md plugin -The record behind the skill body's `## Boundary` section for `cc-plugin-agents-md`. +The record behind the skill body's `## Boundary` section for `cc-plugin-agents-md` and its +droppability decision (conditions A to D and the nested-`AGENTS.md` verdicts). - **Claim**: Claude Code ships a built-in `agents-md` plugin (ID `agents-md@builtin`) that reads `AGENTS.md` as the project instructions. Its **Project instructions** option (`instructionFiles`) takes four values: `claude-md-or-agents-md`, the default ("Your `CLAUDE.md` files, or your `AGENTS.md` files when you have no `CLAUDE.md` or `CLAUDE.local.md` in your - working directory or above it"), `claude-md-and-agents-md`, `claude-md` ("Your `CLAUDE.md` - files only") and `managed-only` ("Only your organization's managed `CLAUDE.md` and auto memory - at launch ... every `AGENTS.md` [is] left out"). Disabling the plugin in `/plugin` is one of the - sessions where "Claude reads `CLAUDE.md` files only". The plugin loads files; nothing upstream - says it moves content or writes a shim. -- **Basis**: [memory](https://code.claude.com/docs/en/memory), fetched 2026-10-01 (49,677 bytes; - slug in `llms.txt`; first heading "How Claude remembers your project"), sections "Choose which - instruction files load" ("Add it under the built-in `agents-md` plugin's ID in `pluginConfigs`", - with the example key `"agents-md@builtin"`) and "When AGENTS.md support is unavailable" ("You - disabled the built-in `agents-md` plugin in `/plugin`"). The + working directory or above it"), `claude-md-and-agents-md` ("Your `CLAUDE.md` and `AGENTS.md` + files together, each directory's `CLAUDE.md` files first and its `AGENTS.md` after them"), + `claude-md` ("Your `CLAUDE.md` files only") and `managed-only` ("Your project, local, and user + `CLAUDE.md` files, your `.claude/rules/` files, and every `AGENTS.md` are left out. A + subdirectory's `CLAUDE.md` and `.claude/rules/` files, and path-scoped rules, still load when + Claude reads a file there"). The value is read from `pluginConfigs` in "`~/.claude/settings.json`, + a `--settings` file, or managed settings. Claude Code ignores it in project and local settings + files." Condition A: the files that "Count, so Claude reads them instead of `AGENTS.md`" are "a + `CLAUDE.md`, `.claude/CLAUDE.md`, or `CLAUDE.local.md` in your working directory or any + directory above it". Nested files under the default: "a subdirectory's `AGENTS.md`, when Claude + opens a file there with the Read tool and that subdirectory has none of the three `CLAUDE.md` + files of its own". The page states no subdirectory trigger for `claude-md-and-agents-md`, and + does not say whether a subdirectory `CLAUDE.md`'s `@AGENTS.md` import expands under + `managed-only`; the skill keeps the shims in both cases for that reason. Condition D: "In these + sessions Claude reads `CLAUDE.md` files only, and **Project instructions** doesn't appear in the + `/config` settings panel": "You're on a Claude Code version before v2.1.277", "You disabled the + built-in `agents-md` plugin in `/plugin`", and "In some cases, it's your first session after you + upgrade from v2.1.276 or earlier"; also "Before v2.1.281, some sessions, such as those on Amazon + Bedrock or with telemetry disabled, read `CLAUDE.md` files only". The difference table adds that + for "Directories you add with `--add-dir` while `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD` is + set", "Their `AGENTS.md` doesn't load". The removal procedure for an `@AGENTS.md` shim: "Remove + the `CLAUDE.md` if it holds nothing else, or keep it if some of your sessions can't load + `AGENTS.md` directly." The plugin loads files; nothing upstream says it moves content or writes + a shim, and no page states how a disabled built-in plugin is recorded in settings, which is why + condition C reads `enabledPlugins` for whichever ID the inventory prints rather than one spelling. +- **Basis**: [memory](https://code.claude.com/docs/en/memory), fetched 2026-10-01 by the rung-1 + route (50,074 bytes; slug in `llms.txt`; first heading "How Claude remembers your project"), + sections "When Claude Code reads AGENTS.md" (line 363), "Choose which instruction files load" + (line 381; "Add it under the built-in `agents-md` plugin's ID in `pluginConfigs`", with the + example key `"agents-md@builtin"`), "When AGENTS.md support is unavailable" (line 406), "Where + AGENTS.md differs from CLAUDE.md" (line 416) and "Remove an earlier AGENTS.md workaround" (line + 426). [settings-reference](https://code.claude.com/docs/en/settings-reference), fetched + 2026-10-01, `pluginConfigs`: "Built-in plugins store their options under the same key with an + `@builtin` suffix". The [changelog](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md) entry for 2.1.277 reads "Added AGENTS.md support: in a project with no CLAUDE.md, Claude Code reads AGENTS.md instead; change it under \"Project instructions\" in `/config`", and the 2.1.281 entry reads @@ -333,9 +358,15 @@ The record behind the skill body's `## Boundary` section for `cc-plugin-agents-m `cc-plugin-agents-md` with alias `agents-md`, whether the loader requires it in this session type, its availability gate and the gate's default, and whether it declares any skill, agent or command. No upstream page states them. Read them from `/claude-ops:inventory`'s `builtin_plugins` - lane (`builtin_plugins.cc-plugin-agents-md`: `aliases`, `load`, `gated`, `gate_flags`, `skills`, - `agents`, `commands`) on the build in hand. + lane (`builtin_plugins.cc-plugin-agents-md`: `aliases`, `in_loader`, `load`, `gated`, + `gate_flags`, `skills`, `agents`, `commands`) on the build in hand. On 2.1.287 on 2026-10-01 a + `--binary-only` run printed `id` `cc-plugin-agents-md@builtin`, alias `agents-md`, `in_loader` + true, `load` `unconditional`, `gated` true on `tengu_agents_md_mod` with default true; that is + one build's reading, not the decision's input. - **As of**: 2026-10-01, Claude Code 2.1.287. -- **Recheck trigger**: the memory page changes the "Choose which instruction files load" table or - the "When AGENTS.md support is unavailable" list, or a changelog entry names `AGENTS.md`, - `instructionFiles` or the `agents-md` plugin. +- **Recheck trigger**: the memory page changes the "When Claude Code reads AGENTS.md" list, the + "Choose which instruction files load" table, the "When AGENTS.md support is unavailable" list, + the difference table or the shim bullet under "Remove an earlier AGENTS.md workaround"; it comes + to state when a subdirectory's `AGENTS.md` loads under `claude-md-and-agents-md` or whether an + import expands under `managed-only`; settings-reference documents how a built-in plugin is + disabled; or a changelog entry names `AGENTS.md`, `instructionFiles` or the `agents-md` plugin. From 010b801c4b10f0f4e69ceb61508c8f1f4cddc7f7 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Thu, 1 Oct 2026 21:46:51 -0400 Subject: [PATCH 2/6] fix(instruction-placement): tighten the shim-droppable rule and move it to a reference file Address the verifier's sufficiency findings, staying fail-closed: - D asks whether any user, machine or organization sets Project instructions to claude-md, and covers Vertex AI, Foundry, LLM gateways and the undocumented surfaces (Agent SDK, cloud/web, claude-code-action). - New E: an external @ import in any AGENTS.md keeps the shim. - New F: a hook depending on InstructionsLoaded keeps the shim unless the operator accepts the loss. - A covers nested directories holding .claude/CLAUDE.md or a non-shim CLAUDE.local.md; the nested verdict follows. - The /config signal is scoped to the three documented bullets. The detail lives in reference/shim-droppable.md; SKILL.md keeps a pointer. Co-Authored-By: Claude Opus 5.5 --- plugins/instruction-placement/CHANGELOG.md | 20 +++--- .../skills/migrate/SKILL.md | 38 +++-------- .../migrate/reference/shim-droppable.md | 67 +++++++++++++++++++ .../skills/migrate/reference/sources.md | 24 +++++-- 4 files changed, 104 insertions(+), 45 deletions(-) create mode 100644 plugins/instruction-placement/skills/migrate/reference/shim-droppable.md diff --git a/plugins/instruction-placement/CHANGELOG.md b/plugins/instruction-placement/CHANGELOG.md index 97a11a6c66..3ff739b7dd 100644 --- a/plugins/instruction-placement/CHANGELOG.md +++ b/plugins/instruction-placement/CHANGELOG.md @@ -8,15 +8,17 @@ All notable changes to the `instruction-placement` plugin are documented here. F ### Added - **`/instruction-placement:migrate` decides shim removal against the built-in `AGENTS.md` - loader.** A new step before `remove-shims` recommends dropping the `@AGENTS.md` shim only when - four conditions hold: no other `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` takes - precedence; the **Project instructions** mode reads `AGENTS.md` without a `CLAUDE.md`; the - built-in `agents-md` plugin is present and not disabled, read at run time from - `/claude-ops:inventory` and settings; and the operator confirms no unsupported session type - matters to the repository's users. Any condition failed or unknown keeps the shim and is named. - Nested shims stay under every mode where the memory page does not say when a subdirectory's - `AGENTS.md` loads. Nothing is removed automatically; the dated quotes are in - `reference/sources.md`. + loader.** A new step before `remove-shims`, detailed in `reference/shim-droppable.md`, + recommends dropping the `@AGENTS.md` shim only when six conditions hold: no `CLAUDE.md`, + `.claude/CLAUDE.md` or `CLAUDE.local.md` takes precedence at or above the working directory or in + a nested `AGENTS.md` directory; this machine's **Project instructions** mode reads `AGENTS.md` + without a `CLAUDE.md`; the built-in `agents-md` plugin is present and not disabled, read at run + time from `/claude-ops:inventory` and settings; the operator confirms no other user, organization, + session type or surface (Agent SDK, cloud, `claude-code-action`) needs the shim; no `AGENTS.md` + imports a file outside the working directory; and no hook depends on `InstructionsLoaded` for + the load. Unknown counts as failed and keeps the shim. Nested shims stay under every mode where + the memory page does not say when a subdirectory's `AGENTS.md` loads. Nothing is removed + automatically; the dated quotes are in `reference/sources.md`. ## [0.16.11] - 2026-10-01 diff --git a/plugins/instruction-placement/skills/migrate/SKILL.md b/plugins/instruction-placement/skills/migrate/SKILL.md index 42566717a5..ab4488cbb5 100644 --- a/plugins/instruction-placement/skills/migrate/SKILL.md +++ b/plugins/instruction-placement/skills/migrate/SKILL.md @@ -271,35 +271,13 @@ detector by design. ### Is the shim droppable here? Decide before `remove-shims` -`cutover-check` grades the build and the fleet. Whether this repository's users lose instructions -without the shim depends on the built-in `agents-md` loader, so recommend removal only when all four -hold. Report each as held, failed or unknown with its evidence; one failed or unknown keeps the -recommendation at "keep the shim" and names it. This recommends, never removes: `remove-shims` and -its `--confirm` gate stay the only path. - -| # | Holds when | Read it from | -|---|---|---| -| A | Nothing else takes precedence: no `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` at or above the working directory other than the shims going | `DIR` and `SUPPRESS` rows, a Glob for `.claude/CLAUDE.md` and `CLAUDE.local.md`, and the operator for the uncommitted `CLAUDE.local.md` contributors keep | -| B | **Project instructions** reads `AGENTS.md` with no `CLAUDE.md`: `claude-md-or-agents-md` (default) or `claude-md-and-agents-md` | `pluginConfigs["agents-md@builtin"].options.instructionFiles` in user, managed and any `--settings` file; absent everywhere is the default. Project and local settings are ignored for it. `claude-md` or `managed-only` fails | -| C | The loader is present and not disabled | `/claude-ops:inventory --bundled`, `builtin_plugins.cc-plugin-agents-md` (`in_loader`, `load`, `gated`, `gate_flags`), and no `enabledPlugins` entry set `false` in any scope for `agents-md@builtin` or the `id` the lane prints. Inventory absent, the lane `broken` or the entry missing is unknown | -| D | No session type without support matters to the repository's users | Ask the operator, listing: a CLI before v2.1.277; the plugin disabled in `/plugin`; in some cases the first session after upgrading from v2.1.276 or earlier; before v2.1.281, Amazon Bedrock and telemetry-disabled sessions; `--add-dir` directories under `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD`, whose `AGENTS.md` does not load. In those sessions **Project instructions** is missing from `/config` | - -B and C read this machine; the setting is per user and no repository can ship it, so the operator -answers D for everyone else's machines too. - -**Nested `AGENTS.md`, per mode**, as the memory page states it: - -- `claude-md-or-agents-md`: a subdirectory's `AGENTS.md` loads "when Claude opens a file there with - the Read tool and that subdirectory has none of the three `CLAUDE.md` files of its own". Nested - shims follow the root verdict and leave with it. -- `claude-md-and-agents-md`: "each directory's `CLAUDE.md` files first and its `AGENTS.md` after - them"; the page does not say when a subdirectory's file loads. A repository with nested - `AGENTS.md` keeps every shim under this mode, since `remove-shims` takes them together. -- `claude-md`, `managed-only`: no `AGENTS.md` loads. Under `managed-only` a subdirectory's - `CLAUDE.md` still loads on Read, and the page does not say whether its import expands. Keep them. - -Quotes, dates and triggers: [`reference/sources.md`](reference/sources.md), "The built-in -agents-md plugin". +`cutover-check` grades the build and the fleet, not whether this repository's users lose +instructions without the shim. Before offering `remove-shims`, read +[`reference/shim-droppable.md`](reference/shim-droppable.md) and grade its six conditions: +precedence, this machine's mode, the loader's state, the operator's answers for every other user +and surface, external `@` imports, and `InstructionsLoaded` dependents. It also gives the verdict for +nested `AGENTS.md` under each mode. **Unknown is failed**: any condition not shown to hold keeps the +recommendation at "keep the shim" and names it. This recommends, never removes. ### `remove-shims`, one repository per run @@ -374,7 +352,7 @@ directly at all. `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` in the working directory or above it, and attaches a subdirectory's `AGENTS.md` when a Read opens a file there and that subdirectory has none of those three names of its own. Reading it directly needs v2.1.277 or later; the sessions - that cannot are condition D's list above. A `CLAUDE.md` containing `@AGENTS.md` never makes + that cannot are listed in `reference/shim-droppable.md`, condition D. A `CLAUDE.md` containing `@AGENTS.md` never makes Claude read the file twice. - **Basis**: [memory](https://code.claude.com/docs/en/memory), fetched 2026-09-29 (49,601 bytes; slug in `llms.txt`; first heading "How Claude remembers your project"), sections "AGENTS.md", diff --git a/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md b/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md new file mode 100644 index 0000000000..6f4c913c5e --- /dev/null +++ b/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md @@ -0,0 +1,67 @@ +# Is the shim droppable here? + +`cutover-check` grades the build and the fleet. Whether this repository's users lose instructions +without the `@AGENTS.md` shim depends on the built-in `agents-md` loader and on settings no +repository can see, so removal is recommended only when **every** condition below holds. + +Report each condition as held, failed or unknown, with its evidence. **Unknown is failed**: one +failed or unknown condition keeps the recommendation at "keep the shim" and names the condition. +This recommends, never removes: `remove-shims` and its `--confirm` gate stay the only path. + +The quotes, dates and recheck triggers behind every condition are in [`sources.md`](sources.md), +"The built-in agents-md plugin". + +## The conditions + +| # | Holds when | Read it from | +|---|---|---| +| A | Nothing takes precedence over an `AGENTS.md`: no `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` at or above the working directory other than the shims going, **and** no directory holding a nested `AGENTS.md` keeps a `.claude/CLAUDE.md` or a non-shim `CLAUDE.md` or `CLAUDE.local.md` of its own | `plan-migration.sh` `DIR` and `SUPPRESS` rows, a Glob for `.claude/CLAUDE.md` and `CLAUDE.local.md` at every level, and the operator for the uncommitted `CLAUDE.local.md` files contributors keep | +| B | **Project instructions** on this machine reads `AGENTS.md` with no `CLAUDE.md`: `claude-md-or-agents-md` (the default) or `claude-md-and-agents-md` | `pluginConfigs["agents-md@builtin"].options.instructionFiles` in user, managed and any `--settings` file; absent everywhere is the default. Project and local settings are ignored for it, so never read them as the answer. `claude-md` or `managed-only` fails | +| C | The loader is present and not disabled on this machine | `/claude-ops:inventory --bundled`, `builtin_plugins.cc-plugin-agents-md` (`in_loader`, `load`, `gated`, `gate_flags`), and no `enabledPlugins` entry set `false` in any scope for `agents-md@builtin` or the `id` the lane prints. Inventory absent, the lane `broken`, or the entry missing is unknown | +| D | Every user, machine and organization the repository serves reads `AGENTS.md` directly | Ask the operator, with the list below. Any yes, and any "don't know", fails | +| E | No `AGENTS.md`, root or nested, has an `@` import of a file outside the working directory | Every `@path` in every `AGENTS.md`. A target outside the repository root, outside a subdirectory contributors start sessions in, or one that cannot be resolved fails | +| F | No hook depends on `InstructionsLoaded` reporting the `AGENTS.md` load, or the operator accepts losing that | Grep the repository's `.claude/settings*.json`, its hook scripts and its CI for `InstructionsLoaded`. A hit the operator has not accepted fails | + +B and C read this machine only. The setting is per user and no repository can ship it, so D is +where the operator answers for every other machine. + +## What D asks the operator + +Ask each, and record the answer beside the condition: + +1. **Does any user, machine or organization set Project instructions to `claude-md` or + `managed-only`?** The memory page says to keep the `@AGENTS.md` import "when you've set + **Project instructions** to `claude-md`"; under `managed-only` no `AGENTS.md` loads at all. +2. **Does anyone run a session the memory page lists as unable to read `AGENTS.md`?** In these + three, "Claude reads `CLAUDE.md` files only, and **Project instructions** doesn't appear in the + `/config` settings panel": + - a Claude Code version before v2.1.277; + - the built-in `agents-md` plugin disabled in `/plugin`; + - in some cases, the first session after upgrading from v2.1.276 or earlier. +3. **Does anyone run a CLI before v2.1.281 on Amazon Bedrock, Google Vertex AI, Microsoft + Foundry, an LLM gateway, or with telemetry disabled?** The memory page names Bedrock and + telemetry-disabled sessions; the 2.1.281 changelog entry names the full list as the sessions + that release extended support to. +4. **Does anyone add this repository with `--add-dir` while + `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD` is set?** There "Their `AGENTS.md` doesn't + load". +5. **Does anyone reach this repository through the Agent SDK, a cloud or web session, or + `claude-code-action`?** The memory page does not say whether those surfaces read `AGENTS.md` + directly. The CI canary in `sources.md` covers one `claude-code-action` pin and CLI, not every + one, so the operator confirms each surface in use or the shim stays. + +## Nested `AGENTS.md`, per mode + +What the memory page states, and the verdict that follows: + +- `claude-md-or-agents-md`: a subdirectory's `AGENTS.md` loads "when Claude opens a file there + with the Read tool and that subdirectory has none of the three `CLAUDE.md` files of its own". + Nested shims leave with the root only when condition A holds for every such subdirectory; + a subdirectory with its own `.claude/CLAUDE.md` or `CLAUDE.local.md` keeps its shim, and since + `remove-shims` takes root and nested together, the repository keeps all of them. +- `claude-md-and-agents-md`: "each directory's `CLAUDE.md` files first and its `AGENTS.md` after + them"; the page does not say when a subdirectory's file loads. A repository with nested + `AGENTS.md` keeps every shim under this mode. +- `claude-md`: no `AGENTS.md` loads. Keep every shim. +- `managed-only`: every `AGENTS.md` is left out; a subdirectory's `CLAUDE.md` still loads on Read, + and the page does not say whether its import expands. Keep every shim. diff --git a/plugins/instruction-placement/skills/migrate/reference/sources.md b/plugins/instruction-placement/skills/migrate/reference/sources.md index fd8f3df666..63121827c0 100644 --- a/plugins/instruction-placement/skills/migrate/reference/sources.md +++ b/plugins/instruction-placement/skills/migrate/reference/sources.md @@ -308,8 +308,8 @@ The 2026-09-20 measurement above was not repeated on this pass. ## The built-in agents-md plugin -The record behind the skill body's `## Boundary` section for `cc-plugin-agents-md` and its -droppability decision (conditions A to D and the nested-`AGENTS.md` verdicts). +The record behind the skill body's `## Boundary` section for `cc-plugin-agents-md` and +[`shim-droppable.md`](shim-droppable.md) (conditions A to F and the nested-`AGENTS.md` verdicts). - **Claim**: Claude Code ships a built-in `agents-md` plugin (ID `agents-md@builtin`) that reads `AGENTS.md` as the project instructions. Its **Project instructions** option @@ -335,7 +335,17 @@ droppability decision (conditions A to D and the nested-`AGENTS.md` verdicts). upgrade from v2.1.276 or earlier"; also "Before v2.1.281, some sessions, such as those on Amazon Bedrock or with telemetry disabled, read `CLAUDE.md` files only". The difference table adds that for "Directories you add with `--add-dir` while `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD` is - set", "Their `AGENTS.md` doesn't load". The removal procedure for an `@AGENTS.md` shim: "Remove + set", "Their `AGENTS.md` doesn't load"; for "An `@path` import of a file outside your working + directory" (condition E), an `AGENTS.md` read through the setting "Loads only if you already + approved external imports for this project, with no prompt"; and for `InstructionsLoaded` hooks + (condition F), "Don't fire. They fire as usual for an `AGENTS.md` that a `CLAUDE.md` imports or + symlinks to". Condition D's first question: "Share one file with other coding tools" says to put + the `@AGENTS.md` import in a `CLAUDE.md` "when your project also has a `CLAUDE.md`, when you've + set **Project instructions** to `claude-md`, or in sessions that can't load `AGENTS.md`". The + `/config` sentence quoted above introduces exactly the three bullets that follow it; the + pre-2.1.281 sentence sits outside that list, so the skill does not attach the `/config` signal to + it. The page does not mention the Agent SDK, cloud or web sessions, or `claude-code-action`, so + condition D asks the operator about each rather than inferring coverage. The removal procedure for an `@AGENTS.md` shim: "Remove the `CLAUDE.md` if it holds nothing else, or keep it if some of your sessions can't load `AGENTS.md` directly." The plugin loads files; nothing upstream says it moves content or writes a shim, and no page states how a disabled built-in plugin is recorded in settings, which is why @@ -345,8 +355,8 @@ droppability decision (conditions A to D and the nested-`AGENTS.md` verdicts). sections "When Claude Code reads AGENTS.md" (line 363), "Choose which instruction files load" (line 381; "Add it under the built-in `agents-md` plugin's ID in `pluginConfigs`", with the example key `"agents-md@builtin"`), "When AGENTS.md support is unavailable" (line 406), "Where - AGENTS.md differs from CLAUDE.md" (line 416) and "Remove an earlier AGENTS.md workaround" (line - 426). [settings-reference](https://code.claude.com/docs/en/settings-reference), fetched + AGENTS.md differs from CLAUDE.md" (line 416), "Remove an earlier AGENTS.md workaround" (line + 426) and "Share one file with other coding tools" (line 435). [settings-reference](https://code.claude.com/docs/en/settings-reference), fetched 2026-10-01, `pluginConfigs`: "Built-in plugins store their options under the same key with an `@builtin` suffix". The [changelog](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md) entry for 2.1.277 @@ -366,7 +376,9 @@ droppability decision (conditions A to D and the nested-`AGENTS.md` verdicts). - **As of**: 2026-10-01, Claude Code 2.1.287. - **Recheck trigger**: the memory page changes the "When Claude Code reads AGENTS.md" list, the "Choose which instruction files load" table, the "When AGENTS.md support is unavailable" list, - the difference table or the shim bullet under "Remove an earlier AGENTS.md workaround"; it comes + the difference table, the "Share one file with other coding tools" conditions, or the shim + bullet under "Remove an earlier AGENTS.md workaround"; it comes to name the Agent SDK, cloud or + web sessions, or `claude-code-action`; it comes to state when a subdirectory's `AGENTS.md` loads under `claude-md-and-agents-md` or whether an import expands under `managed-only`; settings-reference documents how a built-in plugin is disabled; or a changelog entry names `AGENTS.md`, `instructionFiles` or the `agents-md` plugin. From 17d7a17299fa0910c01dce0b92bfe6c13cdf31b0 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Thu, 1 Oct 2026 22:32:01 -0400 Subject: [PATCH 3/6] fix(instruction-placement): walk every ancestor and every hook source before dropping a shim - Condition A walks every directory from the session start directory to the filesystem root for CLAUDE.md, .claude/CLAUDE.md and CLAUDE.local.md; only ~/.claude/CLAUDE.md is exempt, and an unreadable directory fails. - Condition F inventories InstructionsLoaded hooks in user, managed and --settings files and installed plugins, not only the repository; a source nobody can answer for fails. - The memory page does not say when a subdirectory AGENTS.md loads under claude-md-and-agents-md, so the skill body and the render-index.sh comment no longer call an UNWIRED row a false positive there. Co-Authored-By: Claude Opus 5.5 --- plugins/instruction-placement/CHANGELOG.md | 11 +++++++- .../scripts/render-index.sh | 11 ++++---- .../skills/migrate/SKILL.md | 10 ++++--- .../migrate/reference/shim-droppable.md | 27 +++++++++++++++++-- .../skills/migrate/reference/sources.md | 8 +++--- 5 files changed, 52 insertions(+), 15 deletions(-) diff --git a/plugins/instruction-placement/CHANGELOG.md b/plugins/instruction-placement/CHANGELOG.md index 3ff739b7dd..c6434dc37e 100644 --- a/plugins/instruction-placement/CHANGELOG.md +++ b/plugins/instruction-placement/CHANGELOG.md @@ -18,7 +18,16 @@ All notable changes to the `instruction-placement` plugin are documented here. F imports a file outside the working directory; and no hook depends on `InstructionsLoaded` for the load. Unknown counts as failed and keeps the shim. Nested shims stay under every mode where the memory page does not say when a subdirectory's `AGENTS.md` loads. Nothing is removed - automatically; the dated quotes are in `reference/sources.md`. + automatically; the dated quotes are in `reference/sources.md`. Precedence is checked by walking + every ancestor directory to the filesystem root, and the `InstructionsLoaded` check covers user, + managed, `--settings` and installed-plugin hooks, not only the repository's. + +### Fixed + +- **An unimported nested `AGENTS.md` is no longer said to load under + `claude-md-and-agents-md`.** The memory page does not say when a subdirectory's `AGENTS.md` + loads under that value, so `UNWIRED` stays a finding there; the migrate skill body and the + `render-index.sh` comment now say so. ## [0.16.11] - 2026-10-01 diff --git a/plugins/instruction-placement/scripts/render-index.sh b/plugins/instruction-placement/scripts/render-index.sh index 5d169a03ea..9ef4d19034 100755 --- a/plugins/instruction-placement/scripts/render-index.sh +++ b/plugins/instruction-placement/scripts/render-index.sh @@ -453,14 +453,15 @@ fi # way loads and is not a finding. Entry points are read from the filesystem, so # a gitignored CLAUDE.local.md shim counts. # -# An UNWIRED row is a finding under the DEFAULT instruction-files mode. Under -# the user-settings option `claude-md-and-agents-md` both files load, "each -# directory's `CLAUDE.md` files first and its `AGENTS.md` after them", so an -# unimported nested AGENTS.md does load there and the row is a false positive. +# An UNWIRED row is a finding under the DEFAULT instruction-files mode, and +# under the user-settings option `claude-md-and-agents-md` too: that option +# reads both files, "each directory's `CLAUDE.md` files first and its +# `AGENTS.md` after them", but the page does not say when a subdirectory's +# AGENTS.md loads under it, so the row is not treated as a false positive. # The import stays harmless either way: "Claude Code skips an `AGENTS.md` it has # already loaded, so one that your `CLAUDE.md` imports or symlinks to isn't read # twice" (code.claude.com/docs/en/memory, "Choose which instruction files load"; -# fetched 2026-09-19; recheck when that table changes). The setting is a user, +# fetched 2026-10-01; recheck when that table changes). The setting is a user, # `--settings` or managed one, which no repository can ship, so the gate keeps # the default's answer. # diff --git a/plugins/instruction-placement/skills/migrate/SKILL.md b/plugins/instruction-placement/skills/migrate/SKILL.md index ab4488cbb5..89abd16924 100644 --- a/plugins/instruction-placement/skills/migrate/SKILL.md +++ b/plugins/instruction-placement/skills/migrate/SKILL.md @@ -381,14 +381,16 @@ detects a load through the `InstructionsLoaded` hook, so it measures a **shimmed cannot see an `AGENTS.md` that Claude reads directly. **One setting changes the reading, and no repository can ship it.** Under `instructionFiles: -claude-md-and-agents-md`, Claude Code loads both files, "each directory's `CLAUDE.md` files first -and its `AGENTS.md` after them", so an unimported nested `AGENTS.md` does load and an `UNWIRED` row -is a false positive for that operator. The import stays harmless there: "Claude Code skips an +claude-md-and-agents-md`, Claude Code reads "Your `CLAUDE.md` and `AGENTS.md` files together, each +directory's `CLAUDE.md` files first and its `AGENTS.md` after them". The page does not say when a +subdirectory's `AGENTS.md` loads under that value, so an `UNWIRED` row stays a finding there too +and the nested shim stays. The import is harmless there: "Claude Code skips an `AGENTS.md` it has already loaded, so one that your `CLAUDE.md` imports or symlinks to isn't read twice". The value is a user, `--settings` or managed setting, ignored in project and local settings, so a repository cannot rely on it and the gates keep the default's answer ([memory](https://code.claude.com/docs/en/memory), "Choose which instruction files load"; fetched -2026-09-28, quotes unchanged; recheck when that table changes or a release note names the setting). +2026-10-01; recheck when that table changes, the page comes to state when a subdirectory's +`AGENTS.md` loads under that value, or a release note names the setting). ## Boundary, the built-in `cc-plugin-agents-md` plugin diff --git a/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md b/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md index 6f4c913c5e..8d6af5f4a2 100644 --- a/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md +++ b/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md @@ -15,16 +15,39 @@ The quotes, dates and recheck triggers behind every condition are in [`sources.m | # | Holds when | Read it from | |---|---|---| -| A | Nothing takes precedence over an `AGENTS.md`: no `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` at or above the working directory other than the shims going, **and** no directory holding a nested `AGENTS.md` keeps a `.claude/CLAUDE.md` or a non-shim `CLAUDE.md` or `CLAUDE.local.md` of its own | `plan-migration.sh` `DIR` and `SUPPRESS` rows, a Glob for `.claude/CLAUDE.md` and `CLAUDE.local.md` at every level, and the operator for the uncommitted `CLAUDE.local.md` files contributors keep | +| A | Nothing takes precedence over an `AGENTS.md`: no `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` at or above the working directory other than the shims going, **and** no directory holding a nested `AGENTS.md` keeps a `.claude/CLAUDE.md` or a non-shim `CLAUDE.md` or `CLAUDE.local.md` of its own | The ancestor walk below, run from each directory contributors start sessions in; a Glob for the three names at every level inside the repository, tracked or not; and the operator for the other machines, whose ancestors (a `~/work/CLAUDE.md`) and uncommitted `CLAUDE.local.md` files this machine cannot see | | B | **Project instructions** on this machine reads `AGENTS.md` with no `CLAUDE.md`: `claude-md-or-agents-md` (the default) or `claude-md-and-agents-md` | `pluginConfigs["agents-md@builtin"].options.instructionFiles` in user, managed and any `--settings` file; absent everywhere is the default. Project and local settings are ignored for it, so never read them as the answer. `claude-md` or `managed-only` fails | | C | The loader is present and not disabled on this machine | `/claude-ops:inventory --bundled`, `builtin_plugins.cc-plugin-agents-md` (`in_loader`, `load`, `gated`, `gate_flags`), and no `enabledPlugins` entry set `false` in any scope for `agents-md@builtin` or the `id` the lane prints. Inventory absent, the lane `broken`, or the entry missing is unknown | | D | Every user, machine and organization the repository serves reads `AGENTS.md` directly | Ask the operator, with the list below. Any yes, and any "don't know", fails | | E | No `AGENTS.md`, root or nested, has an `@` import of a file outside the working directory | Every `@path` in every `AGENTS.md`. A target outside the repository root, outside a subdirectory contributors start sessions in, or one that cannot be resolved fails | -| F | No hook depends on `InstructionsLoaded` reporting the `AGENTS.md` load, or the operator accepts losing that | Grep the repository's `.claude/settings*.json`, its hook scripts and its CI for `InstructionsLoaded`. A hit the operator has not accepted fails | +| F | No hook depends on `InstructionsLoaded` reporting the `AGENTS.md` load, or the operator accepts losing that | Every hook source, inside the repository and out: grep for `InstructionsLoaded` in the repository's `.claude/settings*.json`, hook scripts and CI; `~/.claude/settings.json` and `~/.claude/settings.local.json`; the managed settings file and any `managed-settings.d/` beside it; every `--settings` file contributors pass; and each installed plugin's `hooks/hooks.json` and `plugin.json` `hooks` under `~/.claude/plugins/`. A source that cannot be read here, and every other machine, is the operator's to answer. A hit the operator has not accepted, or a source nobody can answer for, fails | B and C read this machine only. The setting is per user and no repository can ship it, so D is where the operator answers for every other machine. +## The ancestor walk for condition A + +Every directory from the working directory up to the filesystem root, not only the repository's +tracked files and `~/`: an ancestor such as `~/work/CLAUDE.md` is read instead of the `AGENTS.md` +below it. From each session start directory: + +```bash +d=$(cd && pwd -P) +while :; do + { [ -r "$d" ] && [ -x "$d" ]; } || printf 'UNREADABLE\t%s\n' "$d" + for f in CLAUDE.md .claude/CLAUDE.md CLAUDE.local.md; do + [ -e "$d/$f" ] && printf 'FOUND\t%s\n' "$d/$f" + done + [ "$d" = / ] && break + d=$(dirname "$d") +done +``` + +On Windows, walk to the drive root (`C:\`) the same way. Every `FOUND` row that is not one of the +shims `remove-shims` would remove fails A. `~/.claude/CLAUDE.md` is not on the list: the memory page +says it does not count, so its `FOUND` row is the one exemption. An `UNREADABLE` row is unknown, +and fails A. + ## What D asks the operator Ask each, and record the answer beside the condition: diff --git a/plugins/instruction-placement/skills/migrate/reference/sources.md b/plugins/instruction-placement/skills/migrate/reference/sources.md index 63121827c0..f40dca46dc 100644 --- a/plugins/instruction-placement/skills/migrate/reference/sources.md +++ b/plugins/instruction-placement/skills/migrate/reference/sources.md @@ -324,9 +324,11 @@ The record behind the skill body's `## Boundary` section for `cc-plugin-agents-m a `--settings` file, or managed settings. Claude Code ignores it in project and local settings files." Condition A: the files that "Count, so Claude reads them instead of `AGENTS.md`" are "a `CLAUDE.md`, `.claude/CLAUDE.md`, or `CLAUDE.local.md` in your working directory or any - directory above it". Nested files under the default: "a subdirectory's `AGENTS.md`, when Claude - opens a file there with the Read tool and that subdirectory has none of the three `CLAUDE.md` - files of its own". The page states no subdirectory trigger for `claude-md-and-agents-md`, and + directory above it", the walk to the filesystem root, while "Don't count, and keep loading + alongside `AGENTS.md`: your `~/.claude/CLAUDE.md`, your organization's managed `CLAUDE.md`, and + `.claude/rules/` files", which is the walk's one exemption. Nested files under the default: "a + subdirectory's `AGENTS.md`, when Claude opens a file there with the Read tool and that + subdirectory has none of the three `CLAUDE.md` files of its own". The page states no subdirectory trigger for `claude-md-and-agents-md`, and does not say whether a subdirectory `CLAUDE.md`'s `@AGENTS.md` import expands under `managed-only`; the skill keeps the shims in both cases for that reason. Condition D: "In these sessions Claude reads `CLAUDE.md` files only, and **Project instructions** doesn't appear in the From 7fc0a891aa770c9a0e87c4212da12e64ff2ac714 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Thu, 1 Oct 2026 23:10:53 -0400 Subject: [PATCH 4/6] fix(instruction-placement): walk the full import graph and per-session plugins before dropping a shim - Condition E walks the whole @ import graph from every AGENTS.md with the plugin's own discover.sh model (_ip_imports_of, four-hop bound); an external, unresolvable or past-the-limit import fails E. - Condition F also asks about plugins loaded with --plugin-dir, --plugin-url or CLAUDE_CODE_PLUGIN_DIRS, and greps those plus ~/.claude/skills plugins for InstructionsLoaded hooks. - D closes with a catch-all question: any other instruction-loading path or InstructionsLoaded consumer, or "don't know", keeps the shim. Co-Authored-By: Claude Opus 5.5 --- plugins/instruction-placement/CHANGELOG.md | 5 ++- .../migrate/reference/shim-droppable.md | 37 ++++++++++++++++++- .../skills/migrate/reference/sources.md | 18 ++++++++- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/plugins/instruction-placement/CHANGELOG.md b/plugins/instruction-placement/CHANGELOG.md index 538a9df375..cc19f981af 100644 --- a/plugins/instruction-placement/CHANGELOG.md +++ b/plugins/instruction-placement/CHANGELOG.md @@ -20,7 +20,10 @@ All notable changes to the `instruction-placement` plugin are documented here. F the memory page does not say when a subdirectory's `AGENTS.md` loads. Nothing is removed automatically; the dated quotes are in `reference/sources.md`. Precedence is checked by walking every ancestor directory to the filesystem root, and the `InstructionsLoaded` check covers user, - managed, `--settings` and installed-plugin hooks, not only the repository's. + managed, `--settings` and installed-plugin hooks, not only the repository's, plus plugins loaded + with `--plugin-dir`, `--plugin-url`, `CLAUDE_CODE_PLUGIN_DIRS` or from `~/.claude/skills/`. The + external-import check walks the whole `@` import graph to the four-hop limit with the plugin's + own `discover.sh` model, and a closing operator question catches any loading path not listed. ### Fixed diff --git a/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md b/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md index 20891b1d13..b8cca6a515 100644 --- a/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md +++ b/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md @@ -19,8 +19,8 @@ The quotes, dates and recheck triggers behind every condition are in [`sources.m | B | **Project instructions** on this machine reads `AGENTS.md` with no `CLAUDE.md`: `claude-md-or-agents-md` (the default) or `claude-md-and-agents-md` | `pluginConfigs["agents-md@builtin"].options.instructionFiles` in user, managed and any `--settings` file; absent everywhere is the default. Project and local settings are ignored for it, so never read them as the answer. `claude-md` or `managed-only` fails | | C | The loader is present and not disabled on this machine | `/harness-ops:inventory --bundled`, `builtin_plugins.cc-plugin-agents-md` (`in_loader`, `load`, `gated`, `gate_flags`), and no `enabledPlugins` entry set `false` in any scope for `agents-md@builtin` or the `id` the lane prints. Inventory absent, the lane `broken`, or the entry missing is unknown | | D | Every user, machine and organization the repository serves reads `AGENTS.md` directly | Ask the operator, with the list below. Any yes, and any "don't know", fails | -| E | No `AGENTS.md`, root or nested, has an `@` import of a file outside the working directory | Every `@path` in every `AGENTS.md`. A target outside the repository root, outside a subdirectory contributors start sessions in, or one that cannot be resolved fails | -| F | No hook depends on `InstructionsLoaded` reporting the `AGENTS.md` load, or the operator accepts losing that | Every hook source, inside the repository and out: grep for `InstructionsLoaded` in the repository's `.claude/settings*.json`, hook scripts and CI; `~/.claude/settings.json` and `~/.claude/settings.local.json`; the managed settings file and any `managed-settings.d/` beside it; every `--settings` file contributors pass; and each installed plugin's `hooks/hooks.json` and `plugin.json` `hooks` under `~/.claude/plugins/`. A source that cannot be read here, and every other machine, is the operator's to answer. A hit the operator has not accepted, or a source nobody can answer for, fails | +| E | Nothing reachable through the `@` import graph of any `AGENTS.md`, root or nested, lies outside the working directory | The import-graph walk below, from every `AGENTS.md` and from each session start directory. Any `EXTERNAL`, `UNRESOLVED` or `DEPTH` row fails | +| F | No hook depends on `InstructionsLoaded` reporting the `AGENTS.md` load, or the operator accepts losing that | Every hook source, inside the repository and out: grep for `InstructionsLoaded` in the repository's `.claude/settings*.json`, hook scripts and CI; `~/.claude/settings.json` and `~/.claude/settings.local.json`; the managed settings file and any `managed-settings.d/` beside it; every `--settings` file contributors pass; each installed plugin's `hooks/hooks.json` and `plugin.json` `hooks` under `~/.claude/plugins/`; and the plugins loaded per session or outside an install: ask the operator whether contributors use `--plugin-dir`, `--plugin-url` or `CLAUDE_CODE_PLUGIN_DIRS`, and grep each directory or archive they name, plus every `~/.claude/skills/*/` that holds a `.claude-plugin/plugin.json`. A source that cannot be read here, and every other machine, is the operator's to answer. A hit the operator has not accepted, or a source nobody can answer for, fails | B and C read this machine only. The setting is per user and no repository can ship it, so D is where the operator answers for every other machine. @@ -48,6 +48,35 @@ shims `remove-shims` would remove fails A. `~/.claude/CLAUDE.md` is not on the l says it does not count, so its `FOUND` row is the one exemption. An `UNREADABLE` row is unknown, and fails A. +## The import-graph walk for condition E + +The walk reuses this plugin's own import model in `/scripts/lib/discover.sh`, the +one `ip_index_target_loaded` runs on: `_ip_imports_of` parses a file's `@` imports (fenced code and +inline code spans skipped, `~/` and relative targets resolved) and `_ip_reaches` bounds the walk at +four hops, the loader's limit. Run it once per session start directory, over every `AGENTS.md` +in the repository: + +```bash +. /scripts/lib/discover.sh +base=$(cd && pwd -P) +e_walk() { # ; its imports are hop + 1, and the loader follows hops 1 to 4 + local f="$1" hop="$2" imp real + while IFS= read -r imp; do + [ -n "$imp" ] || continue + if [ "$hop" -ge 4 ]; then printf 'DEPTH\t%s\t%s\n' "$f" "$imp"; continue; fi + real=$(ip_realpath "$imp") + if [ ! -f "$real" ]; then printf 'UNRESOLVED\t%s\t%s\n' "$f" "$imp"; continue; fi + case "$real" in "$base"/*) ;; *) printf 'EXTERNAL\t%s\t%s\n' "$f" "$real"; continue ;; esac + e_walk "$real" $((hop + 1)) + done < <(_ip_imports_of "$f") +} +for a in ; do e_walk "$(ip_realpath "$a")" 0; done +``` + +`EXTERNAL` is a reachable target outside the start directory, which loads only where external +imports were already approved. `UNRESOLVED` is a target that is not a file. `DEPTH` is an import +past the fourth hop, which the loader drops, so the chain is not what it reads. Each fails E. + ## What D asks the operator Ask each, and record the answer beside the condition: @@ -72,6 +101,10 @@ Ask each, and record the answer beside the condition: `claude-code-action`?** The memory page does not say whether those surfaces read `AGENTS.md` directly. The CI canary in `sources.md` covers one `claude-code-action` pin and CLI, not every one, so the operator confirms each surface in use or the shim stays. +6. **Is there any other way instructions reach Claude for this repository, or anything else that + consumes `InstructionsLoaded`, beyond what conditions A to F and questions 1 to 5 cover?** This + question catches every case not listed here, so a gap found later keeps the shim without a + change to this file. "Don't know" fails D. ## Nested `AGENTS.md`, per mode diff --git a/plugins/instruction-placement/skills/migrate/reference/sources.md b/plugins/instruction-placement/skills/migrate/reference/sources.md index 1dfa137b53..916e2560e2 100644 --- a/plugins/instruction-placement/skills/migrate/reference/sources.md +++ b/plugins/instruction-placement/skills/migrate/reference/sources.md @@ -347,7 +347,20 @@ The record behind the skill body's `## Boundary` section for `cc-plugin-agents-m `/config` sentence quoted above introduces exactly the three bullets that follow it; the pre-2.1.281 sentence sits outside that list, so the skill does not attach the `/config` signal to it. The page does not mention the Agent SDK, cloud or web sessions, or `claude-code-action`, so - condition D asks the operator about each rather than inferring coverage. The removal procedure for an `@AGENTS.md` shim: "Remove + condition D asks the operator about each rather than inferring coverage. Condition E's bound: + "Imported files can recursively import other files, with a maximum depth of four hops" (line + 104), the same limit `scripts/lib/discover.sh` `_ip_reaches` encodes. Condition F's per-session + plugins, from [plugins/create](https://code.claude.com/docs/en/plugins/create) "Develop without + a marketplace" (fetched 2026-10-01, 25,706 bytes, first heading "Create a Claude Code plugin", + slug in `llms.txt`; the `plugins` page, which has no "Test your plugins locally" heading on this + date, sends `--plugin-dir` readers there): "You can + load a plugin for a single session in three ways: from a directory or `.zip` archive on disk + with `--plugin-dir`, from a URL with `--plugin-url`, or from an environment variable", namely + `CLAUDE_CODE_PLUGIN_DIRS`, and "Each plugin loads for that session only, and nothing is written + to your settings for it", which is why no settings read finds them; and "Claude Code loads any + folder there [`~/.claude/skills/`] that contains a `.claude-plugin/plugin.json` as a plugin in + every session, with no flag and no install step". The removal procedure for an `@AGENTS.md` + shim: "Remove the `CLAUDE.md` if it holds nothing else, or keep it if some of your sessions can't load `AGENTS.md` directly." The plugin loads files; nothing upstream says it moves content or writes a shim, and no page states how a disabled built-in plugin is recorded in settings, which is why @@ -380,7 +393,8 @@ The record behind the skill body's `## Boundary` section for `cc-plugin-agents-m "Choose which instruction files load" table, the "When AGENTS.md support is unavailable" list, the difference table, the "Share one file with other coding tools" conditions, or the shim bullet under "Remove an earlier AGENTS.md workaround"; it comes to name the Agent SDK, cloud or - web sessions, or `claude-code-action`; it comes + web sessions, or `claude-code-action`; it changes the four-hop import limit; plugins/create + changes the ways a plugin loads for one session or without an install; it comes to state when a subdirectory's `AGENTS.md` loads under `claude-md-and-agents-md` or whether an import expands under `managed-only`; settings-reference documents how a built-in plugin is disabled; or a changelog entry names `AGENTS.md`, `instructionFiles` or the `agents-md` plugin. From efc6ea669894e0e44e00ac454aa49d2356fc66bd Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Thu, 1 Oct 2026 23:27:44 -0400 Subject: [PATCH 5/6] fix(instruction-placement): resolve the user config and plugins roots before reading them The shim-droppable rule read user settings and plugins at a fixed ~/.claude. It now resolves the config root as ${CLAUDE_CONFIG_DIR:-$HOME/.claude} and the plugins root as CLAUDE_CODE_PLUGIN_CACHE_DIR or /plugins, the expression the performance verify skill uses, for conditions B, C and F. The user CLAUDE.md exemption in the ancestor walk holds only at the default root, and D asks whether contributors set either variable; "don't know" fails. Co-Authored-By: Claude Opus 5.5 --- plugins/instruction-placement/CHANGELOG.md | 3 ++ .../migrate/reference/shim-droppable.md | 34 ++++++++++++++----- .../skills/migrate/reference/sources.md | 17 ++++++++-- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/plugins/instruction-placement/CHANGELOG.md b/plugins/instruction-placement/CHANGELOG.md index cc19f981af..68dbfe8e53 100644 --- a/plugins/instruction-placement/CHANGELOG.md +++ b/plugins/instruction-placement/CHANGELOG.md @@ -24,6 +24,9 @@ All notable changes to the `instruction-placement` plugin are documented here. F with `--plugin-dir`, `--plugin-url`, `CLAUDE_CODE_PLUGIN_DIRS` or from `~/.claude/skills/`. The external-import check walks the whole `@` import graph to the four-hop limit with the plugin's own `discover.sh` model, and a closing operator question catches any loading path not listed. + User settings and plugins are read under `${CLAUDE_CONFIG_DIR:-$HOME/.claude}` and + `CLAUDE_CODE_PLUGIN_CACHE_DIR` rather than a fixed `~/.claude`, and the operator is asked whether + contributors set either. ### Fixed diff --git a/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md b/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md index b8cca6a515..52f95b0313 100644 --- a/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md +++ b/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md @@ -11,16 +11,29 @@ This recommends, never removes: `remove-shims` and its `--confirm` gate stay the The quotes, dates and recheck triggers behind every condition are in [`sources.md`](sources.md), "The built-in agents-md plugin". +## The user roots + +Never read user settings or plugins at a hard-coded `~/.claude`. Resolve the two roots first, with +the expression the performance plugin's verify skill already uses: + +- **Config root** ``: `${CLAUDE_CONFIG_DIR:-$HOME/.claude}`. User settings are + `/settings.json` and `/settings.local.json`; personal-skill plugins are + `/skills/*/`. +- **Plugins root** ``: `CLAUDE_CODE_PLUGIN_CACHE_DIR` when set, else `/plugins`. + +`CLAUDE_CONFIG_DIR` can come from the shell, user settings or managed settings `env`, so check all +three. A root that cannot be resolved here is unknown, and fails every condition that reads it. + ## The conditions | # | Holds when | Read it from | |---|---|---| | A | Nothing takes precedence over an `AGENTS.md`: no `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` at or above the working directory other than the shims going, **and** no directory holding a nested `AGENTS.md` keeps a `.claude/CLAUDE.md` or a non-shim `CLAUDE.md` or `CLAUDE.local.md` of its own | The ancestor walk below, run from each directory contributors start sessions in; a Glob for the three names at every level inside the repository, tracked or not; and the operator for the other machines, whose ancestors (a `~/work/CLAUDE.md`) and uncommitted `CLAUDE.local.md` files this machine cannot see | -| B | **Project instructions** on this machine reads `AGENTS.md` with no `CLAUDE.md`: `claude-md-or-agents-md` (the default) or `claude-md-and-agents-md` | `pluginConfigs["agents-md@builtin"].options.instructionFiles` in user, managed and any `--settings` file; absent everywhere is the default. Project and local settings are ignored for it, so never read them as the answer. `claude-md` or `managed-only` fails | -| C | The loader is present and not disabled on this machine | `/harness-ops:inventory --bundled`, `builtin_plugins.cc-plugin-agents-md` (`in_loader`, `load`, `gated`, `gate_flags`), and no `enabledPlugins` entry set `false` in any scope for `agents-md@builtin` or the `id` the lane prints. Inventory absent, the lane `broken`, or the entry missing is unknown | +| B | **Project instructions** on this machine reads `AGENTS.md` with no `CLAUDE.md`: `claude-md-or-agents-md` (the default) or `claude-md-and-agents-md` | `pluginConfigs["agents-md@builtin"].options.instructionFiles` in `/settings.json`, managed settings and any `--settings` file; absent everywhere is the default. Project and local settings are ignored for it, so never read them as the answer. `claude-md` or `managed-only` fails | +| C | The loader is present and not disabled on this machine | `/harness-ops:inventory --bundled`, `builtin_plugins.cc-plugin-agents-md` (`in_loader`, `load`, `gated`, `gate_flags`), and no `enabledPlugins` entry set `false` in any scope (`` user settings, managed, project, local, `--settings`) for `agents-md@builtin` or the `id` the lane prints. Inventory absent, the lane `broken`, or the entry missing is unknown | | D | Every user, machine and organization the repository serves reads `AGENTS.md` directly | Ask the operator, with the list below. Any yes, and any "don't know", fails | | E | Nothing reachable through the `@` import graph of any `AGENTS.md`, root or nested, lies outside the working directory | The import-graph walk below, from every `AGENTS.md` and from each session start directory. Any `EXTERNAL`, `UNRESOLVED` or `DEPTH` row fails | -| F | No hook depends on `InstructionsLoaded` reporting the `AGENTS.md` load, or the operator accepts losing that | Every hook source, inside the repository and out: grep for `InstructionsLoaded` in the repository's `.claude/settings*.json`, hook scripts and CI; `~/.claude/settings.json` and `~/.claude/settings.local.json`; the managed settings file and any `managed-settings.d/` beside it; every `--settings` file contributors pass; each installed plugin's `hooks/hooks.json` and `plugin.json` `hooks` under `~/.claude/plugins/`; and the plugins loaded per session or outside an install: ask the operator whether contributors use `--plugin-dir`, `--plugin-url` or `CLAUDE_CODE_PLUGIN_DIRS`, and grep each directory or archive they name, plus every `~/.claude/skills/*/` that holds a `.claude-plugin/plugin.json`. A source that cannot be read here, and every other machine, is the operator's to answer. A hit the operator has not accepted, or a source nobody can answer for, fails | +| F | No hook depends on `InstructionsLoaded` reporting the `AGENTS.md` load, or the operator accepts losing that | Every hook source, inside the repository and out: grep for `InstructionsLoaded` in the repository's `.claude/settings*.json`, hook scripts and CI; `/settings.json` and `/settings.local.json`; the managed settings file and any `managed-settings.d/` beside it; every `--settings` file contributors pass; each installed plugin's `hooks/hooks.json` and `plugin.json` `hooks` under ``; and the plugins loaded per session or outside an install: ask the operator whether contributors use `--plugin-dir`, `--plugin-url` or `CLAUDE_CODE_PLUGIN_DIRS`, and grep each directory or archive they name, plus every `/skills/*/` that holds a `.claude-plugin/plugin.json`. A source that cannot be read here, and every other machine, is the operator's to answer. A hit the operator has not accepted, or a source nobody can answer for, fails | B and C read this machine only. The setting is per user and no repository can ship it, so D is where the operator answers for every other machine. @@ -44,9 +57,11 @@ done ``` On Windows, walk to the drive root (`C:\`) the same way. Every `FOUND` row that is not one of the -shims `remove-shims` would remove fails A. `~/.claude/CLAUDE.md` is not on the list: the memory page -says it does not count, so its `FOUND` row is the one exemption. An `UNREADABLE` row is unknown, -and fails A. +shims `remove-shims` would remove fails A. The user `CLAUDE.md` is not on the list: the memory page +says "your `~/.claude/CLAUDE.md`" does not count, so a `FOUND $HOME/.claude/CLAUDE.md` row is the +one exemption, and only while `` resolves to `$HOME/.claude`. With `CLAUDE_CONFIG_DIR` +pointing elsewhere, the page does not say whether that file still counts, so the row fails A. An +`UNREADABLE` row is unknown, and fails A. ## The import-graph walk for condition E @@ -101,8 +116,11 @@ Ask each, and record the answer beside the condition: `claude-code-action`?** The memory page does not say whether those surfaces read `AGENTS.md` directly. The CI canary in `sources.md` covers one `claude-code-action` pin and CLI, not every one, so the operator confirms each surface in use or the shim stays. -6. **Is there any other way instructions reach Claude for this repository, or anything else that - consumes `InstructionsLoaded`, beyond what conditions A to F and questions 1 to 5 cover?** This +6. **Does anyone set `CLAUDE_CONFIG_DIR` or `CLAUDE_CODE_PLUGIN_CACHE_DIR`, and to what?** Each + moves the user settings or the plugins this rule reads, so conditions B, C and F hold for that + contributor only when their roots are named and read. "Don't know" fails D. +7. **Is there any other way instructions reach Claude for this repository, or anything else that + consumes `InstructionsLoaded`, beyond what conditions A to F and questions 1 to 6 cover?** This question catches every case not listed here, so a gap found later keeps the shim without a change to this file. "Don't know" fails D. diff --git a/plugins/instruction-placement/skills/migrate/reference/sources.md b/plugins/instruction-placement/skills/migrate/reference/sources.md index 916e2560e2..1bf7336041 100644 --- a/plugins/instruction-placement/skills/migrate/reference/sources.md +++ b/plugins/instruction-placement/skills/migrate/reference/sources.md @@ -359,7 +359,18 @@ The record behind the skill body's `## Boundary` section for `cc-plugin-agents-m `CLAUDE_CODE_PLUGIN_DIRS`, and "Each plugin loads for that session only, and nothing is written to your settings for it", which is why no settings read finds them; and "Claude Code loads any folder there [`~/.claude/skills/`] that contains a `.claude-plugin/plugin.json` as a plugin in - every session, with no flag and no install step". The removal procedure for an `@AGENTS.md` + every session, with no flag and no install step". The user roots, from + [env-vars](https://code.claude.com/docs/en/env-vars) (fetched 2026-10-01, 158,869 bytes): + `CLAUDE_CONFIG_DIR` "Override the configuration directory (default: `~/.claude`). All settings, + session history, and plugins are stored under this path. ... Set it in your shell, user + settings, or managed settings. Ignored in project and local settings", and + `CLAUDE_CODE_PLUGIN_CACHE_DIR` "Override the plugins root directory ... Defaults to + `~/.claude/plugins`"; [plugins/loading](https://code.claude.com/docs/en/plugins/loading) + (fetched 2026-10-01, 37,348 bytes) says the same root "is `~/.claude/plugins` unless you set + `CLAUDE_CODE_PLUGIN_CACHE_DIR`". The resolution expression is the one + `plugins/performance/skills/verify/SKILL.md` uses. The memory page names the exempt user file + only as "your `~/.claude/CLAUDE.md`" and does not say how `CLAUDE_CONFIG_DIR` changes that, so + the walk exempts it only at the default root. The removal procedure for an `@AGENTS.md` shim: "Remove the `CLAUDE.md` if it holds nothing else, or keep it if some of your sessions can't load `AGENTS.md` directly." The plugin loads files; nothing upstream says it moves content or writes @@ -394,7 +405,9 @@ The record behind the skill body's `## Boundary` section for `cc-plugin-agents-m the difference table, the "Share one file with other coding tools" conditions, or the shim bullet under "Remove an earlier AGENTS.md workaround"; it comes to name the Agent SDK, cloud or web sessions, or `claude-code-action`; it changes the four-hop import limit; plugins/create - changes the ways a plugin loads for one session or without an install; it comes + changes the ways a plugin loads for one session or without an install; env-vars or + plugins/loading changes what `CLAUDE_CONFIG_DIR` or `CLAUDE_CODE_PLUGIN_CACHE_DIR` relocates; it + comes to state when a subdirectory's `AGENTS.md` loads under `claude-md-and-agents-md` or whether an import expands under `managed-only`; settings-reference documents how a built-in plugin is disabled; or a changelog entry names `AGENTS.md`, `instructionFiles` or the `agents-md` plugin. From 2e4af4ce50fe770105ce81bb06aa3f1ef3ff8a71 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Fri, 2 Oct 2026 00:13:42 -0400 Subject: [PATCH 6/6] fix(instruction-placement): gate the loader on hook policy and ask about A's relocated config root Condition C now holds under disableAllHooks or allowManagedHooksOnly only on v2.1.287 or later, where settings-reference and the mods overview say built-in mods such as cc-plugin-agents-md keep running; an older or unknown version is unknown and keeps the shim. Question 2 asks about pre-2.1.287 CLIs with either setting, since an earlier memory page listed both. Question 6 names condition A alongside B, C and F, because a relocated CLAUDE_CONFIG_DIR moves the user CLAUDE.md the ancestor walk exempts. sources.md carries the dated record. Co-Authored-By: Claude Opus 5.5 --- plugins/instruction-placement/CHANGELOG.md | 4 +++- .../migrate/reference/shim-droppable.md | 10 +++++++--- .../skills/migrate/reference/sources.md | 19 ++++++++++++++++--- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/plugins/instruction-placement/CHANGELOG.md b/plugins/instruction-placement/CHANGELOG.md index a2804d3403..29a25843b7 100644 --- a/plugins/instruction-placement/CHANGELOG.md +++ b/plugins/instruction-placement/CHANGELOG.md @@ -26,7 +26,9 @@ All notable changes to the `instruction-placement` plugin are documented here. F own `discover.sh` model, and a closing operator question catches any loading path not listed. User settings and plugins are read under `${CLAUDE_CONFIG_DIR:-$HOME/.claude}` and `CLAUDE_CODE_PLUGIN_CACHE_DIR` rather than a fixed `~/.claude`, and the operator is asked whether - contributors set either. + contributors set either, a question that also covers condition A's user `CLAUDE.md`. Under + `disableAllHooks` or `allowManagedHooksOnly`, the loader counts as present only on v2.1.287 or + later, where built-in mods are verified to keep running. ### Fixed diff --git a/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md b/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md index 52f95b0313..501bb9c1e8 100644 --- a/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md +++ b/plugins/instruction-placement/skills/migrate/reference/shim-droppable.md @@ -30,7 +30,7 @@ three. A root that cannot be resolved here is unknown, and fails every condition |---|---|---| | A | Nothing takes precedence over an `AGENTS.md`: no `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` at or above the working directory other than the shims going, **and** no directory holding a nested `AGENTS.md` keeps a `.claude/CLAUDE.md` or a non-shim `CLAUDE.md` or `CLAUDE.local.md` of its own | The ancestor walk below, run from each directory contributors start sessions in; a Glob for the three names at every level inside the repository, tracked or not; and the operator for the other machines, whose ancestors (a `~/work/CLAUDE.md`) and uncommitted `CLAUDE.local.md` files this machine cannot see | | B | **Project instructions** on this machine reads `AGENTS.md` with no `CLAUDE.md`: `claude-md-or-agents-md` (the default) or `claude-md-and-agents-md` | `pluginConfigs["agents-md@builtin"].options.instructionFiles` in `/settings.json`, managed settings and any `--settings` file; absent everywhere is the default. Project and local settings are ignored for it, so never read them as the answer. `claude-md` or `managed-only` fails | -| C | The loader is present and not disabled on this machine | `/harness-ops:inventory --bundled`, `builtin_plugins.cc-plugin-agents-md` (`in_loader`, `load`, `gated`, `gate_flags`), and no `enabledPlugins` entry set `false` in any scope (`` user settings, managed, project, local, `--settings`) for `agents-md@builtin` or the `id` the lane prints. Inventory absent, the lane `broken`, or the entry missing is unknown | +| C | The loader is present and not disabled on this machine | `/harness-ops:inventory --bundled`, `builtin_plugins.cc-plugin-agents-md` (`in_loader`, `load`, `gated`, `gate_flags`), and no `enabledPlugins` entry set `false` in any scope (`` user settings, managed, project, local, `--settings`) for `agents-md@builtin` or the `id` the lane prints. Inventory absent, the lane `broken`, or the entry missing is unknown. With `disableAllHooks` or `allowManagedHooksOnly` set `true` in any scope, C holds only on v2.1.287 or later, the build on which built-in mods are verified to keep running under both; an older or unknown version is unknown | | D | Every user, machine and organization the repository serves reads `AGENTS.md` directly | Ask the operator, with the list below. Any yes, and any "don't know", fails | | E | Nothing reachable through the `@` import graph of any `AGENTS.md`, root or nested, lies outside the working directory | The import-graph walk below, from every `AGENTS.md` and from each session start directory. Any `EXTERNAL`, `UNRESOLVED` or `DEPTH` row fails | | F | No hook depends on `InstructionsLoaded` reporting the `AGENTS.md` load, or the operator accepts losing that | Every hook source, inside the repository and out: grep for `InstructionsLoaded` in the repository's `.claude/settings*.json`, hook scripts and CI; `/settings.json` and `/settings.local.json`; the managed settings file and any `managed-settings.d/` beside it; every `--settings` file contributors pass; each installed plugin's `hooks/hooks.json` and `plugin.json` `hooks` under ``; and the plugins loaded per session or outside an install: ask the operator whether contributors use `--plugin-dir`, `--plugin-url` or `CLAUDE_CODE_PLUGIN_DIRS`, and grep each directory or archive they name, plus every `/skills/*/` that holds a `.claude-plugin/plugin.json`. A source that cannot be read here, and every other machine, is the operator's to answer. A hit the operator has not accepted, or a source nobody can answer for, fails | @@ -105,6 +105,10 @@ Ask each, and record the answer beside the condition: - a Claude Code version before v2.1.277; - the built-in `agents-md` plugin disabled in `/plugin`; - in some cases, the first session after upgrading from v2.1.276 or earlier. + + Also ask whether anyone runs a CLI before v2.1.287 with `disableAllHooks` or + `allowManagedHooksOnly` set: an earlier revision of the page listed both here, and no release + note dates their removal. 3. **Does anyone run a CLI before v2.1.281 on Amazon Bedrock, Google Vertex AI, Microsoft Foundry, an LLM gateway, or with telemetry disabled?** The memory page names Bedrock and telemetry-disabled sessions; the 2.1.281 changelog entry names the full list as the sessions @@ -117,8 +121,8 @@ Ask each, and record the answer beside the condition: directly. The CI canary in `sources.md` covers one `claude-code-action` pin and CLI, not every one, so the operator confirms each surface in use or the shim stays. 6. **Does anyone set `CLAUDE_CONFIG_DIR` or `CLAUDE_CODE_PLUGIN_CACHE_DIR`, and to what?** Each - moves the user settings or the plugins this rule reads, so conditions B, C and F hold for that - contributor only when their roots are named and read. "Don't know" fails D. + moves the user settings, user `CLAUDE.md` or plugins this rule reads, so conditions A, B, C and + F hold for that contributor only when their roots are named and read. "Don't know" fails D. 7. **Is there any other way instructions reach Claude for this repository, or anything else that consumes `InstructionsLoaded`, beyond what conditions A to F and questions 1 to 6 cover?** This question catches every case not listed here, so a gap found later keeps the shim without a diff --git a/plugins/instruction-placement/skills/migrate/reference/sources.md b/plugins/instruction-placement/skills/migrate/reference/sources.md index 1bf7336041..551c931b42 100644 --- a/plugins/instruction-placement/skills/migrate/reference/sources.md +++ b/plugins/instruction-placement/skills/migrate/reference/sources.md @@ -376,6 +376,13 @@ The record behind the skill body's `## Boundary` section for `cc-plugin-agents-m `AGENTS.md` directly." The plugin loads files; nothing upstream says it moves content or writes a shim, and no page states how a disabled built-in plugin is recorded in settings, which is why condition C reads `enabledPlugins` for whichever ID the inventory prints rather than one spelling. + Condition C's hook policy: under `allowManagedHooksOnly`, "Mods built into Claude Code keep + running"; under `disableAllHooks`, "Mods built into Claude Code keep running in both cases"; and + the mods overview's "Mods built into Claude Code" table lists `cc-plugin-agents-md`. The memory + page's unavailable list no longer names either setting. An earlier revision did (this + repository's `plugins/harness-config/reference/agents-md-liveness.md`, as of 2026-09-21, quotes + "You or your organization set `disableAllHooks` or `allowManagedHooksOnly`"), and no changelog + entry dates the change, so the skill trusts the exemption only from 2.1.287. - **Basis**: [memory](https://code.claude.com/docs/en/memory), fetched 2026-10-01 by the rung-1 route (50,074 bytes; slug in `llms.txt`; first heading "How Claude remembers your project"), sections "When Claude Code reads AGENTS.md" (line 363), "Choose which instruction files load" @@ -384,7 +391,11 @@ The record behind the skill body's `## Boundary` section for `cc-plugin-agents-m AGENTS.md differs from CLAUDE.md" (line 416), "Remove an earlier AGENTS.md workaround" (line 426) and "Share one file with other coding tools" (line 435). [settings-reference](https://code.claude.com/docs/en/settings-reference), fetched 2026-10-01, `pluginConfigs`: "Built-in plugins store their options under the same key with an - `@builtin` suffix". The + `@builtin` suffix"; fetched again 2026-10-02 (416,824 bytes, first heading "All settings"), + "What runs under `allowManagedHooksOnly`" (line 4008) and "`disableAllHooks`" (line 4021). + [plugins/mods/overview](https://code.claude.com/docs/en/plugins/mods/overview), fetched + 2026-10-02 (23,269 bytes, first heading "Mods overview"), "Mods built into Claude Code" (line + 220). The [changelog](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md) entry for 2.1.277 reads "Added AGENTS.md support: in a project with no CLAUDE.md, Claude Code reads AGENTS.md instead; change it under \"Project instructions\" in `/config`", and the 2.1.281 entry reads @@ -399,7 +410,7 @@ The record behind the skill body's `## Boundary` section for `cc-plugin-agents-m `--binary-only` run printed `id` `cc-plugin-agents-md@builtin`, alias `agents-md`, `in_loader` true, `load` `unconditional`, `gated` true on `tengu_agents_md_mod` with default true; that is one build's reading, not the decision's input. -- **As of**: 2026-10-01, Claude Code 2.1.287. +- **As of**: 2026-10-01, Claude Code 2.1.287; the hook-policy exemption 2026-10-02, same build. - **Recheck trigger**: the memory page changes the "When Claude Code reads AGENTS.md" list, the "Choose which instruction files load" table, the "When AGENTS.md support is unavailable" list, the difference table, the "Share one file with other coding tools" conditions, or the shim @@ -410,4 +421,6 @@ The record behind the skill body's `## Boundary` section for `cc-plugin-agents-m comes to state when a subdirectory's `AGENTS.md` loads under `claude-md-and-agents-md` or whether an import expands under `managed-only`; settings-reference documents how a built-in plugin is - disabled; or a changelog entry names `AGENTS.md`, `instructionFiles` or the `agents-md` plugin. + disabled; settings-reference or the mods overview changes whether built-in mods keep running + under `disableAllHooks` or `allowManagedHooksOnly`, or stops listing `cc-plugin-agents-md` as + one; or a changelog entry names `AGENTS.md`, `instructionFiles` or the `agents-md` plugin.