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
5 changes: 3 additions & 2 deletions docs/specs/agent-doc-surfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ guidance <200 lines per CLAUDE.md.
| Convention | File(s) | Auto-read |
|---|---|---|
| AGENTS.md open standard (Linux Foundation-stewarded) | `AGENTS.md` root + nested, nearest wins | Native in Codex, Cursor, Copilot agent, Gemini CLI (config), Windsurf, Zed, Roo, others; and in Claude Code since v2.1.277, where no `CLAUDE.md` displaces it (row 9). Claude Code concatenates the ancestor chain rather than resolving nearest-wins |
| Cursor rules | `.cursor/rules/*.mdc` (+ nested); legacy `.cursorrules` deprecated | Per-rule types: Always / Auto Attached (globs) / Agent Requested / Manual; also reads AGENTS.md + CLAUDE.md |
| Cursor rules | `.cursor/rules/*.mdc` (+ nested); legacy `.cursorrules` deprecated | Per-rule types: Always / Auto Attached (globs) / Agent Requested / Manual; CLI observed to read `AGENTS.md`, `CLAUDE.md` and `CLAUDE.local.md` together, without `@`-import expansion; `.cursor/rules/*.md` is inert and an `.mdc` needs frontmatter (`plugins/instruction-placement/skills/migrate/reference/sources.md`) |
| GitHub Copilot | `.github/copilot-instructions.md`; `.github/instructions/**.instructions.md` (`applyTo:` globs); AGENTS.md (agent) | Auto-added to matching requests |
| Gemini CLI | `~/.gemini/GEMINI.md`; workspace + ancestors; JIT subdir scan; `@` imports; `context.fileName` configurable | Concatenated into every prompt |
| Windsurf | `global_rules.md`; `.windsurf/rules/` (newer docs prefer `.devin/`); legacy `.windsurfrules`; AGENTS.md | Per-rule `trigger:` manual / always_on / model_decision / glob |
Expand All @@ -78,7 +78,8 @@ corroborate the competitor paths themselves.
- Claude-side rows corroborate mostly within the single Anthropic publishing pool. **Accepted**:
the effort's claim ladder requires verification against current official docs, not
multi-publisher independence, for harness-behavior claims.
- Cursor / Copilot / Windsurf / Cline rows are MEDIUM confidence (vendor doc hosts egress-blocked
- Cursor / Copilot / Windsurf / Cline rows are MEDIUM confidence, except that Cursor's
AGENTS.md and CLAUDE.md loading is observed (see the row). Vendor doc hosts were egress-blocked
in the research container; sourced via domain-filtered search + Anthropic's `/init` interop
list as path corroborator). **Accepted for their purpose**: ecosystem awareness rows, not
harness claims. Optional implementation-time task: re-fetch the four vendor pages from an
Expand Down
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.1",
"version": "0.16.2",
"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
7 changes: 7 additions & 0 deletions plugins/instruction-placement/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
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.2] - 2026-09-30

### Changed

- **`migrate` sources record the observed Cursor, Grok Build and Muse Code loader behavior.** The parked-install record is replaced by the results of running the loader recipe against each tool, with the bullets that stay open and their reasons. `verification.md` carries the recipe and expected results, and the record cites the upstream pages that exist
([#4283](https://github.com/melodic-software/claude-code-plugins/issues/4283)).

## [0.16.1] - 2026-09-30

### Fixed
Expand Down
63 changes: 54 additions & 9 deletions plugins/instruction-placement/skills/migrate/reference/sources.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,16 +175,61 @@ Each row was re-derived on 2026-09-28 by resolving the tag to its commit and rea
`agents-md-cutover-check` in `.github/recurring-schedule.json`, or an in-scope repository
becoming unreadable or readable.

## Install-dependent loader tests (#4283)
## Loader behavior of Cursor, Grok Build, and Muse Code

- **Claim**: empirical loader tests for Cursor, Grok Build, and Muse Code have not been run.
Claims about those tools stay at docs or source grade.
- **Basis**: #4283's acceptance criteria are unmet and no run exists on main. The tools were not
installed in the environment that did the migration research, an environment limit and not a
decision. Whether and where to install them is pending an owner decision on #4283.
- **As of**: 2026-09-29.
- **Recheck trigger**: the owner's decision on #4283, or a host that records results for a named
tool into this file.
- **Claim**: Each tool was run headless against one recipe tree, and these results are
observed, not docs or source grade. Cursor CLI loads `AGENTS.md`, `CLAUDE.md` and
`CLAUDE.local.md` together at session start, follows symlinks, applies no size cap through
262,156 bytes, and does not expand `@path` imports. It reads no other name (`Agents.md`,
`AGENT.md`, `.claude/CLAUDE.md` are absent). `.cursor/rules/*.md` never loads; `.mdc` loads
only with frontmatter. Started at the git root, nested files attach when a file under them is
read; started in the nested directory, the ancestor chain loads (12 levels seen). In the
non-git copy the attach on read did not occur. Grok Build loads all eight names of its
documented list per directory, and the two `.claude/` names are gated by
`GROK_CLAUDE_AGENTS_ENABLED` (set to `false`, they vanish). No switch stops it
reading a plain `CLAUDE.md`. It does not expand `@path` imports, follows symlinks, and shows no
size cap through 262,156 bytes. In a trusted folder outside a git repository it loads the
working directory only, so "nothing outside a git repository loads" is contradicted; with
folder trust off nothing project-level loads. Muse Code loads one file per directory with
`AGENTS.md` first, and `CLAUDE.md` alone loads when no `AGENTS.md` exists. It does not expand
`@path` imports and follows symlinks. It skips an `AGENTS.md` over 256,000 bytes ("over the
256000 byte load limit"), loads one of 244,676 bytes with only the head reaching the model
(65,536-byte delegation startup limit), and skips project files unless the workspace is
trusted (`--trust-workspace`). In a git repository it loads the chain from root to working
directory (12 levels seen); from the root, a read under a nested directory attaches nothing;
outside git it loads the working directory only.
Still open, each with its reason:
- Cursor Team, Project, User precedence: needs a Team plan and the editor rules UI, not
observable headless.
- Cursor editor against CLI, and the editor's "always applied" wording: the editor was not run.
- Cursor `~/.cursor/rules` as a synced file: the path does not exist on the host, and sync
cannot be observed headless.
- Grok path-only reminder text for out-of-chain files: contents stay unloaded and the model
later read the nested files itself, but the streamed transcript carries no reminder text.
- Grok `MAX_WALK_DEPTH` of 10: a working directory at depth 12 loaded all 12 levels, so the
recipe does not show what the constant bounds.
- Muse "sibling `CLAUDE.md` never opened": Muse names the shadowed file on stderr ("is ignored
this session because AGENTS.md takes precedence in that directory"), but whether the file
is opened needs `strace`, which is not installed on the host.
- Muse user-rules path and its Windows resolution: no Muse-native user-rules file exists on
the host, and the Windows path needs a Windows host.
- **Basis**: headless runs on one Linux host with cursor-agent `2026.09.28-64d2043`
(`cursor-agent -p --mode ask --trust`), grok `1.0.41 (4220f3b224a6)` (`grok -p --tools ""`
with `GROK_FOLDER_TRUST=0`, and `grok inspect --json`), and Muse Code `1.4.1 (1.4.1-R4503.1)`
(`muse exec --trust-workspace --disable-shell --disable-write`). The tree, prompt,
invocations and expected results are in
[`reference/verification.md`](verification.md#the-loader-recipe-for-other-tools); the raw
transcripts are not committed. Each result is one model sample except where repeats agreed.
- **Upstream pointers**: Cursor rules, `https://cursor.com/docs/rules` (fetched 2026-09-30, HTTP
200, mentions `AGENTS.md`); Grok Build, `https://docs.x.ai/build/overview` (fetched
2026-09-30, HTTP 200, mentions `AGENTS.md`). Neither page states the loader semantics above,
which is why they are recorded as observed. Muse Code: no public documentation or issue
tracker was found, so its results rest on the recipe alone.
- **As of**: 2026-09-30.
- **Recheck trigger**: a new release of any of the three tools, a host that can run the
editor, a Team plan, a Windows host, or `strace`, which would settle the open bullets, either
upstream page coming to state loader behavior, or any change to the recipe in
[`reference/verification.md`](verification.md#the-loader-recipe-for-other-tools).

## The canary recipe

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,65 @@ file-specific, which is the common case. The fallback: canary an **existing** pa
so the mechanism is still proven on this repository, and report that the migration created none.
Never invent a rules file to have something to canary.

## The loader recipe for other tools

The procedure behind the Cursor, Grok Build and Muse Code records in
[`reference/sources.md`](sources.md). Run it again when a recheck trigger there fires.

**Tree.** Build it outside every repository, once as a git repository and once as a copy without
`.git`. Give every instruction file its own random canary, named by label and never committed.

- root `AGENTS.md`; `CLAUDE.md` holding `@AGENTS.md`, `@imported.md` and its own canary;
`imported.md`; `.claude/CLAUDE.md`; `.cursor/rules/` with `x.md` and `x.mdc`, both with
`alwaysApply: true` frontmatter, plus a `.mdc` without frontmatter
- `sub/AGENTS.md`, `sub/CLAUDE.md`, and a non-instruction file `sub/inner/note.txt` as the read
trigger
- `symdir/` with `AGENTS.md` and `CLAUDE.md` as symlinks
- `big32k/`, `big245k/`, `big256k/`, `big1m/`: an `AGENTS.md` of 32,784, 244,676, 262,156 and
1,048,596 bytes, canaries at head, middle and tail
- `deep/l01/` through `l12/`, one `AGENTS.md` each
- `names/`: `Agents.md`, `Claude.md`, `CLAUDE.md`, `CLAUDE.local.md`, `AGENT.md`, `AGENTS.md`,
`.claude/CLAUDE.md`, `.claude/CLAUDE.local.md`
- two more trees holding only a `CLAUDE.md`, and only an `AGENTS.md`

**Prompt.** Quote every line containing `CANARY` from the loaded instructions, without reading
files. For the read-trigger probes, allow one read of `sub/inner/note.txt` and nothing else.

**Invocations**, each from the tree root and from a nested directory, with the workspace trusted:

```bash
cursor-agent -p --mode ask --trust --output-format text
GROK_FOLDER_TRUST=0 grok -p --tools "" --disable-web-search --max-turns 4
grok inspect --json
muse exec --no-session-log --disable-web-tools --trust-workspace --disable-shell --disable-write --no-foreign-personal-context
```

Without `GROK_FOLDER_TRUST=0` Grok lists only `~/.claude/CLAUDE.md`, and without
`--trust-workspace` Muse skips every project file. `grok inspect --json` is deterministic and
needs no model call. Each model result is one sample; repeat a probe before trusting a `NONE`.

**Expected results**, as observed on cursor-agent `2026.09.28-64d2043`, grok `1.0.41`, and
Muse Code `1.4.1`:

| Probe | Cursor | Grok Build | Muse Code |
|---|---|---|---|
| Root `AGENTS.md` and `CLAUDE.md` | both load | both load | `AGENTS.md` only |
| `CLAUDE.md` with no `AGENTS.md` | loads | loads | loads |
| `@path` import in an instruction file | not expanded | not expanded | not expanded |
| Symlinked instruction file | followed | followed | followed |
| `Agents.md`, `AGENT.md`, `.claude/CLAUDE.md` | not read | all eight names read | not read |
| 262,156-byte `AGENTS.md` | loads | loads | skipped, "over the 256000 byte load limit" |
| Nested files, cwd at the git root | attach when a file under them is read | absent | absent, and a read attaches nothing |
| Nested files, cwd in the nested directory | ancestor chain loads, 12 levels | chain loads, 12 levels | chain loads, 12 levels |
| Non-git copy, cwd nested | ancestors load | cwd directory only | cwd directory only |
| Non-git copy, cwd at the root, nested file read | attach does not occur | not tested | not tested |
| `.cursor/rules/x.md` | never loads | loads as a rule | not tested |
| `.cursor/rules/x.mdc` | loads with frontmatter only | not loaded | not tested |

Grok's two `.claude/` names disappear with `GROK_CLAUDE_AGENTS_ENABLED=false`; the six top-level
Comment thread
kyle-sexton marked this conversation as resolved.
names stay. Muse prints "is ignored this session because AGENTS.md takes precedence in that
directory" on stderr for a shadowed sibling.

## Progressive disclosure, with a caveat

Run `/docs-hygiene:audit-progressive-disclosure` via the Skill tool, when it is installed, on the
Expand Down
Loading