Skip to content

fix(context): dedupe subdirectory hints by content digest and skip backup/vendor dirs - #72448

Closed
BKStock wants to merge 1 commit into
NousResearch:mainfrom
BKStock:fix/context-hint-dedupe
Closed

fix(context): dedupe subdirectory hints by content digest and skip backup/vendor dirs#72448
BKStock wants to merge 1 commit into
NousResearch:mainfrom
BKStock:fix/context-hint-dedupe

Conversation

@BKStock

@BKStock BKStock commented Jul 27, 2026

Copy link
Copy Markdown

Problem

SubdirectoryHintTracker dedupes by directory, never by content. The same AGENTS.md reachable through more than one path is injected once per path.

This is not hypothetical — three common setups alias a single file:

  • symlinked shared workspaces (one agent workspace whose context files are symlinks into another tree)
  • hardlinks
  • timestamped backup copies (backups/2026-07-27/AGENTS.md)

There is also no exclusion list, so directories that only ever hold copiesbackups/, node_modules/, venv/, site-packages/, .git/, .Trash/, vendor/ — are scanned like real project directories. Taking a backup of a context file permanently inflates every subsequent session.

Measured on a session that touched a symlinked workspace plus a backup directory: 3 injections, ~24,000 chars, all three byte-identical.

Change

Content digest. Every injected hint body is recorded as a sha256. Repeat content is skipped regardless of the path it arrived through. The working directory's own context file is seeded at construction, so the copy prompt_builder already loaded at startup is recognised as a duplicate rather than sent a second time.

Excluded directories. A frozenset of directory names that hold copies rather than authoritative context. Screening is relative to working_dir, so a project that legitimately lives under vendor/ keeps discovering its own subdirectory hints — only segments below the working dir are checked.

Same session after the change: 1 injection, 8,112 chars.

Tests

14 added (18 → 32), covering:

  • symlink aliasing, byte-identical copies, working-dir seeding
  • genuinely different content still injected (dedupe must not over-suppress)
  • each excluded directory name, parametrized
  • excluded ancestors block descendants
  • working dir inside an excluded name still discovers its own subdirs

155 passed, 1 skipped across test_subdirectory_hints.py + test_prompt_builder.py.

Run directly with python -m pytest rather than scripts/run_tests.sh — the wrapper's venv probe is broken on this checkout, fixed separately in #72447.

…ckup/vendor dirs

SubdirectoryHintTracker re-injected identical context files whenever the same
AGENTS.md was reachable through more than one path. Symlinked shared
workspaces, hardlinks, and timestamped backup copies all alias a single file,
so a normal session could ship the same 8KB of instructions two or three
times. Nothing deduped it and nothing excluded directories that only ever
hold copies.

Two changes:

* Track a sha256 of every injected hint body. Repeat content is skipped, and
  the working directory's own context file is seeded at construction so the
  copy prompt_builder already loaded at startup is never sent again.
* Skip directories that hold copies rather than authoritative context
  (backups, node_modules, venv, site-packages, .git, .Trash, vendor, caches).
  Screening is relative to working_dir, so a project that legitimately lives
  under vendor/ keeps discovering its own subdirectory hints.

Measured on a real session that touched a symlinked shared workspace:
3 injections / ~24,000 chars before, 1 injection / 8,112 chars after.

14 new tests cover symlink aliasing, byte-identical copies, working-dir
seeding, distinct content still being injected, each excluded directory name,
excluded ancestors, and the working-dir-inside-excluded-name case.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for targeting a real context-bloat path: current main only remembers visited directories in agent/subdirectory_hints.py:72-74, while both executor paths append tracker output to tool results (agent/tool_executor.py:1210-1217, 1884-1889).

Problems

  • The new working-directory seed is unconditional, but project context is only built when skip_context_files is false (agent/system_prompt.py:482-498). It can therefore suppress an identical subdirectory hint that was never injected. It also does not match startup precedence: .hermes.md is selected before AGENTS.md (agent/prompt_builder.py:2169-2175).
  • The vendor and third_party exclusions suppress all descendant hints below those names. That changes the documented hierarchical discovery behavior for an owned nested component; the PR only tests the separate case where the working directory itself is inside vendor.

Suggested changes

  • Seed only the actual startup-selected context source, and only when context-file injection is enabled; cover skip_context_files and .hermes.md precedence.
  • Narrow or qualify the broad directory-name exclusions and cover an owned project nested beneath vendor.

Automated hermes-sweeper review.

``prompt_builder`` already loads the working directory's context file at
startup. Seeding its digest here means the same content reached through
a different path (a symlink farm, a shared workspace) is recognised as a
duplicate instead of being sent a second time.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This seed is unconditional, but agent/system_prompt.py:482-498 skips project-context injection when skip_context_files=True; in that mode an identical subdirectory hint has not already been delivered and must not be suppressed. It also needs to follow the .hermes.md-first startup precedence in agent/prompt_builder.py:2169-2175.

".git", ".hg", ".svn",
".Trash", ".cache", ".tox", ".mypy_cache", ".pytest_cache",
"site-packages", "dist-packages",
"backups", "backup", ".backups",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

vendor is not reliably a copy-only directory: this suppresses project/vendor/component/AGENTS.md even when the agent is working in that owned nested component. The current exemption only covers a working directory already inside vendor; please preserve or explicitly scope discovery for nested projects.

@alt-glitch alt-glitch added type/perf Performance improvement or optimization P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels Jul 30, 2026
@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 30, 2026
@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Thanks @BKStock — the gap you fixed is not only still open on current main, we CONFIRMED THE LIVE SYMPTOM in our own triage sessions today (the same .worktrees/*/AGENTS.md injected multiple times per session). Salvaged with your authorship in #77787: both groups ported (vendor-dir skip + sha256 digest dedupe), your dead outside-workdir branch dropped since main now rejects those paths earlier (f4953bc), tests merged into main's existing file, both mechanisms mutation-checked. Closing in favor of the salvage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/perf Performance improvement or optimization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants