From 8d6e4002c7dd47996a5a61d86131cba90d270460 Mon Sep 17 00:00:00 2001 From: Test User Date: Wed, 11 Feb 2026 21:17:21 -0700 Subject: [PATCH 1/3] fix: include pre-loaded file contents in ReactAgent system prompt (#373) The ContextLoader already loaded relevant files into context.loaded_files with token budgeting, but _build_system_prompt() only included file paths, forcing the agent to waste ~30% of its iteration budget re-reading files. Changes: - Add "Relevant Source Files" section to system prompt with file contents - Soften "ALWAYS read before editing" rule to acknowledge pre-loaded files - Update initial user message to not mandate file exploration - Add 4 TDD tests for the new behavior --- codeframe/core/react_agent.py | 23 +++++-- tests/core/test_react_agent.py | 112 ++++++++++++++++++++++++++++++++- 2 files changed, 127 insertions(+), 8 deletions(-) diff --git a/codeframe/core/react_agent.py b/codeframe/core/react_agent.py index adf89193..80b1da3b 100644 --- a/codeframe/core/react_agent.py +++ b/codeframe/core/react_agent.py @@ -61,10 +61,12 @@ ## Rules -- ALWAYS read a file before editing it. Never assume file contents. +- Read files before editing them unless their contents are already provided in \ +the "Relevant Source Files" section below. Never assume file contents that aren't shown. - Make small, targeted edits. Do not rewrite entire files. - For NEW files: use create_file. For EXISTING files: use edit_file with search/replace. -- Never edit_file on a file you haven't read in this session. +- Never edit_file on a file you haven't seen in this session (either via \ +read_file tool or in the Relevant Source Files section). - Run tests after implementing each major feature, not after every line change. - Keep solutions simple. Do not add features beyond what was asked. - Do not change configuration files (pyproject.toml, package.json, etc.) unless @@ -234,8 +236,8 @@ def _react_loop(self, system_prompt: str) -> AgentStatus: "role": "user", "content": ( "Implement the task described in the system prompt. " - "Start by reading relevant files to understand the current " - "codebase, then make the necessary changes. " + "Relevant source files are provided above when available. " + "Use tools to explore further only if needed. " "When you are done, respond with a brief summary." ), } @@ -499,6 +501,7 @@ def _build_system_prompt(self, context: TaskContext) -> str: Layer 1: Base rules (verbatim) Layer 2: Project preferences + tech stack + file tree summary + + loaded file contents (reduces redundant read_file calls) Layer 3: Task title/description + PRD + answered blockers """ sections: list[str] = [] @@ -506,7 +509,7 @@ def _build_system_prompt(self, context: TaskContext) -> str: # Layer 1: Base rules sections.append(_LAYER_1_RULES) - # Layer 2: Preferences, tech stack, file tree + # Layer 2: Preferences, tech stack, file tree, loaded file contents if context.preferences and context.preferences.has_preferences(): pref_section = context.preferences.to_prompt_section() if pref_section: @@ -523,6 +526,16 @@ def _build_system_prompt(self, context: TaskContext) -> str: tree_lines.append(f" ... and {len(context.file_tree) - 50} more") sections.append("\n".join(tree_lines)) + if context.loaded_files: + file_lines = ["## Relevant Source Files"] + for f in context.loaded_files: + file_lines.append(f"### {f.path}") + file_lines.append("```") + file_lines.append(f.content) + file_lines.append("```") + file_lines.append("") + sections.append("\n".join(file_lines)) + # Layer 3: Task info sections.append(f"## Current Task\n**Title:** {context.task.title}") if context.task.description: diff --git a/tests/core/test_react_agent.py b/tests/core/test_react_agent.py index d6d5fbee..d0f0c3bb 100644 --- a/tests/core/test_react_agent.py +++ b/tests/core/test_react_agent.py @@ -15,7 +15,7 @@ ) from codeframe.adapters.llm.mock import MockProvider from codeframe.core.agent import AgentStatus -from codeframe.core.context import TaskContext +from codeframe.core.context import FileContent, TaskContext from codeframe.core.gates import GateResult, GateCheck, GateStatus from codeframe.core.tasks import Task, TaskStatus from codeframe.core.models import ProgressEvent @@ -234,8 +234,8 @@ def test_system_prompt_contains_all_3_layers( first_call = provider.get_call(0) system_prompt = first_call["system"] - # Layer 1: base rules - assert "ALWAYS read a file before editing" in system_prompt + # Layer 1: base rules (updated for pre-loaded context awareness) + assert "Read files before editing" in system_prompt # Layer 2: tech stack / preferences assert "Python with uv" in system_prompt @@ -244,6 +244,112 @@ def test_system_prompt_contains_all_3_layers( assert "Add hello function" in system_prompt +class TestPreloadedFileContext: + """Tests for pre-loaded file contents in the system prompt (issue #373).""" + + @patch("codeframe.core.react_agent.gates") + @patch("codeframe.core.react_agent.execute_tool") + @patch("codeframe.core.react_agent.ContextLoader") + def test_system_prompt_includes_loaded_files( + self, mock_ctx_loader, mock_exec_tool, mock_gates, workspace, provider, mock_context + ): + """When loaded_files is populated, system prompt should include + a 'Relevant Source Files' section with file contents.""" + from codeframe.core.react_agent import ReactAgent + + mock_context.loaded_files = [ + FileContent(path="src/main.py", content="def hello():\n return 'Hello'", tokens_estimate=10), + FileContent(path="src/utils.py", content="def add(a, b):\n return a + b", tokens_estimate=10), + ] + + provider.add_text_response("Done.") + mock_ctx_loader.return_value.load.return_value = mock_context + mock_gates.run.return_value = _gate_passed() + + agent = ReactAgent(workspace=workspace, llm_provider=provider) + agent.run("task-1") + + first_call = provider.get_call(0) + system_prompt = first_call["system"] + + assert "## Relevant Source Files" in system_prompt + assert "src/main.py" in system_prompt + assert "src/utils.py" in system_prompt + assert "def hello():" in system_prompt + assert "def add(a, b):" in system_prompt + + @patch("codeframe.core.react_agent.gates") + @patch("codeframe.core.react_agent.execute_tool") + @patch("codeframe.core.react_agent.ContextLoader") + def test_system_prompt_no_loaded_files_section_when_empty( + self, mock_ctx_loader, mock_exec_tool, mock_gates, workspace, provider, mock_context + ): + """When loaded_files is empty, no 'Relevant Source Files' section should appear.""" + from codeframe.core.react_agent import ReactAgent + + # mock_context.loaded_files is already empty by default + assert mock_context.loaded_files == [] + + provider.add_text_response("Done.") + mock_ctx_loader.return_value.load.return_value = mock_context + mock_gates.run.return_value = _gate_passed() + + agent = ReactAgent(workspace=workspace, llm_provider=provider) + agent.run("task-1") + + first_call = provider.get_call(0) + system_prompt = first_call["system"] + + assert "## Relevant Source Files" not in system_prompt + + @patch("codeframe.core.react_agent.gates") + @patch("codeframe.core.react_agent.execute_tool") + @patch("codeframe.core.react_agent.ContextLoader") + def test_rules_acknowledge_preloaded_context( + self, mock_ctx_loader, mock_exec_tool, mock_gates, workspace, provider, mock_context + ): + """Base rules should acknowledge that pre-loaded files don't need re-reading.""" + from codeframe.core.react_agent import ReactAgent + + provider.add_text_response("Done.") + mock_ctx_loader.return_value.load.return_value = mock_context + mock_gates.run.return_value = _gate_passed() + + agent = ReactAgent(workspace=workspace, llm_provider=provider) + agent.run("task-1") + + first_call = provider.get_call(0) + system_prompt = first_call["system"] + + # Should NOT contain the old "ALWAYS read" rule + assert "ALWAYS read a file before editing" not in system_prompt + # Should contain updated rule that acknowledges pre-loaded context + assert "Relevant Source Files" in system_prompt + + @patch("codeframe.core.react_agent.gates") + @patch("codeframe.core.react_agent.execute_tool") + @patch("codeframe.core.react_agent.ContextLoader") + def test_initial_message_does_not_mandate_reading( + self, mock_ctx_loader, mock_exec_tool, mock_gates, workspace, provider, mock_context + ): + """The initial user message should not tell the agent to 'start by reading files'.""" + from codeframe.core.react_agent import ReactAgent + + provider.add_text_response("Done.") + mock_ctx_loader.return_value.load.return_value = mock_context + mock_gates.run.return_value = _gate_passed() + + agent = ReactAgent(workspace=workspace, llm_provider=provider) + agent.run("task-1") + + first_call = provider.get_call(0) + messages = first_call["messages"] + initial_message = messages[0]["content"] + + # Should NOT mandate reading + assert "Start by reading relevant files" not in initial_message + + class TestFinalVerification: """Tests for final verification behavior.""" From 43db9d8e1722f7d4a0b3d4ec0ec42fe57270e8f7 Mon Sep 17 00:00:00 2001 From: Test User Date: Wed, 11 Feb 2026 21:32:15 -0700 Subject: [PATCH 2/3] chore: gitignore tests/test_new_feature.py generated by v1 agent (#380) TestWorkerAgent hardcodes its output to the codeframe repo via Path(__file__), causing this placeholder file to reappear after deletion. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 320cb7b0..86e2755d 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ htmlcov/ artifacts/ tests/e2e/run-e2e-full.sh test-results/ +tests/test_new_feature.py # CodeFRAME specific .codeframe/state.db From 73eb480cbb5b3dc9bd175c3bc32d602e63c36553 Mon Sep 17 00:00:00 2001 From: Test User Date: Wed, 11 Feb 2026 21:43:52 -0700 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20address=20PR=20review=20feedback=20?= =?UTF-8?q?=E2=80=94=20tighten=20test=20assertion,=20fix=20stale=20comment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Test now verifies actual updated rule text ("Read files before editing them unless") not just the section header - Comment on line 56 changed from "verbatim" to "adapted" since rules are now conditional on pre-loaded context --- codeframe/core/react_agent.py | 2 +- tests/core/test_react_agent.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/codeframe/core/react_agent.py b/codeframe/core/react_agent.py index 80b1da3b..22d122a3 100644 --- a/codeframe/core/react_agent.py +++ b/codeframe/core/react_agent.py @@ -53,7 +53,7 @@ } # --------------------------------------------------------------------------- -# Layer 1: Base rules (verbatim from AGENT_V3_UNIFIED_PLAN.md) +# Layer 1: Base rules (adapted from AGENT_V3_UNIFIED_PLAN.md) # --------------------------------------------------------------------------- _LAYER_1_RULES = """\ diff --git a/tests/core/test_react_agent.py b/tests/core/test_react_agent.py index d0f0c3bb..61960048 100644 --- a/tests/core/test_react_agent.py +++ b/tests/core/test_react_agent.py @@ -324,6 +324,7 @@ def test_rules_acknowledge_preloaded_context( # Should NOT contain the old "ALWAYS read" rule assert "ALWAYS read a file before editing" not in system_prompt # Should contain updated rule that acknowledges pre-loaded context + assert "Read files before editing them unless" in system_prompt assert "Relevant Source Files" in system_prompt @patch("codeframe.core.react_agent.gates")