fix(runtime): collapse duplicate workspace instruction files in one directory - #3578
Conversation
…irectory Sharing one instruction file across the names different agent CLIs read is the documented way to do it — Claude Code's docs recommend symlinking CLAUDE.md to AGENTS.md, and apache/airflow and deepseek-ai/deepseek-harness both ship that link. Maka read every candidate name and appended each one, so such a repository had the same bytes injected twice and spent most of the 14000-character workspace-instruction budget on the duplicate. Deduplicate on the digest of the cleaned text, scoped to one directory. That catches the symlink and a byte-identical copy alike, where a realpath check would only catch the link. Directories stay independent: the same text at global and project scope is a user repeating themselves deliberately. The digest is taken before truncation, so two files that diverge only past the per-file cap still count as different. The shape follows deepseek-harness, which reads several names and ships the symlink, and reconciles them by content rather than by path. Generated-by: Claude Code Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes duplicated workspace-instruction blocks when multiple supported instruction filenames in the same directory (e.g., AGENTS.md, CLAUDE.md, GEMINI.md) resolve to identical effective instruction text (commonly via symlink or copy). It ensures redundant duplicates don’t waste the shared 14k character workspace-instruction budget, while preserving deliberate layering across global (~/.maka) vs project scopes.
Changes:
- Deduplicate instruction files within a single directory by hashing the cleaned instruction text (SHA-256) before truncation.
- Keep global and project directories independent (no cross-scope collapsing).
- Add targeted tests covering symlinked duplicates, byte-identical copies, non-identical siblings, and identical content across scopes.
Required Conclusion (per code-review skill)
- Is the current solution optimal for the actual problem? Yes. Digest-based deduplication on cleaned text directly addresses both symlink and byte-identical copy cases without breaking the “multiple different instruction files in one directory” behavior.
- If applicable, what production code can be deleted? none identified.
- If applicable, what low-quality tests can be deleted or replaced? none identified.
- Is a deeper refactor required, and what should the final structure be? No deeper refactor appears required; this is a localized behavioral fix at the correct source of truth (
readWorkspaceInstructions). - Is the reviewed revision ready to merge? Yes, based on the change scope and the added regression tests.
- What residual risks or verification gaps remain? No protected-area effect (security/licensing/release/governance) is identified in this diff. Verification gap: broader runtime/desktop/e2e suites were not evidenced in the diff, but the change is small and has direct unit coverage.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| packages/runtime/src/system-prompt/workspace-instructions.ts | Deduplicates same-directory instruction content by SHA-256 digest of cleaned text prior to truncation. |
| packages/runtime/src/tests/workspace-instructions.test.ts | Adds regression tests covering symlink/copy duplicates and ensures no over-collapsing across differing siblings or scopes. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
jackwener
left a comment
There was a problem hiding this comment.
Acting on behalf of repository owner WAWQAQ under the standing Kabi authorization for PR review and APPROVE; exact head 1042a9d7546a3540859e1e905901dd106c35947b.
GO: no P0–P2; one non-blocking P3 inline. Full-patch coverage included digest scope/order, pre-truncation identity, prompt budgets, symlink containment, current-main merge compatibility, and all three GitHub discussion endpoints. Focused tests passed 9/9; removing the sole dedupe branch made exactly the two dedupe regressions fail; restored full Runtime suite passed 3,079 with 13 skips and 0 failures. Desktop and Playwright were not run in this pass.
Fresh machine gates: OPEN, non-draft, mergeable; exact-head test is completed/success. This agent-executed approval does not claim to satisfy the repository's separate required-human-review wording. No merge performed.
| // Scoped to one directory on purpose: the same text at global and project | ||
| // scope is a user repeating themselves deliberately, and collapsing that | ||
| // would silently drop a layer. | ||
| const seenDigests = new Set<string>(); |
There was a problem hiding this comment.
[P3] This set is recreated for each scope read, so the same physical directory is not actually deduplicated when it is both the global and project root. A production-function repro with cwd === <home>/.maka and one <home>/.maka/AGENTS.md produces blocks=2, copies=2, and scopes [global, project]: buildWorkspaceInstructionsPromptFragment calls this function twice, and each call starts with an empty set. This is inside the PR's stated ‘one directory’ contract, not an unrelated enhancement. It is non-blocking because ordinary project roots differ from ~/.maka, but editing the config directory (or a realpath alias of it) still reproduces the duplicate-budget bug. Please share dedupe state by resolved root, or special-case identical resolved roots, and add the same-root regression.
…irectory (apache#3578) Sharing one instruction file across the names different agent CLIs read is the documented way to do it — Claude Code's docs recommend symlinking CLAUDE.md to AGENTS.md, and apache/airflow and deepseek-ai/deepseek-harness both ship that link. Maka read every candidate name and appended each one, so such a repository had the same bytes injected twice and spent most of the 14000-character workspace-instruction budget on the duplicate. Deduplicate on the digest of the cleaned text, scoped to one directory. That catches the symlink and a byte-identical copy alike, where a realpath check would only catch the link. Directories stay independent: the same text at global and project scope is a user repeating themselves deliberately. The digest is taken before truncation, so two files that diverge only past the per-file cap still count as different. The shape follows deepseek-harness, which reads several names and ships the symlink, and reconciles them by content rather than by path. Generated-by: Claude Code Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
readWorkspaceInstructionsreads every candidate name in a directory and appends each result, so a repository that shares one instruction file across the names different agent CLIs read gets it injected twice. That sharing is the documented way to do it — Claude Code's docs recommendln -s AGENTS.md CLAUDE.md, andapache/airflowanddeepseek-ai/deepseek-harnessboth ship that link — and the duplicate then consumes most of the 14000-character workspace-instruction budget, squeezing out whatever genuinely different file sits beside it.Deduplicate on the digest of the cleaned text, scoped to one directory. Digesting rather than comparing
realpathcatches a byte-identical copy as well as a link, which is the other common way people share these files. Directories stay independent, because the same text at global and project scope is a user repeating themselves on purpose and collapsing it would silently drop a layer. The digest is taken before truncation, so two files that diverge only past the per-file cap still count as different.Fixes #3577
Verification
Four new tests in
workspace-instructions.test.ts: a symlinkedCLAUDE.mdcollapses, a byte-identical copy collapses, files in one directory that genuinely differ are both kept, and identical text at global and project scope is kept at both scopes. The first two fail onmainwithexpected: 1, actual: 2; the last two pass either way and are there so the fix cannot over-collapse.End-to-end against a directory holding a real
AGENTS.md, in both duplicate forms:Ran:
workspace-instructions(9) andai-sdk-backend(204) green;biome checkon both changed files;npm run check:asf-headers.packages/runtimetypecheck reports one error inresponses-wire-contract.test.tsabout analibaba-cnprovider literal. Verified pre-existing — the same error appears on a cleanorigin/mainbuild, and this change touches neither file.Not run: the rest of the runtime suite, Desktop, Playwright.
Notes
@AGENTS.md— the import form Claude Code's docs also suggest — is not addressed here. Maka does not expand imports, so a one-lineCLAUDE.mdcontaining@AGENTS.mdis injected as that literal text. It is small and harmless rather than wasteful, but it is also an instruction that means nothing; worth a separate decision about whether to expand or ignore it.For reference, the three strategies other agents use: read a single filename (Claude Code, Codex CLI, Gemini CLI), first-match-wins across a candidate list (opencode, Pi), or content deduplication (deepseek-harness). First-match-wins would be simpler here, but it discards a
CLAUDE.mdthat genuinely differs fromAGENTS.md, which is a supported thing to have.AI use
Select exactly one:
Tool(s) and scope: Claude Code — investigation, comparison of other agents' loaders, the tests, and the fix.
Checklist
Does this PR entail a change in behavior?