| name | machina-authoring |
| description | Author valid, high-scoring state machines in Machina machine JSON format (spec v3.0.0 / v2.0.0 / v1.0.0). USE WHEN: writing or generating a machine definition (states, transitions, guards, actions, context, scenarios); modeling a real workflow (order fulfillment, refunds, signup, retries) as a Machina state machine; fixing or upgrading a machine JSON for validation or higher compliance score; explaining validation failures or low scores; running the bundled machina-validator.py CLI to validate, score, or generate gaps/scenarios; adding retry guards or cycle protection; or preparing machines for the compliance scorer ("Excellent" ≥90). DO NOT USE FOR: modifying the Machina simulator app, its engine, UI, or SPEC_REGISTRY (use
machina-simulator-maintenance), debugging machine-validator.py scripts, XState config authoring,
SCXML documents, or general diagramming. |
| metadata | {"version":"0.3.0"} |
Machina Machine Authoring
Guidance for writing state-machine definitions that open cleanly in the Machina simulator and
score well against its compliance scorer.
Glossary
Use these terms consistently — in prompts, output, and code comments:
| Term | Meaning |
|---|
| Machina | Brand name covering both the simulator app (served by the machina-simulator extension) and its machine schema spec. Qualify which: "Machina simulator" (the app) vs "Machina schema spec" (the JSON contract). Never use bare "Machina" where the referent is ambiguous. |
| State machine | The modeled FSM itself. Always write "state machine", never bare "machine". |
| Machine definition | The JSON document that encodes a state machine (the artifact you author). A file contains one definition. |
| Schema spec / spec version | The versioned field contract (v1.0.0, v2.0.0, v3.0.0) a definition targets via spec_version. Distinct from the definition's own version field. |
| Final state | A state typed "type": "final" (or with no outgoing transitions). Prefer "final state" over "terminal" — matches UML/XState. |
| Event | Named trigger (UPPER_SNAKE) that fires a transition from a state's on map. |
| Transition | { EVENT: { target, guard?, actions? } } — moves between states. |
| Guard | Declarative predicate { type:"compare", key, op, value } gating a transition. Matches SCXML/XState semantics. |
| Action | Declarative side effect { type:"increment"|"assign" } on context. No code strings, ever. |
| Context | Extended state data available to guards/actions; supports dotted paths. |
| Scenario / entry point | A named start into the state machine (scenarios[] with initial, interface ∈ UI·API). |
| Compliance scorer | The deterministic 23-check evaluator producing score/grade/gaps (in-app or via machine-validator.py). 22 checks are weighted (total weight 100 at v2, 119 at v3); tools-exist is a weight-0 informational review check. Not "checker", "linter", or "validator" (validation is only its blocking subset). |
| Gap | A failing check finding: auto (deterministically fillable) or review (needs human judgment). |
Minimal viable machine
Every state machine definition needs at minimum: id, non-empty states, initial resolving
to a state key, and every transition target pointing at an existing state key.
{
"id": "order-fulfillment",
"name": "Order Fulfillment",
"version": "1.0.0",
"spec_version": "3.0.0",
"initial": "pending",
"context": { "attempts": 0 },
"scenarios": [
{ "id": "default", "label": "Default", "initial": "pending", "interface": "API" }
],
"states": {
"pending": {
"description": "Awaiting payment confirmation.",
"on": {
"PAY": { "target": "paid", "actions": [{ "type": "assign", "key": "attempts", "value": 0 }] },
"RETRY_PAY": { "target": "pending", "guard": { "type": "compare", "key": "attempts", "op": "lt", "value": 3 }, "actions": [{ "type": "increment", "key": "attempts" }] }
}
},
"paid": { "description": "Payment confirmed.", "on": { "SHIP": { "target": "shipped" } } },
"shipped": { "type": "final", "description": "Order delivered to carrier." }
}
}
Authoring workflow
- Read references/schema-spec.md — full field reference, naming
conventions, guard/action semantics.
- Model states first: identify every distinct status, mark true final states
"type": "final"
explicitly (never rely on implicit finals).
- Wire events with declarative objects only — guards
{type:"compare",…}, actions
{type:"increment"/"assign",…}. Never embed code strings; the format must stay shareable and
safe to ingest.
- Add
scenarios[] entry points ({id, label, initial, interface ∈ "UI"|"API"}) — one per
meaningful way the workflow starts.
- If any path can loop (retry, rework), add a genuine counter in
context
(retry/attempt naming) and gate the looping transition with a compare lt guard against
it — this is the only pattern the compliance scorer recognizes as cycle protection.
- Give every state a real, human
description — placeholder text is auto-detectable
(generated: true) and reads as a gap.
- Validate & score — use the bundled deterministic engine (see below) or open in Machina:
validate → iterate → target ≥90 ("Excellent") via score --text.
- Gaps flagged
auto can be applied deterministically with the script's apply
(or "Generate missing" in-app).
- Gaps flagged
review need your judgment: missing transitions, convention renames, event/state
naming, unreachable states. Fix these by hand — see references/machine-quality.md
for what each check demands.
Hard rules
- Declare
"spec_version": "3.0.0" explicitly so scoring never assumes latest silently.
- Event names
UPPER_SNAKE; state keys kebab-case.
- Guard
value may be a literal number/string or a context-key name (resolved then numeric-coerced).
- Context paths support dotted notation (
"payment.attempts").
- Terminal =
type:"final" or no outgoing transitions — prefer an explicit final state.
Deterministic tooling — use the bundled script
All deterministic authoring logic from the Machina simulator (validation, the 23-check
compliance scorer, gap analysis, autofill patching, scenario generation, cycle detection,
coverage building) is bundled as a standalone CLI. Run it instead of re-deriving logic or
loading simulator source:
# From workspace root; python3 on Linux/WSL
python3 skills/machina-authoring/scripts/machine-validator.py <command> <machine.json> [options]
| Command | Purpose |
|---|
validate <file> | Hard structural errors (blocking) — run first, always |
score <file> [--text] [--spec V] | Full compliance report; JSON by default, --text for summary |
gaps <file> | Ordered list of deterministic auto-fillable patches |
apply <file> id… [-o out.json] | Apply selected patches (fixed order); default overwrites input |
scenarios <file> | DFS-generated terminal paths + transition coverage % |
cycles <file> | Cycle findings (CRITICAL depth / HIGH unguarded / MEDIUM valid) |
coverage <file> | Exact coverage block "Generate missing" would embed |
Typical authoring loop: validate → iterate → score --text until ≥90 → gaps for remaining
auto-fillable items → apply (or hand-fix review items) → final score.
Known divergence (deliberate): the simulator source's check-inclusion filter
(specRank(since) <= specRank(target) over newest-first ranks) inverts v1/v2 inclusion versus
§14's documented model. The ported script implements the documented semantics (all 17 checks at
v2.0.0, weight = 100). When editing the simulator itself, follow
the machina-simulator extension's canonical maintenance docs
and keep this divergence in mind.
Compliance boundary — what the scorer does and does not verify
The scorer analyzes the machine declaration only; it never executes anything:
| What it verifies | What it does NOT verify |
|---|
Schema structure, internal consistency, reference resolution (targets, tools, else_target) | That any declared tool's runtime behavior actually holds |
tools[] registrations are well-formed and referenced correctly | That a checks[]/requires[]/ensures[] predicate will pass when run |
tools-exist — each tool cmd's machine-relative path resolves to a file on disk (weight-0 review check; static file-stat, no execution) | That a present script is correct, safe, or even runnable |
Consequences to teach authors and consumers alike:
- "Score 100 / Excellent" means declaration-sound, not runtime-sound. A machine can score
100 while a tool's script fails in practice — the scorer never runs it.
- The scorer never executes checker scripts. Only the driver actually runs them; see the
companion
machina-driving skill's "Trust boundary" for where runtime verification happens.
tools-exist is informational (weight 0): a dangling cmd reports a warn/review gap
without lowering the score, because the scorer has no execution context. When the machine file
is scored from disk (score <file>), its machine-relative paths are stat'd; in in-memory or
workspace-copied contexts with no resolvable directory the check passes trivially.
validate + --blocking findings are the soundness gate; the semantic checks above are quality
guidance.
Reference map (load on demand)
Naming discipline in generated output
When authoring definitions or writing about them: say "state machine" or "machine definition"
(never bare "machine"), qualify "Machina simulator" vs "Machina schema spec", and use "final
state", "compliance scorer", and "gap (auto/review)" per the glossary. Field-level vocabulary
(guard, action, event, transition, context, scenario) is already industry-standard —
keep it verbatim.