Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/instruction-placement/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "instruction-placement",
"version": "0.17.1",
"version": "0.18.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",
Expand Down
34 changes: 34 additions & 0 deletions plugins/instruction-placement/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,40 @@
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.18.0] - 2026-10-02

### Added

- **`/instruction-placement:migrate` decides shim removal against the built-in `AGENTS.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 `/harness-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`. 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, 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.
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, 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

- **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.17.1] - 2026-10-02

### Fixed
Expand Down
11 changes: 6 additions & 5 deletions plugins/instruction-placement/scripts/render-index.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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.
#
Expand Down
33 changes: 21 additions & 12 deletions plugins/instruction-placement/skills/migrate/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,16 @@ 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, 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

```bash
Expand Down Expand Up @@ -341,13 +351,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 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",
"When Claude Code reads AGENTS.md", "When AGENTS.md support is unavailable", and "Remove an
Expand Down Expand Up @@ -375,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

Expand All @@ -401,7 +409,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".

Expand Down
Loading
Loading