🤖 fix: present sub-projects to the agent as regular projects#3244
🤖 fix: present sub-projects to the agent as regular projects#3244ammar-agent wants to merge 1 commit intomainfrom
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
@codex review |
This comment has been minimized.
This comment has been minimized.
|
@codex review Both P2s addressed in 2aadcf0:
|
|
Codex Review: Didn't find any major issues. Another round soon, please! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
2aadcf0 to
6150fe5
Compare
|
@codex review Force-pushed a complete pivot. The previous direction (which you approved on the prior commit) made sub-projects a recognized branch with explicit "the X sub-project of Y at Z" framing and AGENTS.md tagging — the user rejected that direction in favor of having the agent see sub-projects as regular projects. This revision:
Please flag anything that looks wrong with the new direction, especially around the AGENTS.md inheritance change. |
|
Codex Review: Didn't find any major issues. What shall we delve into next? ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
6150fe5 to
f0de68e
Compare
|
@codex review Force-pushed an extension of the previous direction. The user wants sub-projects to look like regular projects to the agent (no "sub-project of …" framing, no relative-paths nudge in , no special bash tool description) but still wants parent AGENTS.md concatenated with the sub-project's so the agent inherits parent conventions transparently. This revision keeps everything from the previous round (env block + bash desc neutral, all
Behavior delta the user should know: sub-project workspaces now actually inherit parent AGENTS.md (the previous doubled-path lookup never found it), so anyone whose parent AGENTS.md previously had no effect on sub-project workspaces will start seeing it influence agent behavior. Please flag anything that looks wrong. |
|
Codex Review: Didn't find any major issues. Swish! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
f0de68e to
a62901b
Compare
|
@codex review Force-pushed an extension of the previous round. The user asked: "Is it clear to the agent that the parent AGENTS.md came from the parent? E.g. a This revision adds a small markdown-invisible HTML-comment hint before each AGENTS.md segment naming its source path relative to the cwd:
HTML comments survive markdown rendering as no-ops but are visible to the model in the raw system prompt. We deliberately avoided the rejected v1 8 test cases cover: concatenation order, comment presence and ordering, depth computation across nesting levels, only-parent / only-sub fallbacks, regular-project no-op preservation, and stale-metadata degradation. Please flag anything that looks wrong with the comment-based hint approach. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a62901b91b
ℹ️ 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".
a62901b to
b1e5205
Compare
|
@codex review Force-pushed to address the previous round's P2 finding (good catch — scoped tool/model bodies lost the path-source marker because the top-level HTML comment didn't travel into the extracted section body). Two changes this round:
The H1 is deliberately just a path note ( 9 sub-project test cases now cover: byte-equal Please flag anything that still looks wrong. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b1e5205988
ℹ️ 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".
b1e5205 to
a6424bf
Compare
|
@codex review Force-pushed to address the previous round's P2 finding (great catch — the singular Two changes this round:
Also added 4 unit tests in This is a small behavior change for the rare case where a single AGENTS.md has multiple matching |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a6424bf1fa
ℹ️ 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".
a6424bf to
e2bade5
Compare
|
@codex review Force-pushed to address the previous round's P2 finding (sharp catch — the regex-based injection didn't respect fenced code blocks, so Two changes this round:
Total now: 11 sub-project test cases + 4 new |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e2bade5b4c
ℹ️ 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".
e2bade5 to
f48fae8
Compare
|
@codex review Force-pushed to address the previous round's P2 finding (very nice catch — CommonMark §4.5 says a closing fence cannot have an info string, so Two changes this round:
Total now: 12 sub-project test cases + 4 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f48fae86fa
ℹ️ 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".
Sub-project workspaces now look indistinguishable from a regular
single-project workspace rooted at the sub-project directory:
- The <environment> block uses the same per-runtime description and
guardrails it would use for any project at that cwd. No "the X
sub-project of the Y at Z" framing, no relative-paths preamble.
- Bash tool description is unchanged from the regular single-project
case (same "Runs in <cwd> - no cd needed" form).
- AGENTS.md is concatenated parent → sub-project (parent first so
general rules anchor before specific overrides). Each segment is
wrapped with two complementary path markers:
* A visible H1 heading naming the source path relative to the cwd
(e.g. `` # `../../AGENTS.md` ``). The heading both names the
segment's source AND bounds any scoped `## Tool:` / `## Model:`
sections inside the segment so they can't span across the
segment join into the next segment's narrative.
* A markdown-invisible HTML comment with the same path injected
after every ATX-style `## Tool:` / `## Model:` heading inside
the segment. The H1 doesn't survive scoped extraction, so the
inner comment carries the path provenance into per-tool/per-
model contexts. Heading-line scanning is fence-aware: scoped-
looking lines that sit inside a fenced code block (e.g. a
`markdown` documentation example showing how to structure
scoped sections) do NOT receive an injected comment, mirroring
the downstream markdown parser's fenced-content semantics.
- The parent root is derived by stripping the recorded sub-project
relative segment off the cwd, so worktree/SSH/Docker workspaces
read the parent's AGENTS.md from their own branch's checkout (not
from the user's local checkout, which may be on a different commit).
Depth of the relative-path heading tracks the actual sub-project
nesting depth.
- Stale-metadata fallback: if the recorded subProjectPath isn't a
descendant of projectPath, or the cwd doesn't end with the expected
suffix, we degrade to reading just the cwd's AGENTS.md verbatim
with no path-source heading or comment — historical behavior,
unchanged.
- Regular non-sub-project workspaces are unaffected: no path-source
markers are emitted (one source = no ambiguity), preserving the
exact prompt bytes for single-project workspaces.
Also align extractModelSection's multi-match semantics with
extractToolSection: collect every matching `## Model: …` section in
source order and concatenate, instead of returning only the first
match. Without this, sub-project model overrides were silently
dropped when the parent AGENTS.md also defined a matching `## Model:`
section — only the parent's body landed in the per-model section,
and the sub-project's intended override never reached the agent.
The change is principled (parallel to how tool overrides already
compose for multi-project workspaces) and removes the now-unused
internal extractSectionByHeading helper.
Replaces the earlier doubled-path lookup in
readSingleProjectContextInstructions (which always read the
sub-project's AGENTS.md as if it were the parent's, and tried to read
the sub-project's AGENTS.md from <cwd>/<rel>/<rel> which never
existed) with an inline helper that derives both the parent root and
the relative-path hint deterministically.
|
@codex review Force-pushed to address the previous round's P3 finding (good catch — CommonMark §4.2 lets ATX headings start with up to 3 leading spaces, and markdown-it parses those as headings, but my regex was anchored at column 0). Two changes this round:
Total now: 13 sub-project test cases + 4 |
f48fae8 to
f6c73fe
Compare
|
Codex Review: Didn't find any major issues. Another round soon, please! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Summary
Sub-project workspaces previously gave the agent a deep absolute cwd, called the sub-project directory "a git repository clone" (it isn't — the parent is), and concatenated parent + sub-project
AGENTS.mdsegments via a doubled-path lookup that effectively only ever returned the sub-project'sAGENTS.md. The agent had no anchor for relative-path usage and tended to repeat the absolute cwd prefix in tool calls. This PR makes sub-project workspaces appear to the agent as regular single-project workspaces rooted at the sub-project directory — same<environment>framing, same bash tool description — while still concatenating parent + sub-projectAGENTS.md, with a small H1 path heading at the start of each segment + an inner HTML comment after each scoped section so the agent can disambiguate which root path references resolve against, even for guidance authored under## Tool:/## Model:headings. As a side fix it also alignsextractModelSectionwithextractToolSection's multi-match semantics so sub-project (and multi-project) model overrides actually compose with parent guidance instead of being silently dropped.Background
Reported as: "When using sub-projects, the agent seems to prefer using the full path of the repo instead of relative paths." An earlier iteration of this PR threaded a
SubProjectExecutionContextthrough prompts and tool descriptions to call out "thepackages/apisub-project of the git repository clone at /workspace" with an explicit relative-paths nudge. The user pushed back on that direction — preferring the agent see sub-projects as regular projects instead of as a special branch — but also wantedAGENTS.mdconcatenation with the parent preserved, plus a../AGENTS.md-style note so the agent can tell which root each segment was authored against. This revision implements that: neutral framing in prompts and tool descriptions, parent + sub-projectAGENTS.mdconcatenated under the hood, each segment opening with a lightweight path-as-heading note, plus an inner HTML comment that travels into per-tool/per-model extraction.Implementation
buildEnvironmentContext— nosubProjectparameter, no sub-project branch. The cwd (already pointed at the sub-project directory byresolveWorkspaceExecutionPath) is presented through the same per-runtime description and guardrail lines as a regular project at that path.readSingleProjectContextInstructions— derives the parent root and a relative-path hint viaderiveSubProjectLayout, which strips the recorded sub-project relative segment off the cwd and computes how many../levels separate the cwd from the parent root. ReadsAGENTS.mdfrom[parentRoot, cwd], wraps each non-empty segment withtagAgentsSegment, and concatenates the wrapped segments with\n\n.tagAgentsSegment— wraps a segment with two complementary path markers:# `../../AGENTS.md`(or# `./AGENTS.md`for the sub-project segment) at the top. The H1 names the segment's source AND bounds any scoped## Tool:/## Model:sections inside the segment, so they can't span across the segment join into the next segment's narrative. Without this bound, a parent's## Model:section would extend across the segment join andstripScopedInstructionSectionswould delete the sub-project's narrative along with it.<!-- ../../AGENTS.md -->injected right after every ATX## Tool:/## Model:heading inside the segment. The H1 above doesn't survive scoped extraction (extractToolSectionreturns the section body, not the surrounding H1), so the inner comment carries the path provenance into per-tool/per-model contexts. Heading-line scanning is fence-aware: scoped-looking lines that sit inside a fenced code block (e.g. an AGENTS.md authored with amarkdownexample showing how to structure scoped sections) do NOT receive an injected comment, matching the downstream markdown parser's fenced-content semantics.# `../../AGENTS.md`), not the v1 framing# Project context (root: …). It tells the agent which directory the segment was authored against without dressing the sub-project up as a special structural feature.extractModelSection(markdown.ts) — aligned withextractToolSection's multi-match semantics: collects every matching## Model: …section in source order and concatenates them, instead of returning only the first match. Codex caught that the singular behavior silently dropped sub-project model overrides whenever the parentAGENTS.mdalso defined a matching## Model:section — only the parent's body landed in the per-model section, never the sub-project's intended override. The new behavior parallels how tool overrides already compose for multi-project workspaces. The unused internalextractSectionByHeadinghelper is removed.AGENTS.mdis read from the workspace's own checkout (derived fromworkspacePath), not frommetadata.projectPath, so worktree/SSH/Docker workspaces see parent guidance from their own branch rather than the user's local checkout.metadata.subProjectPathdoesn't sit cleanly undermetadata.projectPath, or the cwd doesn't end with the expected suffix, the helper returns null and we degrade to reading just the cwd'sAGENTS.mdwith no path-source markers.AGENTS.mdas if it were the parent's, and tried to read the sub-project'sAGENTS.mdfrom<cwd>/<rel>/<rel>(a path that never existed).buildBashToolDescription/ToolConfiguration/aiService.ts/runtimeHelpers.ts— reverted to baseline. NoSubProjectExecutionContext,resolveSubProjectContext, ordescribeRuntimeRootplumbing anywhere.Sub-project plumbing in the runtime layer is unchanged:
resolveWorkspaceExecutionPathstill appends the sub-project relative path so the agent's cwd is the sub-project directory. The pivot is purely about what the agent sees in prompts.Validation
make static-check— green (lint, typecheck, prettier, doc snapshot, code-to-docs links).bun test src/node/utils/main/markdown.test.ts src/node/services/systemMessage.test.ts— 47/47 pass, including 11 sub-project cases and 4 newextractModelSectionunit tests.bun test src/node/services/streamContextBuilder.test.ts src/node/services/aiService.test.ts src/node/runtime/runtimeHelpers.test.ts src/node/services/tools/bash.test.ts— 152/152 pass.New / updated test cases in
systemMessage.test.ts > sub-project workspaces look like regular projects:<environment>for a sub-project workspace is byte-equal to one produced for a regular single-project workspace at the same cwd.<environment>does not contain the stringssub-projectorPrefer paths relative to.AGENTS.mdis concatenated parent → sub-project with# `../../AGENTS.md`and# `./AGENTS.md`H1 headings, each preceding its corresponding content; no# Project context (root: …)/# Sub-project context (root: …)verbose headings.apisub-project produces# `../AGENTS.md`, not# `../../AGENTS.md`.AGENTS.mdcases each load the available file alone with the matching path-source heading.AGENTS.mddefine matching## Tool:and## Model:sections, the per-tool extraction includes both bodies with their respective<!-- ../../AGENTS.md -->and<!-- ./AGENTS.md -->comments preceding each rule, AND the per-model section also includes both bodies in parent → sub order. The<custom-instructions>block strips both scoped sections (including the inner comments) but keeps the top-level H1 path headings.markdownexample containing a## Tool: bashline inside the fence does NOT receive an injected provenance comment. The fenced region in<custom-instructions>is preserved verbatim, the real (non-fenced)## Tool: bashsection still gets the provenance comment in the per-tool extraction, and the example body does not leak into the tool extraction.subProjectPath(not a descendant ofprojectPath) degrades to reading just the cwd'sAGENTS.mdwith no path-source markers.New unit tests in
markdown.test.ts > extractModelSection:## Model:sections in source order, joined with\n\n(parallel toextractToolSection).## Model:heading matches the active model.Risks
Behavior change is scoped to sub-project workspaces and the (rare) multi-match
## Model:case:<environment>block and bash tool description for sub-project workspaces no longer mention the sub-project at all — they look identical to a regular project at that cwd. The cwd was already the sub-project directory.AGENTS.mdare now both visible to the agent. Previously only the sub-project'sAGENTS.mdwas loaded (the doubled-path lookup never found the parent's). Anyone whose parentAGENTS.mdpreviously had no effect on sub-project workspaces will start seeing it influence agent behavior.<custom-instructions>— a small visible structural change. The headings are minimal (just a backticked path) and serve double duty as a path note and a section boundary.extractModelSectionnow collects all matching## Model:sections in source order rather than returning only the first match. This affects any AGENTS.md (single-project, multi-project, or sub-project) that has multiple matching## Model:regex sections. I checked the repo and didn't find any authoring patterns relying on first-match semantics — the change essentially only takes effect in the new sub-project / existing multi-project flows.subProjectPath) are unaffected —deriveSubProjectLayoutreturns null, the function collapses to a singlereadInstructionSetFromRuntime(runtime, workspacePath)call, and no path-source markers are emitted.Pains
extractModelSectionwas singular whileextractToolSectionwas plural, fixed by aligning the two; and (3) the regex-based injection naively matched scoped-looking lines inside fenced code examples, fixed by switching to a line-by-line scanner that tracks fence state. While investigating (1) also discovered that scoped sections in the parent segment could span across the segment join into the sub-project's narrative (since neither HTML comments nor plain text bound a section), prompting the move from a top-level HTML comment to a top-level H1 heading that does bound scoped sections.readSingleProjectContextInstructionswas silently producing sub-project-onlyAGENTS.md, which made the bug invisible in dogfooding but a tripwire for anyone trying to reason about the inheritance semantics from the source. Replacing it with the explicit suffix-strip helper removes that confusion and unlocks correct parent inheritance for sub-projects.Generated with
mux• Model:anthropic:claude-opus-4-7• Thinking:max• Cost:$40.78