Name docs/ and notes/ in the root map, and test that the project's own tree stays named - #496
Conversation
…n tree stays named
leaf-agent
left a comment
There was a problem hiding this comment.
The notes/ paragraph's justification is false, and it is the one claim in this diff a session would act on directly. ci.yaml's lint job runs pre-commit/action@v3.0.1 — pre-commit run --all-files — and .pre-commit-config.yaml's exclude: carves out only notes/mcp-apps/experiments/[0-9]+/(results/|output\.txt$), with its own comment reading "lint the authored commands and findings, not the captures". So ruff gates notes/ask-placement-eval/outline.py and each experiment's observe.py and serve.py, shellcheck gates run.sh and commands.sh, and typos and the whitespace hooks gate every note. A session that reads notes/ as unchecked reddens lint on the first note it writes.
The narrower fact the dating rule actually rests on is true: nothing under tests/ reads notes/, so no test checks a note against the tree. Suggestion inline.
pre-commit runs --all-files in ci.yaml's lint job, and the config's exclude carves out only the MCP App experiment captures, so ruff, shellcheck, typos, and the whitespace hooks all gate authored notes. The dating rule rests on the narrower fact that no test reads notes/.
…ule (#543) `scripts/CLAUDE.md` tells a session that a clean `git status` after `scripts/vendor.py <bundle>` is the check that a bundle still matches the script, and that the check holds only where every fetched input is pinned. It then sorts the bundles into those two classes — and two of the seven are in neither, so a session rebuilding `floating-ui` or `mcp-app` has no answer for whether a diff it sees is drift to fix or an upstream patch to take. Each belongs in a class the paragraph already describes: - `floating-ui` reproduces. `@floating-ui/dom`, `@floating-ui/core`, and `@floating-ui/utils` are all in `PINS`, and that closure is complete: `dom` depends on `core` and `utils`, `core` on `utils`, `utils` on nothing. Rebuilding it left `git status` clean. - `mcp-app` fetches an input `PINS` does not name. `@modelcontextprotocol/ext-apps@1.7.5` declares `@standard-schema/spec ^1.1.0`, which npm's resolver picks, so the bundle sits with `plot` and `pierre`. It reproduced when I rebuilt it today — which is exactly the reading the paragraph exists to keep a session from relying on. This is drift rather than a deliberate omission. The paragraph dates from `c7b37062` (2026-08-30); `mcp-app` landed in #188 on 2026-09-01 and `floating-ui` in #537 last night, and #533 — the most recent commit to touch the paragraph — added `jsdiff` to it, so it is meant to be kept current. The test follows the shape this repo already uses for instruction lists (#93, #105, #331, #496): it reads the bundle names out of `vendor.py`'s `BUILDS` and `COPIES` rather than restating them, and asserts each is named in that one paragraph. On `main` it fails with `unplaced in scripts/CLAUDE.md: ['floating-ui', 'mcp-app']`. What it deliberately does not check is *which* class a bundle is placed in — deciding that means resolving each package's dependency graph, which is a network call this suite should not make. A name in the wrong class still needs a reader to catch; a name that is absent no longer does. <details><summary>Verification</summary> - `uv run pytest tests/test_interact_layer.py` — 167 passed, 6 skipped. - The new case alone, with `scripts/CLAUDE.md` reverted to `main`'s text: `AssertionError: unplaced in scripts/CLAUDE.md: ['floating-ui', 'mcp-app']`. - `scripts/vendor.py floating-ui` and `scripts/vendor.py mcp-app` each left `git status` clean. - `npm view @floating-ui/dom@1.8.0 dependencies` → `{ '@floating-ui/core': '^1.8.0', '@floating-ui/utils': '^0.2.12' }`; `@floating-ui/core@1.8.0` → `{ '@floating-ui/utils': '^0.2.12' }`; `@floating-ui/utils@0.2.12` → none; `@modelcontextprotocol/ext-apps@1.7.5` → `{ '@standard-schema/spec': '^1.1.0' }`. </details> Co-authored-by: leaf-agent <318509791+leaf-agent@users.noreply.github.com>
CLAUDE.md's repository map names seven of the nine directories in the project's own tree. The two it misses,docs/andnotes/, are both places sessions are actively sent —d4801b6awrotenotes/codex-integration.md,8ac42a81wrotenotes/workspace-followups.mdand theTODO.mdsection that links to it, anddocs/holds the five product documentsscripts/site.pypublishes as leaf.page. A session that lands in either has only the files in front of it to say what the directory is for.What the map now says
Two paragraphs beside the existing
examples//tests//scripts//worker/one:docs/is the site's own content — each product document there is a Leaf source, so changing what leaf.page says is a page edit rather than a template edit. That fact currently lives only inscripts/site.py's docstring, which is the wrong place for a session deciding where to make the change.TODO.mdis the ordered priority list andnotes/holds what its items link out to, with the dating rule stated: where a note and the tree disagree, the tree is right.The dating rule is scoped to what CI actually reads.
ci.yaml's lint job runspre-commit/action@v3.0.1over the whole tree, and.pre-commit-config.yaml'sexclude:carves out only the MCP App experiment captures — so ruff, shellcheck, typos, and the whitespace hooks all gate an authored note, and a session told otherwise would reddenlinton the first one it writes. What holds is the narrower fact the rule rests on: nothing undertests/readsnotes/, so no test checks a note against the tree.The test
test_the_root_instructions_name_every_directory_of_the_projects_own_treederives the set fromshipped_payload()rather than a list, so the next top-level directory added without a paragraph goes red instead of staying green — the same construction, and the same reason, astest_the_root_instructions_name_every_directory_ci_gates_on_its_owndirectly above it. Dotted directories are excluded: a session finds.github/or.config/through the host rather than through this map.It matches the backticked prefix without a closing backtick, because a part is named at whatever depth it is owned at —
bin/leafis a launcher andskills/leaf/assets/is a tree, and both name their directory. The existing test above requires the closing backtick because aworking-directory:is always named as the bare directory.Verification
uv run pytest tests/test_interact_layer.py— 165 passed, 6 skipped.CLAUDE.mdparagraphs stashed and the test kept, it fails withAssertionError: unnamed in CLAUDE.md: ['docs', 'notes'].uv run pre-commit run --files notes/mcp-apps/experiments/10/observe.py notes/mcp-apps/experiments/10/commands.sh— ruff, typos, shellcheck and the whitespace hooks each ran on those notes rather than skipping, which is the reading behind the scoped sentence above.uv run pre-commit run --files CLAUDE.md— passed.This is prose plus its gate; there is no product path to regress.