Docs · Understand · Concepts: the gate
Concepts: the deterministic gate.
runward's job is to let you cross a phase gate on evidence, not on assertion. The agent in your harness writes the code and fills the deliverables; runward reads what was written and returns a verdict. This page explains what that verdict checks, what it deliberately refuses to check, the exact exit codes, why runward check --strict is the single authority, and why rerunning it gives you the same answer every time. The gate is deterministic and zero-LLM: no model sits in the decision path and nothing reaches the network. (The LLM lives in your harness, where the code gets written; it is absent from the gate.) The gate reads files as bytes and compares them; it never executes your code and never asks a model for an opinion.
runward frames what your AI agent builds: the agent writes the code, a deterministic gate verifies it on evidence, and once green the evidence is sealed.
The two commands
runward check— the gap analysis: for every deliverable expected across the mission's build phases, is it filled, started (placeholders remain), still a raw template, or missing. Exit 1 if anything is not filled. This is the presence check.runward check --strict— the conformance gate: everythingcheckdoes, plus it verifies each gated phase's "Rule conformance" manifest and the evidence behind it. The flag is documented as "also verify the rule-conformance manifests: rows, typed pointers, signatures, drift, seal (deterministic)".
runward status is the read-only snapshot ("where am I"): current gate, phase progress, decision journal, activity, workflows. It changes nothing.
What --strict verifies
The strict gate is deliberately narrow. For every CRITICAL/HIGH craft rule mapped to a gated phase, it verifies the rule is accounted for in that deliverable's ## Rule conformance markdown table.
The three ways a rule is accounted for
A rule is accounted for one of three ways:
- applied — with an evidence pointer in the Evidence column. An
appliedrow with no pointer fails. - deviated — with a matching ADR that actually exists in
runward/adr/. Adeviatedrow whose ADR is absent fails. - n/a — with a real one-line reason. A reason under 8 characters, or a bracketed template token like
[why], counts as trivial and fails.
Any status outside applied | deviated | n/a fails, and an empty status ("a scaffolded row is not a decision") fails with its own message.
Verification layers on top of manifest well-formedness
On top of manifest well-formedness, --strict runs these layers, whose violations all join the same verdict:
- Non-vacuity floor — the mapping cannot be silently stripped to make the gate pass on an empty set. Each phase has a pinned minimum of mapped CRITICAL/HIGH rules (
architect: 6, topology: 4, floor: 10, govern: 12, handover: 4); dropping below it raises a(mapping)violation. - Unknown / duplicate rows — a manifest rule not in
runward/rules/fails (with a rename/removal hint when known), and a rule listed twice fails. - Evidence resolution (typed pointers) — pointers of the form
file:PATH[:LINE][#SYMBOL],test:PATH[::NAME],adr:NNNNmust resolve to real, non-empty, regular files inside the project (an absolute path or a../escaping every base is rejected, not resolved), the file must have at least:LINElines, and a named#SYMBOL/ test name must actually appear in the content. - Signatures — a rule can declare a
signature:regex; theappliedevidence's pointed content must match it, which is how "cited but not applied" is caught deterministically. Dangerous nested-quantifier regexes are screened out up front to avoid a self-inflicted CI hang. - Drift — an
appliedprose pointer whose file path no longer resolves fails the gate (existence only; typed pointers are diagnosed by the evidence layer instead, so one row is never diagnosed twice). - Evidence seal (
--freeze, opt-in) — hashes every resolvable evidence file intorunward/evidence-lock.json(SHA-256). On later--strictruns, a sealed file that changed or disappeared fails until you re-verify and re-seal; forged lock paths that escape the project are rejected. - Reconstruction lifecycle — a retroactively reconstructed ADR is a hypothesis until ratified. ADRs still marked
DRAFT-,Status: hypothesis, orwhy: UNKNOWNfail the gate ("an agent's guess must not pass as a decision").
The five gated pairs
The five gated (phase → deliverable) pairs are:
architect → architecture.mdtopology → execution-topology.mdfloor → floor.mdgovern → governance/threat-model.mdhandover → handover.md
What it explicitly does NOT do
This is the load-bearing boundary. The gate proves a decision was traced and points at something real — never that the code is good.
- It does not judge code quality or whether the pointer truly implements the rule. That judgment "stays the operator's, at the gate".
- It never runs your code, tests, or build. The evidence layer "reads files; it never executes anything and never calls a model". The gate is "not a runtime".
- It never calls a model or the network. Zero-LLM, zero-network by construction.
- Behavioral proof and the semantic verify pass are advisory, above the gate. When a strict gate is green, runward reports whether your test-suite command and verify-workflow findings are present and fresh, but "advisory, agent-executed, never blocks the gate".
- Documentation/decision coverage (
--coverage) is advisory and "does not affect the verdict".
Two proofs, kept separate on purpose: this gate is the documentary proof (the decisions are traced); the behavioral proof is your test suite, which runward points at but never runs.
Exit codes (exact)
From the code, check uses three exit codes:
- 0 — current gate clean. No unfilled deliverables, no strict gaps, no failed hooks (
clean = gaps === 0 && strictGaps === 0 && hookFailed === 0). Process exits 0 by default. - 1 — gaps. At least one deliverable unfilled, or (under
--strict) at least one conformance/evidence/seal/reconstruction violation, or a failed opt-in hook. Set viaprocess.exitCode = 1. The summary names the counts: "N deliverable(s) not filled · N floor rule-conformance gap(s) · N hook(s) failed". - 2 — no mission found. No
runward/here or above; the command tells you to runrunward initfirst (process.exit(2)).statususes the same code 2 when no mission is found.
This matches the CLI-wide contract: 0 = success · 1 = gaps/warnings · 2 = missing prerequisite or CLI misuse. In --json mode the same verdict is emitted as a stable object with an exitCode field (0 clean / 1 gaps / 2 no-mission) and a verdict string, but the exit code stays the primary signal.
For an agent driving runward: gate on the exit code (or the JSON exitCode); do not scrape the human text.
What "the authority is check --strict" means
runward ships skills and workflows that help an agent apply the right rule at the point of action, and a semantic verify workflow that adversarially re-checks cite-vs-apply. None of these is the judge.
check --strict is "the sole authority and verifies the manifest deterministically. A rule surfaced here but not accounted for still fails the gate".
The design decision is explicit: "The gate stays the sole authority. A skill only helps the agent apply a rule; it never replaces the manifest verification. check --strict remains the deterministic judge... A skill without the gate is just advice" (ADR-0018, decision point 2).
The alternative — an LLM-judged, soft framing where loading a skill counts as compliance — is exactly what runward refuses, because a probabilistic judge can be talked out of its verdict.
In practice
The only thing that lets a phase close is check --strict returning 0. Advisory outputs (behavioral proof, verify findings, coverage) inform the human crossing the gate; they never gate it.
Determinism (rerun → same verdict)
The verdict is a pure function of the bytes on disk. In the decision path, the gate:
- reads rule files, manifests, and pointed evidence;
- sorts its inputs deterministically (e.g.
expectedRules(...).sort(), and the evidence lock writes keys in sorted order); - compiles signatures to a fixed
RegExp; - compares — no clock, no randomness, no model, no network in the decision path.
Because nothing is executed and nothing is sampled, running it twice on the same tree yields the identical verdict — the README states it as "rerun it and get the same verdict" and "reruns byte-for-byte".
This is also why it "cannot be jailbroken by injection": there is no model in the gate path to jail-break.
A note on writes (the gate is read-mostly, and never touches .git/)
Plain check and check --strict only read. Two behaviors write, both to the mission directory and both opt-in, never to your repository's .git/:
--freezewritesrunward/evidence-lock.json(and refuses to seal a red gate).--hooksruns operator-authored commands configured inrunward/hooks.json.
runward does not install git hooks or wire the gate into CI for you; adapters are inert samples you wire on your own say-so. An agent can drive runward end to end, including performing that wiring when you tell it to — but runward itself never silently modifies your repo.
Quick reference for an operating agent
- Cross a gate:
runward check --strict→ require exit 0. - Machine-readable:
runward check --strict --json→ parse.verdict(clean/gaps) and.exitCode; theconformancearray lists{scope, rule, problem}for each gap. - Seal a green gate for later erosion detection:
runward check --freeze(implies--strict, writesrunward/evidence-lock.json). - Never present a loaded skill, a passing verify workflow, or a fresh test artifact as "the gate passed" — only exit 0 from
check --strictis that.
Why this choice
The central design choice is keeping the gate deterministic and LLM-free rather than letting a model judge whether code satisfies a rule.
The rejected alternative is the "soft" framing every delivery competitor uses: an LLM reads the code and decides if it complies, or loading a skill counts as compliance. runward rejects that because a probabilistic judge can be jailbroken by prompt injection and does not rerun identically, so its verdict is not something an auditor or a CI pipeline can rely on (ADR-0018 section 2).
The cost of the choice is honesty about scope: the gate can only prove a decision was traced and points at real content, not that the code is correct — so runward keeps the behavioral proof (your tests) and the semantic verify pass explicitly advisory and above the gate.
The floors in constants.ts and the non-vacuity check exist to close the obvious loophole of a deterministic gate: stripping the rule mapping so the gate passes on nothing.
The design itself is older than AI. Every time software absorbed an unstable component, the industry converged on the same answer: a stable contract in front, the unstable part behind — information hiding (Parnas, 1972), then ports and adapters (Cockburn, 2005), retold as onion and Clean Architecture. runward applies that fifty-year-old lesson to the most unstable component software has ever absorbed: a model that is non-deterministic by design. The model proposes behind a port; deterministic code decides. The same lineage shapes the method's own architecture default — a modular core behind ports and adapters — and it stays a default: the gate checks the decision was confronted and traced, never the style.