feat(agent-mode): make AGENTS.md the canonical instruction source - #2706
Conversation
…pt UI Follow-up to the AGENTS.md canonicalization. Six fixes found reviewing it against issue #238's success criteria. Instructions reached no backend for existing projects. Removing the generated mirror also removed the only thing that put a project's instructions where codex/opencode/Claude look for them, and the two new call sites are both click handlers — so every project whose instructions live in the `project.md` body sent nothing at all until the user happened to open the popover row. `ensureAgentsFileForDiscovery` runs that same initialization at session start. It creates nothing when there is nothing to preserve, so a fresh project folder stays empty and the issue's "no bulk migration" constraint holds. This also stops an un-opened legacy mirror from double-injecting the workspace policy and todo steering, both of which moved into the always-on prompt. Chat mode lost its Default System Prompt picker and its "add prompt" button, but `defaultSystemPromptTitle` is still read by ChatManager and still labeled "(Default)" in the chat gear — leaving the setting live with no UI to change it, and `SystemPromptAddModal` dead. Both are restored under the new "Chat system prompts" section. The vault AGENTS.md now seeds from the user's effective custom prompt on first open, mirroring how a project's file seeds from `project.md`: that prompt WAS Agent Mode's user instructions before this change, so carrying it over keeps existing setups working. The landing-capture signature still fingerprinted the `project.md` body, which no longer determines what a session captures. It now stats the project's AGENTS.md, so editing instructions replaces a stale empty landing (and editing the inert body no longer discards a good one). The project workspace policy moved back outside the "disable builtin" branch. It is operational wiring — where the agent may write and read — and pre-change it rode the mirror and `<project_instructions>`, neither of which that toggle suppressed. Also: `lastError` clearing is now sequence-guarded instead of relying on microtask ordering between two creates' ready chains (the added await before `newSession` would otherwise let a success wipe a sibling's failure); CLAUDE.md is hidden from the project file list; a hidden-folder AGENTS.md reports its path instead of opening an empty leaf; the Claude import check no longer appends a duplicate for `@./AGENTS.md` or an import below a heading; PR2b-1 residue keeps only its body, not its config frontmatter; and the dead chat-prompt restart subscription is gone. Verification: tsc clean; 343 suites / 4867 tests pass; eslint clean on all 18 changed files (the full-repo `npm run lint` was still running after 70 minutes and was not waited out). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WTuR9SEu7Bn6TQMdCXqDbc
Base moved six commits ahead; the prompt builder conflicted with #2715's document-processor steering. Resolution keeps both: base's `COPILOT_PLUS_DOCUMENT_STEERING` / `COPILOT_MIYO_DOCUMENT_STEERING` selection stays inside the builtin branch, this PR's `AGENT_TODO_PLANNING_STEERING` follows it, and `COPILOT_PROJECT_WORKSPACE_POLICY` stays outside the branch. The test file took this PR's restructured suite — base's copy still asserted the `<user_custom_instructions>` and `<project_instructions>` paths this PR removes — with base's two genuinely new cases (proactive web routing, the Miyo fail-closed document route) re-added on top. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WTuR9SEu7Bn6TQMdCXqDbc
Budget-gate cut: a three-line wrapper over `getProjectLandingCaptureSignature(this.app, record)` with two call sites earns less than it costs. Both sites now call it directly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WTuR9SEu7Bn6TQMdCXqDbc
Upstream moved every Copilot sub-folder under one configurable root (#2695) and restructured `projectPaths.test.ts` into the nested per-callable shape. Both touched files this branch also edits. - `ensureAgentsMirror.{ts,test.ts}`: stay deleted. Upstream only re-pathed them; this branch removes the generated mirror outright and nothing references it. - `projectPaths.test.ts`: take upstream's structure, which already carries everything this branch had plus `getProjectAnchorFromConfigPath()` tests. Drop "mirror" from the two AGENTS.md test titles, since there is no mirror. - `AdvancedSettings.tsx`: take upstream's removal of the System Prompts Folder Name control; the folder now derives from the Copilot root. - `docs/`: take upstream's Copilot-folder wording. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
…prompt Each harness loads instruction files its own way. Pinned opencode 1.16.0 walks up from the session cwd and collects every ancestor AGENTS.md nearest-first, so a project session receives the project file BEFORE the vault one — the reverse of Copilot's intended broad-to-specific order — and neither that order nor Codex's and Claude's is settable through a documented seam. Its `instructions` config array cannot fix it either: those paths are merged into the same Set that discovery already filled, so an already-discovered file keeps its position. Stating the rule in prompt text is the one place it holds for every backend at once, and it costs the same handful of bytes in all of them. Also locks the cache contract this PR exists for: the product prompt is a provider cache prefix, so the new tests assert byte equality (`toBe`) across Chat-prompt selection, model, project and folder changes, and assert the prompt carries no path, date or session id. Containment assertions would pass while extra bytes shifted everything after them out of the prefix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
…ENTS.md Splits the two things `project.md` was carrying. It keeps the project's context metadata — folders, notes, URLs — and its instruction text moves to `<project>/AGENTS.md`, the file every backend already discovers from the session working directory. Keeping both would leave two sources of truth for the same instructions, one of which no backend reads. The move is guarded so it can only happen once and can never lose text: it needs prompt text to move AND no existing AGENTS.md to overwrite, it writes the new file before clearing the old field, and a failed write leaves `project.md` untouched. It runs at session start and when the user opens the file from the project popover, so instructions are in place before the backend spawns rather than waiting for a click. The vault-level file no longer seeds from the selected Chat prompt. A blank AGENTS.md is the honest starting point: the Chat prompt is a Chat artifact the user switches freely, and freezing whichever one happened to be selected into a permanent vault file is a surprise, not a migration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
…mpt controls The AGENTS.md row belongs where someone goes looking for it. Basic is the tab that already owns "where your files live"; Advanced is for logs and diagnostics. Advanced's "User system prompt" section is gone. Its global default picker fed a Chat-mode behavior Agent Mode no longer reads, and the prompts-folder box was already dead — upstream derives that path from the Copilot root. The per-chat picker in the chat settings popover is untouched, so v3 Chat users keep the control they actually use. In its place, one notice, shown on both tabs and only to users who saved prompts: their files are still in the system-prompts folder, and here is where to paste anything the agent should keep following. Nothing is copied for them — the selected Chat prompt is a Chat artifact they switch freely, and freezing whichever one was selected into a vault-wide file would be a surprise rather than a migration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
The `claude_code` preset stamps working directory, git status, and memory paths into the system prompt. Those change when the user switches project or simply when the date rolls over, which invalidates the cached prefix ahead of everything after it — including the Copilot prompt this PR just made byte-stable. `excludeDynamicSections` is the SDK's documented answer: it strips those sections and re-injects them as the first user message, so the model keeps the facts while the prefix stops moving. Sent unconditionally, since it concerns the preset rather than Copilot's append. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
…nd codex seams Both backends already had containment checks, which pass while stray bytes push everything after them out of the provider cache prefix. These assert equality instead, at every seam Copilot controls: both opencode managed agent prompts, and both codex `developer_instructions` paths. Each also varies something a session carries but the prompt must not see — a different model and binary path for opencode, a different vault path for codex — and asserts the bytes do not move. The opencode case additionally pins the decision not to configure its `instructions` array: discovery already collects every ancestor AGENTS.md, so the config would be dead weight, and inlining the text would put user bytes back into the prefix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8fc020dd70
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Records why this change exists, in one place a newcomer can read start to finish: the three homes user instructions used to have, the captured OpenCode request with each segment annotated, and why the 367-character runtime block sitting 6,162 characters into the system message is what re-processes 41,249 characters of unchanged instructions and skills every time the date rolls over. Also writes down the findings that shaped the design and are not visible in the diff: opencode's `findUp` already collects every ancestor AGENTS.md, its `instructions` config array cannot reorder them because those paths dedupe into the discovery Set, and its filename loop stops before CLAUDE.md so the sibling import file is invisible to every backend but Claude. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 41669a8f3f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…md mirror Three review findings, all in how a project's instruction file is located and initialized. A generated mirror is not the user's file. `moveProjectPromptToAgentsFile` treated any existing AGENTS.md as user-authored and returned, so a project still holding an older build's marker-owned mirror kept its legacy text in `project.md` AND left the mirror for the following blank ensure to overwrite, which emptied it. The move now asks `agentsFileIsUninitialized`, true for an absent file or a marked mirror, so the mirror gets converted to the legacy body instead of blocking the move. `convertLegacyGeneratedFile` also stops blanking a mirror when the caller has nothing to put in its place: with empty content it strips the marker and keeps the body, closing the same bug on the path where there was never any legacy text to migrate. The projects root is not the record's root. `resolveScopeCwd` derives the session cwd from `dirname(record.filePath)`, so resolving the folder from the live root writes AGENTS.md into a directory the agent never opens during the second a Copilot-folder change takes to propagate. Every site now anchors on `getProjectAnchorFromConfigPath(record.filePath)`: the move, the session-start ensure, the project popover, and the landing fingerprint. A hidden root cannot be fingerprinted. Under a `.copilot`-style root Obsidian never indexes AGENTS.md, and once the move clears `project.md` the fallback body is empty forever, so an edit could never invalidate an empty landing session. The fingerprint now reports `agents:unverifiable` for a dot-segment path and the reuse gate spawns fresh rather than hand back a backend that read the file before the user's last edit. Tested on the path, not the cache, which is also empty for a folder Obsidian has merely not indexed yet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a679e1e7b8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
With no QA patterns configured — the default — createCopilotPatternFilter returned true after only the system-root check, so the instruction-file exclusion lived exclusively in the TFile branch that never ran. Vault-root AGENTS.md and CLAUDE.md then surfaced in relevant-note and Miyo results despite being agent-facing content this PR promises to keep out of retrieval. The raw-path check now runs ahead of the fast path, covering both consumers through the one shared filter. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a2ece8f44d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The product prompt's project-workspace policy triggers on the presence of a <project_context> block, because the prompt itself must stay byte-identical across scopes. But the materializer's two source-less paths returned no block at all, so a project with no configured context sources never armed the policy: the backend started in the project folder with the agent never told to keep writes under outputs/ or stay scoped. The retired generated mirror used to carry that policy unconditionally, so this branch had regressed it. Those paths now emit a minimal block naming the project workspace, which arms the policy on the session side and leaves the prompt bytes untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bc503c2d08
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…he upgrade note Two review findings on round six. The project popover hid CLAUDE.md by basename, which was right for the import-only wiring Copilot writes but wrong the moment a user puts rules in that file — Claude reads them as live instructions with no visible surface anywhere in the UI. Visibility now follows content: `isClaudeImportOnly` (exported from agentsFile so it shares the append guard's line pattern) hides pure wiring and lists anything the user wrote. The file list becomes effect-backed state since content reads are async. docs/system-prompts.md still promised the vault AGENTS.md initializes from the Agent Mode custom prompt, a sentence stranded from before that migration was reversed. A user trusting it would skip copying their instructions and run with a blank file. The upgrade section now says what ships: project text moves automatically, the vault file starts blank, and the old prompt files stay in the system-prompts folder that the settings notice points at. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5208f6122a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… shipped prompt flow isInternalExcludedPath compared exact-case while matchSystemRoots already folds on case-insensitive filesystems. On macOS/Windows a pre-existing `agents.md` IS the file the backends read when they ask for `AGENTS.md`, so those live instructions could still reach QA/Miyo indexing. The exclusion now folds behind the same hasCaseInsensitiveFilesystem gate, covering the root filenames and the project-folder basename check; Linux keeps exact matching. The docs still walked users through UI this branch removed: the Advanced "From Settings" prompt-creation steps, the global-default dropdown, and an Advanced-tab path for the AGENTS.md row that now lives in Basic. Rewritten to the shipped flow — create prompt files in the system-prompts folder, pick per conversation from the chat gear — with a note that a default set by an earlier version keeps applying until a conversation picks another prompt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 870d59e781
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…a project v4-preview's deleteProject dropped the marker-owned AGENTS.md mirror; this branch had lost that, so deleting a never-migrated project left the stale mirror behind, kept the folder from emptying, and let a same-named project created later convert that mirror at session start and inherit the dead project's instructions. Delete now removes exactly what Copilot generated — a marker-owned mirror and an import-only CLAUDE.md — through one remover in the instructions module that reuses the same ownership predicates the ensure path uses. Anything the user wrote stays preserved, matching the delete path's existing contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 60a9ed62a3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The notice is new user-visible React work, and the gallery workflow that landed on v4-preview asks for an adjacent story covering its meaningful states. The component read plugin state through hooks, so per the workflow's presentational-boundary rule it splits into LegacyChatPromptsNoticeView (count + folder as props, renders nothing at zero) with the existing export as its store-connected wrapper. The story renders the plural, singular, and empty states from fixture props. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 90a628bbb2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ssions too Claude sees a vault-root AGENTS.md only through the sibling CLAUDE.md import, and that import was written by the global-scope ensure alone. A user who hand-created the root file and whose first session was a Claude project session had their vault-level instructions silently ignored until they happened to open a global session or Settings. Project scope now runs the same root-level ensure the global branch runs. It creates nothing when no root AGENTS.md exists, so a fresh vault still gets no conjured files, and opencode/codex are unaffected since they discover the root file natively. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c795232f47
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…t modal Opening a file in Obsidian was the only way to write instructions, which made the settings row a signpost rather than a control. Both surfaces now carry a textarea backed by the same AGENTS.md the agent reads: settings edits the vault file, the project modal edits the project's. Settings saves as the user types (debounced, flushed on close, since settings have no Save button); the modal saves when the dialog is accepted, before the project write, so a rename carries the file along with its folder. Titles and descriptions drop "Agent Mode", which no longer distinguishes anything in v4, and the project field states that it supersedes the vault one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ff65553cfe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…Project The settings row centers its label column against the control, which left the title floating beside the middle of a tall editor. The project dialog was still at Obsidian's default width, too narrow now that it carries an instructions editor; it takes the same min-width as the context dialog it opens. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
…tors An empty instruction box said nothing about what belongs in it. Both editors now type examples through the placeholder the way the chat composer does, reusing its typewriter machine: one types in, holds, clears, and the next follows. The examples name conventions of the user's own vault rather than ways to format an answer, which is what is worth writing into an AGENTS.md once. Lives in its own leaf component so the animation re-renders the textarea alone rather than the settings tab or the project dialog around it, and it stops while the field has text. The editors also gain an accessible name, which their hosts could not give them: both render the visible title as text, not a label. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7b108edd89
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Both branches deleted the block the other kept in `session/types.ts`: v4-preview retired `McpServerSpec` with managed MCP configuration (#2749), this branch retired `ProjectProfile` when project instructions moved to AGENTS.md. Apply both deletions, and drop the SDK's `setProjectProfileProvider()` suite along with the method it covered. The warm-backend reuse test flushed a fixed microtask count to observe the in-flight preload; `createSession` now awaits the scope's instruction-file ensure first, so the tick count in front of the preload is no longer a stable fact. Flush a macrotask instead, matching the surrounding tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Editing a project from Agent Home before entering it read only AGENTS.md, so an upgraded project whose instructions still sat in `project.md` rendered a blank box over live text — and typing there stranded the old body. Run the same one-shot move session start runs, then read. The dialog also wrote AGENTS.md before `onSave`, which is right (a rename carries the file along with its folder) but left the edit persisted when the project update rejected and the modal stayed cancelable. Capture the previous body and put it back on failure. `resolveFileByPath` matches the vault cache exactly and otherwise returns a synthetic adapter file, so on a case-insensitive volume a vault holding `agents.md` took that path for `AGENTS.md`: reads and writes bypassed Obsidian's file state, and Open called an ordinary note unreachable. Resolve the cache case-insensitively first, and key the read/write split on the resolved file's own path rather than the canonical spelling. Gallery coverage for both instruction editors, per the component-gallery workflow. Neither surface could render from fixture data, so each gets the presentational seam the guide prescribes: the popover's file rows split from the record/CLAUDE.md resolution, and the dialog's instruction field split from a form that cannot mount without a live project record. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`setSessionMode()` gained its permission-mode suite upstream (#2765) in the same block this branch emptied of `setProjectProfileProvider()`. Keep the new suite, drop the retired one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e7576e575f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The case-insensitive AGENTS.md lookup was unconditional, which is only ever a repair on a volume where two spellings name one file. On a case-sensitive Linux vault it adopted an unrelated `agents.md` as canonical: the exact-name file the backends discover would never be created, and instructions edited through Copilot would land where no agent reads. Gate it on the same platform predicate the search exclusions use, moved to `vaultAdapterUtils` now that it has a second caller. Settings' Open button flushed the debounced save without awaiting it, so `openAgentsFile` raced the write. Both create the file when it is missing, so the loser failed with already-exists and took whatever was typed inside the debounce window with it. Return the write from the debounced callback and await the flush. A whole-materialization failure returned a result with no `<project_context>` block, and the workspace policy triggers on that block's presence — so a broken context load silently stopped telling the agent to keep writes under the project's `outputs/`. The shared frozen result now carries a block saying the context could not be loaded, still with no `contextSignature`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…og open `resolveInstructionFile` walked every file in the vault before asking whether the path exists at all. The overwhelmingly common call is a scope with no instruction file yet — session start asks on every new session — so that scan ran twice per project session on the path that finds nothing. Let `resolveFileByPath` answer first: a null means no file under any spelling (on a case-insensitive volume `exists` already answered for all of them), so the scan now runs only when something exists that the cache missed under this exact name, which is the only state it can repair. The project editor ran the legacy `project.md` move when it opened, so merely opening the dialog wrote to the vault and Cancel could not undo it — and `formData` still held the old prompt, so saving wrote the legacy text straight back and left two live copies. Seed the field from `project.md` for display only, and let the save complete the move by clearing `systemPrompt` alongside the AGENTS.md write it already does. A user-authored AGENTS.md keeps its own body and the legacy copy is left alone, matching what `moveProjectPromptToAgentsFile` refuses to overwrite. The test vault's adapter now folds case when the platform says the filesystem does, which is what makes a cached `agents.md` look like an uncached `AGENTS.md` to the resolver in the first place. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2c6cc5ffed
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
An AGENTS.md the user deliberately cleared reads the same as one that was never created, so the editor seeded it from `project.md` and a save of any unrelated field wrote deleted instructions back. Ask `agentsFileIsUninitialized` instead — absent or a Copilot-generated mirror — which is the same predicate the session-start move consults, so the two agree on which files are Copilot's to initialize. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2863ffa8c4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Upstream folded Agents into Basic (#2713) and added Property as a context source (#2723). The Basic conflicts are additive on both sides — the desktop gate and the legacy-prompts notice, the desktop mock and the system-prompts mock. `projectContentTracker` keeps upstream's design note about the absent `propertyPatterns` branch, with this branch's file list (there is no generated mirror any more). `projectSystemPrompt` stays deleted: upstream only added a `properties: []` fixture field to its test, and the merged tree has no callers left. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three findings, one root cause: file *contents* were standing in for questions about a file's *state*, and "" answered for both "absent" and "the user emptied it". The dialog's rollback wrote the captured body back, so a failed project update on a project that had no AGENTS.md left a blank, markerless one — which `agentsFileIsUninitialized` then reports as user-owned, permanently blocking that project's legacy `project.md` move and stranding its instructions. Capture a snapshot of both instruction files instead, and restore it: files the write created are removed, an existing body is put back, and a deliberately empty file stays empty. `removeGeneratedInstructionFiles` deleted any import-only CLAUDE.md as Copilot's own. Unlike AGENTS mirrors it carries no ownership marker, so a CLAUDE.md a user wrote as `@AGENTS.md` to share their own rules with Claude was indistinguishable — and hard-deleted, not trashed, under a hidden project root. Leave it: a stale import holds no instructions, so keeping it costs nothing and deleting it can only destroy. The landing fingerprint still resolved AGENTS.md exact-case, so on a vault holding `agents.md` it missed the file the editors write and fell through to the legacy body — empty after the move — giving a signature that never moved however the user edited their instructions. Share the resolver via `findCachedInstructionFile`. That resolver now looks for a cased variant among the folder's own children rather than walking every file in the vault: these paths are built from a folder Obsidian itself cased, so only the file name can disagree. Cheap enough for the synchronous fingerprint path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0b0d425f36
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Debouncing bounds how often a write starts, not how long one takes. On a synced or network-backed vault a save can still be in flight when the next debounce fires, and if that older write lands second it overwrites the newer text — no error, nothing on screen, the user's last sentence gone. Chain the writes behind one promise so they land in the order they were made, which also gives `flush()` something that settles only once every queued write has landed rather than just the last one. The queue costs each write a microtask before it starts, so the fake-timer tests advance inside an async `act`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 78f4d8833e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The Agent Mode section still sent users to a **Agent instructions** section that no longer exists, and told them Copilot has no Settings editor for these files — which stopped being true when the inline boxes landed. Both scopes now have two surfaces that write the same file; say so, and name them as they appear. The FAQ carried the same dead path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 33144a711c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Fixes logancyang/obsidian-copilot-preview#238
Design doc: https://symposium.site/d/z5f861mdfb84evna (also in this PR at
designdocs/AGENT_INSTRUCTIONS_AND_PROMPT_CACHING.md)Summary
Why. Agent Mode user instructions had two Copilot-managed sources of truth, and both leaked variable data into the one part of the request Copilot controls. The selected Chat prompt was copied into
buildAgentSystemPrompt(); project instructions lived in theproject.mdbody and were copied into a generatedAGENTS.mdmirror the user could not safely edit.That second problem is a cache problem. In a captured OpenCode 1.16.0 request the joined system message is 47,781 characters, and the Copilot product prompt is its first 6,162:
A provider's prefix cache matches from byte 0 and stops at the first byte that differs. The runtime block is the wall: it carries today's date, so when the date rolls over — or the user switches model, project or vault — the 41,249 characters after it are re-processed even though nothing in them changed. Copilot cannot move that block; it is stamped inside opencode. What Copilot owns is the product prompt, the one region sitting before the wall, and that was the region carrying per-user, per-project, per-prompt-switch text.
What changes. Instructions move out of the product prompt into
AGENTS.mdfiles, which land after the wall in a region that was already being re-processed. The product prompt becomes byte-identical in every vault, project, model and session.User experience
Instructions are plain
AGENTS.mdfiles: one at the vault root, one per project folder. Each has a text box that edits the file directly — Settings → Basic → Custom instructions for the vault one, the Edit Project dialog for a project's. The same text is still an ordinary note, so anyone who prefers a full editor can open it in Obsidian and both views agree. Settings saves as you type; the project dialog saves when you accept it.Nothing is auto-migrated into the vault file — it starts blank. Anyone who had saved Chat system prompts sees a notice on both Basic and Advanced naming the folder those files are still in, so they can paste across whatever they want the agent to keep following. Project instructions typed into the old Project System Prompt box are moved automatically into that project's
AGENTS.md, once, withproject.mdkeeping the context metadata.Advanced loses the global Default System Prompt picker and the prompts-folder box. The per-chat prompt picker in the chat settings popover is untouched, so v3 Chat keeps the control people actually use. Switching prompts, changing project, or starting a session no longer restarts the agent backend.
AGENTS.mdandCLAUDE.mdno longer appear in Copilot search results.Decisions
instructionsarrayfindUpcollects every ancestorAGENTS.md, nearest-first — so a project session already receives both files, with the project one before the vault one, the reverse of the intended broad-to-specific order. No documented seam reorders that, on any harness. One sentence in the product prompt does.project.mdsplits. It keeps context metadata (folders, notes, URLs); its instruction text moves toAGENTS.md, where every backend already looks. The move needs text to move AND no existingAGENTS.mdto overwrite, and writes before clearing, so it runs once and cannot lose text.excludeDynamicSections: truestrips cwd, git status and memory paths out of theclaude_codesystem prompt and re-injects them as the first user message — the documented SDK answer to the same volatile-runtime problem opencode gives us no lever for.AGENTS.md, and a v3 chat project simply stops seeing the moved text.lastErrorSeqstays. It was flagged as unrelated scope, but it is not: this PR adds an await beforenewSession, which reorders two concurrent creates' ready chains so a success can wipe a sibling's error banner. Removing it fails a test that predates this branch (c2ac27ed). It is this PR's own regression fix.Changes
fb603183— merge latestv4-preview. Upstream unified Copilot sub-folders under one root and restructuredprojectPaths.test.ts; the generated mirror stays deleted, and the retired prompts-folder control takes upstream's removal.649ef193— add the AGENTS.md precedence rule to the product prompt, and lock the cache contract with byte-equality tests: identical output across Chat-prompt selection, model, project and folder changes, and no path, date or session id anywhere in it.b2cf0efb— move a project's prompt text fromproject.mdinto<project>/AGENTS.mdat session start and on popover open; the vault file no longer seeds from the Chat prompt.a9916322/1faaa9df— Agent instructions row moves to Basic; Advanced's prompt controls retire behind one notice pointing at the old prompt files, shown only to users who have some.f6a52c92— sendexcludeDynamicSections: trueon the Claude preset so its system prefix survives a project switch or a new day.8fc020dd— exact-byte assertions at both opencode managed agent prompts and both codexdeveloper_instructionspaths, each varying a session fact (model, binary path, vault path) that must not move the bytes.41669a8f— the design doc: the problems, the annotated captured payload, the reversals, and the findings from reading the pinned opencode source that are not visible in the diff.a679e1e7— review fixes: a legacy generated mirror no longer blocks the project-prompt move or gets blanked by an empty ensure; every project path anchors on the record's ownfilePath(matchingresolveScopeCwd) instead of the live projects root; a project under a hidden root reports an unverifiable landing fingerprint and always spawns fresh.a2ece8f4— the instruction-file exclusion now runs on the pattern filter's raw-path fast path, so default QA settings excludeAGENTS.md/CLAUDE.mdfrom relevant-note and Miyo results too.ff65553c— editAGENTS.mdinline instead of only opening it: a text box in Basic settings backed by the vault file, one in the Edit Project dialog backed by the project's. Settings debounces its writes and flushes on close; the dialog writes before the project save, so renaming a project carries the file along with its folder. Titles and descriptions drop "Agent Mode", which no longer distinguishes anything in v4.bce50fad/e7576e57— mergev4-preview. Both sides had emptied the block the other kept: upstream retiredMcpServerSpecwith managed MCP configuration ([Managed MCP removal] - Step 1: Remove unsupported MCP configuration #2749), this branch retiredProjectProfilewhen project instructions moved toAGENTS.md, so both deletions apply and the SDK'ssetProjectProfileProvider()suite goes with the method. The second merge keeps upstream's newsetSessionMode()permission-mode suite (Let users choose what Claude's Auto mode permits #2765) out of the same region.createSessionnow awaits the scope's instruction-file ensure before reaching the backend, so the warm-reuse test flushes a macrotask instead of a fixed microtask count.a1035de0— review fixes. Editing a project from Agent Home before entering it read onlyAGENTS.md, so an upgraded project whose text still sat inproject.mdrendered a blank box over live instructions; the dialog now runs the same one-shot move before the draft read. The dialog'sAGENTS.mdwrite stays ahead ofonSave(a rename carries the file along with its folder) but captures the previous body and restores it when the project update rejects, so a cancelable dialog cannot leave a persisted edit.agentsFileresolves the vault cache case-insensitively before falling back to a synthetic adapter file and keys the vault-vs-adapter split on the resolved file's own path — on a vault holdingagents.md, Open now opens the real note and edits go throughvault.modifyrather than an adapter write that would strand an open editor. Gallery coverage for both instruction editors, each behind the presentational seam the workflow prescribes:ProjectFilesListsplits from the record/CLAUDE.md resolution, andProjectInstructionsFieldsplits from a form that cannot mount without a live project record.d6dbc944— second review round. The case-insensitive lookup above is now gated on the platform: it is only ever a repair where two spellings name one file, and on a case-sensitive volume adopting an unrelatedagents.mdwould leave the exact-name file the backends discover uncreated.hasCaseInsensitiveFilesystem()moved fromsearchUtilstoutils/vaultAdapterUtilsnow that it has a second caller. Settings' Open button awaits the flushed save instead of racing it — both paths create a missingAGENTS.md, so the loser failed with already-exists and took the text typed inside the debounce window with it. And a whole-materialization failure no longer drops the<project_context>block: the workspace policy triggers on its presence, so a broken context load had been silently ceasing to tell the agent to keep writes under the project'soutputs/.Explicitly deferred
toolsmap, which its docs mark deprecated and which is keyed by version-specific tool names.~/.claude/CLAUDE.mdcompatibility discovery.cache_controlobjects, or routing keys.Verification
At
d6dbc944, on Node 22:npx tsc --noEmit— cleannpm test— 5,550 passed, 402 suites, 0 failed (7 skipped)npm run lint— cleannpm run format:check— clean(On Node 20 the two
dev/gallery/gen-gallery-stories.test.tscases fail identically onv4-preview: the generator importsglobfromnode:fs/promises, which Node 20.18.1 does not export. Pre-existing, unrelated to this PR; CI runs 22.x.)🤖 Generated with Claude Code
https://claude.ai/code/session_01EdTUSSqtjbKZbf2Py68EUB