agentleFS
TiagoGranelliTiagoGranelli/slopproof/AGENTS.md

slopproof

AGENTS.md2 starsChanged 20 days agoMITView on GitHub

Slopproof is a full-stack template built around a verifiable API contract. src/contract generates openapi.json, which generates src/sdk; the running API and the tests must agree with all three. A change is done when pnpm check passes, plus pnpm check:drift and pnpm build && pnpm verify:app for changes to the contract, database, auth, or UI flows. - UI and page routes reach data only through the SDK (#/sdk/...) or a server function in #/lib/*.functions.ts. src/server, drizzle-orm, pg and better-auth (its client…

  • Reads credentials
  • Installs packages
  • Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.

    Of the AGENTS.md files we read, the 31,542 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.

  • In this repository10 other files it gives its agents.

The file

We split this AGENTS.md into the rules it gives an agent

10 rules. About 0 are common, 1 is distinctive, and 9 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 9

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Import the file that defines a symbol.

    line 42
  2. Very specific:

    First setup: Run pnpm install && pnpm bootstrap.

    line 91
  3. Distinctive:

    Read tests/AGENTS.md before you write or change a test: it says which layer a behavior belongs in and how each layer's harness works.

    line 96
  4. Very specific:

    Every gate's failure message says how to fix it.

    line 111
  5. Very specific:

    Keep node_modules/, .output/, and .repos/ out of every tool's scope.

    line 120
  6. Very specific:

    Run heavy commands one at a time (pnpm build, type-aware lint, verify:app, lighthouse).

    line 124
  7. Very specific:

    Read upstream code with gh api or a sparse clone of the listed paths into .repos/ (dependency-sources.md), never a full clone.

    line 126
  8. Very specific:

    CSP: No policy allows 'unsafe-inline' (ADR 0010): style with classes, and put head scripts and styles through the route's head() so the …

    line 158
  9. Very specific:

    Tailwind: Keep @import "tailwindcss" source("../") in src/styles/app.css.

    line 160
  10. Very specific:

    TanStack Start and Router: run pnpm dlx @tanstack/intent@0.4.0 list, then pnpm dlx @tanstack/intent@0.4.0 load <package>#<skill> for the …

    line 171

≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

AGENTS.md

Slopproof is a full-stack template built around a verifiable API contract. src/contract generates openapi.json, which generates src/sdk; the running API and the tests must agree with all three. A change is done when pnpm check passes, plus pnpm check:drift and pnpm build && pnpm verify:app for changes to the contract, database, auth, or UI flows.

Architecture map

PathRoleMay import
src/contract/Effect HttpApi contract: schemas, endpoints, tagged errors, middleware tagseffect and other src/contract modules only
src/server/Server-only: Effect handlers (api/), Better Auth (auth.ts), Drizzle (db/), repositories, env.ts, shutdown lifecycle.ts. Read src/server/AGENTS.md before changing itcontract, sdk
src/sdk/Hey API client and TanStack Query options (generated)nothing
src/lib/Shared plumbing: api-client.ts (isomorphic SDK client), api-error.ts, utils.ts. *.functions.ts are server functions (createServerFn): session.functions.ts (route guards), auth.functions.ts (every account action)contract, sdk. The server adapters (*.functions.ts, api-client.ts) also import server and lib
src/components/Shared UI that knows no feature: ui/ (shadcn primitives, Radix, style radix-nova), errors/ (router error and not-found states, SectionErrorBoundary, ApiErrorAlert), layouts/ (site header, pending state), form/, lists/, time/, confirm-delete/lib, contract, sdk
src/features/<name>/One feature each (posts, auth): api/ (query options and mutation hooks), components/, utils/components, lib, server adapters, contract, sdk. Never another feature
src/routes/, router.tsx, start.ts, styles/The app layer. Page routes define the route (loader, guards, head) and compose features. api/$.ts hands requests to the Effect API; api/auth/$.ts to Better AuthPage routes: features, components, lib, server adapters, contract, sdk. api/** is a server adapter: server, contract, sdk, lib
  • UI and page routes reach data only through the SDK (#/sdk/...) or a server function in #/lib/*.functions.ts. src/server, drizzle-orm, pg and better-auth (its client included) stay behind those: account actions are server functions that run Better Auth's router in-process (callAuthEndpoint), so rate limits and the endpoint allowlist apply. Authorization happens in the Effect Authentication middleware; the _authed route guard is only a UX redirect.
  • Boundaries are enforced by Fallow zones in .fallowrc.json and by no-restricted-imports in .oxlintrc.json. A new top-level src/ directory needs a zone before it can be used.
  • Imports use the #/ alias (package.json#imports) with explicit .ts or .tsx extensions.

Where code goes

The UI follows Bulletproof React, with src/routes/ as the app layer. Imports flow one way: components → features → app (routes/).

  • Feature code: src/features/<name>/. Read src/features/AGENTS.md before adding or changing one: it holds the data-access and mutation-hook conventions.
  • UI shared by several features and free of feature knowledge: src/components/ (table above); shadcn primitives go in components/ui/ through pnpm exec shadcn add.
  • Route files hold the route definition and a small page component that composes features (skill add-page).
  • Naming: files and folders are kebab-case (Oxlint unicorn/filename-case; folders by tests/unit/repo-policy.test.ts). TanStack route names keep their prefixes (__root.tsx, _authed.tsx, $.ts, -private/, (group)/). src/sdk/ is generated and exempt.
  • Import the file that defines a symbol. Barrel files (index.ts re-exporting a folder) fail Oxlint oxc/no-barrel-file; the only one is src/server/db/schema/index.ts, Drizzle's schema entry.

Code style

Code is read by agents one file at a time: a module fits one read, a name greps to its definition, and a failure says what was wrong. Oxlint (.oxlintrc.json) enforces the numbers; the rest is for review. Tests: tests/AGENTS.md; dependencies of the Effect API: src/server/AGENTS.md.

  • Size. Functions up to 20 lines, components (.tsx) up to 80, files up to 300, blank and comment lines not counted (max-lines-per-function, max-lines). Over a limit, split by responsibility, not by moving lines: a component extracts a child component, a Layer moves its methods into functions that take the service (listPublic(db, page)), a switch that maps codes to text becomes a table.
  • Nesting. At most two nested blocks per function (max-depth), three nested callbacks (max-nested-callbacks), JSX four elements deep (react/jsx-max-depth), three parameters, more go in an options object (max-params). Return early; no nested ternaries (no-nested-ternary).
  • Names. A name says what the value is, specifically enough that rg -w <name> finds its definition and its uses: outcome, listed, lintRun, never result, info, item, tmp (id-denylist). data and handler are keys TanStack and Effect impose: keep them as keys, destructure into a specific name (({ data: credentials })), and never declare your own. Name a module after what it does, never utils.ts or helpers.ts (src/lib/utils.ts is shadcn's).
  • Types. No any (no-explicit-any, no-unsafe-*): read an untyped library value as unknown and narrow it. Exports of src/server, src/lib, src/contract and scripts declare their return type (explicit-module-boundary-types); components and hooks infer theirs.
  • Errors. A message names the offending value and the shape expected: `${name} must be an integer between ${min} and ${max} (got "${raw}")` (unicorn/error-message only rejects an empty one). Never echo a secret, a password or user content: give its length, scheme or path (env holds credentials as Redacted, which print as <redacted>). Across a trust boundary the client gets a generic message and log gets the cause (src/lib/server-function-errors.ts).
  • Comments. Say why, not what. Keep them when you refactor, update them when the reason changes. A line that exists because of an upstream bug or limit links the issue (better-auth#9920) and says when to remove it. Exports of src/server, src/lib and features/*/{api,utils} get a JSDoc sentence on intent, plus a one-line example when the types do not make the call obvious.
  • Logging. Server code logs through log (src/server/log.ts), one JSON object per line; console is for scripts (no-console).

Workflows

Each multi-step workflow is a project skill in .agents/skills/ (linked into .claude/skills/). Load it first:

TaskSkill
Endpoint, schema or typed error in src/contractapi-change
Table, column, index or migrationdatabase-change
A feature across database, API and UIadd-feature
Account action, Better Auth config, creating usersauth-change
New page, form or UI stateadd-page
Any dependency bump, above all the pre-release pinsupgrade-prerelease-deps

First setup. Run pnpm install && pnpm bootstrap. This creates .env with a secret, starts Postgres and Mailpit (pnpm mail:up, the local inbox for account emails), and applies migrations.

Verify

Read tests/AGENTS.md before you write or change a test: it says which layer a behavior belongs in and how each layer's harness works.

CommandCovers
pnpm checkEvery gate that needs no database and no build (format, type-aware lint, typecheck, Effect diagnostics, dead code, complexity, duplication, security sinks, unit/api/component tests with coverage, drift, migration lint, licenses), about 16 s. Run it before every hand-off; the lefthook pre-commit hook runs the jobs your staged files touch
pnpm test:unit|test:api|test:component [filter ...]One fast layer; pnpm test:fast runs all three with coverage
pnpm test:db [filter ...]The db layer on a throwaway Postgres database. Needs Postgres, no build
pnpm format, pnpm lint:fixAutofixes
pnpm check:drift [contract|migrations|auth|database]Generated files match their sources, and the auth schema holds what Better Auth writes; database needs Postgres
pnpm test [filter ...], pnpm test:e2e [filter ...]The integration or E2E layer against the built app; each starts its own servers on a throwaway database. Needs pnpm build, Postgres and Mailpit (pnpm mail:up)
pnpm build && pnpm verify:appThe db, integration and E2E layers and contract coverage. Run it, and test:e2e, through the verifier subagent, and wait for its report before reporting the work
pnpm build && pnpm lighthouse [--page=<name>]The Lighthouse gate behind the Caddy edge (needs Docker)
pnpm ci:local [job ...]The CI jobs in the Playwright Ubuntu container (needs Docker)

Every gate's failure message says how to fix it. What each gate covers, how to make a reviewed exception, the Lighthouse policy and the less common commands (audit:check, sbom:release) are in docs/agents/gates.md.

Memory safety

A misconfigured lint once used 17 GB of memory.

  • To check a few files, pass explicit paths: pnpm lint src/x.ts, pnpm exec oxfmt --check src/x.ts.
  • Keep node_modules/**, .output/**, and .repos/** out of every tool's scope. .oxlintrc.json and .oxfmtrc.json list all three in ignorePatterns, tsconfig.json includes only src, scripts, tests, and *.config.ts (and excludes node_modules, .output, and .repos), and .fallowrc.json lists all three in ignorePatterns. A new tool or config needs the same exclusions.
  • Run heavy commands one at a time (pnpm build, type-aware lint, verify:app, lighthouse); on Linux under a memory cap, see docs/operations.md.
  • Read upstream code with gh api or a sparse clone of the listed paths into .repos/ (dependency-sources.md), never a full clone.

Generated and vendored files

Regenerate these files; edit their sources instead. Claude Code refuses edits to them (.claude/settings.json): a refused edit means change the source and run the command.

  • src/sdk/** and openapi.json: pnpm codegen
  • src/routeTree.gen.ts: pnpm dev or pnpm build
  • drizzle/**: pnpm db:generate (only the new SQL file may be adjusted by hand, before it is committed; tests/unit/repo-policy.test.ts rejects edits to migrations already in the journal)
  • .agents/skills/shadcn/** and skills-lock.json: the skills CLI (docs/agents/skills.md). The other skills in .agents/skills/ are this repo's own and are edited by hand.
  • .repos/**: the sparse clones in dependency-sources.md

Version policy

Dependencies are pinned exactly, fresh releases are quarantined for a day, and the pre-release packages (nitro, @tanstack/react-start, oxfmt, @hey-api/openapi-ts) are upgraded one per PR with check, check:drift and verify:app passing. The upgrade-prerelease-deps skill holds the full policy; load it before changing package.json or pnpm-workspace.yaml.

Sharp edges

  • Effect v4. v4 became stable in October 2026, so most examples on the web still use the v3 API. Before writing Effect code, read node_modules/effect/AGENTS.md in full; look up APIs in node_modules/effect/src.
  • TypeScript. The project has one TypeScript, 7.0.2 (no JS compiler API). Hey API stays on its next snapshot; 0.99.0 crashes on TS 7 (ADR 0002). scripts/*.ts run through Node's type stripping, so use erasable syntax only (no enums, namespaces, or constructor parameter properties). The TanStack lint plugins run without type information under Oxlint (pnpm-workspace.yaml explains the peer rule).
  • CSP. No policy allows 'unsafe-inline' (ADR 0010): style with classes, and put head scripts and styles through the route's head() so the router adds the nonce.
  • Tailwind. Keep @import "tailwindcss" source("../") in src/styles/app.css. Without it, Tailwind scans .output, SSR and client CSS hashes diverge, and the CSS returns 404 in production (guarded by tests/integration/assets.test.ts and tests/unit/repo-policy.test.ts).
  • Origin. APP_URL must be the public origin. SSR uses it as the SDK base URL, and a mismatch changes TanStack Query keys and causes a refetch after hydration. src/start.ts rejects state-changing requests from any other origin.
  • Prerender. Nitro, the deployment layer, prerenders the routes in vite.config.ts and writes each page's hash-based CSP in its prerender:generate hook (ADR 0004).

Library docs and skills

  • TanStack Start and Router: run pnpm dlx @tanstack/intent@0.4.0 list, then pnpm dlx @tanstack/intent@0.4.0 load <package>#<skill> for the matching skill (for example, @tanstack/start-client-core#start-core/execution-model). The Load: lines that intent list prints use @tanstack/intent@latest; replace @latest with @0.4.0.
  • shadcn/ui: the skill in .agents/skills/shadcn loads automatically. Run the pinned CLI with pnpm exec shadcn … wherever the skill says npx shadcn@latest.
  • Better Auth, Fallow, and Playwright traces: see docs/agents/skills.md.
  • Upstream docs or source not in node_modules: see docs/agents/dependency-sources.md. Snapshots in .repos/ are read-only reference material.

Decisions

Architecture decisions are recorded in docs/decisions/. Read the relevant ADR before reversing a choice, for example adding tRPC or Zod, moving prerendering, or upgrading Drizzle to 1.0.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.