01 / OPENINGStarted by adding a content type
i started monday trying to add a content type. by lunch i was rewriting the schema.
the content type was the journal entry — this one, in fact. fifty.dev runs a content pipeline that turns ideas into posts: an agent called DECK plans, WRITER fills copy, DESIGNER produces the visuals. the pipeline has been shipping LinkedIn carousels and X threads for a few weeks. journal entries were next on the list. i opened the schema, started typing, and the schema pushed back.
02 / WHAT BROKEWhat the flat model could not hold
the v1 schema was flat. one brief equals one channel post equals one sequential array of "cards." that shape worked for a LinkedIn carousel — eight cards, one channel, one post. it worked for an X thread — four tweets, one channel, one post. it did not work for a journal entry. a journal entry is not a carousel. it is one long body of prose with figures embedded at specific moments, plus a hero image above the title, plus an OG image — a social-preview card meta-tagged for sharing — plus a newsletter mirror that wants the same body in a different envelope.
the "cards" abstraction was leaking the LinkedIn carousel into the schema. it had been load-bearing for a month and i had not noticed, because every artifact we had shipped happened to be card-shaped.
so the question stopped being "how do i add journal support?" and became "what is the right shape?"
03 / THREE LEVELSThree levels
what came out is a three-level model — idea at the top, deliverables in the middle, assets at the bottom. an idea is the unit of meaning ("ship FR-008, write it up"). deliverables are the channel-specific posts that carry that idea — a li-personal LAB_NOTE, a journal long-form, a newsletter mirror. assets are the concrete files inside each deliverable — a hero image, an OG card, two inline figures, the body prose. one idea fans into many deliverables; each deliverable holds many assets.
the shape is type-rooted, channel-fanned, format-and-asset at the leaves. it sounds obvious written down. it took half a day to see and an afternoon to feel like the only shape that worked.
04 / REGISTRIESRegistries and a routing tree
the second move was harder. the v1 DECK agent had its content-type taxonomy, channel list, and routing rules baked into prose inside its system prompt. that was fine when there were three channels. it was going to be a maintenance fire at twelve. so the taxonomy moved out of prose and into five registries — flat lookup tables, one row per content type, one row per channel, one row per format. registry here just means a small reference table: the source of truth for "is this a real content type?" lives in a row, not in a paragraph. adding a channel becomes a one-row edit and a downstream regression run. the routing logic — given an idea of type X, in mode Y, which deliverables should fan out? — moved into a decision tree (a small step-by-step rules engine; given inputs, return a set of deliverables). DECK reads the tree instead of recomputing the routing each time from prose intuition.
that piece is the part i keep mentally underlining. the registries and the routing tree together are what makes this a context-engineering problem, not a copywriting problem. the system gets cheaper to extend the more it has to handle. that is the test.
05 / ASSET LAYERThe asset layer is video-ready
the asset layer is where i paid a design tax up front. every asset carries two fields: kind and mode. kind is image or video. mode is generated (an AI/render pipeline produces it) or captured (filmed, recorded, photographed — the human-made variant). a third concept, renderer dispatch — the small lookup that says "for kind=image, mode=generated, send to the diffusion renderer; for kind=video, mode=captured, expect a file path from disk" — turns adding video into a one-row dispatch addition. producible video drops in as a new dispatch row. filmed reels slot in as captured assets without touching the renderer. none of that is built yet. the schema is ready for it. that was the tax.
06 / MIGRATIONMigration via coexistence
the migration question got answered by the calendar. the linkedin weekly manager — the thing that actually keeps content shipping — could not pause for a schema rewrite. so the migration strategy was coexistence. migration via coexistence means: the new schema goes live, and the v1 flat briefs already in the system don't get rewritten; they remain orphan deliverables with no idea_id and no parent manifest, and the v1 parser continues to read them unchanged. nothing breaks. the weekly manager kept shipping monday through wednesday while the schema was being torn up underneath it. when the bonfire option exists and you take it, you pay in dropped weeks. coexistence pays in slightly weirder data for a while. the data settles; the dropped weeks don't come back.
07 / PHASESSix phases, every one reviewed
i ran the redesign in six phases. each phase ended at a WARDEN review — WARDEN is the internal review gate that says "this phase is closed; the receipts hold; proceed." phase 1 was the model. phase 2 was the registries. phase 3 was the deliverable schema and the asset schema. phase 4 was DECK's routing tree. phase 5 was orchestrator wiring. phase 6 was migration and verification. the concrete receipt that matters most is orchestrator/test_manifest.py — a regression suite that asserts every registry entry round-trips through the schema and every routing-tree path produces a valid deliverable graph. it is the first persistent test suite in that repo. it stays.
08 / SMOKE TESTThe live smoke test
phase 6 is where the post stops being smooth.
after phase 5 the system passed every static check. schemas validated. unit tests green. WARDEN cleared the gate. i ran a live smoke test anyway — actual idea in, actual DECK invocation, actual deliverable file written to disk. it failed. not loudly. it just stopped. DECK produced a §1, signed off, and nothing picked up the brief.
that surfaced TD-012. the Python orchestrator was missing the DECK-review state machine entirely. the new schema added a PENDING_DECK_REVIEW status — the gate where DECK reviews WRITER's §2 before DESIGNER intake — and the orchestrator's status-transition table did not know about it. a brief in PENDING_DECK_REVIEW sat there. it wasn't a regression. it was a pre-existing gap that the old flat model had been hiding because the old pipeline never needed that state. the redesign exposed it. i hunted it the same afternoon, wired the transition, re-ran the smoke test end to end, and the deliverable cleared all the way through.
the lesson i keep writing in the margins of my notebook: live verification surfaces what static review can't. static review tells you the parts are correct. live verification tells you whether the parts are wired correctly. those are different claims. both reviews shipped today. only one of them caught TD-012.
09 / BANKEDWhat got banked
what got banked, then.
the flat model wasn't wrong. it was correct for the artifacts it had seen. it just could not express the new content type, and trying to make it express the new type was the architecture pressure-test that produced the real shape. shapes get pressure-tested by the artifact that doesn't fit, not by the artifact that does.
the three-level model with registries and a routing tree is the right shape for this — both for current load and for what i can see coming (the brand page, video, captured assets, more channels). i don't yet know if it is the right shape for things i cannot see coming. that is a different review, six months out.
migration via coexistence is the right move when the production path cannot pause. the dropped-week cost of a bonfire migration is too high when the pipeline is the thing the brand is.
live verification surfaces what static review can't. i had been treating static review as sufficient. it is necessary and not sufficient.
10 / FORWARDForward — TD-023 is this entry
there is a question this post is also a test of, and i want to leave it open rather than wave it past. TD-023 asks whether live DECK actually emits a journal deliverable for a journal-typed idea — meaning, whether the routing tree and the registries hold under real load, not just under the smoke test. this entry is that test. if you are reading it on the journal page, with a hero above and three figures inline, the routing held. if anything looks wrong — a missing figure, an OG card that doesn't share, a structural seam — that is the answer too, and it goes back in the queue. there are a handful of smaller contract gaps that surfaced today (mostly around how the newsletter mirror inherits its body from the journal canonical) that i am leaving open this week on purpose. the right next move is to let them break under one more cycle of real use before fixing them. seams that haven't been pressure-tested aren't seams yet. they are guesses.