Skip to content

feat(context): compose context files instead of first-match-wins - #2846

Open
Mibayy wants to merge 2 commits into
NousResearch:mainfrom
Mibayy:feat/compose-context-files
Open

feat(context): compose context files instead of first-match-wins#2846
Mibayy wants to merge 2 commits into
NousResearch:mainfrom
Mibayy:feat/compose-context-files

Conversation

@Mibayy

@Mibayy Mibayy commented Mar 24, 2026

Copy link
Copy Markdown
Contributor

Fixes #2835

Problem

build_context_files_prompt() used an or-chain that silently dropped every context file after the first match. A project with both .hermes.md and AGENTS.md would never see the second one.

Solution

Load all present primary sources and concatenate them. A shared 20,000-char budget is distributed proportionally when the combined content would exceed it.

New composition order:

Source Role Behavior
.hermes.md / HERMES.md Per-project user instructions Always loaded if present
AGENTS.md / agents.md Recursive dev guide Always loaded if present
CLAUDE.md / claude.md Claude-specific instructions Always loaded if present
.cursorrules / .cursor/rules/ Legacy fallback Only loaded when none of the above are present

Backward compatibility

  • Projects with a single context file type: identical behavior
  • Cursor-only projects (.cursorrules, no AGENTS.md): identical behavior
  • Only change: projects with multiple primary types now get all of them

build_context_files_prompt() now loads all present primary context
sources (.hermes.md, AGENTS.md, CLAUDE.md) and concatenates them,
rather than stopping at the first match.

Behaviour change:
- Projects with both .hermes.md and AGENTS.md now get both
- Projects migrating from Cursor keep .cursorrules until they add
  a primary source (AGENTS.md / .hermes.md)
- Shared 20,000-char budget is distributed proportionally when the
  combined content would exceed it
- .cursorrules is a pure fallback: skipped when any primary source
  is present (backward-compatible for cursor-only projects)
- Single-source projects behave exactly as before

Closes NousResearch#2835
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint area/config Config system, migrations, profiles labels May 2, 2026

@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 the focused proposal. The reported behavior is still present on current main: build_context_files_prompt() uses the first-successful loader chain at agent/prompt_builder.py:1976-1982.

Problems

  • This is a reversal of an intentional design, not merely an omitted composition step. Commit 2da79b13dfae6476c8a0c268b2ea9ac7cd91a665 explicitly replaced prior composition because it could bloat the system prompt with redundant or conflicting instructions. Maintainer agreement is needed on the new conflict-resolution contract.
  • The proposed fixed 20,000-character aggregate cap does not preserve current main's cap contract. agent/prompt_builder.py:1201-1219 resolves an explicit configured cap or a model-window-derived dynamic cap, and agent/system_prompt.py:451-453 passes context_length into the builder.
  • The PR changes documented behavior but omits updates to website/docs/user-guide/features/context-files.md:109, website/docs/user-guide/configuration.md:1973, and website/docs/developer-guide/prompt-assembly.md:188.

Suggested changes

  • Preserve the current context_length flow and resolve one aggregate budget through _get_context_file_max_chars(context_length).
  • Add aggregate-budget tests for explicit configuration and dynamic-cap cases, then update the affected documentation.

Automated hermes-sweeper review.

Comment thread agent/prompt_builder.py
if primary_parts:
# Distribute the shared budget proportionally across all present sources
total_chars = sum(len(p) for p in primary_parts)
if total_chars > CONTEXT_FILE_MAX_CHARS:

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.

Current main no longer has a fixed context-file cap: agent/prompt_builder.py:1201-1219 resolves context_file_max_chars or a model-window-derived budget, and the builder receives context_length. A salvage should preserve that flow and derive the aggregate budget through _get_context_file_max_chars(context_length) rather than hard-coding 20,000.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles 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-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compose context files instead of first-match-wins

3 participants