marionette

The project plan is the agent's script; the compiler guarantees the script is sound; humans author and gate it.

Marionette is a plain-text language for project trajectories — the phases a project moves through, the decisions that connect them, and the conditions under which each path opens. You write a plan as a small, legible script (or let an AI draft it from your notes); a compiler validates it into a canonical JSON graph; an AI agent then executes the project by walking that graph — bounded, auditable, and unable to route around the plan. Decisions marked @human are ones the agent cannot take: it must stop and escalate to you.

The design is borrowed from Ink, the interactive-fiction language, with the player swapped out: instead of a human choosing their way through a story compiled by inklecate, an AI agent chooses its way through your project — and the human holds the gates.

The three layers, in Ink and in Marionette
LayerInkMarionette
LanguageNarrative script: knots, choices, diverts, state Trajectory script: phases, decisions, gates, state, human checkpoints, bounded loops
Compilerinklecate → story JSON marionette compile → trajectory JSON (the contract) + static validation
PlayerHuman, in a game AI agent walking the graph; humans author, review, and gate

A plan is a file

One legible file: the original ask up top, then phases whose prose says what the work is and what "done" means. Iterate on checkout conversion up to five times, a human decides when it ships (or when to go back to research), and every route ends somewhere on purpose.

checkout.mar
# project: checkout-revamp
# summary: Lift checkout conversion behind a feature flag — up to five evidence-driven iterations, a human ship gate, and a forced rethink when the budget runs dry.
# prompt: """
Checkout conversion has been flat for two quarters. Rebuild the flow
behind a flag and iterate on cohort evidence — five attempts max. I take
the ship call, and if it never converts we go back to research rather
than grinding out attempt six.
"""
VAR attempts = 0

=== build_checkout ===
Rebuild the checkout flow behind a feature flag.
Ship one measurable change per attempt to the flag cohort and read the
conversion funnel after a full week; an attempt is done when its data is in.
~ attempts += 1
* [Cohort converts] @human -> rollout
+ {attempts < 5} [Conversion flat — iterate] ~loop~ -> build_checkout
* {attempts >= 5} [Not converging — rethink] @human -> rethink

=== rethink ===
Five iterations without lift: take the flow back to research.
Run the checkout usability study and write up why the five attempts failed;
that write-up is the input to whichever door is taken next.
+ [New direction agreed] @human ~loop~ -> build_checkout
* [Park the revamp] @human -> END

=== rollout ===
Ramp the flag to 100% and retire the old flow.
Watch error rates and conversion during the ramp; done means the old flow is
deleted, not merely dark.
-> END

The compiler holds the line

Dead ends, unreachable phases, undeclared cycles, loops with no satisfiable exit, human checkpoints with no escalation path — all build failures, each with a line number and a fix. This is a real session (press play, or step through it):

validate — a broken plan fails the buildexit 1
$ marionette validate checkout.marcheckout.mar:19: error[MAR006]: phase "rollout" is a dead end: no choices and no divert  19 | === rollout ===  help: add a choice or divert (e.g. "-> END") so every path has an exitcheckout.mar:17: error[MAR008]: undeclared cycle: build_checkout -> build_checkout  17 | + [Conversion flat — iterate] -> build_checkout  help: cycles must be intentional: mark the returning choice with ~loop~ checkout.mar: 2 errors, 0 warnings

The autonomy boundary is enforced, not advisory

Once the plan compiles, an agent traverses it step by step, recording a rationale for every decision. When it reaches a choice you marked @human, the walker refuses — the agent's only move is to escalate to you:

state choose — the walker refuses an agent at @humanexit 1
$ marionette state choose checkout.mar 0 --actor agent --rationale "metrics look good"error: choice "Cohort converts" is an @human checkpoint: an agent may not take it autonomously. Escalate to a human; a human records the decision with --actor <name>.

Where to go