saphan doc
Document contract validation (EXEC-STATUS-V1 and successors)
saphan doc
Document contract validation (EXEC-STATUS-V1 and successors)
Subcommands
| Subcommand | What it does |
|---|---|
saphan doc check | Validate 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)
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
| Option | Type | Default | Description |
|---|---|---|---|
--actor | string | unset | the checking actor recorded on the receipt (falls back to env SAPHAN_ACTOR, then ~/.saphan/config's identity.actor) |
--as-return | bool | false | the 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 |
--family | string | auto | document family: auto|exec-status|order|review|review-status|train-order |
--record | bool | false | write a doc-check receipt after a passing check (requires --workspace; never records a malformed document) |
--require-checked | bool | false | refuse (doc-unmeasured, exit 3) when nothing was validated — implied by --record |