Expand description
The command path: one CommandEnvelope in, one committed event plus its
projection out.
The order of the steps is the design, not an implementation detail:
- Lock the writer row — before ANY read this decision depends on. The lock is held to commit, so reading the current version after taking it is what makes the decision and the write the same instant.
- Answer a replay from the log — project-wide, by key. Doing this ahead
of the CAS is what makes a retry stable: a retry presents the same
expected_versionit did the first time, which the CAS would now call a conflict. - Decide —
gwk_domain::transition::applyfor the two state machines in this phase, a plain version compare for the rest. - Append and project in ONE transaction. The plan requires events and projections to land together; splitting them would let a crash leave a log the projections do not reflect.
Before any of it sits the epoch (crate::epoch): a log with only its
genesis event is SEALED and admits activate_kernel alone, and a log
without even that admits nothing. The check is read under the writer lock
taken in step 1, and activation’s own CAS re-proves it at append time.
Between ownership and the decision sits authority: a gated command is evaluated against the grants on record, and a page commits its receipt and attention item before refusing. That is the one refusal here that leaves rows behind, and deliberately so — a page whose evidence rolls back cannot be told apart from a command nobody sent.
Scope is every command in the contract: task, attempt, engine session,
lease, worktree, dispatch node, budget, checkpoint, round, finding, message,
execution command, gate, evidence, authority grant, attention item, and
ingestion. [route_of] matches them all WITHOUT a wildcard arm, so a
command added to the contract fails to compile here rather than falling into
a silent no-op.