Skip to content

AI guardrails

A rulebook your assistant reads before it writes, checks that stop what slips through, error messages that tell it how to fix its own code, and one command that tells it when the work is done.

In short

  • CLAUDE.md and AGENTS.md point your assistant to ten files of conventions.
  • 17 custom lint rules and strict TypeScript block the mistakes assistants make most.
  • Your assistant checks its own work with yarn verify before it says it's done.

A rulebook your assistant reads first

CLAUDE.md holds the product decisions, the definition of done, the house rules, the commands and the known pitfalls in about 80 lines. AGENTS.md links to it for assistants that look for that name, and it points to ten files with the full rules of each area.

  • Stack, tooling, code style, front end, back end, design system, testing, CI, security, sign-in and billing
  • A complete example feature shows every rule applied, ready to copy
CLAUDE.md
  • AGENTS.md→ CLAUDE.md
  • CLAUDE.md
  • docs/conventions/
  • 01-stack.md
  • 02-monorepo-and-tooling.md
  • 03-code-style.md
  • 04-frontend.md
  • 05-backend.md
  • 06-design-system.md
  • 07-testing.md
  • 08-ci-and-git-workflow.md
  • 09-security.md
  • 10-auth-and-billing.md

## House rules

  • Short `//` comments only, never JSDoc; comment WHY, never WHAT.
  • Named exports only. Functional components only. One exported thing per util/hook/constant/type file.
  • Imports through aliases (`@/`, `~/`, `src/`), never `../`. No barrel files in `app-front`.
  • Exhaustive `switch` over unions ends with `default: assertUnreachable(value)`.
  • Icons only from `app-ui/icon`, never `@tabler/icons-react`; use the canonical icon from the icon dictionary.

17 rules for the mistakes assistants make

The custom rules run with oxlint on every change, next to strict TypeScript and the tests. There are no warnings: a rule is either enforced or deleted.

  • Security: every endpoint guarded, workspace data only through its repository
  • Consistency: names, files and folders where everyone expects them
  • Design: colors from the theme, never typed by hand

API and data4

  • app/graphql-resolvers-should-be-guarded
  • app/rest-api-methods-should-be-guarded
  • app/prefer-workspace-scoped-repository
  • app/enforce-module-boundaries

React5

  • app/component-props-naming
  • app/effect-components
  • app/matching-state-variable
  • app/no-state-useref
  • app/no-navigate-prefer-link

Styles3

  • app/styled-components-prefixed-with-styled
  • app/sort-css-properties-alphabetically
  • app/no-hardcoded-colors

Files and names4

  • app/max-consts-per-file
  • app/types-folder-filename
  • app/no-miscased-acronym-in-identifier
  • app/folder-structure

Accessibility1

  • app/no-storybook-a11y-disable

Errors that explain the fix

Each rule says what to change and why, in one message. An assistant that reads it corrects its own code without guessing, and so can you.

  • Some rules even fix the code themselves, such as the order of CSS properties
  • CI runs the same checks on every pull request
npx nx lint
@InjectRepository(ProjectEntity)project.service.ts:18:5 error app(prefer-workspace-scoped-repository): Use @InjectWorkspaceScopedRepository(ProjectEntity) instead of @InjectRepository(ProjectEntity): the workspace-scoped repository adds the workspaceId to every read and write, so one workspace can never read or change the data of another. If ProjectEntity is not owned by a workspace, add an exemption with its reason to the lint config.
@Mutation(() => ProjectDto)project.resolver.ts:64:3 error app(graphql-resolvers-should-be-guarded): GraphQL resolver `archiveProject` has no permission guard. Add a *PermissionGuard (for example SettingsPermissionGuard(...)) with @UseGuards on the method or its class: every endpoint must state which permission it requires. Use NoPermissionGuard to declare explicitly that none is needed.
const countRef = useRef(0);ProjectList.tsx:12:20 error app(no-state-useref): useRef is reserved for DOM elements, typed as such: useRef<HTMLDivElement>(null). A ref change does not re-render the component, so state kept in a ref leaves the screen out of date; use useState or an atom instead.

From prompt to passing check

Your assistant writes a hardcoded color, the lint rule stops it, the assistant reads the message, uses a theme variable instead and the check passes. The same loop runs for every rule.

One command says when it's done

yarn verify checks what changed, quickest checks first: forbidden words, lint and formatting, the generated files, translations, types, unit tests and the database migration. It ends with "All checks passed", or with each failure and how to fix it.

  • In Claude Code, a hook lints every file the assistant edits, and another makes it run yarn verify before it ends its turn
  • Eight step-by-step recipes: a guided start, a feature, a table, a page, translations, background jobs, Clerk and Stripe, the deploy
  • Cursor reads the same rules from .cursor/rules; Codex and others from AGENTS.md
  • yarn verify:full checks everything, like CI: builds, API tests, end-to-end tests and the design system's stories

Questions

Which assistants does it work with?

Any assistant that reads CLAUDE.md or AGENTS.md, such as Claude Code, Codex or Cursor. Claude Code also runs the checks by itself, through hooks. The checks run the same, whoever writes the code.

Can I change or remove a rule?

Yes, it's your code: each rule lives in the repository with its tests. Change the conventions and the lint settings together, so the rulebook and the checks keep saying the same thing.

Do I still need to review the code?

Yes. The checks catch broken rules, not wrong product decisions. The guide ends with the list of things a person should still check.

How do I know the assistant really checked its work?

Its last message should say that yarn verify passed. Not sure? Run yarn verify yourself: it takes a few seconds to a few minutes and ends with "All checks passed" or with what to fix.

Start your next app from a clean base

Pay once and get the full source code, with every future update.