diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f4cb018..a9918a11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,19 @@ new version heading in the same commit. ## [Unreleased] +## [0.469.5] - 2026-11-10 +### Fixed +- **Agents no longer load the software checkout's own CLAUDE.md.** Claude Code walks up from the cwd and + loads every ancestor CLAUDE.md, and the default data home (`./data`) sits INSIDE the checkout — so on any + box installed that way every agent also loaded the repo's ~83k-char maintainer notes. On globex that + pushed `qa`'s 98k prompt to 181k chars, over Claude Code's 150k instruction budget ("2 instruction files + add up to 181.2k chars"), and every run on every agent paid ~20k tokens for text written for us, not + them. `terminal/claude-launch.sh` now writes `claudeMdExcludes` for each ancestor's + `CLAUDE.md`/`CLAUDE.local.md`/`.claude/CLAUDE.md`/`.claude/rules/**` (logical and physical path), so an + agent's instructions are its own CLAUDE.md plus the composed system prompt and nothing else. Verified + against claude 2.1.322: a planted ancestor instruction is followed without the setting and absent with + it, while the agent's own CLAUDE.md still loads. + ## [0.469.4] - 2026-11-10 ### Fixed - **The terminal toolbar no longer covers the agent's own controls.** The font stepper, Help, Pop out, diff --git a/package-lock.json b/package-lock.json index bb52b6f8..e7813ace 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "agent-os", - "version": "0.469.4", + "version": "0.469.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "agent-os", - "version": "0.469.4", + "version": "0.469.5", "license": "MIT", "bin": { "agent-os": "bin/agent-os" diff --git a/package.json b/package.json index 966d371d..01fe1399 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-os", - "version": "0.469.4", + "version": "0.469.5", "description": "A generic, governed operating system for running autonomous agents safely across brands. Ships with a local web console.", "license": "MIT", "type": "commonjs", diff --git a/terminal/claude-launch.sh b/terminal/claude-launch.sh index 158fc7e9..5bfba3ae 100755 --- a/terminal/claude-launch.sh +++ b/terminal/claude-launch.sh @@ -246,9 +246,40 @@ fi # wins over user settings, and project/local `false` wins even over managed settings, so the safe # direction holds. NOT `disableRemoteControl` — that kills the feature outright, and a human who has # attached to a session in the browser terminal should still be able to type `/rc` deliberately. +# ANCESTOR CLAUDE.md FILES ARE NOT THIS AGENT'S INSTRUCTIONS. Claude Code walks UP from the cwd and +# loads every CLAUDE.md / CLAUDE.local.md / .claude/CLAUDE.md / .claude/rules/ it finds. An agent's +# instructions are its OWN CLAUDE.md plus the composed system prompt (Company context, briefs) — nothing +# above the agent folder is meant to reach it. But the default data home is `./data`, i.e. INSIDE the +# software checkout, so on any such box every agent also loaded the repo's own maintainer CLAUDE.md +# (~83k chars of dev notes): live on globex it pushed a 98k-char agent prompt to 181k, over Claude Code's +# 150k instruction budget, and every run on every agent paid ~20k tokens for text written for us. +# `claudeMdExcludes` (picomatch against the absolute path; User/Project/Local types only) names each +# ancestor's instruction files — both the logical and the physical path, since claude opens the +# workspace by its real path (the macOS /var → /private/var symlink). A path holding a JSON- or +# glob-significant character is skipped rather than risk a broken settings file. +CLAUDE_MD_EXCLUDES="" +_aos_add_ancestor_excludes() { + local d="$1" parent + while :; do + parent="$(dirname "$d")" + [ "$parent" = "$d" ] && break + d="$parent" + case "$d" in *[\"\\\*\?\[\]\{\}\(\)\!]*) continue ;; esac + local base="${d%/}" + for f in CLAUDE.md CLAUDE.local.md .claude/CLAUDE.md '.claude/rules/**'; do + CLAUDE_MD_EXCLUDES="$CLAUDE_MD_EXCLUDES${CLAUDE_MD_EXCLUDES:+, }\"$base/$f\"" + done + done +} +_aos_add_ancestor_excludes "$(pwd -L)" +_AOS_PHYS="$(pwd -P)" +[ "$_AOS_PHYS" != "$(pwd -L)" ] && _aos_add_ancestor_excludes "$_AOS_PHYS" +CLAUDE_MD_EXCLUDES_LINE="" +[ -n "$CLAUDE_MD_EXCLUDES" ] && CLAUDE_MD_EXCLUDES_LINE=" \"claudeMdExcludes\": [ $CLAUDE_MD_EXCLUDES ]," cat > .claude/aos-settings.json <