Created
April 7, 2026 13:54
-
-
Save ljw1004/ba0eedb29db274c4430113b5f5b50041 to your computer and use it in GitHub Desktop.
AGENTS.md, Apr'26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # <PROJECT TITLE> | |
| We are working to add <PROJECT DESCRIPTION>. The following guidance files will help you do your job effectively. | |
| - AGENTS.md -- This current file is already inserted into the start of every agent conversation, and explains how the user wants the AI to behave. | |
| - <COMPANY-MD> -- general information about how to develop in the <COMPANY> codebase | |
| - ai/LEARNINGS.md -- If planning or writing project code you *MUST ALWAYS* read this file, which contains your hard-won wisdom and experience, learned through costly trial and error. You must read this to avoid making the same mistakes in all work you do. | |
| - ai/ARCHITECTURE.md -- This is where you keep your notes about the architecture of <PROJECT>, how to develop within it, and also hard-won wisdom and experience about how its functions work. If planning or writing project code, read this to understand the code you're working in. | |
| - ai/PLAN.md -- Describes the overall context and goal of the project. You will normally only read this if asked to plan the next milestone. | |
| - ai/PLAN_M{n}.md -- Describes the implementation plan for a particular milestone. You will normally read this if asked to execute on a milestone, or fix bugs in one. | |
| IMPORTANT: we must validate every step of the way. The project milestones will reflect this. | |
| ## Course-correction and learnings | |
| When the user course-corrects you, or when you try something and it doesn't work and you learn yourself what to do next, you must persist the lesson in the correct durable file. Use this decision tree: | |
| - **Repo-wide and durable** (works across projects): put it in `ai/LEARNINGS.md`. | |
| - **<PROJECT>>-specific** (about the architecture of <PROJECT> that will remain true at the end of the project): put in in `ai/ARCHITECTURE.md` | |
| - **Milestone-specific** (about scope/insights relating to the current milestone that will become irrelevant by the end): put it in `ai/PLAN_M{n}.md` | |
| - **Symbol-local contract** (one module/function/type behavior): put it in code docblocks near the symbol, or by renaming symbols to better describe their purpose. | |
| - Quick test: if it remains true after renaming modules and shipping new features, it is likely `ai/LEARNINGS.md`; if it depends on current <PROJECT> behavior, it belongs in `ai/ARCHITECTURE.md` | |
| - Quick test: if `ai/ARCHITECTURE.md` mentions a milestone, then either rework it into a "durable truth" that will still be true of the codebase after we've forgotten about milestones, or move it into `ai/PLAN_M{n}.md` | |
| Do not rely on conversational memory alone for corrections. | |
| ## Codebase style and guidelines | |
| Coding style: All code must also be clean, documented and minimal. That means: | |
| - Keep It Simple Stupid (KISS) by reducing the "Concept Count". That means, strive for fewer functions or methods, fewer helpers. If a helper is only called by a single callsite, then prefer to inline it into the caller. | |
| - At the same time, Don't Repeat Yourself (DRY). There is a tension between KISS and DRY. If you find yourself in a situation where you're forced to make a helper method just to avoid repeating yourself, the best solution is to look for a way to avoid even having to do the complicated work at all. | |
| - Prefer functional-style code, where variables are immutable "const", there's almost no "if/else" branching branching, and most functions are side-effect free. Prefer to use ternary expressions "b ? x : y" rather than separate lines and assignments, if doing so allows for immutable variables. | |
| - If some code looks heavyweight, perhaps with lots of conditionals, then think harder for a more elegant way of achieving it. | |
| - Code should have comments, and functions should have docstrings. The best comments are ones that introduce invariants, or prove that invariants are being upheld, or indicate which invariants the code relies upon. | |
| - **Name side-effecting functions to expose the side effect.** A function named `load()` implies it returns data. If its real purpose is to populate module-scoped state and fire a callback, that must scream from the name — e.g. `loadIntoStateAndNotify()`. Every side effect is dangerous and unclean; the function name is the best place to call it out. Pure functions (input→output, no mutation) can have simple names; impure functions must wear their impurity on their sleeve. | |
| - **Never use `void asyncFn()` for fire-and-forget.** Discarding a promise with `void` silently swallows exceptions or causes unhandled rejections. The caller should always `await` the async function. If fire-and-forget is truly needed (rare), the function itself must handle all errors internally and return `void` (not `Promise`), with a name that makes the contract explicit (e.g. `saveFireAndForget()`). Prefer `await` — it keeps error propagation explicit and lets the caller decide how to handle failure. | |
| - The same rule applies to async dispose functions: if there's async disposal, then the caller must await it somehow. | |
| - Avoid optional fields, and optional/default parameters. If a parameter/field isn't needed, give it a type "{something} | undefined". | |
| - All source files under <DIR> must be kept under 1000 lines | |
| - All edits you make must be typechecker clean and lint clean | |
| I am adamant about clean engineering. What I look for: | |
| - Your learnings must be added to `ai/LEARNINGS.md` (for durable senior-engineer level general wisdom) or `ai/ARCHITECTURE.md` (for wisdom and experience specific to the current project). | |
| - Invariants are the best way to document all aspects of code. These include code invariants (stating what assumptions a function makes about shared data, and how it upholds them), and architecture invariants (for instance the main index.js never touches state except through component accessors). | |
| - Functional style is best. Prefer consts, immutable-data, and side-effect-free "pure" functions. Avoid as many if/else branches as possible. | |
| - **Address prerequisites cleanly, don't hack around them.** When working toward a goal, you will often discover that a prerequisite needs fixing first. Do the clean-engineering right thing for the prerequisite, even if this leads down a long detour of better-engineering and refactoring. Never just hack around it to reach the goal faster. The user is positively DELIGHTED when we discover new reasons, justifications, opportunities for refactoring and improved clean engineering. If you find yourself in a situation "I have been asked to do X, but I need to do Y first, and that in turn needs Z", then it's positively desirable to set X aside while we start on an entirely new plan for Z. | |
| You must document *meaning* of every field, and also enums and disjoint type fields. | |
| - "Meaning" says briefly what the field/enum represents. From a well-written meaning, a smart reader will be able to deduce all the invariants around this field/enum, and deduce how it will be used in the code. | |
| - It is hard work to distill a good meaning! You must put considerable effort into it. | |
| We must use exceptions carefully. | |
| - Some exceptions are because we believe code is unreachable; throwing the exception represents a coding bug on our part. Such exceptions must have (1) comments that prove why this exception will never be thrown, (2) telemetry so we can know centrally if we have a coding bug, (3) clear user-facing recovery. | |
| - Other exceptions are used as a normal control flow mechanism in expected situations. We prefer to avoid these because it's hard to distinguish coding-bugs from control-flow: it's always better for a function to have distinguished return types for its different kinds of expected behavior. | |
| ### Close the loop, autonomy | |
| The agent is responsible for fully validating every change, end-to-end, autonomously. The user should not have to prompt for testing, deployment, or production verification — the agent must pursue these proactively. | |
| - **Test before presenting to the user**: if you've made changes, they must be built, validated (if appropriate including screenshots) before telling the user it's done or asking them to look at it. You don't need to run full test suites after every individual edit — but you must test before handing off to the user. | |
| - **Test all paths**: don't just test the happy path. Exercise failure, if in IDE then click every relevant button, test error states. | |
| - **Proactively gather metrics**: timing data, track counts, cache sizes. Record them without being asked. These help the human evaluate whether the implementation meets performance goals. | |
| - Solve your own obstacles. If testing requires setup (e.g. a Chromium profile for production, a running server), figure out how to set it up. Try the obvious approach first. Only ask the human if you've confirmed the obstacle truly requires human intervention. | |
| Use these commands for checking: | |
| - Auto-format, ~2s: <SNIP> | |
| - Typecheck, ~30s: <SNIP> | |
| - Typecheck test-files: <SNIP> | |
| - Eslint, ~20s: <SNIP> | |
| - Unit tests: <SNIP> | |
| - Integration tests: <SNIP> | |
| ## Second opinion by Claude | |
| You will at times be asked to invoke Claude to get a second opinion. Invoke Claude by preparing your prompt in <DIR>/ai/claude.{id}.input.md, where `{id}` might be something like "m1_plan" or "m2_review_style", and shelling out to it like this: | |
| ``` | |
| cd <DIR> && claude --verbose --output-format stream-json -p "Please respond to <DIR>/ai/claude.{id}.input.md and respond directly, not to a file" | scripts/claude_stream.jq > <DIR>/ai/claude.{id}.output.md | |
| ``` | |
| - Claude does best if provided with goals and some kind of context about what you want from it, why, what you hope to achieve. | |
| - You will often be asked to invoke Claude in rounds: invoke it, act on its feedback, invoke it again, repeat until there's nothing left to act upon. Do NOT reference previous rounds when you invoke it: Claude does best if starting from scratch each round, so it can re-examine the whole ask from fundamentals. Note that each time you invoke Claude it has no memory of previous invocations, which is good and will help this goal! Also, avoid asking it something like "please review the updated files" since (1) you should not reference previous rounds implicitly or explicitly, (2) it has no understanding of what the updates were; it only knows about the current state of files+repo on disk. | |
| - Claude can and will do its own research of the codebase and find context (and you can expect it to take 5-10 minutes to do so); you can also provide context yourself in the input file. Don't limit it! Let it search what it wants, read what it wants, to best answer your questions. | |
| - In the claude-input.md file you can mention other filenames, but you must use absolute filepath. If it deems fit, Claude will read them. | |
| - You should use Claude when explicitly instructed (for planning, for code review), but also loop in Claude proactively any time there is a weighty decision to be made. | |
| - You don't have to follow all of Claude's suggestions! But if you disagree with its findings, you should do a further round where you pre-emptively justify your opinion, with the hope that Claude's further round will no longer complain. | |
| - Claude will have read AGENTS.md, and can do its own research. If there are specific files you want it to read, tell it about them. | |
| - Claude often takes a long time to give a thoughtful response; expect up to 10-15mins before it finishes. You can see its progress in the `claude-output.md` file that you piped into. | |
| - If you have been told to do Claude review, it is *mandatory*. If it's not working for some reason, then you must try again, or abandon your work and report to the user. Never proceed without Claude review. | |
| - *Feedback is a gift*. You are striving to be your best self, and feedback is what will help you get there. Don't view Claude as a gatekeeper/blocker; view it as a mechanism to achieve ever greater quality. | |
| - Leave the input+output files in place, for the user to review. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment