Get in touch
All systems

// teardown

Content as code

Decks with a type system.

in production·2026
TypeScriptNext.js 16ConvexClaude Agent SDKHono on Railway

Content · typed and versioned

Typed content
Patches
Validated version
publish ↓

Access · data, not code

Access in the DB
Viewer
Dwell analytics

what gets read shapes the next revision

// overview

A deck changes constantly and goes out one person at a time. So the content gets what code gets: a type, a diff, a version, and edits an agent can make safely. Who can see it, through which link and for how long lives in the database, where it changes without a deploy. I have built that split three times, and in its furthest form the content is a typed tree an in-product agent patches.

// how it works

01 · author

The content has a type

Every deck starts from a type contract that lives in the repo. On this site and at Ryse, a deck is a TypeScript file exporting a typed deck object, registered by slug, so a slide that breaks its type breaks the build. In a client's deck studio the same idea becomes data: decks, one-pagers and whitepapers are trees of 17 typed primitives stored in Convex, styled only through design tokens.

02 · patch

Edits are patches, not rewrites

In the code builds, an edit is a git diff, reviewed like any other change whether a person or a coding agent wrote it. In the studio, the in-product agent edits through five typed operations: patch a node, or replace, insert, delete and reorder slides. A person's inline text fix uses the same node patch and the same reducer, and the live preview follows the agent slide by slide. Anything outside the content, like a rename, arrives as a proposal a human applies.

03 · version

Validated, then versioned

The agent learns the schema from a declarations file generated from the Convex validators, and the validator it can call as a tool is a generated copy of the one the server runs. Applying a session re-validates the entire tree, confirms every referenced image exists, and writes a new numbered version. Restoring an old version opens a fresh session from it instead of rewriting history. In the code builds, the commit is the version and the type check is the gate.

04 · access

Access is a row

Who can see a deck is data, never code. On this site a deck stays private until a database flag makes it public, and share links can be revoked, can expire, and can ask for an email first. At Ryse, a deck is open to the admin team until someone restricts it; that person becomes the owner and adds viewers one at a time, each grant stamped with who made it and when. The studio's links are public, email-gated or invite-only.

05 · present

Private decks are never prerendered

Public deck pages are prerendered only for slugs that exist in the registry and are marked public in the database, and flipping the flag triggers on-demand revalidation. Share links resolve on every request. An unknown, revoked or expired token gets the same notice, so a dead link reveals nothing. Clearing an email gate turns the viewer into a person record, in the same table the contact form and whitepaper downloads write to.

06 · measure

Dwell counts only visible time

The viewer sends four events: opened, slide view, slide dwell and completed. Dwell comes from a pure timer that accrues only while the tab is visible, reports increments rather than totals, and flushes on a 15-second heartbeat, on every slide change, and by beacon when the tab hides or the page closes. Glances under 250 ms are dropped. The admin view rolls it up into completion rate, median session length and where viewers drop off.

// decisions

The parts that were a call, not a default — and what the alternative would have cost.

chose

Content in code, access in the database

not

One system holding both, whether a CMS or a hardcoded allowlist

Content wants what code has: types, diffs, review, and agents that already know how to edit files. Access wants the opposite. It changes per recipient, has to take effect in minutes, and should leave a record. Splitting them makes sending a deck to one more person a row, not a release.

chose

Storage follows the editor

not

One content store for every build

When the editors are developers and coding agents, files in git are the cheapest review loop available. A client team revising collateral constantly can't wait on a deploy, so there the same contract becomes a typed tree in the database that an in-product agent patches. The types stay in code either way.

chose

A composable tree of typed primitives

not

A fixed set of slide templates, or free-form React

Templates hit their ceiling by the second deck, and every new layout becomes a code change. Free-form React puts every layout choice, and every way to break the build, in the model's hands. Composable primitives with token-only styling express new layouts without a deploy, validate mechanically on every apply, and keep the brand in the tokens. A registry component is the escape hatch.

chose

Narrow, named tools for the agent

not

A shell and a validation script

A shell is the widest tool an agent can hold, and it can read the environment its credentials live in. The editing agent gets file tools inside an isolated workspace plus named tools: patch operations, a validator generated from the server's own, and proposals for anything with side effects. Every capability it has is one someone chose.

chose

Share links pin a version

not

Links that always serve the latest edit

A recipient should see what was sent. Pinning means publishing a revision can't quietly change a deck someone is halfway through. The admin view counts links that have drifted behind the current version, and moving a link forward is one explicit, audited step.

// guarantees

Access without a deploy

Visibility, share links and per-person grants live in the database. Publishing, restricting or revoking a deck is a row change, and every link or grant records when it was made.

What you sent stays sent

Studio links pin an immutable version. A revision never changes a deck someone is reading; drifted links are counted, and moving one forward is explicit and audited.

Dwell means on screen

Per-slide time accrues only while the slide is visible and is sent by beacon when the tab hides. Tracking fails quietly: it can lose an event, never break the viewer.

// by the numbers

17
typed primitives in the agent's vocabulary
1
patch reducer for human and agent edits
0
deploys to change who sees a deck

// let's talk

Let's build something.

Have a product that needs both sharp strategy and someone who can ship it? I'd love to hear about it.

Get in touch

Command Palette

Search for a command to run...