# Machina Driving

> Drive a Machina state machine to a deterministic outcome on behalf of an AI agent. USE WHEN: a task is governed by a state machine (a machine definition JSON with states, transitions, guards, actions, tools, checks, scenarios); executing a workflow that must be deterministic and auditable; running a skill whose SKILL.md declares a `machina:` machine; firing events, checking status, satisfying evidence checks, verifying run integrity with `check`, or producing a terminal report for a machine-driven run; escalating a STUCK run to a human conductor. DO NOT USE FOR: authoring or scoring machine definitions (use machina-authoring); modifying the Machina simulator app or its engine (use machina-simulator); implementing or modifying the driver/runtime tooling such as `scripts/machine-driver.py` (engine development) unless you are a maintainer of this skill upgrading the driver itself; general diagramming or XState/SCXML authoring.

- Skill: `arisng/machina-driving` (Agent Skill, multi-file: 28 files)
- Install (CLI): `npx skillmds@latest add arisng/machina-driving`
- Raw SKILL.md: https://api.skillmd.com/api/skills/arisng/machina-driving/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: arisng (https://skillmd.com/u/arisng)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/arisng/machina-driving

---


# Machina Driving

Execute a task under a Machina state machine: the driver (`scripts/machine-driver.py`)
is the sole mutator of run state; you perform the real work, choose events, and
satisfy evidence checks. The outcome is a deterministic, auditable report.

## Run History Convention

All machina run history must be stored in:
  `<session-workspace>/machina-runs/<run-id>/`

This convention ensures:
- Consistent traceability across sessions
- Easy discovery of run artifacts
- Standardized audit trail

The `--run-dir` parameter should point to `machina-runs/` under the session workspace.
The driver defaults to `machina-runs/` (resolving via `COPILOT_DOJO` or `~/.copilot-dojo` env vars for the session workspace root; falls back to `<cwd>/machina-runs/`).

## Quick start

From the skill directory (or any workspace with this skill installed):

```python
python3 scripts/machine-driver.py init --machine <machine.json> --scenario <id> --input k=v
python3 scripts/machine-driver.py status --run <run_id>
python3 scripts/machine-driver.py fire <EVENT> --run <run_id> --note "what you did"
python3 scripts/machine-driver.py check --run <run_id>
python3 scripts/machine-driver.py report --run <run_id>
```

Every command prints exactly one strict-JSON object. A blocked `fire` is a first-class outcome, not an error.

## Workflow

1. **Discover** the governing machine — read the relevant skill's `SKILL.md`
   frontmatter (`machina: { machine, scenario }`), or use the machine file the
   human conductor names.
2. **Init** the run with all required scenario inputs.
3. **Work the current state** — the state's `description` tells you what the
   state demands. Do real work before firing events.
4. **Check status** — `status` shows enabled vs blocked events and why.
5. **Fire** events with a `--note`; iterate on `blocked` outcomes by fixing the
   underlying condition (guard or evidence), never by forcing the machine.
6. **Verify integrity** — `check` re-runs the full ledger + artifact-hash
   verification and returns `{ok:true}` only when the run is intact.
7. **Report** at terminal — ground your final summary to the report facts.

## Tamper prevention

The driver makes accidental or silent tampering fail closed. Four mechanisms
(v0.3.0):

1. **Ledger hash chain** — `ledger.jsonl` records chain via `prev_hash`/`hash`;
   any insert, remove, or reorder is detected on every command.
2. **Artifact-hash binding** — `init` pins the run's machine definition
   (`machine_sha256`) and every referenced tool/checker script (`tool_hashes`)
   in the init record; `status` / `fire` / `check` / `report` recompute them and
   fail closed on mismatch. A mid-run edit to `machine.json` or a checker script
   is a ledger integrity violation.
3. **Repo-worktree run-dir refusal** — `init` refuses to create a run dir
   inside a git worktree (only overridable via `MACHINA_ALLOW_REPO_RUNS=1`), so
   run state cannot silently land in something you might commit.
4. **Report bound to the ledger** — `report.json` carries `ledger_final_hash`
   (the last ledger record's hash), so a report can be traced to the exact
   ledger state it was generated from.

**Honest ceiling:** this is *detect-and-fail-closed*, not cryptographic proof.
The driver verifies hashes it derives from the same run directory, so an
attacker who can rewrite all of it (including the init record) is not stopped —
there is no OS-level read-only and no HMAC by design. The guarantee is that any
edit to the definition copy, a checker script, or the ledger is *detected* and
the run refuses to continue.

## Hard rules

- **INV-1**: never hand-edit a machine's state, context, history, or logs. The
  driver is the only writer; the ledger is tamper-evident.
- **INV-2**: never inline code into machine JSON. Tools are named references to
  scripts the driver executes.
- **Never run runs from inside a repo**: `init` refuses repo-worktree run dirs
  (override with `MACHINA_ALLOW_REPO_RUNS=1`); run state stays session-scoped.
- **Post-init context immutability**: after `init`, only machine actions and
  tool-output mappings mutate context. Your `--note` never touches context.
- **STUCK is not a crash**: a non-final state with zero enabled events is a
  modeling gap. Report it to the human conductor; never improvise a transition
  or fabricate completion.
- **Ground your summary to the report**: render only report facts
  (`result`, `final_state`, `path`, `events`, `evidence`, `context_snapshot`).

## References (load on demand)

| File | Load when |
|---|---|
| [references/driving-protocol.md](references/driving-protocol.md) | Any driving task — the full protocol: command surface, driving loop, evidence, phase states, STUCK/escalation, report grounding, discovery |
| [references/schema-v3.md](references/schema-v3.md) | Reading or writing machine JSON — v3 field reference, tools registry, checks/requires/ensures, inputs, limits, phase states |

## Dependency

This skill **depends on** [`machina-authoring`](../machina-authoring/SKILL.md):
`scripts/machine-driver.py` imports the shared engine (guard/action evaluation,
terminal detection, compliance scoring) from `machina-authoring/scripts/machine-validator.py`.
Distributing this skill to a workspace **implicitly distributes `machina-authoring`**.
The `machina-simulator` Copilot extension is **optional** (human UI only).

For precision: the driver consumes the engine's **blocking validation**
(`run_compliance(...)["blocking"]`) as the structural gate and executes the machine's declared
tool scripts itself. It does **not** consume the authoring compliance *score* — see
[machina-authoring's Compliance boundary](../machina-authoring/SKILL.md). "Excellent" is a
declaration-quality signal; runtime soundness is established only by actually running the tools.

## Samples

- [samples/docs-authoring.machine.json](samples/docs-authoring.machine.json) — a
  v3 machine with tools, checks, requires, inputs, and a phase state.
- [samples/scripts/check_file.py](samples/scripts/check_file.py) — a read-only
  checker script referenced by the sample machine.

## Trust boundary

The driver executes referenced checker scripts with the user's privileges.
Machines are trusted artifacts (authored by the user or by `machina-authoring`).
No sandboxing in v1.

Corollary of the [compliance boundary](../machina-authoring/SKILL.md): a machine can be declared
"Excellent" yet fail at runtime if its tool scripts are missing, incorrect, or hostile — the
driver is what establishes runtime behavior by executing the declared read-only checkers, which is
exactly why they must exist and stay read-only.

## Hook hardening (defense-in-depth)

Three agent hooks provide lifecycle-based enforcement that augments the driver's internal tamper prevention. These hooks are **advisory-to-mandatory** depending on the event type and fill gaps where the driver's prompt-level instructions are insufficient.

| Hook | Event | Enforcement | Gap filled |
|---|---|---|---|
| `machina-ensures-runner` | `postToolUse` | `modifiedResult` (mandatory data) | `ensures[]` post-conditions declared in schema but not enforced by driver |
| `machina-event-gate` | `postToolUse` | `modifiedResult` (mandatory data) | `status` shows events "enabled" when evidence checks would block them |
| `machina-summary-guard` | `agentStop` | `decision:"block"` (forced turn) | Agent can fabricate summary contradicting `report.json` |

**How they work:**

- **Fire guard**: After a `fire` command, runs `transition.ensures[]` tools and
  appends pass/fail results to the tool output. The agent sees post-condition
  failures as part of the fire result.
- **Status enhancer**: After a `status` command, cross-references
  `enabled_events` against `state.checks[]` and replaces the output with
  accurate categorization (truly enabled vs checks-failed vs checks-pending).
- **Report validator**: Before session ends, compares the last assistant message
  against `report.json` facts. If contradictions are found, forces another turn
  to correct the summary (capped at 8 consecutive blocks).

**Deployment:** These hooks live in `hooks/machina-*/` and are published to
`.github/hooks/` via `publish-hooks.ps1`. They activate automatically when the
agent uses the machina-driving skill.

## Report output contract

Your final user-facing summary is a **grounded report** that renders only facts
from the terminal `report` output. It is not prose — each field must trace to a
`machina.report.v1` field.

| Report field | Required content | Source (`report`) field |
|---|---|---|
| `result` | `SUCCESS` · `ESCALATED` · `ABORTED` · `STUCK` · `IN_PROGRESS` | `result` |
| `final_state` | the final state id | `final_state` |
| `path` | the visited state sequence | `path` |
| `events` | count + comma-separated event names | `events`, `agent_notes[].event` |
| `evidence` | `passed N / failed M` | `evidence.passed`, `evidence.failed` |
| `blocked_events` | only when present | `blocked_events` |
| `context_snapshot` | only when it affects the outcome | `context_snapshot` |

Rules:

- Keep it under ~80 words; one paragraph per report.
- **Never invent claims beyond the report.** If `result` is `STUCK`, say
  `STUCK` — do not claim success.
- On `STUCK` or `ESCALATED`, head the summary as a **grounded escalation**:
  state the blocked events, the evidence failures, and the decision the human
  conductor must make. Never improvise a transition or fabricate completion.
- On `ABORTED`, render the abort reason from the report.
- If the driver emitted a strict-JSON `machina.report.v1` object, that object is
  the report — do not paraphrase it into different fields.

## Resources

- `scripts/` — `machine-driver.py` implements the `init` / `status` / `fire` /
  `abort` / `check` / `report` command surface. Execute it directly; do not load
  into context.
- `references/` — `driving-protocol.md` (the driving loop, evidence, STUCK,
  report grounding, tamper prevention) and `schema-v3.md` (machine JSON v3
  reference). Load on demand per the table above.
- `samples/` — `docs-authoring.machine.json` and its `scripts/` checkers for a
  worked example of a driven run.

