For AI-native developers

The SaaS base
your AI agent can't break

You're not buying code your agent can already write. You're buying the guardrails it can't write for itself: the walls that stop it breaking your login when it touches payments — enforced by the compiler and CI, not a soft convention it can rationalize past.

The agent sees a slice, and edits confidently anyway

Every session, your agent sees only part of the project — so it changes code without knowing what elsewhere depends on it. Month one feels electric. By month six the same tool is slower than before, because tech debt isn't paid down, it's added to, and eventually collected. A blank repo makes it worse: with no patterns to hold on to, the agent drifts toward its own defaults and quietly erodes the architecture. By prompt 50 it's spaghetti.

In the words of people shipping with agents

Debt that gets collected later

"Tech debt isn't paid down, it's being added to, and at some point in the future it will need to be collected." — antihipocrat, Hacker News (item 45405177)

No consistent patterns to hold

"llm code has none of that [consistent patterns], if yes its by pure chance that won't repeat." — kakacik, Hacker News (item 45405177)

The security edge cases it misses

AI agents "generate RLS policies that accidentally expose data to other tenants, forget to validate webhook signatures on payment events, create permission checks that authenticated users can bypass." — MakerKit blog

Walls the agent physically can't cross

Point your agent at the domain layer and tell it to just import Prisma there. It can't — ESLint fails the build. Compiler-and-lint-enforced DDD boundaries mean domain code cannot import Express, Prisma, or infrastructure. This isn't a soft AGENTS.md suggestion the agent can reason its way around. It's a wall in CI.

eslint · domain boundary
 1  modules/billing/domain/subscription.ts
 2    import { prisma } from '@prisma/client'
 3           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 4    error  no-restricted-imports
 5    domain/ must remain framework-agnostic.
 6    See ADR-001.
 7  
 8  ✖ 1 problem — CI blocked

Payload drift, caught before it ships

The agent renames a field on the server and forgets the client. Type drift a compiler catches. But payload drift — the form sends fullName, the server wants name — sails through typecheck and 400s in production. Here, client Zod schemas import from a shared @app/contracts package, and openapi-fetch types are regenerated and gated by the api-types-fresh CI check. The generated surface and the server can't disagree without turning CI red.

ci · api-types-fresh
 1  $ bun run generate:api
 2  $ git diff --exit-code \
 3      apps/server/openapi.json \
 4      apps/client/lib/api/openapi-types.ts
 5  
 6    openapi.json changed — types are stale
 7  ✖ api-types-fresh failed — regenerate & commit

The three bugs a blank repo ships every time — already closed here

Sessions that outlive a reset

The recurring bug security reviews keep finding in AI-generated apps: sessions that do not log out on password change — you reset your password, but the old login token still works. Here sessions are server-side rows in Postgres — every one can be revoked on demand, and ending concurrent sessions on a credential change is a documented flag, not a rewrite.

Webhooks that double-charge

The other classic is the webhook handler that is not idempotent: when Stripe sends the same message twice (which it does), the app charges or credits the user twice. Purchases here are idempotent on externalOrderId, delivered through a durable outbox event bus.

Tenant data that leaks

AI agents "generate RLS policies that accidentally expose data to other tenants" (makerkit.dev). Here an org-scoped tenant-isolation guard — org-scoped repos plus a Prisma query guard — returns 404 across orgs. No RLS to forget.

A harness your agent can vibe-check itself against

E2E Playwright CI gate

Golden-path flows run on every PR (bun run e2e:golden). The code → test → code loop the agent needs to self-correct — with a validate step that reflects what you actually want, not what looks plausible.

DDD boundaries, lint-enforced

The layering is ESLint rules, not documentation: domain and application layers cannot import frameworks, and cross-context runtime imports are forbidden. The wall the agent keeps hitting is the wall keeping your architecture intact.

A CLAUDE.md of enforced rules

A CLAUDE.md ships in the repo — DDD layering, the contracts rule, commit and PR discipline — so the agent inherits your long-term thinking instead of averaging the public internet. TypeScript is strict end to end.

Solved once, so your agent only builds what is yours

Auth

BetterAuth — 2FA, magic-link, OAuth, email verification, password reset. Sessions and flows already wired and tested.

Billing

Stripe, MercadoPago, and Polar behind one typed interface — checkout, customer portal, plan changes, signed webhooks.

Orgs & RBAC

Organizations, members, invitations, role-based permissions, and API keys — real multi-tenancy, not a users table with a role column.

Jobs

BullMQ queues, processors, and schedulers, with a Bull Board dashboard to watch them.

Events

A durable outbox event bus so domain events survive a crash and deliver reliably.

Observability

OpenTelemetry, Sentry (@sentry/bun), Pino, /metrics — each boots to a no-op until you configure it.

Questions from people who ship with agents

Isn't a CLAUDE.md or AGENTS.md enough?+
That's table stakes now — every kit ships one, and an agent can reason its way past a soft convention. The difference here is enforcement: the DDD boundaries are ESLint rules and the client-server contract is gated in CI. The agent physically can't import infrastructure from the domain layer or drift the API contract without turning the build red.
Do you sell speed?+
No. This segment has been burned by velocity without a spine. You're buying control: walls, typed contracts, and CI gates that keep the agent inside them. The point isn't to go faster on day one — it's to not be slower by month six.
What stack is it?+
Bun + Express + Prisma on the server, Next.js App Router on the client, DDD-layered and TypeScript-strict end to end. A real backend with separation of concerns, not API routes bolted onto a frontend.
Which agents does it work with?+
Any of them. The guardrails live in ESLint, the compiler, and CI — not in a single tool's rules file — so Claude Code, Cursor, or anything else builds inside the same walls.

Point your agent at this, not a blank repo.

A blank repo forces the agent to invent architecture and repeat the same three security bugs. Give it walls it can't cross instead.