Production
AI SDLC.
Beyond "vibe coding": concrete systems, guardrails, and real files from shipping 3 production platforms end-to-end with AI agents.
Three architectures. Built from zero to production.
woonprijs.nl
Predicts Dutch neighborhood house prices 5 years ahead. Combines CBS valuations, PDOK polygons, and Sentinel-2 satellite imagery (NDVI greenery, urban heat) via Google Earth Engine.
Lesson: Hybrid local GeoParquet ETL → zero cloud GPU bills.
kerntest.nl
Bilingual (NL/EN) psychometric platform. Next.js 15 SSR, 340 static routes, 120 compatibility pairs, iDEAL/Stripe checkout, and server-rendered 20-page A4 PDF reports.
Lesson: 9 deterministic verification gates + 0 runtime LLM calls.
bedankt.me
Collaborative thank-you boards with drag-and-drop masonry, Giphy, rich text, QR sharing, custom PDF generation, and strict multi-tenant access control.
Lesson: Emulator security rule unit tests + modular <300-line files.
AI is an amnesia-prone senior coder on 10 espressos.
Your repo is the brain.
Spec & Anti-Spec
Define what to build, what NOT to copy, and freeze architectural choices in ADR files before a single line of code is written.
Repo-Native Memory
Chats disappear. Context windows degrade. State lives in AGENTS.md, BACKLOG.md, DECISIONS.md, and PROGRESS.md.
Deterministic Gates
Never ask AI to "be careful." Build domain-specific linters, matrix tests, and anti-reward-hacking rules that block bad code automatically.
Seven phases. Phases 4–6 run in an autonomous loop.
Grill Me
AI interviews me 1 question at a time until 95% clarity.
Recon & Spec
Competitor teardown, PLAN.md, DECISIONS.md, BACKLOG.md.
Rules & DoD
AGENTS.md + strict DEFINITION_OF_DONE.md.
Thin Slice
Pick 1 task, small files (<350 lines), write code + test.
Verify Harness
QUALITY_REPORT.md (npm run verify 9 gates).
Red-Team
AI attacks its own work in 9 hostile expert personas.
Human Ship
AI writes HUMAN_TODO.md. Human flips live keys & deploys.
Use a /grill-me skill — then lock answers into ADRs.
Why standard prompting fails: When a prompt has 5 hidden ambiguities, an LLM will never stop to ask. It silently guesses all 5 — and builds the wrong architecture in 3 minutes.
- Reverse the prompt: Force AI to interview you one question at a time down the decision tree.
- Recommended defaults: Tell AI to propose its recommended option first so you can just reply "Yes" or tweak it.
- Freeze into docs/DECISIONS.md: Every answer becomes a numbered Architectural Decision Record (ADR) so future chats never re-debate it.
## ADR-002: Clean Psychometric IP Separation
* Context: Competitor gorisont.com uses "MBTI" in URLs
and unlicensed PVQ-21 scales (legal risk).
* Decision:
1. Big Five = public-domain IPIP-50 items.
2. 16 Types = 48 original Jungian items. NEVER use
"MBTI" in URLs, titles, or UI labels.
* Consequences: Zero trademark risk, clean IP.
## ADR-004: Google Consent Mode v2 Zero-Leakage
* Decision: GA4 script must NOT load until explicit
consent. Verified via Playwright network test.
Write an "Anti-Spec" + a Single-Source Config File.
Spend 50% of your time on the Spec — but include two things most teams miss:
1. The Competitor "Anti-Spec"
Before coding kerntest.nl, AI crawled a competitor and listed 5 weaknesses we must never repeat: broken mobile zoom (user-scalable=no), contradictory "5 min vs 10 min" text, and leaked {mbti_total} placeholders.
2. Single Source of Truth Config (site.ts)
LLMs love hardcoding numbers across 50 pages—and they always drift. Force a rule: all facts (price, item count, duration, KvK) must be imported from one file.
# VARIABLES (src/config/site.ts is ONLY source of truth)
BRAND: Kerntest ("Ken je kern" / "Know your core")
PRICE_FULL_REPORT_EUR: 6.99 (nl format "€ 6,99")
COMPARE_REPORT_EUR: 3.99
// Rule in AGENTS.md:
Facts (question count, minutes, price) come ONLY
from config and render everywhere, so pages, FAQ,
structured data (JSON-LD), and meta descriptions
NEVER contradict each other.
# BACKLOG.md
77 tasks across 12 epics written with strict
acceptance criteria before Line 1 of code.
AGENTS.md — Why it works & how to structure it.
Why AGENTS.md is a superpower
- Universal standard: Read automatically by Cursor, Claude Code, Codex, Copilot, and Gemini/Antigravity.
- Git-versioned team brain: Lives in the repo, reviewed in PRs, onboards both AI agents and new human devs.
- Progressive disclosure (Keep it <150 lines!): If
AGENTS.mdis 2,000 lines, the LLM loses attention in the middle. KeepAGENTS.mdas a sharp index that points to docs/PLAN.md,docs/LICENSES.md, etc.
1. # MISSION Role + exact product goal
2. # VARIABLES Single config path, stack, region
3. # NON-NEGOTIABLE RULES
- No fake stats/reviews (only real DB when n>=200)
- No clinical/diagnostic claims
- Visual motif: growth rings (NEVER nuclear atoms)
4. # STATE FILES Where to read/write memory
5. # THE AUTONOMOUS LOOP Phase 0 → Phase 6
6. # LOOP DISCIPLINE Anti-cheat rules
7. # DEFINITION OF DONE Exact exit gates
How to maintain AGENTS.md: The 2-Strike Rule.
1. Same mistake twice = Rule
Never fix the same AI mistake twice by hand in chat. Turn the root cause into a permanent line in AGENTS.md (or a linter check) immediately.
2. Always include the "Why"
LLMs follow constraints 3× more reliably when given the consequence: "Never set user-scalable=no because it breaks WCAG 2.2 AA pinch-zoom."
3. End-of-PR Self-Reflection
At the end of a tricky bugfix, ask the agent: "What 1-line rule should we add to AGENTS.md so you never make this mistake again?"
# Added after agent tried to deploy untested code:
## Deployment Rule
* DO NOT run deployment commands (firebase deploy,
gcloud deploy). The user performs all deployments
manually. Only run local builds & verification.
# Added after CSS blur leaked paid text in DevTools:
## Free/Paid Boundary Rule
* Paywall shows the REAL first two lines of each
locked section, server-side truncated (NEVER a
client-side CSS blur of the full text).
DEFINITION_OF_DONE.md: Stop AI from "cheating" to green.
The dirty secret of AI coding: Agents optimize for finishing the turn. If a test fails or TypeScript complains, an unguarded AI will:
- Delete or
.skip()the failing test - Slap
as anyor@ts-ignoreon broken types - Lower the Lighthouse/coverage threshold from 95 to 70
- Claim "All done and verified! ✅" without running the command
# LOOP DISCIPLINE (Mandatory before claiming Done)
- Never say "done" without running `npm run verify`
and pasting REAL terminal results in PROGRESS.md.
- Never lower a threshold, delete a failing test,
or mask an error. Fix the root cause.
- If a fix breaks another gate: revert, try another
approach, log it.
- After 3 failed attempts on a task, mark it blocked
with the reason and move on.
# DEFINITION OF DONE
All 9 verify gates green · 3,125 profiles × 2 locales
pass · 0 analytics requests before consent ·
PAYMENTS_ENABLED=false safe · HUMAN_TODO.md complete.
Build domain-specific linters & matrix tests (QUALITY_REPORT.md).
Standard unit tests only check 3 happy paths. Because AI makes writing test scripts cheap, build exhaustive verification gates:
- Combinatorial Matrix Gate: Test all 3,125 Big Five score combinations × 2 languages (6,250 full reports) in
1.21s. - Honesty & Placeholder Linter: Regex-scans every file and HTML page for banned hype words ("gold standard", "guaranteed") and leaked variables (
{...},NaN,undefined). - Shingle Similarity Crawler: Fails build if any two programmatic SEO pages share >60% text similarity.
- Emulator Security Rules Gate: Tests Firestore rules with
aliceAuth,bobAuth, andunauth.
| Gate ID (`scripts/verify.mjs`) | Time | Status |
|---|---|---|
| 1. typecheck (340 routes strict TS) | 2.12s | 🟢 PASSED |
| 2. unit_tests (IPIP-50 reverse-key math) | 14.04s | 🟢 PASSED |
| 3. matrix_verification (3,125 × 2 locales) | 1.21s | 🟢 PASSED |
| 4. claims_linter (`test-claims.ts`) | 0.47s | 🟢 PASSED |
| 5. seo_content_audit (<60% similarity) | 1.63s | 🟢 PASSED |
| 6. link_integrity_audit (0 broken links) | 0.45s | 🟢 PASSED |
| 7. firestore_security_rules (emulator) | 0.24s | 🟢 PASSED |
| 8. deep_audit_10_rounds (invariants) | 0.48s | 🟢 PASSED |
| 9. playwright_e2e (desktop + mobile + GDPR) | 13.13s | 🟢 PASSED |
Use AI at build-time, deterministic code at runtime.
Zero Runtime LLM Calls
Paid 20-page PDF reports do NOT call OpenAI/Claude at runtime. AI authored versioned JSON content blocks at build-time; a deterministic engine assembles them.
→ 0 hallucinations, 50ms speed, €0 API cost.
Local Heavy Compute → Edge JSON
Satellite imagery (Earth Engine) + ML training run locally in Python into compressed GeoParquet. Only final neighborhood JSON predictions go to Firestore.
→ Near-zero cloud hosting & DB bill.
Strict <350-Line Module Cap
Above 400 lines, AI edits degrade and break existing features. Small, single-purpose modules fit cleanly in context so diffs are surgical and instant.
→ Fast diffs, zero accidental regressions.
The 5-File Memory System: Survive context compaction.
Long chat sessions degrade. Instead of one giant chat, run short fresh-context sessions anchored by 5 repo files:
1. BACKLOG.md
Ordered P0/P1/P2 tasks with explicit acceptance criteria and `[x]` status checkboxes.
2. PROGRESS.md
Append-only cycle log. After every task, the agent logs what changed + pastes real gate scores.
3. QUALITY_REPORT.md
Auto-generated by npm run verify. Single dashboard of all 9 gate statuses & timings.
4. docs/DECISIONS.md
Numbered ADRs (why we chose X, rejected Y) so a fresh session never undoes past choices.
5. HUMAN_TODO.md
The AI's handoff list to you: DNS records, KvK checks, Stripe live keys, legal sign-off.
The Cycle Start Rule
"Re-read BACKLOG.md and QUALITY_REPORT.md at the start of every cycle so context loss cannot derail you."
Don't ask AI "Is this good?" — Force a 9-Persona Red-Team.
When you ask an LLM to review its own code, it is polite and lazy. Instead, run Phase 5 Adversarial Loops in a fresh context wearing 9 hostile personas:
The Stop Rule: Log findings in BACKLOG.md, fix P0/P1, re-verify. Stop only when 2 consecutive rounds find zero P0/P1 bugs.
// Codified into automated invariants:
✅ [ROUND 1/10] Extreme Scoring (All 1s, 3s, 5s)
✅ [ROUND 2/10] 120 Pairs Uniqueness & Word Count
✅ [ROUND 3/10] NL/EN Reciprocal hreflang Mapping
✅ [ROUND 4/10] Banned Clinical Claims Scan
✅ [ROUND 5/10] Unrendered {Placeholder} Leak Scan
✅ [ROUND 6/10] Legal Footer & KvK Identity Audit
✅ [ROUND 7/10] Mobile Viewport Pinch-Zoom Audit
✅ [ROUND 8/10] Consent Mode v2 Zero-Leakage
✅ [ROUND 9/10] Payment Gating Security Invariant
✅ [ROUND 10/10] Title (<60c) & Meta (140-160c)
Mock-first flags + HUMAN_TODO.md handoff.
How do you let AI build and E2E-test a full payment checkout when you don't want to give AI live Stripe keys?
- Adapter + Mock Provider: AI builds against a
PaymentProviderinterface using aMockPaymentProviderin local/emulator mode so Playwright tests the full unlock + PDF flow end-to-end. - Honest Production Gate (
PAYMENTS_ENABLED=false): In production without keys, it never simulates a purchase—it automatically shows an honest "Coming soon — notify me" waitlist. - Zero-Code Go-Live: Switching to live iDEAL/Stripe requires only adding 3 env secrets and flipping the flag.
PAYMENTS_ENABLED (default false in production):
(a) In local/emulator/test: MockPaymentProvider
simulates payment so full unlock + PDF flow
is E2E tested in Playwright.
(b) In production with PAYMENTS_ENABLED=false:
NEVER simulate a purchase and NEVER unlock
paid content; show honest waitlist state.
(c) Switching to live payments requires ONLY
adding STRIPE_* env secrets and setting
PAYMENTS_ENABLED=true — zero code change.
For Mobile Teams & Product Leaders: The 1-Hour "Executable Spec".
For mobile engineering leaders & product managers
In native iOS & Android, building a feature in Swift/Kotlin and waiting for App Store review takes weeks. Text tickets in Jira always hide 20 UX edge cases.
- Step 1 (15 min): Run
/grill-mewith AI on your feature idea to uncover missing edge cases (offline state, empty state, permissions). - Step 2 (45 min): Have AI build a clickable mobile-viewport web prototype using mock data.
- Step 3: Test it on your phone with 3 users that afternoon.
- Step 4: Hand native devs the clickable prototype + the generated
SPEC.md.
Old Flow vs. AI-First Mobile Flow
❌ Old Way: 2-page Jira ticket → 3 weeks Swift/Kotlin coding → TestFlight build → "Oh, this flow feels confusing" → 2 weeks rework.
✅ AI SDLC Way: 1-hour clickable prototype on phone → kill bad UX on Day 1 → devs get crystal-clear state machine + copy + edge cases → build native once.
Three loops for three types of engineering work.
A · Greenfield Autonomous Loop
Used on: kerntest.nl (0 → 340 pages)
- Phase 0: Competitor recon + 77-task BACKLOG.md
- Phase 1–2: Foundation, config single-source, pages
- Phase 3–4: 9-gate
verify.mjs+ auto-fix loop - Phase 5: 9-persona adversarial red-team rounds
- Exit: 2 clean rounds → HUMAN_TODO.md
B · Daily Feature Slice Loop
Used on: bedankt.me (existing app)
- 1. Grill-me: Resolve UX & data model questions
- 2. Slice: Touch <3 files, keep modules <350 lines
- 3. Security: Update
firestore.rules+ emulator test - 4. E2E: Write Playwright test & run until green
- 5. Commit: Atomic commit + update PROGRESS.md
C · Root-Cause Bugfix Loop
Used across all 3 platforms
- 1. Reproduce: Write a failing test that proves the bug
- 2. Grep Callers: Inspect every caller before editing
- 3. Fix Once: Fix root cause in shared code
- 4. Full Verify: Run all 9 gates (no regressions)
- 5. Immunize: Add rule to AGENTS.md or linter
Every way AI breaks — and the exact countermeasure.
| Failure Mode (What AI Does Left Alone) | Why It Happens | Production Countermeasure (Click to View) |
|---|---|---|
| 1. Silently guesses vague requirements | Trained to be helpful immediately | /grill-me skill + freeze in DECISIONS.md |
| 2. Breaks old features when editing | Files >400 lines overflow attention | 350-line file cap + atomic 1-task commits |
| 3. Deletes tests or lowers thresholds | Reward-hacking to finish the turn | Anti-cheat rules in DoD + paste stdout to log |
| 4. Contradicts prices/facts across pages | Hardcodes strings in templates | Single-source config (site.ts) + regex placeholder linter |
| 5. Invents fake stats or risky claims | Marketing training bias | Claims linter banning hype words & unbacked numbers |
| 6. Weak auth / leaks paid data in CSS | Optimizes for visual UI first | Server-side truncation + Emulator test-rules.js |
| 7. Loses context after 40 messages | Context window compaction | 5-File Memory System (BACKLOG, PROGRESS, REPORT) |
How to roll this out in an iOS / Android / Web team in 1 week.
Day 1–5 Rollout Blueprint
- Day 1: Drop a 100-line AGENTS.md + DEFINITION_OF_DONE.md into your iOS, Android, and backend repos.
- Day 2: Wire a single
./scripts/verify.shcommand (SwiftLint/ktlint + unit tests + snapshot tests) that outputs QUALITY_REPORT.md. - Day 3: Give Product & Mobile Leaders the
/grill-me+ 1-hour clickable prototype workflow before Jira tickets are written. - Day 4–5: Start the 2-Strike Rule in PR reviews: every AI mistake caught in code review becomes a 1-line rule in
AGENTS.md.
# Mobile Definition of Done (iOS & Android)
Before claiming a task is complete, the agent MUST run
`./scripts/verify.sh` and paste the output into the PR:
- [ ] SwiftLint / ktlint: 0 warnings, 0 errors
- [ ] Architecture check: ViewModels have 0 UIKit/
SwiftUI/Android Context imports; files < 350 lines
- [ ] Unit + Snapshot tests: Light + Dark + Dynamic Type
pass (NEVER delete or skip a failing test)
- [ ] Accessibility: Every interactive element has an
accessibilityLabel / contentDescription
- [ ] Memory: Update PROGRESS.md & docs/DECISIONS.md
4 Ready-to-Use Prompts & Files (1-Click Copy).
1. The /grill-me Pre-Spec Prompt
Interview me relentlessly about every aspect of this feature/plan until we reach 95% shared understanding. Walk down each branch of the design tree, resolving dependencies one-by-one.
Rules:
1. Ask ONE question at a time.
2. If a question can be answered by exploring the repo, read the repo instead.
3. For every question, provide your recommended answer first with a 1-sentence reason.
4. When done, write our decisions into docs/DECISIONS.md (ADR format) and tasks with acceptance criteria into BACKLOG.md.
2. Anti-Reward-Hacking DEFINITION_OF_DONE.md
# LOOP DISCIPLINE & DEFINITION OF DONE
- NEVER say "done" without running the verification command and pasting the real terminal output into PROGRESS.md.
- NEVER lower a coverage/quality threshold, delete a failing test, add .skip(), or use `any` / `@ts-ignore` to mask an error. Fix the root cause.
- Keep every modified file under 350 lines; split into focused modules if larger.
- If a fix breaks another gate, revert, try another approach, and log it.
- After 3 failed attempts on a task, mark it blocked in BACKLOG.md with the reason and stop.
3. Multi-Persona Adversarial Audit Prompt
Run an adversarial red-team audit of the current codebase from 5 hostile perspectives:
(1) Security Engineer checking auth boundaries & data leaks,
(2) Accessibility Auditor checking WCAG 2.2 AA & keyboard/zoom,
(3) Performance Engineer checking bundle/query bottlenecks,
(4) EU Privacy/GDPR Reviewer checking pre-consent network calls,
(5) Skeptical First-Time Mobile User on a 360px screen.
Output only concrete defects with file:line references and severity (P0/P1/P2) into BACKLOG.md.
4. End-of-Task Self-Healing AGENTS.md Prompt
Review the mistakes, failed test runs, or rework that happened during this task.
1. Propose at most ONE concise rule (in "Never X because Y; instead do Z" format) to add to AGENTS.md so future sessions never repeat this mistake.
2. Can this mistake be caught automatically by a fast script or linter in our verify suite? If yes, write the check.
Spec.
Memory.
Gates.
Where does AI break in your workflow today?
Context loss in big repos, mobile UI regressions, or noisy PRs?
Which upcoming feature could we prototype in 1 hour?
Let's test the /grill-me → clickable spec flow on a real roadmap item.
woonprijs.nl · kerntest.nl · bedankt.me