Reading a Run
此内容尚不支持你的语言。
The classic advice for debugging is: don’t panic, treat the bug as a puzzle, and read the error message before you touch any code. That advice assumes you have to go looking for what happened. In mashin you don’t. Every run is recorded in full as it executes, so by the time something fails, the evidence already exists. Reading a run is the whole first act of debugging.
Open a run
Section titled “Open a run”Every run has a page. From Running in the sidebar, pick a run, or follow a link from a machine’s Runs tab. The page opens in Trace view by default; switch to Cockpit debugger view with the toggle at the top for the layout this page describes (a run URL looks like /running/<run_id>?view=cockpit, so it’s a link you can send someone).
The failure envelope
Section titled “The failure envelope”If the run failed, a banner sits at the top of the cockpit before you scroll past a single step. This is the failure envelope, and it exists so you never have to reconstruct what went wrong from a stack trace:
| Field | What it tells you |
|---|---|
outcome |
How the run ended |
error_category / error_code |
What kind of failure this was |
error_message |
The failure in words |
retryable |
Whether trying again could plausibly help |
root_cause_intent_ref |
The exact step that caused it |
denial_stage / denial_reason |
If governance stopped it, which stage and why |
That last pair matters: a permission denial is not an error the machine produced. It’s a decision the runtime made on purpose, and it is reported as one. You are never left guessing whether something broke or something was refused.
The execution spine
Section titled “The execution spine”Below the envelope is the spine: one row per step, in order. Each row carries:
- A type glyph (
compute,reason,call,decide,await,remember), so you can see the shape of the run without reading names. - A governance tick (allow, hold, or deny) in the left gutter, for any step that went through a directive.
- A heat bar on the right showing cost and latency, so an expensive or slow step stands out before you click into it.
- A status color: completed, failed, denied, or running.
Click any row to move the playhead there. The playhead is the same thing a debugger’s current line is, except it never has to stop and wait for you: the run already finished, so moving the playhead just changes what you’re looking at.
A transport bar above the spine gives you four ways to move it: step forward or back, jump to the next failed step, jump to the next denied step, or jump to the next cost or latency outlier. A run minimap along the top lets you click straight to any step by position. None of this replays anything. It’s navigation over a record that already exists.
The variable inspector
Section titled “The variable inspector”Selecting a step opens its variable panel: the exact environment at that point in the run; input, prior step outputs by name, state, and output so far. This is the answer to “what did this step actually see,” without adding a single log statement anywhere.
Two details make it more than a JSON dump:
- Provenance. An entry in the
stepscope is clickable, and clicking it moves the playhead back to the step that produced it. You can walk a value’s history backward, one producer at a time. - Redaction. A field the machine declared sensitive renders as a locked
REDACTEDchip, never the raw value. The inspector shows you everything you’re allowed to see and nothing you aren’t, in the same view.
What the run was refused
Section titled “What the run was refused”If any step in the run was denied, a panel above the spine lists each one in plain language, for example: “network.http.post to api.payments.example is outside the wall declared in permissions_wall_denied.” Each entry jumps the playhead straight to the step it happened at. A run with a clean governance record shows nothing here. There is nothing to scroll past.
Reading it, worked
Section titled “Reading it, worked”Here is a real machine, denied on purpose, so you can see what its run actually looks like in the cockpit rather than take the description on faith.
machine permissions_wall_denied accepts amount as number, default: 100
responds with result as text
behaves ask hop, from: "@mashin/actions/service/call" endpoint: "https://api.payments.example/charge" payload: {amount: input.amount}
compute done {result: steps.hop.status ?? "unreached"}
ensures permissions allowed to network.http to "api.weather.example"
verifies test "INTENTIONAL DENIAL: the callee's request to a host outside the composer's wall is refused" given {amount: 100} expect deniedpermissions_wall_denied declares that it may reach api.weather.example and nothing else. Its one step, hop, calls the standard library’s @mashin/actions/service/call, an ordinary machine that would happily make this request on its own. Called from inside this wall, its request to api.payments.example is outside the boundary this machine drew, so it is refused: a composer can narrow what it calls, never widen it.
Open this run in the cockpit and you’d see:
- The failure envelope reads
outcome: denied, withdenial_stageanddenial_reasonnaming the exact constraint that fired. - The spine shows one row for
hop, marked denied, governance tick showing a refusal rather than an allow. - The denials panel carries one entry: which host, which policy line, and a jump straight to
hop. - The variable inspector on
hopshowsinput.amountand nothing past it, because nothing past it ran.
That is the whole investigation. No reproduction step, because the run is the reproduction. No log statement, because the ledger already recorded the decision. The only thing left to do is decide, as a human, whether the wall should widen or the call should go.
What this replaces
Section titled “What this replaces”The mechanical parts of classic debugging (reproduce the bug, read the error, gather evidence) exist in other tools because the runtime forgets. mashin’s runtime doesn’t. The behavioral ledger is the log you didn’t have to write, and a run’s cockpit view is that log, laid out to read. What’s left is the part no substrate can do for you: deciding what the failure means and what to change. That’s the subject of the next page.
Related
Section titled “Related”- The Behavioral Ledger: what gets recorded and why it can be trusted
- Governance: how a denial gets decided in the first place
- Permissions and the Blast Radius: the full walkthrough this page’s example comes from
- Forking and Replaying a Run: testing a fix without touching anything real, next