docs: trim AGENTS.md to the non-derivable core and add CLAUDE.md - #1385
docs: trim AGENTS.md to the non-derivable core and add CLAUDE.md#1385mattbodle wants to merge 3 commits into
Conversation
AGENTS.md was 556 lines / 21.5 KB, the point at which agents start ignoring it. Claude Code targets under 200 lines per instruction file and Codex caps the whole merged instruction chain at 32 KiB, so this file alone consumed 66% of that budget. It is now 199 lines / 9.5 KB. Cut the content an agent can derive by reading the repo: the annotated src/ tree, the Key Files table, the glossary, the private-module listing, and the ASCII flow diagrams (ARCHITECTURE.md already holds the real diagrams). Kept the commands, the conventions that differ from tool defaults, and the gotchas. Fixed things that had gone stale or were wrong: - Open PRs against main, not master. The semantic target-branch gate accepts only main and build/*, master is 51 commits behind main, and development is a legacy alias at master's commit. - npm test runs Karma only. Jest is a separate npm run test:jest. - src/ is TypeScript now, not a JS/TS mix; mParticle#1370 finished that migration. - The src/ tree omitted kits/ entirely, ~30 vendored integrations. - ARCHITECTURE.md exists, so drop the "if available" hedge. - Only 1 of the 7 skills in .claude/skills/ was listed. Added the traps that cost real debugging time and are invisible from the script names: npm test needs a Firefox binary and Karma's exit code can lie; both runners need a built dist/ but only npm test builds first; dist/ is committed and every build rewrites it; Prettier is pinned to 1.18.2 and silently stops formatting a file containing ?.[; Prettier checks only .js; Jest skips kits/adobe and kits/rokt. CLAUDE.md is a symlink to AGENTS.md because Claude Code reads CLAUDE.md and not AGENTS.md, so these instructions were invisible to it even though the repo ships seven .claude/skills. sdk-web uses the same symlink.
PR SummaryLow Risk Overview Adds root Replaces long generic SDK/web architecture content (glossary, Fixes several stale instructions removed with the old prose (e.g. PRs against Reviewed by Cursor Bugbot for commit 7375b4a. Bugbot is set up for automated code reviews on this repo. Configure here. |
Second pass, applying one test to every line: if a config file already states it, delete it, because the config cannot go stale and this file can. That removed another 38 lines and caught two errors in the first pass. Deleted because a config file says it: - the whole commit-type -> version-bump table. It was also WRONG: release.config.js gives ci, docs, test, refactor, style, build, chore and revert a *patch* release, not "no release". Replaced with a pointer to that file and a warning that most types cut a release here. - every .prettierrc value (tabWidth, singleQuote, trailingComma). .eslintrc sets "prettier/prettier": "error", so JavaScript formatting is already enforced and documenting the values only creates drift. - tsconfig target, lib and moduleResolution. Kept only that strictNullChecks and noImplicitAny are off, since that one contradicts what an agent assumes about a TypeScript project. - exact file counts (60 .ts / 1 .js, 34 / 7, ~30 kits, ~58 src files) and the .nvmrc version. All drift on the next migration PR. - the annotated layout rows and skill descriptions that ls already shows. Corrected: the first pass claimed ".ts uses ESLint + gts" and "formatting is fully automated". Both false. Measured it - npm run lint passes no --ext, so ESLint lints 21 .js files and zero .ts. npm run prettier globs "**/*.js". gts:check exists but no hook or workflow calls it. And build:types runs `tsc -p tsconfig.types.json || true`, which swallows type errors. So nothing in CI or the pre-commit hook checks TypeScript at all. That is now trap 6, with npm run build:ts and npm run gts:check surfaced in the commands table as the manual gates they are - a more useful fact than any of the config values this pass deleted. AGENTS.md is now 161 lines / 8.4 KB, down from 556 / 21.5 KB.
The trap said 'measured: 21 .js files, zero .ts'. That 21 came from a different branch's working tree, and a count in an instruction file is the staleness bomb this PR is supposed to remove - on this branch it would be 9. The conclusion does not depend on the number: eslint is invoked with no --ext, so it reports .js only and never a .ts file whatever the file set. State that instead.
|



Why
AGENTS.mdhad grown to 556 lines / 21.1 KiB, past the point where agents reliably follow it. It is now 161 lines / 8.2 KiB.Every major agent vendor now publishes a budget for instruction files, and this one blew through them:
project_doc_max_bytes= 32 KiB for the whole merged root→cwd chainThe failure mode isn't an error, it's dilution: past the budget the rules that matter get lost among the ones that don't, and Codex starts silently dropping nested, more-specific files once the chain fills.
CLAUDE.mdsymlink — the part that unblocks somethingClaude Code reads
CLAUDE.md, notAGENTS.md. There was noCLAUDE.md, so all 556 lines were invisible to it — while the repo ships seven.claude/skills/definitions, i.e. it is clearly being used with Claude Code.Anthropic's documented fix is a symlink or an
@AGENTS.mdimport. This adds the symlink (create mode 120000), asROKT/sdk-webdoes. Copilot independently accepts a rootCLAUDE.mdas a substitute forAGENTS.md, so one file serves every consumer with nothing to keep in sync.The rule applied to every line
That is also Anthropic's
/doctortrim rule — cut directory layouts, dependency lists and architecture overviews; keep pitfalls, rationale, and conventions that differ from tool defaults.Deleted because a config file says it:
release.config.js.prettierrcvalue →.prettierrc, and.eslintrcalready sets"prettier/prettier": "error"target/lib/moduleResolution→tsconfig.json.nvmrcversion, and every file count (60 .ts / 1 .js,34 / 7,~30kits,~58src files) — all drift on the next migration PRsrc/tree, the 13-row Key Files table, the 13-term glossary, the private-module listing, five ASCII flow diagrams (ARCHITECTURE.mdhas the real ones), and the per-skill descriptions thatls .claude/skills/already showsKept: the commands whose names don't tell you what they cover, the conventions no config encodes (naming — verified that no lint rule enforces it), and the traps.
Two things the sweep caught
1. The version-bump table was wrong. It claimed
docs,test,refactor,perf,style,chore,ci,buildandrevertproduce "no release".release.config.jsgivesci,docs,test,refactor,style,build,choreandreverta patch release. Hand-copying a config into prose is how that happens, which is the argument for deleting it rather than fixing it.2. Nothing checks the TypeScript. The first pass repeated the old file's claim that "
.tsuses ESLint + GTS". Measured, that is false:npm run lintiseslint src/ test/src/with no--ext, so ESLint reports.jsfiles only and never a.tsone (measured by running it)npm run prettierglobs"**/*.js"gts:checkexists as a script but no hook or workflow calls itbuild:typesrunstsc -p tsconfig.types.json || true— the|| trueswallows type errorspre-commithook is"pre-commit": ["lint"], so it inherits the same.js-only scopeSo neither CI nor the pre-commit hook catches a type error or a formatting slip in a
.tsfile, in asrc/that is now TypeScript throughout. That is trap 6, andnpm run build:ts/npm run gts:checkare surfaced in the commands table as the manual gates they are. Worth a look independently of this PR.Other stale claims fixed
masterbranch"main.Check PR for semantic target branchaccepts onlymainandbuild/*. GitHub still offersmasteras the default base, andCONTRIBUTING.md:8still saysmaster.npm run test— Full suite (Karma + Jest)"npm testis Karma only; Jest isnpm run test:jest.identity.js,events.js,forwarders.js,persistence.jssrc/is TypeScript throughout apart from the stub entry point (#1370 finished that migration).kits/at allkits/is a top-level directory of vendored integrations, each with its own build and tests.ARCHITECTURE.mdif available"/verifylistedTraps added
npm testneeds a Firefox binary —test/karma.config.jsruns['ChromeHeadless', 'FirefoxHeadless']; without it the launcher crashes and takes karma-server with it.TOTAL:line, not$?.dist/— Karma serves../dist/mparticle.js, Jest loads it viasetupFiles.npm testbuilds first;npm run test:jestand a barekarma startdo not, so Jest fails on a fresh clone for reasons unrelated to your diff.dist/is committed and every build rewrites it —git checkout -- dist/before committing.1.18.2(exact, no caret), which predates optional chaining:x?.[key]makes it reportParsing error: Expression expectedand silently stop formatting the whole file.kits/adobe(kit-level Jest) andkits/rokt(Vitest, whose.spec.tsnames Jest would falsely match).Verification
8288cc96, and the ESLint scope was measured by running it rather than inferred.npm run prettierglobs**/*.jsand there is no markdown linter in CI.@pathtokens, so Claude Code won't treat anything as an unintended import when reading through the symlink.For the SDK team to decide
COMMON SECTIONis hand-synced across SDK repos and its banner was stampedLast Updated: 2026-02-16. I trimmed within the markers and kept them, but I can only see this repo — the trim needs mirroring, or the convention wants replacing with a shared file. I also moved the branch-target rule out of the shared block, sincemasterwas wrong here precisely because it lived in the block every SDK copies.CONTRIBUTING.md:8still says "PR into the mParticle SDKmasterbranch". Left alone as out of scope; happy to fix here if preferred..github/workflows/reusable-workflows.ymlstill passesbase_branch: "development"tosecurity-checks.yml@stable— another master-era leftover.--ext .tson the lint script, dropping the|| true, or wiring upgts:check.