Material states#

Every candidate material carries exactly one state, and that state is what the orchestrator claims work from. There are nine, and knowing which three are terminal explains most of what status shows you.

The nine#

State

Set by

Terminal

Meaning

NEW

seeding, or fan-out adoption

no

An element-combination seed, or a freshly adopted child, waiting for its first stage.

COMPOSED

compose

no

A charge-balanced composition exists.

STRUCTURED

structure

no

A candidate crystal structure exists.

SCREENED

screen

no

The MACE relaxation converged and the verdict was RELAXED.

CALCULATED

calculate

no

The DFTB+ WorkChain converged. Runs without the optional stage never park here.

EXTRACTED

extract

no

record.json is written.

DONE

finalize

yes

The material completed the configured funnel.

FAILED

a verdict or an exhausted budget

yes

Terminally rejected or terminally faulted; status shows the stage and the reason.

EXPANDED

fan-out settlement

yes

A parent whose children were all adopted. Not a failure.

The six non-terminal states form one chain, in funnel order:

NEW → COMPOSED → STRUCTURED → SCREENED → CALCULATED → EXTRACTED → DONE

A stage claims materials in the state before it and completes them into the state after it. Movement along the chain is strictly forward — nothing regresses, and a completion that has already been recorded is a no-op, so an interrupted run replays safely.

A skipped stage skips its state. With no calculate: in your config, screen completes a material straight from SCREENED to EXTRACTED; nothing ever parks in CALCULATED. The orchestrator derives each stage’s input state from the funnel you configured, not from a fixed position in the chain.

The three terminal states, and why they differ#

DONE is retirement, and it is deliberate. A material becomes DONE only when the run executes through the configured last stage. A run stopped mid-funnel — by pipeline.stop_after or by run --only <stage> — leaves its materials resting at that stage’s output state, live and claimable, so a later run continues them in the same workspace. This is why a stop_after: structure run leaves everything at STRUCTURED rather than retiring it: those materials still have a funnel ahead of them.

FAILED covers two different things, and the difference is visible in the attempt count:

Cause

Attempts

Example

A deterministic science verdict

1

MACE relaxed the cell and the result is unphysical; a DFTB+ WorkChain exhausted the plugin’s own healing ladders.

A fault that spent retry_budget

retry_budget

A missing input file, a crashed job, an unreachable backend.

A verdict spends no budget, because re-running reproduces it. A fault might not be reproducible, so it is retried — and when in doubt, the tool treats an outcome as a fault rather than a verdict. Both end in FAILED, and matmelt retry brings either back.

EXPANDED is a success. compose and structure are fan-out stages: one seed becomes several compositions, one composition becomes several candidate crystals. The parent has no further work of its own, so it retires as EXPANDED once all its children are adopted. Its children ride the funnel individually, each addressable by id, and run --materials <parent-id> reaches them through lineage.

Seeing where materials are#

$ matmelt status
$ matmelt status --state STRUCTURED
$ matmelt status --json

status alone gives the tally per state, the failures with their stage and reason, and any jobs in flight. --state <STATE> lists the material ids in one state — the picking list for run --materials. monitor shows the same information as a live dashboard.

Moving materials back#

Terminal is terminal: no stage claims a DONE, FAILED or EXPANDED material. One command deliberately reverses that, and only for failures:

$ matmelt retry --dry-run
$ matmelt retry --stage structure --yes

retry resets FAILED materials to the state they should resume from, derived per material from its own completion timeline — the output state of the last stage it actually completed, or NEW if it never completed one. That derivation uses no config, so it is right even if you have since changed the funnel. The reset clears the lease, the attempt count and the recorded error, and preserves the completions timeline, so the material’s history stays intact.

Nothing resets DONE or EXPANDED. A finished material is re-run by pointing a stage at it explicitly (run --only <stage> --materials <id>), which is a different operation with a different meaning.

Durability#

State lives in a single SQLite database under run.work_dir, with the write-ahead log on and every read-then-write in an immediate transaction. Claiming is atomic, so a material is never worked twice; a claim that outlives pipeline.lease_timeout_s returns to its queue.

Alongside state, an in-flight ledger records every submitted job’s durable handle — a PID or a scheduler job id — at the moment of submission. That is what makes an interrupted run resumable: a fresh process re-polls the jobs the old one started instead of launching duplicates, so work already running on a cluster is picked back up rather than repeated.