From 9e9a7d99816680a5db8f3f3f7d79b3e6507ffc1a Mon Sep 17 00:00:00 2001 From: Dao Ho <84757503+Dao-Ho@users.noreply.github.com> Date: Tue, 15 Sep 2026 16:30:03 -0400 Subject: [PATCH 1/3] feat: core.md --- core/core.md | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 core/core.md diff --git a/core/core.md b/core/core.md new file mode 100644 index 0000000..b0271b1 --- /dev/null +++ b/core/core.md @@ -0,0 +1,119 @@ +# Engineering Principles + + + +Stack-agnostic rules that apply to every project. Stack-specific rules belong in +sibling module files; project-specific commands, paths, and architecture belong +in the project's own `AGENTS.md`. Don't add either here. + +## Before you call it done + +- Run the project's format, lint, typecheck, and test commands. A change isn't + done until they pass — say so plainly if they don't. +- Re-read your own diff. Delete anything you added and stopped using. +- Changed an API route? Regenerate the spec and client in the same change. +- Changed behavior the docs describe? Update the docs in the same change. + +## Architecture boundaries + +Layer names differ by project (handler/controller, service, repository/ +transaction), the boundaries don't: + +| Layer | Owns | Never contains | +| --- | --- | --- | +| Handler / Controller | HTTP in/out, parsing, validation, status codes | business logic | +| Service | business rules | HTTP types, SQL | +| Repository / Transaction | data access | business logic | + +- Data flows down, errors flow up: repository → service → handler. +- Cross a boundary through an interface, not a concrete type. +- Pass dependencies explicitly through constructors. No globals, no singletons + reached via import side effects, no hidden state. + +## Errors + +- One error taxonomy per project. Don't invent an ad-hoc error shape per endpoint. +- Convert infrastructure errors to domain errors at the service boundary. +- Never return a raw database error, driver error, or stack trace to a client. + Log the full error server-side; return a safe message and the correct status. +- Translate known constraint violations into something a user can act on: + unique violation → "Email already exists", FK violation → "Referenced + resource not found". + +## Functions and naming + +- One function, one job. Prefer under ~40 lines. Extract a helper before nesting + a third conditional. +- Name by what it does, not how: `FindUserByID`, `CalculateInvoiceTotal`. + Reject `Handle`, `Process`, `DoThing`, `data`, `temp`. +- No abstraction for a single caller. No interface until there's a second + implementation or a test that needs to mock it. +- The surrounding code's idiom, comment density, and naming beat any general + preference stated here. + +## No hardcoded values + +Anything someone might reasonably want to change without a code review belongs +in config, constants, design tokens, or environment variables — page sizes, +timeouts, retry counts, colors, spacing, URLs, feature flags, limits. + +## Dead code + +- No unused imports, variables, or parameters. +- No commented-out code. Git remembers it. +- No leftover debug output — `println`, `console.log`, `.only` in tests. +- Production paths use structured logging, not print statements. + +## Data access + +- No unbounded lists. Every list endpoint paginates; prefer cursor/keyset over + offset, which degrades as the table grows. +- Cap page size server-side. A client asking for 10,000 rows gets the cap. +- Return only the fields the caller needs. +- No N+1. Fetch related data in one round trip; batch instead of looping. + +## Secrets and input + +- Secrets come from the secret manager or the environment. Never commit one, + never log one, never put one in an error message. +- Validate and parse every request payload at the edge, before it reaches + business logic. Reject unknown fields on write paths. +- Parameterize every query. Never build SQL by string concatenation. + +## Tests + +Cover, in rough order of what actually catches bugs: + +- **Happy path** — the expected flow. +- **Error paths** — invalid input, missing resource, unauthorized. +- **Edge cases** — empty, boundary values, duplicates, malformed input, + concurrent requests. +- **Lifecycle** — create → read → update → delete → verify gone. +- **Idempotency** — calling it twice is safe. + +Rules: + +- Mock external services and the clock. Tests must not depend on the network, + wall-clock time, or execution order. +- A test that asserts nothing is not a test. +- When you fix a bug, add the test that would have caught it. + +## Version control + +- Conventional Commits: `feat:`, `fix:`, `docs:`, `chore:`, `refactor:`, `test:`. +- Small, focused PRs. Split a large feature into reviewable pieces. +- The message explains *why*; the diff already shows *what*. +- Never hand-edit generated files — mocks, API clients, migration output. + Change the source and regenerate. + +## Refactoring + +Preserve behavior unless told otherwise. Reduce complexity, improve names, +remove duplication, enforce the boundaries above. Don't expand into unrelated +files along the way. + +## When you're unsure + +- An existing pattern in this codebase beats the general advice here. +- If two existing patterns conflict, ask rather than silently picking one. +- State any assumption you had to make when you summarize the change. From 1a2a1d0a73bcb7ab5183fbad6d6e728298c9c99c Mon Sep 17 00:00:00 2001 From: Dao Ho <84757503+Dao-Ho@users.noreply.github.com> Date: Tue, 15 Sep 2026 16:45:20 -0400 Subject: [PATCH 2/3] feat: context + additional changes --- README.md | 104 +++++++++++++++++++++++++++++++++++++ template/project-header.md | 63 ++++++++++++++++++++++ 2 files changed, 167 insertions(+) create mode 100644 README.md create mode 100644 template/project-header.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..1a1c457 --- /dev/null +++ b/README.md @@ -0,0 +1,104 @@ +# agent-template + +Shared engineering principles for GenerateNU projects, in the format coding +agents actually read. + +Every project writes down the same things — layer boundaries, error handling, +pagination rules, test expectations — and every project writes them slightly +differently, in a `CONTRIBUTING.md` that agents never open. This repo keeps one +copy, versioned, so a fix lands everywhere instead of in one repo. + +## What's here + +| Path | What it is | +| --- | --- | +| `core/core.md` | Stack-agnostic principles. Always loaded. ~120 lines. | +| `template/project-header.md` | The per-project sections you fill in: commands, architecture, stack, local conventions. | + +## Install + +From the root of your project: + +```bash +TPL=/path/to/agent-template +cat $TPL/template/project-header.md $TPL/core/core.md > AGENTS.md +echo '@AGENTS.md' > CLAUDE.md +``` + +Then **fill in the TODO sections at the top of `AGENTS.md`** — commands, +architecture, stack, project conventions. The shared principles below the +divider are already done. + +### Why two files + +`AGENTS.md` is the cross-tool convention: Cursor and Codex read it directly. +Claude Code does *not* read `AGENTS.md` — it reads `CLAUDE.md` — so the one-line +`CLAUDE.md` imports it. One source of truth, both tools work. + +If you need Claude-specific instructions, add them below the import: + +```markdown +@AGENTS.md + +## Claude Code + +Use plan mode for changes under `src/billing/`. +``` + +A symlink (`ln -s AGENTS.md CLAUDE.md`) works too, if you'll never need that. +Not on Windows without Developer Mode. + +## Writing rules that work + +Things worth knowing before you add to this, or to your own `AGENTS.md`: + +- **It's a token budget, not a wiki.** Every line is re-read on every turn of + every session. The test for inclusion isn't "is this a good principle" — it's + "does this change agent behavior, in a way worth paying for on every turn?" +- **Target under 200 lines.** Longer files measurably reduce adherence. `core.md` + is 119; keep the project sections tight and you have room. +- **Write rules that are checkable.** "Run `bun test` before claiming done" + beats "test your changes." "Use `bg-bg-container`, not `bg-gray-100`" beats + "use the design system." +- **Rules the model already follows are pure cost.** "Write clean, maintainable + code" earns nothing. Rules earn their slot by correcting a *default* behavior. +- **HTML comments are stripped before the file reaches the agent.** The `` + markers in the template cost zero tokens — which also means an unfilled + section is silently empty rather than obviously broken. Fill them in. +- **If it's mechanically checkable, make it a hook instead.** A `PreToolUse` hook + enforces a rule 100% of the time for zero tokens. `AGENTS.md` is guidance, not + enforcement. +- **If it only applies to one kind of task, make it a skill.** Skills load on + demand. Release procedures, incident response, and migration walkthroughs + don't belong in a file that loads every session. + +## Contributing + +Open a PR. One rule per PR, and the description must name **the agent failure it +prevents** — an actual thing that went wrong in an actual repo. If nobody can +name the failure, the rule doesn't go in. That's the only thing keeping this +file from growing to 600 lines of generic advice nobody reads. + +Fixes go here, not in your project's copy. Editing the shared section in one +repo means the next project inherits the bug. + +## Updating + +The install is a copy, so a project doesn't pick up changes automatically. Each +generated `AGENTS.md` carries its version in an HTML comment +(``). To see what a project is missing: + +```bash +git diff core-v0.1..main -- core/core.md +``` + +Apply the parts you want. Tag a new version here whenever `core.md` changes in +a way projects should know about. + +## Sources + +Mined from the `CLAUDE.md`, `docs/`, and `CONTRIBUTING.md` of +[toggo](https://github.com/GenerateNU/toggo), +[dearly](https://github.com/GenerateNU/dearly), and +[selfserve](https://github.com/GenerateNU/selfserve). Every rule in `core.md` +is something at least two of the three already agreed on. diff --git a/template/project-header.md b/template/project-header.md new file mode 100644 index 0000000..4699eb0 --- /dev/null +++ b/template/project-header.md @@ -0,0 +1,63 @@ +# + + + + +## Commands + + + +| Task | Command | +| --- | --- | +| Install deps | `` | +| Start backend | `` | +| Start frontend | `` | +| Run tests | `` | +| Lint | `` | +| Format | `` | +| Typecheck | `` | +| Create migration | `` | +| Apply migrations | `` | +| Regenerate API spec/client | `` | +| Regenerate mocks | `` | + + + +## Architecture + + + +``` + +``` + +## Stack + + + +## Project conventions + + + +--- + + + From d58ae718de4d734b159ca5cdc75b7c3dba931691 Mon Sep 17 00:00:00 2001 From: Dao Ho <84757503+Dao-Ho@users.noreply.github.com> Date: Tue, 15 Sep 2026 16:51:45 -0400 Subject: [PATCH 3/3] feat: update readme --- README.md | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/README.md b/README.md index 1a1c457..6f767c0 100644 --- a/README.md +++ b/README.md @@ -40,38 +40,6 @@ If you need Claude-specific instructions, add them below the import: ```markdown @AGENTS.md -## Claude Code - -Use plan mode for changes under `src/billing/`. -``` - -A symlink (`ln -s AGENTS.md CLAUDE.md`) works too, if you'll never need that. -Not on Windows without Developer Mode. - -## Writing rules that work - -Things worth knowing before you add to this, or to your own `AGENTS.md`: - -- **It's a token budget, not a wiki.** Every line is re-read on every turn of - every session. The test for inclusion isn't "is this a good principle" — it's - "does this change agent behavior, in a way worth paying for on every turn?" -- **Target under 200 lines.** Longer files measurably reduce adherence. `core.md` - is 119; keep the project sections tight and you have room. -- **Write rules that are checkable.** "Run `bun test` before claiming done" - beats "test your changes." "Use `bg-bg-container`, not `bg-gray-100`" beats - "use the design system." -- **Rules the model already follows are pure cost.** "Write clean, maintainable - code" earns nothing. Rules earn their slot by correcting a *default* behavior. -- **HTML comments are stripped before the file reaches the agent.** The `` - markers in the template cost zero tokens — which also means an unfilled - section is silently empty rather than obviously broken. Fill them in. -- **If it's mechanically checkable, make it a hook instead.** A `PreToolUse` hook - enforces a rule 100% of the time for zero tokens. `AGENTS.md` is guidance, not - enforcement. -- **If it only applies to one kind of task, make it a skill.** Skills load on - demand. Release procedures, incident response, and migration walkthroughs - don't belong in a file that loads every session. - ## Contributing Open a PR. One rule per PR, and the description must name **the agent failure it