Skip to content

fix(workspace): the owner's CLAUDE.md and auto memory stay out of the bots - #66

Merged
httpsphl merged 21 commits into
mainfrom
fix/owner-claude-md
Sep 30, 2026
Merged

httpsphl merged 21 commits into
mainfrom
fix/owner-claude-md

Conversation

@httpsphl

Copy link
Copy Markdown
Owner

Closes the "CLAUDE.md do dono nos bots" row that #63 left open in spec section 19. On top of #63 (merge #63 first, with a merge commit; until then this PR also shows its commit). The change itself is the last two commits.

The problem

Bots start with --setting-sources project,local so the owner's personal Claude Code setup stays out. But Claude Code loads CLAUDE.md, CLAUDE.local.md, .claude/CLAUDE.md and .claude/rules/ from the working directory and from every folder above it, whatever that flag says. Bot folders sit in %USERPROFILE%\Botloft\... by default, so %USERPROFILE%\.claude\CLAUDE.md, the owner's personal instructions, reached every bot as project memory.

What changes

The settings.json the daemon writes for each bot (spec 7.5) gets two keys.

claudeMdExcludes (first commit). For every folder above the crew's folder, from workspaces_root up to the drive root, six exact patterns: CLAUDE.md, CLAUDE.local.md, AGENTS.md, .claude/CLAUDE.md, .claude/AGENTS.md, .claude/rules/**.

  • Keeps loading: the bot's own CLAUDE.md and rules, the work folder's CLAUDE.md and rules (the shared folder or the one the owner chose, through --add-dir), and a CLAUDE.md the owner puts in the crew's folder.
  • No pattern starts with **, so nothing outside those folders is touched. A chosen work folder inside a project is not affected; Claude Code does not load the CLAUDE.md files above an --add-dir folder anyway (tested).
  • A managed policy CLAUDE.md cannot be excluded and still applies.

autoMemoryEnabled: false (second commit, can be dropped on its own if bots should keep it). Auto memory was on in the bots. Asked to remember something, a bot wrote its notes to %USERPROFILE%\.claude\projects\<project>\memory\: outside its folder, with no permission request in Manual mode, not shown in the app, left behind when the bot is deleted, and instead of the CLAUDE.md that Botloft gives it as memory. With the bot folders inside a git repository (a dev daemon), the bot also loaded the notes Claude Code keeps for the owner in that repository, which is the same leak by another door. Off, the bot saves to its own CLAUDE.md, and its system prompt is about 3k tokens smaller. Notes written before stay on disk; the daemon deletes nothing in .claude.

Windows path form (real test, Claude Code 2.1.284)

claude -p --setting-sources project,local in a scratch tree under the user profile, reading memoryFiles from the get_context_usage control request:

  • C:/Users/... and C:\Users\... match. /c/Users/... and the //c/Users/... form of permission rules do not.
  • Matching is case sensitive, drive letter included, and Claude Code builds the paths from the working directory as it was given. The daemon builds the patterns from the same path it passes as the working directory.
  • In a folder named odd (x) [y] {z}, an exact file path still matches but .../.claude/rules/** does not; with ? in place of those six characters it does. Spaces, accents and !+@#$%^&',;=~ match as they are.
  • AGENTS.md above only loads with instructionFiles: claude-md-and-agents-md; the pattern removes it.

Through a dev daemon (workspaces inside the profile, a test CLAUDE.md in every folder above, a Haiku bot): with the generated settings.json, memoryFiles and the bot itself listed only the crew folder's CLAUDE.md, the bot's, its rules and the shared one. Asked to remember something, the bot edited its own CLAUDE.md, and no memory folder was created. The debug log had no message text.

All of it is recorded in spec 5, 7.4, 7.5 and two rows of section 19.

Tests

cargo fmt --check, clippy -D warnings and cargo test --workspace pass. New: unit tests for the patterns (workspace/memory.rs), for the generated settings, and the RPC acceptance test now checks the file a new bot gets.

Manual check with the real Claude Code

  1. Put a line in %USERPROFILE%\.claude\CLAUDE.md (for example "always answer with the word PINEAPPLE first").
  2. Start the daemon, create a bot and ask it which instruction files it was given. Before: it follows the line. After: it lists only its own CLAUDE.md, its rules and the work folder's CLAUDE.md.
  3. Ask it to remember something: the note goes to the CLAUDE.md in the bot's folder (in Manual mode, after an Allow), and nothing new appears under %USERPROFILE%\.claude\projects\.

Existing bots get the new file on their next start.

Delete is the last action of the bot's menu (header and right-click) and of the crew's menu. The confirmation names the bot or crew and says what goes (the conversation, routines and tasks, for good) and what stays (the folder, with its path). A message from a bot that left, archived or deleted, reads as from a bot no longer in the crew.
The other lists leave archived crews and bots out, so nothing showed them. archive.list returns both, the latest archived first, for the app to list and delete.
Settings gets an Archived part: the crews and the bots the owner archived, which the rest of the app does not show, with the same delete confirmation as the menus. The confirmation is now a component of its own (DeleteBot, DeleteCrew) and says "leaves" instead of "stops now" for what was already stopped.
…or good

With the shell's IFileOperation on a thread of its own (it needs a single-threaded apartment). A progress sink aborts any item the shell would destroy instead of recycling, so what the bin cannot take stays where it is. The tests use the real Recycle Bin, check the folder is in it and take it out again.
…n when asked

bots.delete and crews.delete take recycleFolder. The method answers at once; the folder moves afterwards, off the server's workers, tried again while the process that was just killed still holds its files, and folder.recycled tells the apps how it ended. Only a folder under the workspaces goes, and never one that holds a work folder the owner chose.
… Recycle Bin

Unchecked by default: the folder stays. Checked, the confirmation says the folder goes to the Recycle Bin, where it can still be got back, and a notice tells the owner if it could not go.
The bin records the long path, and the temporary folder of the CI runner comes in the short one (RUNNER~1), so the tests did not find the folder they had just moved.
Claude Code's Bash and PowerShell tools take an optional `description`
next to `command`, written by the model, and it reaches the permission
tool unchanged (checked with 2.1.284, spec 19). The daemon now passes it
along as `explanation` on the tool and approval chat items, so the app
can put plain words before the command. `summary` is the command itself
again, as the spec says.

The bot writes the explanation, so it never replaces the command: a
command's input is kept up to 32 KB instead of 4 KB, for the owner to
read all of what they allow, and Claude Code still gets back the input
it asked about.

The generated rules ask each bot to describe every command for an owner
who may not be technical, in the language the owner writes in. Without
the rule the text was terse and, on Sonnet, in English. The field stays
optional: real Sonnet bots were seen leaving it out.
The card for a request to run a command used to show the raw command,
which an owner who does not read commands allowed without knowing what
it did. It now shows what the bot says the command is for as its main
text, says the bot wrote it and that what runs is the command, and keeps
the whole command behind "See the command". With no explanation the card
says so and opens the command.

The tool line above the card shows the same words, and opens to the
command itself instead of its JSON. Other tools keep their summary, with
the input under "Details".

The preview's seed has a long heredoc command with an explanation and a
plain one without.
…onversation

Spec 7.4, 8.6, 9.2, 11.2, 11.3, 12 and the findings in section 19.
bots.setEffort restarts the bot with --effort when nothing is in progress;
the daemon asks Claude Code what the session applies and how full the
conversation is (control requests on stdin), follows each model request,
and bots.compact writes /compact. The fake daemon of the app knows the
new fields and methods; the UI comes next.
Spec 15.1. Next to the model, a slider from faster to smarter sets the
bot's effort, with the level its model uses by itself marked as the
recommended one; a model without levels says so. A ring shows how full
the conversation is, how far it is from compacting by itself, and
compacts it now. Compactions read as notices in the owner's language.
In a narrow chat the controls on the right move to their own line.
… the bots

Claude Code loads CLAUDE.md, CLAUDE.local.md, .claude/CLAUDE.md and
.claude/rules from the working directory and from every folder above it,
whatever --setting-sources says. Bot folders sit inside the user profile by
default, so the owner's personal .claude/CLAUDE.md reached every bot as
project memory.

The generated settings.json now names those files in claudeMdExcludes, for
each folder above the crew's folder up to the drive root. The bot's own
CLAUDE.md and rules, the crew's folder and the work folder keep loading.

Checked against Claude Code 2.1.284 (spec 19): patterns need the C:/Users/...
form with the letter case of the working directory, the //c/... form of
permission rules does not match, and brackets, braces and parentheses in a
folder name have to become "?".
Auto memory is on by default in claude -p. A bot asked to remember something
wrote its notes to the owner's profile (.claude/projects/<project>/memory),
outside its folder and without a permission request, instead of the CLAUDE.md
that Botloft gives it as memory. The owner cannot see those notes in the app,
and they stay behind when the bot is deleted. With the bot folders inside a
git repository, the bot also loaded the notes Claude Code keeps for the owner
in that repository.

The generated settings.json now sets autoMemoryEnabled to false: the bot's
memory is the CLAUDE.md in its folder (spec 5.1, 7.5). Notes written before
stay where they are. Checked against Claude Code 2.1.284 (spec 19).
Brings in main (#52, #53, #54, #55, #59, #62, #67) and feat/delete-recycle (#56, #58, #60), so this PR merges in any order with them.

- app/src/lib/fakeBots.ts, crates/botloftd/src/state.rs, docs/spec.md: both sides kept (bot.context next to bot.deleted, crew.deleted and folder.recycled; the context store next to the trash).
- crates/botloftd/src/supervisor/spawn.rs: both sides kept, without recording the session at launch (#67 records it from the first turn).
- tests/bot_effort.rs and tests/compaction.rs: the bot has a turn before its conversation is resumed, as #67 described.
…mmand-explanation

Brings in feat/bot-effort-context (#63) with main and the delete stack, so this PR merges in any order with them.

- crates/botloft-core/src/chat.rs: a command's line in the list shows the bot's explanation, and a compaction notice still leaves the list alone.
- docs/spec.md: both sides kept (settings.json and rules\botloft.md in the bot's folder, the fields of Bot, the date of the section 19 check).
- crates/botloft-store/src/delete/tests.rs: the approval item the delete tests build has the new explanation field.
Brings in feat/command-explanation (#61), which carries feat/bot-effort-context (#63), main and the delete stack with their conflicts resolved, so this PR merges in any order with them. No conflict of its own.
Brings in feat/effort-context-app (#64), which carries #61, #63, main and the delete stack with their conflicts resolved, so this PR merges in any order with them.

- docs/spec.md: the bot folder's settings.json keeps what Claude Code does not load in the bot (this branch), and rules\botloft.md how to explain each command to the owner (#61).
@httpsphl
httpsphl enabled auto-merge September 30, 2026 17:23
@httpsphl
httpsphl merged commit aa1f01d into main Sep 30, 2026
3 checks passed
@httpsphl httpsphl mentioned this pull request Sep 30, 2026
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.

1 participant