{"slug": "show-hn-a-typescript-repo-where-ai-agents-can-t-break-the-architecture", "title": "Show HN: A TypeScript repo where AI agents can't break the architecture", "summary": "A developer released a TypeScript reference codebase designed to enforce architectural rules through tooling, preventing AI agents from introducing code degradation. The repo uses dependency-cruiser, mutation testing, and commit hooks to block violations, aiming to solve the problem of AI-generated codebases accumulating duplicated logic and broken tests.", "body_md": "**A reference TypeScript codebase engineered so AI agents produce good code\nin it — architecture as guardrails, not as documentation.**\n\nMost AI-assisted codebases degrade the same way: productivity jumps, and three\nmonths later there's duplicated logic in three files, tests that assert\nnothing, and an agent that breaks the code it wrote when asked to change it.\nThat's not a model problem — it's a workflow problem. Prose conventions don't\nsurvive contact with agents (or with tired humans). Rules that are *machines*\ndo.\n\nThis repo is a small but real invoicing API (draft → send → pay, money as integer cents, a proper state machine) built to demonstrate one thesis:\n\nEvery rule that matters must be enforced by a tool the agent cannot argue with.\n\n-\n**The dependency rule is a build failure, not a diagram.**`dependency-cruiser`\n\nfails`verify`\n\n, the commit hook and CI on any illegal import.`src/domain`\n\nimports*nothing*— not even node builtins. The agent doesn't need to remember the architecture; violating it is impossible to merge. -\n**Mutation testing catches tests that don't test.** Coverage says a line ran.[Stryker](https://stryker-mutator.io)says the tests would*notice if the line broke*. CI breaks below 75% mutation score; the application layer currently scores 100%. -\n**Tests and specs are protected from the agent.** A Claude Code`PreToolUse`\n\nhook blocks agent edits to`tests/**`\n\nand`specs/**`\n\nunless a human explicitly grants an override. \"Make the tests pass\" can no longer be satisfied by deleting the tests (ADR 0004). -\n**Commits are gated on the full verify suite.** A hook runs`typecheck + lint + deps + test`\n\non every`git commit`\n\nand feeds failures back to the agent — feedback loop, not just a wall. -\n**Spec-driven development is a build gate, not a suggestion.** One markdown spec per use case with Given/When/Then acceptance criteria; tests mirror them one`it()`\n\nper criterion — and`npm run specs`\n\n(inside`verify`\n\n, the commit hook and CI) fails on any use case without a spec, any orphan spec, and any criterion without its mirrored test. The golden path is`npm run new -- <use-case>`\n\n: spec + mirrored test stub + use-case stub scaffolded with matching names, workflow printed.\n\nat the root (the`AGENTS.md`\n\n[open standard](https://agents.md)), under 300 lines, imperative, with exact commands — plus**nested AGENTS.md** files in`src/domain`\n\n,`src/application`\n\nand`src/infrastructure`\n\ncarrying each layer's local invariants.`CLAUDE.md`\n\nis one line pointing at AGENTS.md (portable across tools).records the`docs/adr/`\n\n*why*behind decisions, so agents stop relitigating them.- Deterministic seams everywhere: time and identity are ports (\n`Clock`\n\n,`IdGenerator`\n\n);`new Date()`\n\nand`randomUUID()`\n\nexist only in the composition root. Tests never sleep, never flake.\n\nThe end of [ AGENTS.md](/lucasgodt/open-agent-ready-typescript/blob/main/AGENTS.md) contains a self-contained task\n(\n\n`cancel-invoice`\n\n) designed to verify whether *any*coding agent can work in this repo correctly: spec → tests → domain → use case → HTTP,\n\n`verify`\n\ngreen,\nnothing else touched. Point your agent at it and grade the diff. That's the\nwhole point of this repository — it's not just an example, it's a test bench.This repo doubles as a template, and ships a [Claude Code skill](/lucasgodt/open-agent-ready-typescript/blob/main/.claude/skills/scaffold-agent-ready/SKILL.md) that does the transplant for\nyou: it keeps the guardrail system (configs, hooks, CI, layer structure),\nremoves the invoicing example, and grows your new domain spec-first inside it.\nIt covers three project shapes, each with its own playbook in\n[ references/](/lucasgodt/open-agent-ready-typescript/blob/main/.claude/skills/scaffold-agent-ready/references):\n\n**Backend API/service**— this repo's own structure ([backend.md](/lucasgodt/open-agent-ready-typescript/blob/main/.claude/skills/scaffold-agent-ready/references/backend.md))**Frontend app**— React/Vite with the UI as an adapter around a pure, React-free domain; mutation testing targets domain/application, never components ([frontend.md](/lucasgodt/open-agent-ready-typescript/blob/main/.claude/skills/scaffold-agent-ready/references/frontend.md))**Monorepo**— pnpm workspaces where layers become physical packages;`packages/domain`\n\nwith an empty`dependencies`\n\nobject is the loudest guardrail in the repo ([monorepo.md](/lucasgodt/open-agent-ready-typescript/blob/main/.claude/skills/scaffold-agent-ready/references/monorepo.md))\n\nOne-time install (user-wide):\n\n```\nBASE=https://raw.githubusercontent.com/lucasgodt/open-agent-ready-typescript/main/.claude/skills/scaffold-agent-ready\nmkdir -p ~/.claude/skills/scaffold-agent-ready/references\ncurl -sL $BASE/SKILL.md -o ~/.claude/skills/scaffold-agent-ready/SKILL.md\nfor f in backend frontend monorepo; do\n  curl -sL $BASE/references/$f.md -o ~/.claude/skills/scaffold-agent-ready/references/$f.md\ndone\n```\n\nThen from any empty directory, in Claude Code:\n\nScaffold a new agent-ready [backend | frontend | monorepo] project for [your domain]\n\nPrefer doing it by hand? The recipe is the same one the skill follows:\nclone, keep every config/hook/workflow file plus the layer skeleton, delete\n`src`\n\ndomain contents + specs + tests of the example, and rebuild your domain\nfollowing the spec-first workflow in AGENTS.md. GitHub's \"Use this template\"\nbutton also works for the mechanical copy.\n\n```\nnpm install\nnpm run verify        # typecheck + lint + dependency rules + tests\nnpm run dev           # API on :3000\nnpm run mutation      # Stryker (slower)\ncurl -s -X POST :3000/invoices -H 'content-type: application/json' \\\n  -d '{\"customerName\":\"ACME Ltda\",\"currency\":\"BRL\"}'\n```\n\n- The test-protection hook is Claude Code–specific; other agents need an equivalent gate. Mutation testing in CI is the tool-agnostic backstop.\n- JSON-file persistence is deliberately boring and not concurrent-safe across processes (ADR 0003). Swapping in a real database is one adapter + one line in the composition root — which is precisely the demonstration.\n- Mutation survivors that remain are error-message string mutants; the\ncontract is the error\n`code`\n\n, asserted everywhere, not the prose.\n\nMIT", "url": "https://wpnews.pro/news/show-hn-a-typescript-repo-where-ai-agents-can-t-break-the-architecture", "canonical_source": "https://github.com/lucasgodt/open-agent-ready-typescript", "published_at": "2026-07-15 13:06:07+00:00", "updated_at": "2026-07-15 13:18:04.813516+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "ai-agents", "ai-tools"], "entities": ["TypeScript", "dependency-cruiser", "Stryker", "Claude Code", "AGENTS.md"], "alternates": {"html": "https://wpnews.pro/news/show-hn-a-typescript-repo-where-ai-agents-can-t-break-the-architecture", "markdown": "https://wpnews.pro/news/show-hn-a-typescript-repo-where-ai-agents-can-t-break-the-architecture.md", "text": "https://wpnews.pro/news/show-hn-a-typescript-repo-where-ai-agents-can-t-break-the-architecture.txt", "jsonld": "https://wpnews.pro/news/show-hn-a-typescript-repo-where-ai-agents-can-t-break-the-architecture.jsonld"}}