TAOS
← Blog
EngineeringAI DevelopmentArchitecture

Structure First: What Building TAOS Taught Me About AI Velocity

April 13, 2026Patrick Farry

In February I published an article on InfoQ — Working with Code Assistants: The Skeleton Architecture — arguing that prompts are insufficient guardrails for AI-assisted development, and that the real solution is structural: base classes, abstract interfaces, and deterministic validators that the model physically cannot bypass. The core idea was that the skeleton gives the human control while the tissue gives the AI room to move.

Since then I have been building TAOS with two co-founders, using Claude Code as the primary coding engine. In six weeks we have produced approximately 120,000 lines of code. This post is a field report on what the skeleton architecture looks like in practice on a complex production system — and on a pattern I didn't cover in the InfoQ article that turned out to be equally important: structure for Claude's knowledge, not just Claude's code.


The Original Insight Held Up

The core claims from the InfoQ article were validated quickly. When Claude lacks a firm structural constraint, it drifts. The same crosscutting concern gets implemented three different ways across three different slices. A component that should extend a base class gets reimplemented from scratch. A security boundary gets "simplified away" when resolving a test failure.

What the skeleton pattern does is make architectural decisions physically coercive. Claude cannot write a component that bypasses the audit logger because the kernel's base class calls it in a final run() method that Claude never touches. Claude cannot put a credential in a tool call payload because the credential resolver is in the kernel layer, not in the agent context layer. The constraint is in the code, not the prompt.

At 120,000 lines across a distributed system — kernel, control plane, SDK adapters, CLI, and this marketing site — the value of that physical enforcement becomes compounding. Each decision that is baked into the skeleton is a decision Claude doesn't make inconsistently across the next hundred files it generates.


The Gap the Article Didn't Cover

What I underestimated was how much Claude's output quality depends not just on code structure, but on documented architectural decisions. The model has no persistent memory. Every new conversation, every new task, is a blank slate. The skeleton catches structural violations at runtime. But what about the thousand micro-decisions that live above the code level?

  • Why is the token format SESSION_{resource}_{type}_{n} and not something else?
  • Why does the audit system write records before operations execute rather than after?
  • Why is Argon2id chosen over bcrypt for the vault KDF?
  • Why does the OBO implementation follow RFC 8693 rather than a simpler custom credential schema?

These decisions are not encoded in code structure. They are architectural choices with context, tradeoffs, and consequences. And if Claude doesn't have access to them, it will re-derive them — sometimes correctly, often not, and almost never consistently.

The answer we found was structured decision records (the same five-section pattern used in classic Architecture Decision Records) plus published TAOS documentation on this site. The public architecture overview and product pages carry what customers and partners need; internal notes carry the fine-grained deltas we load into Claude. Both turned out to be more than developer documentation — they became the primary way to give the model durable architectural memory.


Decision records and public documentation as shared context

A structured decision record is a short document that captures a single architectural choice. The format we use has five sections:

StatusProposed, Accepted, Deprecated, or Superseded. This single field tells Claude whether a decision is current or has been replaced.

Context — The situation that made a decision necessary. What were we trying to solve? What constraints were we operating under? This is the part most documentation skips. It's also the part Claude needs most. A model that understands why a decision was made can apply the same reasoning to adjacent cases. One that only knows what was decided can only copy it verbatim.

Decision — The actual choice, stated plainly. We keep this to one or two sentences. "We use AES-256-GCM for vault encryption with an Argon2id-derived key. The passphrase is never written to disk." That's the complete decision.

Consequences — What this decision implies going forward. What becomes easier. What becomes harder. What other decisions it constrains. This is useful for humans but it's particularly useful for Claude: it tells the model what to preserve and what to watch out for.

Technical Notes — Implementation details, relevant RFCs, library choices. This is where we link to RFC 8693 for token exchange, explain why we chose Regorus over OPA for edge-deployment policy evaluation, or note that the Merkle tree seal uses Ed25519 rather than RSA because of key size constraints in the audit manifest format.


What our public documentation (and internal notes) actually cover

The same themes appear in TAOS material you can read without repo access — and in the structured notes we attach to Claude sessions. A few examples:

Identity and delegation. Our Delegated authority and architecture pages explain why agent runtime tokens are short-lived and down-scoped from human tokens (RFC 8693 Token Exchange), why we embed a kid header in every JWT for version-aware rotation, and why we chose DPoP (RFC 9449) over bearer tokens for runtime binding. Without that material in context, Claude consistently tries to simplify the token model when resolving errors — it drops the kid, uses a symmetric secret instead of the asymmetric pair, or caches the token beyond its TTL "for performance."

Audit integrity. Secure audit and the execution-audit section of the architecture overview spell out why audit records are committed before operations execute, not after: a crash between execution and logging leaves no record of an operation that actually happened, and the audit writer must be treated as a hard dependency, not a best-effort side effect. Claude, given that framing, never suggests making the audit write asynchronous. Without it, it does — reliably — whenever it's trying to improve perceived latency.

Secrets hygiene. Secrets management states the product rule: no credential ever appears as an environment variable, CLI argument, or config file value — surfaces that show up in process listings, shell history, crash dumps, and debug logs. Claude, absent that guidance, will scaffold a demo or test with os.environ.get('API_KEY') every time. With it in context, it doesn't.

Cryptography choices. Algorithm selections (Argon2id over bcrypt, AES-256-GCM over CBC, SHA-256 for the audit hash chain) are decisions Claude has no way to recover from code alone. We keep the rationale in structured notes and align public copy with the same choices so marketing, security review, and codegen see one story.

Policy language. Policy enforcement and the policy chapter of the architecture overview cover why we use Rego, why evaluation lives in the kernel, and how request context is shaped. Claude can write arbitrary Rego if given a blank page. Given those docs, it writes Rego that matches how the embedded engine expects bundles to behave.


Structure Is for Knowledge, Not Just Code

The design system is another instance of the same pattern. Before we had a documented design system, Claude produced components that were visually inconsistent — rounded corners here, a gradient there, a shadow in the wrong direction. After we codified the Neo-Lichtenstein rules in a style guide (accessible at /docs/style-guide in this repo), those violations stopped — not because Claude got better at guessing our aesthetic, but because it stopped guessing.

The style guide is explicit in the way that makes Claude reliable: "No gradients. No rounded corners. No shadows other than the hard 4px/6px offset. #FFDE00 for primary CTAs. #0057A8 for banners and active states. #E63329 for destructive actions only." The Text component ESLint rule (no-restricted-syntax for raw h1–h6 elements) is the design system's skeleton equivalent — a structural enforcement that makes violations loud rather than silent.

Structured notes and style guides are not the same kind of document, but they serve the same function: they convert tribal architectural knowledge into explicit, queryable, Claude-accessible context — while the public docs index stays the source of truth for anyone outside the repo.

The skeleton architecture article argued that you need structure in the code. That remains true — we use base classes and abstract interfaces extensively. But building a complex system with Claude at scale taught us a second principle: you need equally disciplined structure in the documentation that the code is built from.

Prompts fade. Architecture documents persist.


Practical Notes on the Workflow

A few things about how this works day to day:

Relevant docs go in context on day one of each task. When I start a session that touches the vault, I load Secrets management, the architecture overview, and any internal structured note for edge cases — before any code changes. This is not optional — without them, the first implementation step Claude takes is often inconsistent with prior decisions.

Structured notes stay in markdown, not loose prose. The five-section format is load-bearing. Claude pattern-matches well on structured documents. Prose context — "we decided to use Argon2id because..." — is far less reliable than a formal record with a labeled Decision section.

The record is written when the decision is made, not reconstructed later. Reconstruction is unreliable and gets deferred indefinitely. We draft it during the conversation where the choice is made, often with Claude helping. The act of writing is itself a forcing function — it exposes ambiguities before they become inconsistencies in the code.

ESLint rules and TypeScript types are low-level decision records. The design system ESLint rule that rejects raw heading elements is the equivalent of a decision expressed in code. When Claude generates a component with a raw <h2>, the build fails loudly. That's a better enforcement mechanism than any markdown note. For decisions that can be expressed as types or linter rules, the code is the record.


The Number

Six weeks, 120,000 lines, three engineers. That number usually prompts skepticism — either disbelief at the scale or concern about the quality. Both reactions are reasonable.

The quality concern is the one I take seriously. This is exactly the problem the skeleton architecture was designed to address, and exactly why we invested in structured notes and public documentation before the codebase grew large. The skeleton makes structural violations detectable at runtime. Written context makes generation-time decisions consistent. Together they are the difference between 120,000 lines that are coherent and 120,000 lines that are fast.

The alternative — generating 120,000 lines without structure, then paying down the debt — is the pattern that produces the headlines about AI-generated technical debt. Structure first. Velocity follows.


Patrick Farry is a co-founder of Predictable Labs and a contributing editor at InfoQ. The skeleton architecture article referenced in this post is available at infoq.com/articles/skeleton-architecture.

See these principles in practice

TAOS is the control plane we wish had existed.

Get in touch