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 4 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 Removes duplicated derivable content (glossary, full Corrects misleading guidance in the old doc: PR base branch, Adds Reviewed by Cursor Bugbot for commit a26824a. 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.
AGENTS.md claimed '/verify runs lint, build and Jest in one step'. Read against .claude/skills/verify/SKILL.md that is wrong twice over: the skill runs lint, then npm run test:jest AND npm test (Karma), and there is no standalone build step. Karma builds internally via npm test; test:jest does not, which is trap 3. Point at the trap instead of implying a build happens.
|



Why
AGENTS.mdhad grown to 556 lines / 21.1 KiB, past the point where agents reliably follow it. It is now 162 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), matching the convention already used elsewhere in our SDK estate. 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.a26824a2): every backticked path exists,eslintis stilleslint src/ test/src/with no--extunder eslint 8 (eslintrc mode, so the default extension really is.jsonly),.eslintrcuses@babel/eslint-parserwhich could not parse a.tsfile even if it were passed one,tsconfig.types.jsonsetsnoEmitOnError: falseso the|| truegenuinely swallows type errors,pre-commitis still["lint"], prettier is still1.18.2exact, andrelease.config.jsstill gives a patch release to all nine non-feattypes. One claim of my own was wrong and is fixed ina26824a2: I had written "/verifyruns lint, build and Jest in one step"..claude/skills/verify/SKILL.mdactually runs lint, thennpm run test:jestandnpm test(Karma), with no standalone build step. Corrected, and pointed at trap 3.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..claude/skills/verify/SKILL.mdcontains the same mistake I just fixed, and one more. Its comment reads "Each suite handles its own build internally" — true fornpm test, false fornpm run test:jest, which is exactly trap 3. So/verifycan report a spurious Jest failure on a fresh clone. Left alone because this PR is scoped toAGENTS.md/CLAUDE.md; the skill wants an explicitnpm run buildstep.