G.STANCUTA
Published · 2026 · 05 · 289 min read

The Stack I Hand My AI Agent

  • ai-agents
  • developer-tools
  • next-js
  • workflow

A fixed, opinionated stack handed to an AI coding agent at the start of every session beats letting it pick dependencies each time. Fewer decisions, reusable conventions, compounding familiarity across projects. Here is what I give it and why.

Every time I start a new project with an AI coding agent I hand it the same stack. Not a list of suggestions. A fixed, mandatory configuration the agent is not allowed to deviate from without explicit sign-off. This sounds rigid. It is. That rigidity is the point.

When the agent knows the stack up front, it stops guessing. It reuses patterns it has seen in the same context window, defers to the helpers I have already told it to use, and produces output that slots into the rest of the codebase without friction. When the agent gets to pick the stack, you spend the first three hours litigating whether it chose the right ORM.

Isometric technical diagram of a fixed technology stack: Next.js, Prisma, BullMQ, MinIO, Redis, Docker, Cloudflare arranged as interconnected schematic layers
One fixed stack across every project. The agent learns it once and reuses it everywhere.

The Stack

The choices are not arbitrary. Each one was made after shipping at least two production projects with it and deciding it was good enough to stop reconsidering. That is the bar: not perfect, good enough to stop reconsidering.

  • Next.js App Router + TypeScript strict -- server components, server actions, no separate API server for most projects
  • Tailwind CSS -- utility classes only, no CSS-in-JS, zero runtime overhead
  • MySQL 8 or Postgres 16 via Prisma -- Prisma for the schema, migrations, and type-safe client; MySQL for projects that need it on Ploi, Postgres otherwise
  • Better Auth -- session and OAuth in a few lines, the auth handler at one fixed path the agent already knows
  • Resend -- transactional email, excellent SDK, does not require a dedicated server
  • MinIO -- self-hosted S3-compatible object storage, runs in Docker Compose locally and on Hetzner in production
  • Redis + BullMQ -- background jobs with retries, concurrency controls, and a simple job class pattern
  • Docker Compose -- one command spins up the database, Redis, and MinIO locally; same image versions in production
  • Ploi + Hetzner -- VPS hosting, server management without the ops overhead, predictable cost
  • Cloudflare -- DNS, CDN, SSL termination, DDoS protection in front of everything

Why Fixed Beats Flexible

The AI agent is a stateless executor. It does not carry opinion, loyalty, or habit from session to session. Given a blank slate and a vague instruction like "add a background job", it will pick whatever BullMQ alternative it saw most often in its training data. That might be a different abstraction each time. It might introduce a second Redis client. It might conflict with the auth library's session store.

A fixed stack eliminates that class of decision entirely. The agent does not choose the queue library. It uses BullMQ because the AGENTS.md says so and because the base job class already exists in the codebase. The decision was made once, it is written down, and the agent reads it.

The AGENTS.md: Encoding the Stack as Agent Memory

An AI coding agent has no memory between sessions. The agent that configured your BullMQ worker last week has no idea it exists today. Without a mechanism to persist context, every new session starts from zero and the agent reinvents patterns you already settled.

The mechanism is a file. One markdown file at the project root, loaded into the agent context at the start of every session. I call it AGENTS.md. It is not documentation for humans (the README does that). It is working memory for the agent: the stack is fixed, here are the conventions, here are the commands, here are the patterns that are explicitly forbidden.

The AGENTS.md is the single artifact that turns a stateless LLM into a project-aware collaborator. Write it like it has to survive a context window wipe between every session, because it does.

Below is the actual stack section I paste into every new project's AGENTS.md. The agent reads this before touching any code. Substitute your database choice (MySQL vs Postgres), keep everything else fixed.

md
# AGENTS.md -- Stack & Conventions for AI Coding Agents

## Stack (fixed, do not substitute without explicit approval)

- Framework : Next.js App Router, TypeScript strict mode
- Styles     : Tailwind CSS utility classes only -- no CSS-in-JS, no plain CSS files
- Database   : MySQL 8 or Postgres 16 via Prisma (schema in prisma/schema.prisma)
- Auth        : Better Auth (src/lib/auth.ts) -- do NOT roll a custom session system
- Email       : Resend SDK (src/lib/email.ts) -- transactional only, no bulk marketing
- Object store: MinIO, S3-compatible (src/lib/storage.ts)
- Queues      : BullMQ backed by Redis (src/queues/) -- background jobs only
- Environment : Docker Compose (docker-compose.yml at repo root) -- run locally with:
    docker compose up -d
- Hosting     : Ploi on Hetzner CX21+, Cloudflare in front (proxied, SSL full-strict)

## House conventions

- All server actions in src/app/actions/ -- never in route handlers or components
- API routes under src/app/api/v1/ return { data, error } -- use ApiResponse type
- BullMQ jobs extend BaseJob (src/queues/base-job.ts) -- no bare Queue instantiation
- File naming : kebab-case files, PascalCase components, camelCase utils
- No any in TypeScript -- use unknown and narrow explicitly
- No console.log in committed code -- use src/lib/logger.ts

## Commands

    npx prisma migrate dev   # apply local migrations
    npx prisma generate      # regenerate client after schema change
    docker compose up -d     # start MySQL/Postgres, Redis, MinIO
    npm run dev              # Next.js dev server
    npm run typecheck        # tsc --noEmit
    npm run lint             # ESLint + Prettier check

## Known gotchas

- Better Auth requires the auth handler at src/app/api/auth/[...all]/route.ts exactly
- MinIO presigned URLs expire in 1 h by default -- adjust in src/lib/storage.ts
- BullMQ concurrency defaults to 1 per worker -- set explicitly for each queue
- Prisma Client is a singleton -- import from src/lib/prisma.ts, never instantiate inline

Three things make this file effective. First, it is prescriptive: "do NOT roll a custom session system" is not a suggestion. Second, it names the exact file path for each abstraction (src/lib/auth.ts, src/lib/prisma.ts). The agent does not have to go looking. Third, it lists the gotchas: the ones you hit the hard way so the agent does not have to hit them again.

The Local Environment in One Command

Docker Compose is part of the stack for a specific reason: it makes the local environment a first-class artifact the agent can reason about. When the agent reads AGENTS.md and sees that docker compose up -d starts MySQL, Redis, and MinIO, it knows exactly what to tell you when something is not running. It does not guess whether you have a local MySQL install or a cloud database in development.

yaml
# docker-compose.yml (excerpt -- services the agent can rely on)
services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: 'root'
      MYSQL_DATABASE: 'app'
    ports:
      - '3306:3306'
    volumes:
      - db_data:/var/lib/mysql

  redis:
    image: redis:7-alpine
    ports:
      - '6379:6379'

  minio:
    image: minio/minio
    command: server /data --console-address ':9001'
    environment:
      MINIO_ROOT_USER: 'minio'
      MINIO_ROOT_PASSWORD: 'minio123'
    ports:
      - '9000:9000'
      - '9001:9001'
    volumes:
      - minio_data:/data

volumes:
  db_data:
  minio_data:

The same image versions run locally and in production on Hetzner. The agent knows the connection strings from the environment variables it is told to expect. There is no "works on my machine" surface area.

Schematic diagram of an AI agent reading an AGENTS.md markdown memory file before writing code, with arrows showing context flowing from the file into the agent's session
The agent reads AGENTS.md first. That single file carries the institutional memory of every project decision.

Compounding Familiarity Across Projects

The real payoff is not the first project. It is the fifth. By the time you have used this stack five times, the AGENTS.md is refined. The gotchas section has caught real bugs. The forbidden patterns section has prevented at least two architectural mistakes. The base job class has been extracted into a shared template you can clone.

Every new project inherits the accumulated discipline of the previous ones. The agent walks in on day one already knowing the patterns that took you two projects to settle. That is not magic. It is just documentation that is actually read.

  • Start each project by cloning the AGENTS.md template and adjusting the database choice.
  • Update the AGENTS.md whenever you add a new abstraction, ban a pattern, or hit a gotcha.
  • Treat the file as a living spec: when the codebase changes, the AGENTS.md changes first.
  • Load it explicitly at the start of every agent session -- do not assume the agent will find it.

The Review Gets Faster Too

A secondary benefit worth naming: code review becomes faster when you know the stack. You are not evaluating whether the agent picked a sensible library. You are only evaluating whether it used the agreed library correctly. That is a much narrower question, and it compounds too: the longer you use the stack, the faster you spot a deviation.

The thesis is simple: a fixed stack is a force multiplier for AI-assisted development. The agent knows the tools, you know the tools, the AGENTS.md bridges the sessions. You stop debating infrastructure and start shipping features. That is the whole point.

Portfolio · Drawing Stamp
Drawn by
G. STANCUTA
Discipline
AI & AUTOMATION
Location
MORTER · SÜDTIROL
Status
Available
Languages
IT · EN · RO · DE+
Stack
PLOI · HETZNER
Revision
REV 2026.A
2026

© 2026 Gabriel Stancuta · jumpinotech.com — Architected with AI, built to run itself.