Repository navigation
fix(repo): budget turns off an AGENTS.md beside its own CLAUDE* file - #366
Conversation
Settles item 5 of the follow-ups from the dokimi review of IntelPython/dpnp#3099: `repo budget` modelled only a root CLAUDE.md, .claude/CLAUDE.md or CLAUDE.local.md turning AGENTS.md off. Claude Code 2.1.316 also skips a subdirectory's AGENTS.md when that same directory holds its own CLAUDE.md, CLAUDE.local.md or .claude/CLAUDE.md, also when the agent reaches the directory lazily from the root; budget counted that AGENTS.md, and its imports, as lazy. A new helper, _displaced, decides which AGENTS.md files a CLAUDE* file turns off: every one while a root CLAUDE* resolves (unchanged), else a nested one whose own directory holds a resolvable CLAUDE* file. One in a parent or deeper directory does not count. The existing exceptions apply: a CLAUDE* alias of the file, or an import from a loaded file, keeps it lazy, and so does the same file reached as an AGENTS.md in a directory with no CLAUDE*. A displaced nested AGENTS.md adds no imports of its own. The off warning names the CLAUDE* file for each nested one. The claude-code profile notes, the module docstring, `repo budget --help`, the repo evaluation contract and architecture.md describe the nested rule. budget() drops one branch to _off_warnings, so its growth baseline shrinks from 21 to 20. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
|
✅ OpenCodeReview: Review complete: 0 finding(s) across 11 selected item(s). Review details
|
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues require changes to the nested AGENTS.md displacement update.
No open issues 🎉
What changed in this PR
0 new findings; nothing to fix.
This PR aligns repo budget with measured Claude Code behavior: a nested AGENTS.md is reported as off when its own directory contains a resolving CLAUDE* file, while preserving import and alias exceptions.
Changes:
- Applies nested displacement to file classification and import counting, with warnings naming the responsible file.
- Adds regression tests for directory boundaries, dangling links, imports, aliases, and non-Claude profiles.
- Updates CLI help, profile notes, documentation, and the complexity baseline.
| File | Description |
|---|---|
dokimi/tests/test_repo_budget_nested.py |
Tests nested displacement and its exceptions. |
dokimi/scripts/growth_baseline.json |
Lowers the recorded complexity of budget(). |
dokimi/dokimi/repos/profiles/claude-code.yaml |
Documents measured startup and lazy-loading rules. |
dokimi/dokimi/repos/budget.py |
Implements nested displacement and targeted warnings. |
dokimi/dokimi/commands/repo.py |
Updates budget help to describe nested displacement. |
dokimi/docs/repo-evaluation-contract.md |
Clarifies budget classification and receipt behavior. |
dokimi/docs/architecture.md |
Updates the budget model description. |
dokimi/changelog.d/20261102_120000_antonwolfy_budget-nested-claude.md |
Records the fix and preserved exceptions. |
Review effort: Balanced
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #366 +/- ##
==========================================
- Coverage 94.40% 94.39% -0.01%
==========================================
Files 313 313
Lines 40216 40240 +24
Branches 6822 6828 +6
==========================================
+ Hits 37966 37986 +20
- Misses 1435 1438 +3
- Partials 815 816 +1
🚀 New features to boost your workflow:
|
vlad-perevezentsev
left a comment
There was a problem hiding this comment.
Review at 279275a. The per-directory rule is right for Claude Code, and the alias and import exceptions hold. It also reaches the Copilot profiles, which list CLAUDE.md, while the changelog says Copilot is unchanged; that and two smaller gaps are in the line comments.
What I ran, at 279275a:
make ci-local --offline: 0 failed, 19 passed, 13 skipped (network-only jobs).- A budget repro on this head and on the base: with Copilot's names,
sub/AGENTS.mdbesidesub/CLAUDE.mdgoes fromlazytooff;.claude/AGENTS.mdbeside.claude/CLAUDE.local.mdgoes fromstarttooff.
Checked, no issues
- Root CLAUDE trees:*
_displacedreturns every AGENTS.md for the root CLAUDE*, as before, and the single warning text is the same. - Exceptions: a CLAUDE* link to the AGENTS.md, an import from a loaded file, and an alias reached as an AGENTS.md in another directory keep it
lazy; imports of anofffile are no longer counted. - Docs and changelog: CLI help,
architecture.mdand the contract describe the rule; the fragment is under Fixed.
napetrov
left a comment
There was a problem hiding this comment.
One blocking: the new same-directory rule also fires on the Copilot profiles (inline). Unverified: the new import-cut warning on an off file (sub/d.md: import of sub/AGENTS.md is past the 4-hop limit) may just be noise.
Coverage: plain + edge passes, budget probes on base and head; no live harness, so actual Claude Code loading was not measured.
…AUDE* Review of intel#366 (comments 4445841674, 4446226790): the same-directory rule keyed on any CLAUDE* name in the profile's guidance names, and copilot.yaml and copilot-review.yaml list CLAUDE.md. A Copilot budget of a tree with AGENTS.md, sub/AGENTS.md and sub/CLAUDE.md reported sub/AGENTS.md off 7, with a Claude Code warning, where main reported it lazy, while the changelog says Copilot gets the same budget as before. The rule is now gated on a profile flag, guidance.claude_displaces_agents, an optional boolean added in place to harness_profile.v1 and set only in claude-code.yaml. budget() takes it as the keyword claude_displaces_agents (default false) and both repo budget and the reach receipt pass the profile's value. The root rule is unchanged. Tests run repo budget --harness copilot and copilot-review on the reviewer's tree (lazy) and --harness claude-code on it (off). Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Review of intel#366 (comments 4445841688, 4446226802): _is_root treats .claude/AGENTS.md as a root file, read at start, but _displaced treated it as nested, so .claude/CLAUDE.local.md or .claude/.claude/CLAUDE.md beside it turned it from start 5 (main) to off, with a new warning. The comment "at the root a CLAUDE* would have taken the branch above" did not hold for it. _displaced now skips every file _is_root accepts: only a root CLAUDE* turns AGENTS.md or .claude/AGENTS.md off. A test pins the tree with only .claude/AGENTS.md and .claude/CLAUDE.local.md (or .claude/.claude/CLAUDE.md) at start, with nothing off and no warning. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Review of intel#366 (comment 4445841700): the notes said a CLAUDE* in a parent directory does not turn a lazily reached AGENTS.md off, while budget turns every AGENTS.md off under a root CLAUDE.md, the root being a parent of every directory. The dropped sentence about one CLAUDE.md turning AGENTS.md off for the whole session was what explained it. Both cases were measured on Claude Code 2.1.316 (Haiku 4.5), with and without a root CLAUDE*. guidance.notes now say a CLAUDE* in a parent below the root does not turn the AGENTS.md off, and a root CLAUDE* file (CLAUDE.local.md included) turns every AGENTS.md off for the whole session. The contract, docs/architecture.md, the budget.py docstring and the changelog fragment say "a parent below the root" too. No code change. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Review of intel#366 (comment 4445841710): _displaced spelled out CLAUDE.md, CLAUDE.local.md and .claude/CLAUDE.md again, beside receipt.CLAUDE_ROOT, so a location added to one could miss the other. CLAUDE_ROOT moves to budget.py, which receipt.py already imports (budget imports nothing from receipt, so no cycle), and _displaced builds the names beside an AGENTS.md from it. A test checks that the receipt reads the same object and that the nested rule follows the constant. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
|
Round for @vlad-perevezentsev's and @napetrov's reviews, at 50d006c (CI green, OpenCodeReview 0 findings):
@napetrov's note on the hop-limit warning for an Not changed here, same class as the Copilot point but already on |
Conflicts: - dokimi/scripts/growth_baseline.json: both sides changed neighbouring lines. Kept this branch's dokimi/repos/budget.py::budget at 20 (the nested rule moved into a helper) and main's dokimi/repos/environment/__init__.py::inspect_repo at 22. Every other entry is main's. scripts/check_growth.py passes on the merged tree. docs/reference/cli-interface.json merged without a conflict; scripts/cli_surface.py dump regenerates it unchanged. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Settles the review point that a nested sub/AGENTS.md which is a broken link, beside a real sub/CLAUDE.md, came out `off` with the warning that Claude Code does not read it, on top of the not-readable warning, while the base budget gave it `lazy` with the not-readable warning only. _displaced now applies the same-directory CLAUDE* rule only to an AGENTS.md that resolves (_is_resolvable): a dangling or out-of-tree link keeps the status and the single warning the base gives it. A readable AGENTS.md beside a CLAUDE* file is still `off`. The root rule is unchanged. The module docstring, the contract and the changelog fragment say "that resolves". Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Brings in intel#366 (budget turns off an AGENTS.md beside its own CLAUDE* file), which adds the profile key guidance.claude_displaces_agents in the same places where this branch adds guidance.shadow_file. Each conflict was two additions at the same spot; both are kept: - dokimi/dokimi/repos/harness_profile.py: both properties, HarnessProfile.shadow_file and then claude_displaces_agents. - dokimi/dokimi/repos/profiles/claude-code.yaml: both keys under guidance, each with its comment (shadow_file: CLAUDE.local.md, then claude_displaces_agents: true), and main's reworded notes. - dokimi/dokimi/schemas/harness_profile.v1.json: both optional properties under guidance. - dokimi/docs/repo-evaluation-contract.md: both lines in the §6 example profile (shadow_file, then claude_displaces_agents); main's other contract changes merged without conflict. docs/reference/cli-interface.json, regenerated with scripts/cli_surface.py dump, is unchanged. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Summary
Follow-up to the dokimi review of IntelPython/dpnp#3099, item 5 of its follow-ups:
repo budgetmodels only a root
CLAUDE.md,.claude/CLAUDE.mdorCLAUDE.local.mdturningAGENTS.mdoff, while Claude Code also skips a subdirectory's
AGENTS.mdwhen that same directory holds itsown
CLAUDE*file.Measured with
repo reachon Claude Code 2.1.316 (Haiku 4.5), on synthetic trees with one headingand one line per file, with and without a root
CLAUDE*.sub/AGENTS.mdis not loaded besidesub/CLAUDE.md,sub/CLAUDE.local.mdorsub/.claude/CLAUDE.md, whether the session starts insub/or reaches it from the root (read=sub). ACLAUDE.mdin a parent below the root does notturn a deeper
AGENTS.mdoff on lazy loading (read=sub/deeploadssub/deep/AGENTS.mdbelowsub/CLAUDE.md). A rootCLAUDE*(CLAUDE.local.mdincluded) still turns everyAGENTS.mdoff. A lazily loaded nested
CLAUDE.mddoes not turn off the rootAGENTS.mdloaded at start.budget()onmain(dokimi repo budget <tree>), against what the harness does:sub/AGENTS.mdonmainAGENTS.md,sub/AGENTS.md,sub/CLAUDE.mdlazy,offtotal 0, no warningoffAGENTS.md,sub/AGENTS.md,sub/CLAUDE.local.mdlazy,offtotal 0, no warningoffAGENTS.md,sub/AGENTS.md,sub/.claude/CLAUDE.mdlazy,offtotal 0, no warningoffAGENTS.md,sub/AGENTS.md,sub/deep/CLAUDE.mdlazyCLAUDE.md)lazyAGENTS.md,sub/CLAUDE.md,sub/deep/AGENTS.mdsub/deep/AGENTS.mdlazyread=sub/deeplazyOn
main, a displacedsub/AGENTS.md's own@imports were counted aslazytoo.The rule is Claude Code's. It applies only with the
claude-codeprofile, which sets the newguidance.claude_displaces_agentsflag. The Copilot profiles also listCLAUDE.mdin theirguidance names, and their budget is unchanged (review of this PR: 4445841674,
4446226790).
Changes
repos/budget.py: a new helper,_displaced, decides whichAGENTS.mdfiles aCLAUDE*fileturns off: every one while a root
CLAUDE*resolves (as before). Otherwise, withclaude_displaces_agents, it is a nested one whose own directory holds aCLAUDE.md,CLAUDE.local.mdor.claude/CLAUDE.mdthat resolves (a dangling link does not count, as atthe root). One in a parent below the root, a sibling or a deeper directory does not count, and
neither do the root files
AGENTS.mdand.claude/AGENTS.md: only a rootCLAUDE*turns thoseoff.
budget()uses it both where it filters theAGENTS.mdfiles whose imports count andwhere it classes each entry, so the root rule's exceptions carry over: the file stays
lazywhen one of those
CLAUDE*files is a link to it, or a loaded file@-imports it. Since theoff decision is taken from every path that reaches a file, a file that is also reached as an
AGENTS.mdin a directory with noCLAUDE*(zzz/AGENTS.md -> ../sub/AGENTS.md) is notoff.A displaced nested
AGENTS.mdadds no imports of its own.guidance.claude_displaces_agents, an optional boolean added in place toharness_profile.v1(default false), read by
HarnessProfile.claude_displaces_agentsand set only inclaude-code.yaml.budget()takes it as the keywordclaude_displaces_agents(defaultfalse).
repo budgetand the reach receipt (_print_receipt) pass the profile's value.CLAUDE_ROOT(CLAUDE.md,CLAUDE.local.md,.claude/CLAUDE.md) moves fromrepos/receipt.pyto
repos/budget.py, and_displacedbuilds the names beside anAGENTS.mdfrom it.receipt.pyimports it frombudget, which it already imported, so the two cannot drift._off_warnings, names theCLAUDE*file for each nested case:sub/CLAUDE.md turns AGENTS.md loading off in sub/ and no CLAUDE.md imports sub/AGENTS.md, so Claude Code does not read it unless the agent opens it (import it with@AGENTS.mdfrom a CLAUDE.md beside it;repo reachmeasures it). The root-case warning is unchanged.budget()loses one branch to_off_warnings: its entry inscripts/growth_baseline.jsongoes from 21 to 20.
repos/profiles/claude-code.yamlguidance.notes: the rule measured on 2.1.316. Started in achain with a
CLAUDE*file, noAGENTS.mdloads. On lazy loading, a directory's ownCLAUDE*file turns that directory's
AGENTS.mdoff, aCLAUDE*in a parent below the root does not,and a root
CLAUDE*file (CLAUDE.local.mdincluded) turns everyAGENTS.mdoff for the wholesession. The 2.1.286, 2.1.241 and 2.1.250 notes stay.
budget.pymodule docstring (a new line after theoffbullet, and the checksparagraph),
repo budget --help,docs/repo-evaluation-contract.md(therepo budgetparagraph, the profile example with the new key, the receipt's
offrow, and the Precedenceparagraph: a nested-only displacement has no root winner, so
precedence_observedlists none)and
docs/architecture.md.tests/test_repo_budget_nested.py(tests/test_repo_budget.pyis at 996 of its1,000 lines).
Risk
With
--harness claude-code(the default),repo budgetoutput changes for trees with no rootCLAUDE*file and a nestedAGENTS.mdbeside its ownCLAUDE.md,CLAUDE.local.mdor.claude/CLAUDE.md. ThatAGENTS.mdmoves fromlazytooff, its imports drop out oflazy, and a new warning appears, sorepo budget --fail-on-warningnow exits 1 on such a treewhere it exited 0. The effective-instructions receipt reads budget's
when, so such a file'sexpectedbecomesfalseinclaude-coderuns, which is what the harness does. The same budgetas before for a tree with a root
CLAUDE*file, for.claude/AGENTS.md, and for the Codex andCopilot profiles. A direct caller of
budget()that does not passclaude_displaces_agentsalso gets the old budget.
harness_profile.v1gains one optional key, in place: every existingprofile still validates.
Testing
On a Linux review host, at
50d006c(onmain93ff3a4):make ci-local: 30 passed, 0 failed, 7 skipped (external-catalog-smoke, which needs Docker andthe network;
test-latest,docs.yml:deployandpip-audit.yml:report, whose job conditionsare false on a PR;
cli-surface / Package the result, an expression the local runner does notevaluate;
conda-forge-deps, no micromamba; semgrep secrets, which runs on the PR).12561 tests passed, 5 skipped, 6 xfailed; changed-line coverage 100% of 31 measured lines.
The matrix legs on Python 3.11, 3.12 and 3.14 were not run.
main, the three own-directory trees givesub/AGENTS.mdlazywithoff0. Here they giveoff, with the warning naming theCLAUDE*file. The deeper and parenttrees stay
lazy. With--harness copilotandcopilot-review,AGENTS.md,sub/AGENTS.md,sub/CLAUDE.mdgivessub/AGENTS.mdlazy7 onmain,off7 at 279275a, andlazy7here.
.claude/AGENTS.mdbeside.claude/CLAUDE.local.md(or.claude/.claude/CLAUDE.md) isstart5 onmain,offat 279275a, andstart5 here.tests/test_repo_budget_nested.pyhas 19 tests. Againstmain'sbudget.py, the threeown-directory cases and the displaced-import test fail. The 8 that pin what
mainalready getsright (deeper, parent and sibling
CLAUDE*, a non-Claude profile, a dangling link, the importand alias exceptions, a second
AGENTS.mdpath) pass. Against 279275a, the 6 tests this roundadds fail: Copilot (2), no flag,
.claude/(2), and one sharedCLAUDE*set. The other 13pass.
CLAUDE*counts; a parent'sCLAUDE*counts;d/.claude/CLAUDE.mdnot counted;CLAUDE.local.mdnot counted; a danglingCLAUDE*linkcounts;
exception dropped in the import filter;
AGENTS.mdkeeps its imports; the off decision taken from the shown path only;the warning names the
AGENTS.mdinstead of theCLAUDE*file._displaced;repo budgetdoes not pass theflag;
claude-code.yamldoes not set it; the profile property always false; always true;_displaceddoes not skip root files;_displacedspells theCLAUDE*names out again;receipt.pydefines its ownCLAUDE_ROOT.repo reach --scenarios shadow) onmain93ff3a4, at 279275a:pytest -n auto12575 passed, 5 skipped, 6 xfailed; mypy and ruff clean. Not yet run mergedtogether with the other follow-up branches at this round's head.
Revert
A plain revert. Nothing is stored in a new format. The one schema key is optional.
Checklist
pytestpassesruff,mypy, and schema checks passAGENTS.md.github/.licenserc.yaml)