runward

RW™ · V0.22.0

Docs · Decisions · ADR-0033

ADR-0033.

`runward status` reports a mission's real lifecycle position — its state and which decisions to reopen

Date: 2026-07-20 Status: proposed Deciders: Thibault Souris (maintainer) Method: decision-loop — a multi-agent audit of the resume-existing / brownfield capability found that status, the transmission surface a returning operator reads first, misreports a mission that has passed handover. Reality-checked against the real code (src/lib/mission.ts, src/commands/status.ts) and reproduced on runward's own dogfooding mission; sourced against the build/run (ITIL/SRE) steady-state distinction and runward's own doctrine (iterate as a continuous phase, the mandatory dated reopening trigger every ADR carries).

Context

runward status is the read-only "where the mission stands" a receiving team, or a returning operator (brownfield mode M1 — resume your own mission), reads first. On a mission that has passed all gates and entered continuous improvement, that first screen lies, and it lies in the exact situation resume-existing exists to serve: coming back to a delivered, still-living mission.

Observed in the real code, not imagined:

  1. The mission is a one-way arc that terminates at handover. analyze() (src/lib/mission.ts:147) sets currentPhase to the first incomplete phase across a fixed linear list frame → architect → floor → govern → handover; status.ts:31 stamps ← you are here on it. There is no state for "all gates passed, now iterating." iterate — a first-class workflow (paths.ts:37, method.md phase 4: "add complexity only on an objective trigger … one ADR per switch") and the steady-state where every delivered system lives — appears nowhere in PHASES.

  2. Reproduced on runward's own mission (node dist/cli.js status): Current gate → all gates passed, Next → Run runward check --strict … then runward compliance <regime>. runward is in an active iterate loop (32 product ADRs, roadmap grooming, continuous commits), but status freezes it at the moment handover completed and points forward to a gate already crossed. It says nothing about the real open question a returning operator has: which locked decision should reopen, and on what signal.

  3. The reopening signal is already a firm, parsable convention, and nothing reads it. Every ADR carries a mandatory ## Reevaluation trigger section and a **Trigger set on**: YYYY-MM-DD line (the ADR-0000 template mandates it; 33/33 shipped ADRs conform). This is the encoded answer to "what would make me reopen this decision" — the mission's real backlog once it is in steady-state. status never parses it, so "which decision aged / should reopen" is a manual grep of the whole corpus.

  4. characterize and status do not route between M-modes. characterize (raw code, no runward/) and status (a governed mission exists) are the two entry points, but neither points at the other: a returning operator on a governed repo gets no nudge toward status (M1), and characterize's next-steps never mention it.

Hard constraints: status is the transmission surface, so it stays read-only, deterministic, zero-LLM (the contract that governs characterize, ADR-0014) and it never moves a gate — the operator owns the gate (ADR-0001). The decision is taken at the mission-reading boundary (src/lib/mission.ts), consumed by the presentation boundary (src/commands/status.ts).

Decision

runward status reports a mission's real lifecycle position: its state (ÉTAT), and which locked decisions are due to reopen (À ROUVRIR) — as a faithful, deterministic parse of the mission files, never a one-way arc that ends at handover.

Three moves, all pure parsing of formats already normed, read-only, zero-LLM:

  1. A named steady-state (ÉTAT). analyze() already computes currentIndex === -1 (every gated deliverable filled). status stops rendering this as a finish line and names it the iterate steady-state: the gated arc is complete; the mission now advances by locking one ADR per structural switch, on an objective reevaluation trigger. Next stops presenting check --strict + compliance as the forward milestone (that arc is crossed and stays re-runnable, not pending) and instead names the iterate posture, with the evidence gate named as available any time. The ← you are here marker never claims unfinished linear progress on a mission whose gates are all filled.

  2. Which decisions to reopen (À ROUVRIR). status parses the accepted ADRs' ## Reevaluation trigger section and **Trigger set on** date and surfaces a compact "reopening watch": each decision, the date its trigger was set, and the (verbatim, untruncated-enough) trigger text. It presents; it never decides a trigger has fired — the operator judges. Deterministic (sorted, byte-stable), read-only, no model call. This is the block that turns "reread 84 ADRs" into a glance, and it is the real backlog of a steady-state mission.

  3. M-mode routing (aiguillage). When characterize runs on a repo that already has a governed mission (runward/framing.md exists), it prints that this is M1 and to run runward status. Symmetrically, status's "no mission found" error names characterize for a raw codebase. A fact (existsSync) picks the mode, never instinct — exactly the brownfield rule "re-enter at the right phase, never by instinct."

Explicitly out of scope, named so they are not silently dropped:

  • Reading a JOURNAL.md for a free-text Next: / Blocked backlog. JOURNAL.md is not a runward primitive (not in MISSION_LAYOUT, not template-mandated — the "journal" in the doctrine is the decision journal, i.e. the ADR dir). Surfacing it would mean inventing a convention; the faithful, already-normed backlog of a governed mission is À ROUVRIR above. If a durable journal convention is later adopted, that is its own decision.
  • The partially-filled-but-shipped case (a downstream gate the fill-heuristic marks incomplete though v1 shipped) — needs a deterministic "delivered" signal; its own decision.
  • A separate resume command. For now the enriched status is the M1 gesture; extracting a resume verb that shares a mission-reading primitive is deferred (see trigger).

Alternatives discarded

  • Add iterate as a sixth gated phase in PHASES. Rejected: iterate has no deliverable that is "filled once and done" — it is a recurring posture (one ADR per switch, indefinitely). As a gate it would either never complete (permanent red on every mature mission) or complete falsely. The steady-state is a mode entered after the arc, not a rung on it.
  • Infer "delivered/reopened" from file mtimes. Rejected: mtimes are noisy and non-reproducible across clones — a determinism smell. currentIndex === -1 is exact and already computed; À ROUVRIR reads ADR content (dated trigger lines), not timestamps.
  • Let an LLM summarise the real position. Rejected: status is deterministic and zero-LLM by contract (ADR-0014, ADR-0001). The position must be a reproducible parse of the files, never a generated narrative.
  • Rank / flag which trigger has "probably fired". Rejected as an overclaim: judging a trigger reached is interpretation, which would put status on the operator-owns-the-gate line. status surfaces the triggers verbatim; the operator decides. (Same discipline the audit imposed on borrowing Aider's repo-map: expose facts, never a ranking.)
  • Leave status as is and document the caveat. Rejected: the transmission surface lying to a returning operator is the precise failure resume-existing exists to prevent. An accepted framework whose own status misreports its own mission is the doc↔code drift runward's doctrine condemns.

Consequences

  • Positive. The first transmission screen tells the truth on the most common resume case. Next names the iterate posture instead of an already-crossed gate. À ROUVRIR makes "which decision should reopen" a glance instead of a corpus grep — directly serving M1. The characterize/status aiguillage removes the "which mode am I in" guess. All three respect read-only / deterministic / zero-LLM / operator-owns-the-gate by construction.
  • Negative, accepted. status gains conditional prose (steady-state vs in-delivery) and a new section (À ROUVRIR), a marginal rise in surface. The reopening watch lists triggers but never judges them fired — an honest under-statement, never an overclaim. On a mission with many ADRs the watch is long; it is sorted and can be capped with an explicit "+N more" (never silently truncated).
  • On other boundaries. No change to the gate (check), to compliance, to determinism, or to the zero-LLM contract — status stays a read-only projection. analyze() grows an explicit steady-state flag and (optionally) a parsed-triggers list; any future consumer (e.g. a resume command) reads the same signal, so they never diverge. No ADR superseded; ADR-0013 (transmission) and ADR-0014 (the read-only/deterministic contract) are reinforced, not touched.

Reevaluation trigger (mandatory, dated)

Reopen if (a) a resume command is introduced and the steady-state + À ROUVRIR signals must move from status-presentation into a shared mission-reading primitive both commands consume; (b) a durable JOURNAL.md/backlog convention is adopted, making the free-text RESTE volet groundable rather than invented; or (c) real missions show currentIndex === -1 is too coarse a steady-state signal — e.g. a mission legitimately sits at all-gates-passed while still in first delivery, making the "iterating" label wrong.

Trigger set on: 2026-07-20 · Watched via: the resume-existing audit follow-ups (a resume command, the delivered-signal decision) and dogfooding runward status on runward and Dropyour.

References

  • ADR-0001 — operator-owns-the-gate / the deterministic gate status stays read-only against.
  • ADR-0013 — the transmission phase; status is its live counterpart.
  • ADR-0014 — the read-only / deterministic / zero-LLM command contract this mirrors.
  • src/lib/mission.ts (analyze, PHASES), src/commands/status.ts — the mission-reading and presentation boundaries edited.
  • templates/workflows/method.md (phase 4 iterate), templates/workflows/brownfield.md (M1), runward/adr/ADR-0000-template.md (the mandatory dated reopening trigger À ROUVRIR parses) — the doctrine this brings status into line with.
  • The resume-existing / brownfield audit (2026-07-20): the multi-agent investigation that surfaced the finding.
← All ADRs