walkthrough

This guide follows one project from start to finish. We write a plan for improving a checkout page, let an AI agent work through it, and keep the final launch decision with a person. Along the way, you will see how Marionette catches missing steps and keeps a record of every decision.

You do not need to know Marionette's terminology before you begin. Each new idea is explained when it appears. The terminal examples are recordings of the real command-line tool, but you can understand the guide without reading every line of output.

1 · Describe the project

Our goal is to improve an online checkout. The team will test changes with a small group of customers, try again if the results are poor, and ask a person to approve the full launch. The plan also needs an answer for what to do if repeated changes do not help.

A Marionette plan is a plain-text file. Each section is a stage of the project, and each choice says where to go next. Here is the first draft. It has two mistakes: the retry path has not been marked as intentional, and the rollout stage has no next step.

checkout.mar — first draft
# 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
+ [Conversion flat — iterate] -> build_checkout

=== 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.

2 · Check the plan for missing paths

The validate command checks whether every stage can be reached and whether every path eventually goes somewhere. It reports both mistakes and points to the lines that need attention:

validate — two structural errorsexit 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 first error asks whether returning to an earlier stage is deliberate and, if so, when the retries should stop. The second asks what happens after rollout. Marionette will not give an incomplete plan to an agent.

3 · Fill in the missing decisions

We decide that the team may try up to five versions. The ~loop~ marker says that returning to the build stage is intentional, while attempts < 5 sets the limit. If all five attempts fail, the plan moves to rethink and asks a person what to do. @human marks a choice that an AI agent is not allowed to make. Finally, a successful rollout now leads to END, which means the project is complete.

checkout.mar — fixed
# 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
validate --strict — cleanexit 0
$ marionette validate checkout.mar --strict checkout.mar: 0 errors, 0 warnings

4 · Prepare the plan for an agent

The compile command turns the text file into JSON: a structured version that software can read reliably. Marionette also gives this version a unique fingerprint. Later, it uses that fingerprint to notice if someone changes the plan while work is under way.

compile — create the machine-readable planexit 0
$ marionette compile checkout.marwrote checkout.trajectory.json compiled 3 phases, 5 choices (sha256:24d32e0b2820…)

5 · Review the plan without learning the syntax

Reviewers do not have to read the plan file. The summarize command explains the route in plain language and highlights every decision reserved for a person. The render command creates a flowchart. In that chart, marks a human decision and marks a retry path.

summarize — the reviewer's viewexit 0
$ marionette summarize checkout.mar# Plan summary: checkout.mar**Intent:** 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.> 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.Starts at **build_checkout**. 3 phases, 2 decision points (phases with 2+ choices), 5 choices overall.- **Human checkpoints:** "Cohort converts" (at build_checkout); "Not converging — rethink" (at build_checkout); "New direction agreed" (at rethink); "Park the revamp" (at rethink)- **Declared loops:** build_checkout → build_checkout ("Conversion flat — iterate"); rethink → build_checkout ("New direction agreed")- **Gates:** 2 gated choices, of which 0 unverified (review manually)- **Variables:** attempts: number = 0- **Contract hash:** `sha256:24d32e0b2820…`## Walkthrough### build_checkout (start)Rebuild the checkout flow behind a feature flag.Ship one measurable change per attempt to the flag cohort and read theconversion funnel after a full week; an attempt is done when its data is in.- on entry: `attempts += 1`- **Cohort converts** → rollout (**requires a human decision**)- **Conversion flat — iterate** → build_checkout (only if `attempts < 5`; loops back; repeatable)- **Not converging — rethink** → rethink (only if `attempts >= 5`; **requires a human decision**)### rethinkFive 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** → build_checkout (**requires a human decision**; loops back; repeatable)- **Park the revamp** → END (**requires a human decision**)### rolloutRamp the flag to 100% and retire the old flow.Watch error rates and conversion during the ramp; done means the old flow isdeleted, not merely dark.- otherwise → END## Compiler reportNo defects, no warnings. The plan is structurally sound: every phase is reachable, every path has an exit, and all declared loops have a verified exit.
render — flowchart sourceexit 0
$ marionette render checkout.marflowchart TD  build_checkout["<b>build_checkout</b><br/>Rebuild the checkout flow behind a feature flag."]  rethink["<b>rethink</b><br/>Five iterations without lift: take the flow back to resea…"]  rollout["<b>rollout</b><br/>Ramp the flag to 100% and retire the old flow."]  build_checkout -- "✋ Cohort converts" --> rollout  build_checkout -. "↻ Conversion flat — iterate {attempts &lt; 5}" .-> build_checkout  build_checkout -- "✋ Not converging — rethink {attempts &gt;= 5}" --> rethink  rethink -. "✋ ↻ New direction agreed" .-> build_checkout  rethink -- "✋ Park the revamp" --> END  rollout --> END  END(((END)))  classDef taken fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px;  classDef current fill:#fff59d,stroke:#f57f17,stroke-width:3px;  classDef frontier stroke:#1565c0,stroke-width:2px,stroke-dasharray:4 2;  linkStyle 0 stroke:#c62828,stroke-width:3px;  linkStyle 2 stroke:#c62828,stroke-width:3px;  linkStyle 3 stroke:#c62828,stroke-width:3px;  linkStyle 4 stroke:#c62828,stroke-width:3px;

6 · Start the work

state init starts a run and creates checkout.state.json, a small file that records progress. The run begins in the first stage, build_checkout. Its attempt counter changes to 1, and Marionette shows which choices are available and which are currently blocked.

state init — start and show the first choicesexit 0
$ marionette state init checkout.marinitialised checkout.state.json bound to sha256:24d32e0b2820…current: build_checkoutRebuild the checkout flow behind a feature flag.Ship one measurable change per attempt to the flag cohort and read theconversion funnel after a full week; an attempt is done when its data is in.variables: attempts=1choices:  [0] Cohort converts @human -> rollout  [1] Conversion flat — iterate ~loop~ {attempts < 5} -> build_checkout  [2] Not converging — rethink @human {attempts >= 5} -> rethink  [unavailable: gate {attempts >= 5} is false]

The agent does not need to inspect that file. It runs brief to get its next assignment. The brief contains the current instructions, useful values such as the attempt count, the choices available now, and the command to record the result.

brief — the agent's next assignmentexit 0
$ marionette brief checkout.marwork packet — checkout-revamp (sha256:24d32e0b2820…)status: active   progress: 1/3 phases, 1 stepsintent: 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.=== build_checkout ===Rebuild the checkout flow behind a feature flag.Ship one measurable change per attempt to the flag cohort and read theconversion funnel after a full week; an attempt is done when its data is in.delivery: none · report per-phasevariables: attempts=1choices:  [0] Cohort converts @human -> rollout — Ramp the flag to 100% and retire the old flow.  [1] Conversion flat — iterate ~loop~ {attempts < 5} -> build_checkout — Rebuild the checkout flow behind a feature flag.  [2] Not converging — rethink @human {attempts >= 5} -> rethink — Five iterations without lift: take the flow back to research.  [unavailable: gate {attempts >= 5} is false]

7 · Keep the launch decision with a person

Choice [0] would approve the launch, and it is marked @human. When the agent tries to take it, Marionette refuses. The error is named human-checkpoint, and no progress is recorded. This rule is enforced by the tool itself; it does not depend on the agent remembering an instruction.

state choose — the agent cannot approve launchexit 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>.

The agent can choose to try another version instead. It records why, returns to the build stage, and the attempt counter changes to 2. Anyone checking the project can now see both the decision and the remaining number of tries.

state choose — try again, attempt 2exit 0
$ marionette state choose checkout.mar 1 --actor agent --rationale "conversion flat; iterating on the payment step"current: build_checkoutRebuild the checkout flow behind a feature flag.Ship one measurable change per attempt to the flag cohort and read theconversion funnel after a full week; an attempt is done when its data is in.variables: attempts=2choices:  [0] Cohort converts @human -> rollout  [1] Conversion flat — iterate ~loop~ {attempts < 5} -> build_checkout  [2] Not converging — rethink @human {attempts >= 5} -> rethink  [unavailable: gate {attempts >= 5} is false]
state show — check current progressexit 0
$ marionette state show checkout.marcurrent: build_checkoutRebuild the checkout flow behind a feature flag.Ship one measurable change per attempt to the flag cohort and read theconversion funnel after a full week; an attempt is done when its data is in.variables: attempts=2choices:  [0] Cohort converts @human -> rollout  [1] Conversion flat — iterate ~loop~ {attempts < 5} -> build_checkout  [2] Not converging — rethink @human {attempts >= 5} -> rethink  [unavailable: gate {attempts >= 5} is false]

8 · A person approves the launch

The test results show a 9% improvement. A person now takes the launch choice and records the reason. Marionette keeps their name, the decision, and the evidence in the project's history. After rollout, the plan reaches END.

state choose — a human records the ship decisionexit 0
$ marionette state choose checkout.mar 0 --actor lee --rationale "cohort shows +9% completion; ship it"current: rolloutRamp the flag to 100% and retire the old flow.Watch error rates and conversion during the ramp; done means the old flow isdeleted, not merely dark.variables: attempts=2no choices; divert available -> END (marionette state advance)
state advance — rollout completes the planexit 0
$ marionette state advance checkout.mar --actor agent --rationale "flag at 100%, old flow retired"current: END (completed)variables: attempts=2
brief — the project is completeexit 0
$ marionette brief checkout.marwork packet — checkout-revamp (sha256:24d32e0b2820…)status: completed ✓   progress: 2/3 phases, 4 stepsintent: 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.variables: attempts=2the plan has reached END — nothing left to execute.

9 · Safely change a plan that is already running

Imagine instead that the project is still on its second attempt and you edit the plan to add a postmortem stage after rollout. Marionette notices that the plan's fingerprint has changed. It stops with exit code 3 rather than applying the old progress record to a different plan. This mismatch is called drift.

state show — the plan changed during the runexit 3
$ marionette state show checkout.marplan/state drift detected: the script changed since this state was recorded.  state is bound to  sha256:24d32e0b28205dbad62004391795fe41fc4bfbb40696d30f6e7a62497635efa0  compiled plan is   sha256:241d3a5771722af98cd25c8ce17df13f05a974c8be1f6a403a6205fa6d6ee710Reconcile before continuing: review the plan changes, then either re-initialise the state (marionette state init --force) or restore the previous script version.

If the edit is intentional, run state rebind. This updates the progress file to use the new plan, keeps the existing decision history, and lists what changed. You can review that list before work continues.

state rebind — accept the edit and keep the historyexit 0
$ marionette state rebind checkout.mar migrated checkout.state.json  from sha256:24d32e0b2820…  to   sha256:241d3a577172…current: build_checkoutRebuild the checkout flow behind a feature flag.Ship one measurable change per attempt to the flag cohort and read theconversion funnel after a full week; an attempt is done when its data is in.variables: attempts=2choices:  [0] Cohort converts @human -> rollout  [1] Conversion flat — iterate ~loop~ {attempts < 5} -> build_checkout  [2] Not converging — rethink @human {attempts >= 5} -> rethink  [unavailable: gate {attempts >= 5} is false]

What you have done

You wrote a project plan, checked it for missing paths, gave an agent only the decisions it was allowed to make, and kept a clear history of the work. You also changed a running plan without losing that history.

Next, follow getting started to try this with your own plan. If you are connecting Marionette to another tool, the execution guide explains the machine-readable interface.