Skip to content

Support directory-scoped AGENTS.md instructions - #1976

Merged
jonathanKingston merged 8 commits into
mainfrom
codex/nested-agents-instructions
Sep 9, 2026
Merged

Support directory-scoped AGENTS.md instructions#1976
jonathanKingston merged 8 commits into
mainfrom
codex/nested-agents-instructions

Conversation

@jonathanKingston

@jonathanKingston jonathanKingston commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Directory-scoped AGENTS.md files activate the root-to-nearest instruction chain when prompts or tools reference a path. First edits into a new scope are deferred once so the agent sees its guidance before writing. Trust, canonical path boundaries, nested checkout/generated-directory exclusions, file-size and prompt budgets remain enforced. Settings shows active, scoped and duplicate sources and reports incomplete inventories.

Turn-time discovery reads only referenced ancestor scopes, memoized within the turn. It caches missing files, invalidates discovery after file-tool changes to AGENTS.md, and starts fresh next turn. Already-injected instructions remain fixed for that turn. Settings retains its bounded full inventory. A repeatable real-activation benchmark over 10,000 directories measured 596–773 ms before and 5–14 ms after on this macOS host; these are local measurements, not platform-wide guarantees. The strategy and benchmark are committed.

This branch incorporates #2597 to order API changes: PR activity uses v4 and nested-instruction metadata uses v5. Merge #2597 first.

Validation:

  • Full static checks passed; combined typecheck, formatting, dead-code and E2E syntax passed after protocol reconciliation.
  • Protocol comparison against Show PR comments, reviews, and individual CI checks #2597 passes (v4 → v5).
  • Full combined unit suite passed with one file worker: 8,802 passed, 7 skipped, 0 failed. Parallel runs hit different unrelated two-second hook subprocess timeouts; the affected hook suites passed unchanged in isolation. No assertions or timeout limits were relaxed.
  • Both focused Electron specs passed on the combined branch: nested instructions and PR activity. Screenshots were inspected; nested coverage includes active/scoped rows, duplicate labeling and truncation.

Closes #1354

Latest integration validation: merged main through 92d2330cb, fixed same-millisecond activation ordering with a monotonic sequence, and verified the frozen-clock regression fails before the fix and passes afterward. The complete pnpm run check passed: 8,845 tests, 7 skipped, zero failures. Build and the focused nested-instruction Electron eval passed again; screenshot inspected.

@github-actions

Copy link
Copy Markdown
Contributor

Merge conflict needs a human

main advanced and screenshot conflicts were resolved automatically, but these non-screenshot files conflict and can't be auto-merged. Resolve them by hand (merge or rebase main):

  • src/main/services/agent-service.ts

@jonathanKingston
jonathanKingston force-pushed the codex/nested-agents-instructions branch 2 times, most recently from a230cdb to f631a03 Compare August 31, 2026 18:16
@jonathanKingston

Copy link
Copy Markdown
Collaborator Author

Review outcome: directory-scoped AGENTS.md is meaningful, but this draft changes instruction discovery, trust, symlink and prompt-budget behavior across 16 files and 1,130 lines, is conflicting, and still had full-suite timing failures. Keeping it as a draft for focused security/instruction-precedence review; not approved for merge in this sweep.

@jonathanKingston jonathanKingston left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: directory-scoped AGENTS.md instructions

Verdict: changes needed. CI has never run on this head, so I ran the gate locally on f631a03 and it passes. The blockers are a runtime-cost problem in the discovery cache and a rebase (191 commits behind, one mechanical conflict), not broken code.

Validation on the head

Check Result
pnpm run typecheck pass
pnpm run lint pass
prettier --check on the 15 changed files (this branch predates oxfmt) pass
pnpm run check:dead-code pass
pnpm run check:oracle pass (229 specs live, 15 invariants hold)
pnpm run check:e2e-syntax pass
pnpm test -- project-instructions agent-service agent-system-prompt settings-sources context-estimate 93/93 pass (after rebuilding node-pty for Linux; the initial load failures were environmental)
pnpm run check:screenshots 228 stale, an artefact of the 191-commit gap
WebdriverIO spec not run, no Electron binary in this sandbox
Dry merge with origin/main one conflict: src/main/services/agent-service.ts at the buildSystemPrompt call, where main inserted the invokedAgent block from #1860. Nothing this PR touches was moved into a package, and main has had no nested-instruction work in this area since the merge base.

Blocking

  1. A tree walk in front of nearly every file tool call. NESTED_DISCOVERY_CACHE_MS = 1_000 (project-instructions.ts:34) means activateNestedInstructionSources (:536) re-walks up to 10,000 directories with sequential readdir awaits on every read_file, list_dir, search_code, write_file and the other mapped tools (agent-service.ts:377-388, wrapper at :1672-1701) whenever more than a second has passed since the previous call. The same walk runs on every composer context estimate (context-estimate.ts:60 now passes the draft) and on every Settings open (refreshNestedDiscovery: true in register-handlers.ts). On a large monorepo an agent reading 30 files in a turn pays 30 multi-thousand-readdir walks. Discover once per turn, or key the cache on something that changes (mtime, a watcher), rather than a 1s TTL.

  2. Rebase and get CI on the head before this leaves draft. The conflict resolution is mechanical.

Security

  • Nested files inherit workspace trust with no per-file gate. Once the root is trusted, any directory without a .git (vendored trees, examples, fixtures) can contribute system-prompt text. The agent can also write_file a nested AGENTS.md and have it injected on the next read under that directory within the same turn. The trust="untrusted" envelope applies, so this extends the existing surface rather than adding a new class, but there is no transcript signal when agent-service.ts:1688 appends to the leading system message mid-turn; the only visibility is Settings → Sources, for the latest turn, in process memory. Please emit a transcript notice when a block is injected.
  • Symlink, traversal, NUL and out-of-root handling look correct and are tested: symlinked directories are never walked, a symlinked AGENTS.md must realpath inside the canonical root (:124-153), context paths are realpath-checked via an existing ancestor (:232-257). Bounds (depth 16, 10k dirs, 200 files, 32KB per file, 8 files / 64KB active) are sensible, but truncation is silent: alphabetical DFS stops at the caps, so an AGENTS.md in a later-sorted tree is never applied and nothing reports it. Log it and mark it in Sources. .gitignore is not honoured; the skip list is hardcoded.

Non-blocking

  • Mutating the leading system message mid-turn invalidates provider prompt-cache prefixes on every activation and bypasses the budget computed by prepareAgentHistory (up to +64KB).
  • The deferred edit (agent-service.ts:1695-1699) returns a success-shaped string; if the model does not retry, or maxSteps is hit, the edit silently never happens.
  • docs/user/project-instructions.md overclaims: activation only happens for the ten built-in tools in INSTRUCTION_CONTEXT_PATH_FIELDS; run_shell writes, ACP agents and explore or todo-worker subagent reads do not trigger it. Narrow the wording.
  • deduplicateSources (:301-316) drops a nested file whose content equals the root file from the Sources list entirely.
  • lastNestedActivationByProjectRoot is keyed per project root, so concurrent threads in one project overwrite each other's "active this turn" state.

No as casts beyond as const in tests, no suppressions, no exported type predicates, no object-literal casts. The e2e spec and its reference PNG are present and the oracle sees the spec.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Pushed four commits. e0933bb merges main (the one conflict at the system-prompt build keeps #1860's invokedAgent block ahead of this PR's buildSystemPromptWithMetadata call; no dependency changes). 965ca10 replaces the 1-second cache with a per-turn memo: the system-prompt build seeds one walk, every activateNestedInstructionSources in that turn reuses the memoised promise, the composer's context:estimate uses a shared 30 s cache, Settings still forces a reload, and any edit tool touching an AGENTS.md invalidates the memo so the next file tool re-walks; it also emits one _Loaded directory-scoped instructions from ..._ line per activation, buffered so it lands after the tool result rather than between a tool call and its result. 68a5bc4 records discovery truncation (discoveryTruncated on project rows, a console.warn, and a note in Sources) and keeps content-duplicate nested files listed with a "duplicate" badge instead of dropping them, while keying the "active this turn" record per thread. 4be167d narrows the user doc to the tools in INSTRUCTION_CONTEXT_PATH_FIELDS and documents the once-per-turn discovery. Full format check, typecheck, lint, dead-code, oracle, e2e-syntax and 139 targeted tests pass on the merged tree. The new badge and truncation note have DOM unit coverage but no new e2e screenshot, since that would change the committed reference PNG; worth a follow-up visual spec.


Generated by Claude Code

github-actions Bot added a commit that referenced this pull request Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

🖥️ PR preview

@jonathanKingston

Copy link
Copy Markdown
Collaborator Author

Readiness review: per-turn memoization avoids repeated discovery on every tool, but the first look of every turn still performs a fresh serial tree walk (up to 10,000 directories), bypassing the 30-second discovery cache. I am holding this draft for a large-repository turn-start latency measurement and an agreed invalidation/indexing strategy; the existing focused correctness tests do not establish that cost.

@jonathanKingston

Copy link
Copy Markdown
Collaborator Author

Current audit: the latest precheck failure is an npm-registry socket timeout during pnpm audit, not a demonstrated source regression. That does not make the PR ready: it is 26 commits behind main, and the substantive large-repository blocker remains unchanged — the first nested-instruction lookup each turn can serially walk up to 10,000 directories without a measured latency budget or agreed indexing/invalidation design.

Keep draft and avoid spending CI on a rebase until that design/evidence exists. #1354 already tracks the feature, so no new issue is needed; the next useful work is the bounded large-repository benchmark/investigation recorded here.

@jonathanKingston
jonathanKingston force-pushed the codex/nested-agents-instructions branch from 4be167d to 1afad97 Compare September 6, 2026 09:19
github-actions Bot added a commit that referenced this pull request Sep 6, 2026
jonathanKingston and others added 4 commits September 6, 2026 20:16
The nested AGENTS.md walk was cached for one second, so a turn re-walked
up to 10,000 directories before nearly every file tool call. A turn now
creates a discovery memo (`createNestedInstructionTurn`) that the system
prompt build seeds with one fresh walk; every `activateNestedInstructionSources`
call of that turn reuses it. Callers outside a turn (the composer estimate)
share a 30 s cache, and Settings still forces a reload.

An edit tool whose path is an AGENTS.md invalidates both the memo and the
shared cache after it runs, so the next file tool call sees the file the
agent wrote, moved, or removed. `run_shell` writes are not observed.

Each mid-turn activation now adds a one-line transcript notice naming the
file, emitted as a text chunk at the next step boundary rather than between
a tool call and its result (a text chunk there would start a new assistant
bubble and strand the tool card).

Discovery records when it stopped at a directory, file, or depth cap:
project sources carry `discoveryTruncated` and the walk logs a warning.
A nested file that repeats an already-listed source stays listed and is
marked `duplicateOf` instead of being dropped; its content is still
injected once. The latest-activation record for Settings is now keyed per
thread under the project root, so concurrent threads no longer overwrite
each other; Settings reads the most recent thread.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011jaQbed96h8aSZLijfiHNB
…urces

`instructions:list` passes `duplicateOf` and `discoveryTruncated` through
to the renderer. Sources → Instruction files marks a nested file whose text
repeats a listed one with a quiet "duplicate" badge and says which file
carries it, and appends a single note under the list when nested discovery
stopped at its cap, so a missing deep file no longer looks unwritten.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011jaQbed96h8aSZLijfiHNB
Name the built-in file tools that bring a path into context and say that
`run_shell` writes, ACP agents, and subagent reads do not activate a nested
AGENTS.md. Describe the once-per-turn discovery, the re-walk after the agent
writes an AGENTS.md, the transcript notice, the duplicate badge, and the
truncation note in Sources.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011jaQbed96h8aSZLijfiHNB
@jonathanKingston
jonathanKingston force-pushed the codex/nested-agents-instructions branch from 1afad97 to cc4252e Compare September 6, 2026 20:17
github-actions Bot added a commit that referenced this pull request Sep 6, 2026
…s shape

`ProjectInstructionSummary` gains a required `trusted: boolean` alongside the
optional `scopePath` / `duplicateOf` / `discoveryTruncated`. A new *optional*
result field would have been additive, but a required one is not, so
`gen-api-protocol.mts --compare-ref` classifies `instructions:list` and
`client.instructions.list` as breaking and fails the gate at version 2.

The gate did not exist when this branch forked; it arrived on `main` with the
protocol freeze, so this is the first run that has had to answer for the shape
change. Bumping is the right side of the trade: `trusted` is a real property of
every instruction source, and weakening it to optional purely to stay additive
would put a lie in the type.

The manifest is regenerated with `gen:api-protocol`, not edited: only its
`version` moves, because the manifest records channel arity and binding names,
which this change leaves alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BCcGaKPRL3fmRpLR3rK5WX
@jonathanKingston
jonathanKingston force-pushed the codex/nested-agents-instructions branch from cfd2d06 to d61678f Compare September 6, 2026 20:49
@jonathanKingston
jonathanKingston marked this pull request as ready for review September 9, 2026 02:41
github-actions Bot added a commit that referenced this pull request Sep 9, 2026
@copse-release-bot

Copy link
Copy Markdown
Contributor

Reference screenshots ready for review

Review GitHub’s image diffs in screenshot PR #2621.
Merge it (or enable auto-merge) to apply the accepted PNGs to codex/nested-agents-instructions.

Rendered for 6e10d3c76be7 by CI run 34304241845; the immutable artifact is reference-screenshot-candidates-34304241845.

If this source branch moves, a later successful render closes the stale review PR and replaces this link.

github-actions Bot added a commit that referenced this pull request Sep 9, 2026
@jonathanKingston
jonathanKingston merged commit b363c5b into main Sep 9, 2026
23 checks passed
@jonathanKingston
jonathanKingston deleted the codex/nested-agents-instructions branch September 9, 2026 03:32
github-actions Bot added a commit that referenced this pull request Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support nested AGENTS.md files with directory-scoped instructions

2 participants