Python / TypeScript / React / MetaObjects
One ~5,000-line YAML spine now generates ~19,600 lines of drift-checked Python and TypeScript — 44 entities and 106 LLM prompts declared once, and any divergence between the engine and the platform fails the build instead of shipping.
Draagon AI is an AI agent platform — a Python engine and a TypeScript platform (API, admin UI, web). Until mid-2026, every data shape was hand-declared in three places that drifted, reconciled at runtime by 17 ALTER TABLE patches and, once, by a production bug. Five weeks after adopting MetaObjects, one YAML model generates ~19,600 lines across both languages — and drift fails the build instead of shipping.
Every agent, telemetry record, and config shape lived in three hand-kept declarations: the TypeScript Drizzle schema, the Python persistence layer, and the shared Zod validators and TS types. The cross-language contract between the engine and the platform was maintained by hand and documented in a comment.
The drift wasn't theoretical — it was executable. The Python side carried 17 ALTER TABLE … ADD COLUMN IF NOT EXISTS statements run on every database connect: Python defensively re-patching the schema on startup because it ran ahead of the TypeScript migrations. The two languages reconciled their schema at runtime, at the first connection.
Then it shipped a bug. Hand-written validators duplicated the data model and silently diverged — marking optional config fields as required — and legitimate partial agent-config writes started returning 400 in production. Meanwhile the 100+ LLM prompts were string constants formatted inside a try/except that silently degraded when a payload key went missing, and a ~190-line hand-written mapper re-mapped every field one by one — primed to drop one whenever the model grew.
Hand-syncing the third copy of every shape, with a runtime patcher papering over the gap, was the decision point: the shapes themselves had to become data.
The fix wasn't a better sync process — it was making the model the artifact. 44 entities, 16 value objects, 13 projection views, and 106 prompt templates are now declared once in YAML, and every Python and TypeScript consumer is generated from that spine and drift-gated in CI. One field declaration becomes the table column and the validator that used to be hand-kept:
# meta.platform.yaml
- field.int:
name: maxTokens
column: max_tokens
// Drizzle table (TypeScript)
maxTokens: integer("max_tokens"),
// Zod validator — was hand-kept, now generated
maxTokens: z.number().int().optional(),
From that one model, meta gen produces 471 committed files (~19.6k lines): Python record models, value objects, and prompt payloads with output parsers on the engine side; Drizzle tables, typed query helpers, Zod/TS contracts, TanStack hooks, admin UI, and API routes on the platform side. Business logic and the orchestration engine stay hand-written — the generated layer is the plumbing that used to drift.
Four meta verify gates run in CI, plus a pre-commit hook and a dedicated drift workflow on every push — so a model change that isn't reflected in both languages fails the build, not the first INSERT. The migration took five weeks and 97 commits, alongside normal feature work.
Touch up to ~7 hand-written places across two languages — dataclass, Drizzle table, Zod validator, TS type, route validation, web hook, form — and hope they agree.
Edit one YAML declaration and run meta gen. The field round-trips through both languages automatically.
ALTER TABLEs are gone; both languages generate from one canonical schema, so "Python thinks the table has a column TypeScript doesn't" now fails the build instead of reaching production.The schema patcher that ran 17
ALTER TABLEs on every connect wasn't refactored — it was deleted. The canonical model owns the shape now, in both languages.— verified in-repo: schema patcher removed (−75 lines), June 2026
Draagon's source is private — for a clone-and-read version of the same pattern, run the 10-second drift demo in Wizards of Odd or read smon.
Since 2026-09-09 it has run MetaObjects 1.0.0, every package it consumes pinned to the stable release, with its drift gate enforced in CI.
Doug works directly with teams evaluating MetaObjects. Tell us what you're building and we'll tell you, honestly, whether MetaObjects fits.
Email doug@dougmealing.com