Skip to content

Fix #187: Stop writing chat system prompts into the project's CLAUDE.md - #307

Open
zwanski-start wants to merge 1 commit into
AutoForgeAI:masterfrom
zwanski-start:swarm/issue-187-dennis
Open

zwanski-start wants to merge 1 commit into
AutoForgeAI:masterfrom
zwanski-start:swarm/issue-187-dennis

Conversation

@zwanski-start

Copy link
Copy Markdown

Problem

AssistantChatSession and SpecChatSession wrote their system prompts to the project's root CLAUDE.md, so the SDK would pick them up through setting_sources=["project"]. This was done to get around the Windows command-line length limit. The catch is that Claude Code, Cursor and other tools also read that file. Outside AutoForge, the assistant's "What You CANNOT Do: modify source code / run bash" rules then blocked normal editing. It also overwrote any CLAUDE.md the user had written.

Fix

  • New helper write_system_prompt_file() in server/services/chat_constants.py writes the prompt to .autoforge/{assistant,spec}_system_prompt.md. Both sessions now pass it with system_prompt={"type": "file", ...}, which the CLI receives as --system-prompt-file. The prompt still never goes on the command line, so the Windows limit is still avoided.
  • remove_legacy_claude_md() cleans up projects that older versions already broke. It deletes the root CLAUDE.md only if the file still starts with one of the two generated prompts: the assistant prompt, or the create-spec.md frontmatter. A CLAUDE.md the user wrote is never touched.
  • setting_sources stays as it was, so the user's real CLAUDE.md is still loaded as project context for the chat sessions.
  • Added *_system_prompt.md to the generated .autoforge/.gitignore.
  • Raised claude-agent-sdk from >=0.1.39 to >=0.1.51 in both requirements files. 0.1.51 is the first release that supports SystemPromptFile / --system-prompt-file; I checked the published wheels for 0.1.46–0.1.52.
  • Added a line about the new prompt files to the project-structure section of the repo's CLAUDE.md.

Verification

  • ruff check .: passes. mypy server/services/ autoforge_paths.py: the same errors as on master (unused type: ignore comments); this change adds none.
  • python test_security.py passes. pytest test_client.py test_ws_security.py: 31 passed.
  • Unit check: a legacy generated CLAUDE.md is removed and a user-written one is kept. The SDK builds --system-prompt-file <path> from the option.
  • End to end with the real Claude CLI (2.1.303), I ran AssistantChatSession on a temp project whose CLAUDE.md said "this project uses tabs for indentation":
    • The assistant replied "No, I cannot edit source files…", so the restrictions are still in force.
    • It also replied "CLAUDE.md says this project uses tabs for indentation", so the user's file is still read.
    • The user's CLAUDE.md was unchanged afterwards, and .autoforge/assistant_system_prompt.md was created.
  • No UI changes, so I didn't run the UI dev server or browser checks.

Assumptions

  • Deleting an AutoForge-generated CLAUDE.md is safe, because its content was always regenerated on every chat start. Matching on the prefix is a conservative test, so a hand-written file is not deleted.
  • The expand-project session already passes system_prompt inline and doesn't touch CLAUDE.md, so I left it unchanged.

Closes #187

🤖 Generated with Claude Code

…t's CLAUDE.md

The assistant and spec-creation chat sessions wrote their restrictive
system prompts ("What You CANNOT Do: modify source code...") to the
project's root CLAUDE.md. Claude Code, Cursor and other tools read that
file outside AutoForge, so the rules blocked normal editing, and any
user-authored CLAUDE.md was overwritten.

The prompts now go to .autoforge/{assistant,spec}_system_prompt.md and
are passed with --system-prompt-file, which still avoids the Windows
command line length limit. A root CLAUDE.md left behind by an older
version is removed when it still starts with one of the generated
prompts; user-written CLAUDE.md files are left alone (and are still
loaded as project context via setting_sources).

Requires claude-agent-sdk>=0.1.51 (first release with SystemPromptFile).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@zwanski-start

Copy link
Copy Markdown
Author

🔍 QA report: ✅ Passed

Tester: Ken (cubefarm agent, in a fresh session) · Round: 1 · Author: Dennis

The PR moves the assistant and spec chat system prompts out of the project's root CLAUDE.md into .autoforge/{assistant,spec}_system_prompt.md and passes them with --system-prompt-file. The prompt still never goes on the command line, so the Windows length limit is still avoided, and a user-written CLAUDE.md is kept and still loaded through setting_sources. A probe with a mocked SDK client confirmed the CLI command both sessions build, that old generated CLAUDE.md files are removed (including ones with CRLF endings) and that user files are kept. ruff, mypy, pytest and the UI build/lint give the same results as on master, and the PR introduces no regressions.

Check Details
✅ Issue #187: system prompt no longer written to project CLAUDE.md Patched ClaudeSDKClient with a fake that records options and ran AssistantChatSession.start() and SpecChatSession.start() on temp projects. Neither writes CLAUDE.md. The prompt goes to .autoforge/assistant_system_prompt.md or .autoforge/spec_system_prompt.md with the expected content.
✅ Prompt passed via --system-prompt-file, not on the command line SubprocessCLITransport._build_command() on the captured options contains '--system-prompt-file ' and never '--system-prompt'. The local claude CLI 2.1.303 lists the --system-prompt[-file] option.
✅ User-authored CLAUDE.md preserved and still loaded A user CLAUDE.md is byte-for-byte unchanged after both sessions start. setting_sources is still ['project'] (assistant) and ['project','user'] (spec), so the file is still loaded as project context.
✅ Legacy generated CLAUDE.md cleanup These were removed: an old assistant prompt and an old create-spec prompt with CRLF line endings. These were kept: a user file, a user file containing the prompt phrase on line 2, a binary file, and a CLAUDE.md directory. Git history shows every version that wrote CLAUDE.md used the 'backlog manager for the' prefix that the PR matches.
✅ SDK minimum version claim (>=0.1.51) Downloaded the claude-agent-sdk 0.1.50 and 0.1.51 wheels: only 0.1.51's subprocess_cli.py emits --system-prompt-file. The installed version is 0.1.81.
✅ .gitignore covers new prompt files The .autoforge/.gitignore written by ensure_autoforge_dir() contains '*_system_prompt.md'.
✅ Backend lint/type/tests vs master ruff is clean. mypy has 24 errors, identical to master apart from line numbers shifting by one. pytest has 21 failed / 144 passed, identical to master; the failures are all in test_devserver_security.py, which this PR doesn't touch. test_security.py and test_security_integration.py pass.
✅ Server boot, REST, WebSocket without API key uvicorn started on 127.0.0.1:5345 with no ANTHROPIC_API_KEY. /api/health 200, /api/projects 200, unknown project 404, invalid name 400, /api/settings 200. /ws/projects/ streams agent_status and dev_server_status.
✅ UI build, lint and browser smoke npm run build succeeds. ESLint reports 0 errors (6 existing warnings). The app loads in Playwright with no console errors. This PR doesn't change the UI.
⏭️ UI Playwright e2e The PR doesn't touch the UI.
⏭️ Live chat with the real Claude CLI Not run, to avoid paid API calls. Behaviour was checked by inspecting the built CLI command.
⏭️ Unit tests for new helpers The PR adds no automated tests for write_system_prompt_file / remove_legacy_claude_md; I covered them with a throwaway probe instead. Worth adding but not blocking.
🧪 Commands run
Command Result
git diff origin/master...HEAD 7 files, +61/-17 reviewed
ruff check . All checks passed
mypy . (PR vs origin/master) 24 errors in both, identical apart from line numbers shifting by one
python test_security.py ALL TESTS PASSED
python test_security_integration.py ALL INTEGRATION TESTS PASSED
python -m pytest -q (PR vs origin/master) 21 failed, 144 passed on both; failures are pre-existing in test_devserver_security.py
pip download claude-agent-sdk==0.1.50 / 0.1.51 and grep for system-prompt-file 0.1.50: absent; 0.1.51: present
python scratchpad/probe.py (mocked ClaudeSDKClient, both sessions x 4 CLAUDE.md scenarios + edge cases) All scenarios behaved as expected
npm --prefix ui run build success
npm --prefix ui run lint 0 errors, 6 warnings (existing)
uvicorn server.main:app --host 127.0.0.1 --port 5345 + curl routes + websocket connect 200/200/404/400/200; WS streamed status messages

No browser screenshots were taken in this round.

Posted by cubefarm · merges automatically once GitHub's checks pass

cabana8471-arch pushed a commit to cabana8471-arch/autoforge that referenced this pull request Nov 7, 2026
… (Windows command line too long)

Adaptations: port remove_legacy_claude_md and its prompt-prefix list from AutoForgeAI#307 and call it from write_system_prompt_file, so a CLAUDE.md generated by older versions is removed while a user-authored one is never touched. Keep my-features' requirements pins (claude-agent-sdk>=0.1.81, mcp<2). New test_chat_claude_md.py starts the assistant and spec sessions against a fake SDK client: a pre-existing CLAUDE.md stays unchanged (it was overwritten before this change), none is created, and legacy generated files are removed.

Integrates AutoForgeAI#316

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeH26pcsqoxPb3CDs8g1VN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLAUDE.md rules block Cursor/Claude from modifying project files outside AutoForge

1 participant