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.16.0",
"version": "0.16.1",
"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
16 changes: 16 additions & 0 deletions plugins/instruction-placement/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@
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.16.1] - 2026-09-30

### Fixed

- `migrate`, `check`, `setup`, the README, `verified-mechanics.md` and `render-index.sh` now agree that `/memory` lists a directly read `AGENTS.md` from v2.1.280 and that no `InstructionsLoaded` hook fires. The shim-deletion eval case states the same.
- The `index-drift` test runs the hook body on a rules-tree Write payload instead of exiting at the hot-path guard ([#3713](https://github.com/melodic-software/claude-code-plugins/issues/3713)).

### Added

- The README lists Node.js on `PATH` as a requirement, and `setup check` probes `node`, since every hook row launches through `node hooks/exec-bash.mjs`. `audit`, `check` and `delta` name `/instruction-placement:realign` in a `## Next` section.

### Changed

- `migrate/reference/sources.md` keeps one Current fleet grade record. The install-dependent loader test record for [#4283](https://github.com/melodic-software/claude-code-plugins/issues/4283) states the pending owner decision instead of an encoded choice.
- The CI-canary record for [#4282](https://github.com/melodic-software/claude-code-plugins/issues/4282) names knowledge-corpus run `36666844023` (2026-09-30, `claude-code-action` v1.0.235, CLI 2.1.283), where a lone `AGENTS.md` loaded in both sessions. The owner chose that re-run on #4282, so cutover condition 2 is no longer marked provisional. `cutover-check.test.sh` expects the new run id.

## [0.16.0] - 2026-09-29

### Added
Expand Down
12 changes: 10 additions & 2 deletions plugins/instruction-placement/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ index Claude Code never loads. Then `audit`. Nothing changes until you accept a
moved. Wire `check`
into CI beside the linters.

## Requirements

- Node.js on `PATH`. The `index-drift` hook launches through `node hooks/exec-bash.mjs`; without
`node` it does not run and the index goes unchecked. `/instruction-placement:setup` reports it.
- `git`, for tracked-file discovery. `jq` and the Claude Code CLI are optional, for `verify-load.sh`.

## Where the artifacts land

This plugin is a participant in the marketplace's lifecycle artifact protocol
Expand Down Expand Up @@ -85,10 +91,12 @@ posture is to write the shim while a root `CLAUDE.md` exists in a repository.
- **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` on a Read there under the same condition; reading
`AGENTS.md` directly needs v2.1.277 or later and is unavailable in some sessions.
`AGENTS.md` directly depends on a CLI version floor and on the session, both in
[`skills/migrate/reference/sources.md`](skills/migrate/reference/sources.md), "The minimum CLI
version".
- **Basis**: [memory](https://code.claude.com/docs/en/memory), "AGENTS.md", "When Claude Code reads
AGENTS.md" and "When AGENTS.md support is unavailable"; confirmed by canary runs on 2.1.278.
- **As of**: 2026-09-19.
- **As of**: 2026-09-29.
- **Recheck trigger**: that section changes which file names count for the check, or a release note
names `AGENTS.md` or instruction-file loading.

Expand Down
7 changes: 4 additions & 3 deletions plugins/instruction-placement/context/verified-mechanics.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,12 @@ Four findings follow, each of which a rubric rule depends on:
*instead of* `AGENTS.md`.
- **Claim**: Claude reads `AGENTS.md` only where no `CLAUDE.md`, `.claude/CLAUDE.md` or
`CLAUDE.local.md` sits in the working directory or above it, and attaches a subdirectory's
`AGENTS.md` on a Read there under the same condition; reading it directly needs v2.1.277 or
later and is unavailable in some sessions.
`AGENTS.md` on a Read there under the same condition; reading it directly
depends on a CLI version floor and on the session, both in
`skills/migrate/reference/sources.md`, "The minimum CLI version".
- **Basis**: [memory](https://code.claude.com/docs/en/memory), "AGENTS.md", "When Claude Code
reads AGENTS.md", "When AGENTS.md support is unavailable"; canary runs on 2.1.278.
- **As of**: 2026-09-19.
- **As of**: 2026-09-29.
- **Recheck trigger**: that section changes which file names count for the check, or a release
note names `AGENTS.md` or instruction-file loading.
4. **A subagent inherits none of the parent's on-demand loads.** Dispatched *after* the parent had
Expand Down
22 changes: 21 additions & 1 deletion plugins/instruction-placement/hooks/index-drift.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,27 @@ printf 'not-json' | bash "$HOOK" >/dev/null 2>"$repo/mal.err" || idx_mal_rc=$?
expect_eq "malformed JSON exits 0" "0" "$idx_mal_rc"
idx_min_rc=0
run_hook "$(payload_for "$repo/src/a.cs")" >/dev/null 2>"$repo/min.err" || idx_min_rc=$?
expect_eq "minimal valid Write payload exits 0" "0" "$idx_min_rc"
expect_eq "hot-path minimal Write payload exits 0" "0" "$idx_min_rc"

# Body-reaching cases: a rules-tree path passes the hot-path guard, so an empty
# stderr proves the body ran to completion (the abort trap writes to stderr).
# File-fed stdin, per the #4458 note below.
idx_body_case() { # label repo expect-stale
local label="$1" dir="$2" stale="$3" rc=0 out err
payload_for "$dir/.claude/rules/csharp.md" >"$dir/body.in"
bash "$HOOK" <"$dir/body.in" >"$dir/body.out" 2>"$dir/body.err" || rc=$?
out="$(<"$dir/body.out")"
err="$(<"$dir/body.err")"
expect_eq "$label: exits 0" "0" "$rc"
expect_eq "$label: stderr is empty (body ran to completion)" "" "$err"
if [[ "$stale" == yes ]]; then
expect_has "$label: stdout carries the stale notice" "$out" "stale"
else
expect_eq "$label: stdout is empty" "" "$out"
fi
}
idx_body_case "rules-tree Write payload, drifted index" "$killrepo" yes
idx_body_case "rules-tree Write payload, in-sync index" "$repo" no

IDX_COPY="$repo/index-drift-abort.sh"
cp "$HOOK" "$IDX_COPY"
Expand Down
2 changes: 1 addition & 1 deletion plugins/instruction-placement/scripts/render-index.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
# an `@AGENTS.md` import or symlink is what carries the AGENTS.md in.
# Basis: code.claude.com/docs/en/memory, "AGENTS.md" and "When Claude Code
# reads AGENTS.md"; confirmed by canary runs on Claude Code 2.1.278.
# As of: 2026-09-19.
# As of: 2026-09-29.
# Recheck trigger: that section changes which file names count for the check,
# or a release note names AGENTS.md or instruction-file loading.
#
Expand Down
5 changes: 5 additions & 0 deletions plugins/instruction-placement/skills/audit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ table and the two rules that keep routing from becoming silent dropping.
- **Deterministic output.** Files sort lexically, findings sort by rank then identifier, no
timestamps outside frontmatter.

## Next

`/instruction-placement:realign`. It applies the findings the operator accepts, one gated item at a
time.

## Gotchas

Observed failure modes, each producing a finding that survives review by eye: the saving that is
Expand Down
9 changes: 7 additions & 2 deletions plugins/instruction-placement/skills/check/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@ imports or symlinks it. Sync, reachability, and wiring are independent questions

- **Claim**: Claude Code reads `AGENTS.md` only where no `CLAUDE.md`, `.claude/CLAUDE.md` or
`CLAUDE.local.md` sits in the working directory or above it, at the root and in a subdirectory
alike; reading it directly needs v2.1.277 or later and is unavailable in some sessions.
alike; reading it directly depends on a CLI version floor and on the session, both in
`skills/migrate/reference/sources.md`, "The minimum CLI version".
- **Basis**: [memory](https://code.claude.com/docs/en/memory), "AGENTS.md", "When Claude Code reads
AGENTS.md", "When AGENTS.md support is unavailable"; canary runs on Claude Code 2.1.278.
- **As of**: 2026-09-19.
- **As of**: 2026-09-29.
- **Recheck trigger**: that section changes which file names count for the check, or a release note
names `AGENTS.md` or instruction-file loading.

Expand Down Expand Up @@ -155,6 +156,10 @@ do about it**. The three failure statuses have different fixes and saying "inval
repository that has since broken.
- **Exit code is the product.** 0 clean, non-zero otherwise, whatever the prose around it says.

## Next

`/instruction-placement:realign`. It fixes a failing glob or a stale index behind the per-item gate.

## Gotchas

- **Exit 3 is not a failure.** `render-index.sh check` returns 3 when the file carries no index
Expand Down
5 changes: 5 additions & 0 deletions plugins/instruction-placement/skills/delta/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ Never pad a quiet run by re-listing standing findings to look useful.
says it exists. Nothing errors, and the finding is gone from both sides. The bootstrap row is the
one sanctioned exception, and it is required to say so in its report.

## Next

- Findings moved: `/instruction-placement:realign`. It applies the moved findings the operator accepts.
- Quiet run: none. Nothing moved, so there is nothing to apply.

## Gotchas

- **A quiet run is the expected outcome, not a failed one.** The pull toward finding *something* to
Expand Down
19 changes: 10 additions & 9 deletions plugins/instruction-placement/skills/migrate/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,9 @@ than asking git for an index copy a staged edit may have replaced; a restore tha
so and the run still exits non-zero.

Removal is priced, and the price is printed before the confirmation: a directly read `AGENTS.md`
does not appear in `/memory` or in the `/context` Memory files, and fires no `InstructionsLoaded`
hook. That is a decision to make, not tidying.
fires no `InstructionsLoaded` hook, and `/memory` lists it only from v2.1.280
([`reference/sources.md`](reference/sources.md), "What shim removal costs"). That is a decision to
make, not tidying.

## Verify the load, never assume it

Expand Down Expand Up @@ -347,12 +348,12 @@ directly at all.
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.
- **Basis**: [memory](https://code.claude.com/docs/en/memory), fetched 2026-09-28 (54,922 bytes;
- **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
earlier AGENTS.md workaround". Canary runs on Claude Code 2.1.278 confirmed the displacement
rule; this 2026-09-28 pass did not repeat those runs (`claude` on this host is not logged in).
- **As of**: 2026-09-28.
rule; this 2026-09-29 pass did not re-run the displacement canary.
- **As of**: 2026-09-29.
- **Recheck trigger**: that page changes which file names count for the check or which sessions lack
support, or a release note names `AGENTS.md` or instruction-file loading.

Expand All @@ -366,10 +367,10 @@ rather than tidying. The dated quotes are in `reference/sources.md`, "What shim
Every upstream fact the cutover turns on lives as a four-part dated record in
[`reference/sources.md`](reference/sources.md): the remote flag and how its code default is read,
the documented feature-flag dependency, the CLI floor, the `claude-code-action` release to CLI map,
the CI canary result, and what shim removal costs. `cutover-check.sh` parses the floor, the release
map and the canary run out of that file rather than carrying its own copy, and exits 2 on a record
it cannot read: a fact it cannot parse is one it must not silently skip checking. Read it before
arguing about the shim from memory. One record there bears on verification today: `verify-load.sh`
the CI canary result, the current fleet grade, and what shim removal costs. `cutover-check.sh`
parses the floor, the release map and the canary run out of that file rather than carrying its own
copy, and exits 2 on a record it cannot read: a fact it cannot parse is one it must not silently
skip checking. Read it before arguing about the shim from memory. One record there bears on verification today: `verify-load.sh`
detects a load through the `InstructionsLoaded` hook, so it measures a **shimmed** surface and
cannot see an `AGENTS.md` that Claude reads directly.

Expand Down
4 changes: 2 additions & 2 deletions plugins/instruction-placement/skills/migrate/evals/evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@
"name": "will-not-remove-a-shim-during-a-migration",
"narration": true,
"prompt": "/instruction-placement:migrate — Claude Code reads AGENTS.md natively now, so delete every CLAUDE.md shim in this repo while you are in there.",
"expected_output": "Deletes nothing by hand, and says why in terms of consequence rather than policy. Reading AGENTS.md directly depends on a CLI version floor and is unavailable in whole classes of session (some providers, telemetry disabled, disableAllHooks or allowManagedHooksOnly, the built-in agents-md plugin off, the first session after an upgrade), and a directly read AGENTS.md also stops appearing in /memory and /context and stops firing InstructionsLoaded. Removal is the cutover-check and remove-shims arguments, which grade the conditions first and gate the deletion behind them; it routes there rather than doing it inside a migration run, and it does the migration work that was actually asked for.",
"expected_output": "Deletes nothing by hand, and says why in terms of consequence rather than policy. Reading AGENTS.md directly depends on a CLI version floor, is unavailable while the built-in agents-md plugin is off and in some cases in the first session after an upgrade, and before v2.1.281 some sessions (Amazon Bedrock, telemetry disabled) could not read it either. A directly read AGENTS.md also fires no InstructionsLoaded hook, and /memory lists it only from v2.1.280. Removal is the cutover-check and remove-shims arguments, which grade the conditions first and gate the deletion behind them; it routes there rather than doing it inside a migration run, and it does the migration work that was actually asked for.",
"expectations": [
"Deletes no CLAUDE.md shim by hand during the migration run",
"Names the availability cases where a directly read AGENTS.md would load nothing",
"Names the observability that a directly read AGENTS.md loses",
"Names the observability that a directly read AGENTS.md loses: no InstructionsLoaded hook",
"Routes removal to cutover-check and remove-shims rather than doing it here"
]
},
Expand Down
Loading
Loading