Saphan StudioDocs
Reference/Command reference

saphan doc

Document contract validation (EXEC-STATUS-V1 and successors)

saphan doc

Document contract validation (EXEC-STATUS-V1 and successors)

saphan doc

Subcommands

SubcommandWhat it does
saphan doc checkValidate one record document against its family's rule table (order, exec-status, review or train-order)

saphan doc check

Validate one record document against its family's rule table (order, exec-status, review or train-order)

saphan doc check <file> [flags]

Validate one record document against its family's contract.

All THREE channel families have a rule table: _MASTER_ORDER.md (order), _EXEC_STATUS.md (exec-status) and _REVIEW_STATUS.md (review). Every rule is projected from internal/domain/channelspec.go's one key table, so a check and the parser that reads the same document cannot disagree.

A FOURTH family is not a channel document at all: TRAIN_ORDER.md (train-order), the document that carries a train and its wagons. Its grammar is projected the same way, from its own key table in internal/domain/trainorder.go, and it is judged by the same core — but its key table has been MEASURED AGAINST THE LIVE CORPUS AND MATCHES NOTHING IN IT: a train order carries no top-level - key: bullets at all, so that half of the grammar is deliberately vestigial and an unrecognised bullet there is a WARNING rather than a refusal. Its checker prints that measurement in the report's note. What it DOES refuse is a missing H1 and a train order that names no wagon.

--family auto is a QUESTION ("what is this, and is it well-formed?") and never refuses on an unrecognized H1 — exit 0, family "none", checked:false. An explicit --family is a CLAIM by the caller: --family exec-status on a file with no # STATUS — <slug> H1 is doc-h1-missing, exit 2. Same file, two exit codes, because two different questions were asked (STOP1_PLAN §1 D4).

--record (doc-contracts-w1b) writes a receipt after a PASSING check — fleet_event, source=doc-check, payload_digest=sha256(the document's bytes), keyed on the stream slug parsed from the document's own H1 (D2: the key is the bytes and the slug, never the path — a worktree rename changes nothing). Requires --workspace. Never fires on a malformed document (checked:false), and never on --stdin input (no stream to record against).

--require-checked is the caller saying "I am going to ACT on this answer": it turns checked:false into the named refusal doc-unmeasured, exit 3, so a shell script can tell "checked and clean" from "did not look" without parsing JSON. Without it, a bare question stays exit 0 (D4). --record implies it — asking for a receipt is asking for a measurement.

The TEXT surface distinguishes the two unconditionally: a document that was not checked prints UNMEASURED and never the word ok.

Exit codes: 0 = valid (or family "none") · 1 = bad invocation (unreadable path, unknown --family literal) · 2 = MEASURED AND FAILED (malformed, nothing recorded) · 3 = NOT MEASURED, under --require-checked / --record. 2 and 3 are deliberately different facts: 2 says fix the document, 3 says nothing read it.

Options

OptionTypeDefaultDescription
--actorstringunsetthe checking actor recorded on the receipt (falls back to env SAPHAN_ACTOR, then ~/.saphan/config's identity.actor)
--as-returnboolfalsethe caller CLAIMS this document is a leg's RETURN: refuse (doc-skeleton-not-a-return, exit 2) a document no leg ever wrote — the engine's untouched skeleton, or its death stamp
--familystringautodocument family: auto|exec-status|order|review|review-status|train-order
--recordboolfalsewrite a doc-check receipt after a passing check (requires --workspace; never records a malformed document)
--require-checkedboolfalserefuse (doc-unmeasured, exit 3) when nothing was validated — implied by --record

On this page