Skip to content
4 min read · 708 words

Plans & Self-Repair

Three ways to say the same thing, and exactly one thing they can say. A TypeScript developer writes a chainable builder. A language model writes a pipe string. A program assembling work writes a JSON ops array. All three compile to one neutral MediaPlan — the same plan, byte for byte, regardless of which door it came through — and from that point on nothing downstream knows or cares which front-end produced it. That convergence is not a convenience. It is the reason a 3B model and a senior engineer can drive the identical pipeline without the pipeline growing two code paths, two validators, two sets of bugs. One plan or it doesn't work.

And when a plan is wrong — wrong verb, wrong arg, a capability the deployment doesn't have — the failure is written for whoever has to fix it. Usually that's a model, mid-turn, with no human watching and no second chance to ask a clarifying question. An error a model can't act on without a human reading the stack trace is an error that doesn't get fixed.

Three front-ends, one plan

The builder, the pipe parser, and the ops array all compile to the same neutral MediaPlan:

typescript
const chain = mp(payload).select({ pages: [2, 3, 4, 5] }).convert('pdf')

chain.toPipe()  // 'select pages=2-5 | convert to=pdf'
chain.toOps()   // [{ verb: 'select', args: { pages: [2,3,4,5] } }, { verb: 'convert', args: { to: 'pdf' } }]

Round-tripping is fixed-point: parsePipe(toPipe(plan)) produces an equal plan, and toPipe is idempotent. (It is not string-identity — pages=2,3,4,5 renders canonically as pages=2-5, because there is one canonical form and the parser doesn't care which equivalent spelling you arrived with.) This is the property that lets the few-shot examples in the media_query tool description come straight from toPipe on real plans — they are generated from the parser, so they cannot drift from it. The day-one failure mode of every hand-written grammar doc — examples that quietly stopped parsing three refactors ago and nobody noticed — is structurally impossible here. If an example renders, it parses; if it parses, it runs.

Compile without executing

mp.compile(statement) validates a pipe string or ops array to a plan without moving any bytes — useful for dry-runs, plan caching, and building tooling on top of the IR:

typescript
const plan = mp.compile('select pages=1-3 | extract text')
// throws E_MEDIA_ENGINE_REQUIRED / E_MEDIA_UNKNOWN_VERB / … exactly as query() would

Errors are written for self-repair

An error message that says "invalid input" is a message written for the person who threw it. Every error in this grammar is written for the party who has to fix it — usually a model, mid-turn, with no human watching. That means two layers, both ending in a corrective exemplar, because telling a model what's wrong without showing it what right looks like is half a message:

Syntactic — position-bearing, from the lexer/parser:

text
pipe parse error: unexpected "-" — values containing dashes must be quoted at line 1, col 24.
Write it like: audio transcribe name="value-with-dashes"

Semantic — from the validator, against the verb table and your configured engines:

text
unknown verb "redackt" at segment 2. did you mean "redact"? Available verbs: select, split, …
verb "convert" has no arg "format". did you mean "to"? Args: to. Write it like: convert to=pdf
arg "to" on "convert": "pdff" is not valid; valid values: pdf, html, txt, …
verb "convert" requires the "convert" engine, which is not configured in this deployment.
Do not retry this verb here. Available verbs: …

Did-you-mean matches suffix words too — a model that writes resize width=256 (dropping the namespace) is pointed at image resize. Suggestions never include verbs your deployment can't run, because suggesting a verb that will immediately fail is not a suggestion, it's a prank. And engine narrowing happens at validation, never at parse: the same statement parses identically everywhere; only what's allowed differs. A statement is never "syntactically wrong here but fine over there" — that distinction belongs to the deployment, and the error says so in so many words.

Field note: your prompt's brackets end up in the model's statement

The first live-model runs of the forge surfaced a trap we didn't design for: a prompt that says "replace it with [REDACTED]" gets echoed into the statement as replace=[REDACTED] — brackets and all — which is an unquoted-special-characters lexer error. The model isn't being careless; it's being faithful to your prompt. The error's "write it like: name="value"" exemplar usually triggers an in-turn repair, and watching that repair loop actually work was the error contract earning its keep — but usually is not always, and a retry costs a round-trip. The cheaper fix is upstream: when your system prompt names a replacement token, name the bare word, not the bracketed one. Your prompt is part of the grammar's input surface whether you meant it to be or not.