docs: the rule widened and its public statement did not follow

`ClientFrame::Commands` — the doc a third transport reads first — still said
"at most one `Command::Model`" after the rule became "at most one document
mutation", which understates it by exactly the case the previous commit was
fixing, and would have led a new client straight into it.

Found by grepping the rule's own wording rather than by a test, which is
gotcha 48's third paragraph for the third time on this branch: when a fact is
stated in more than one place, changing it is a grep, not an edit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Edward Langley
2026-09-19 23:24:54 -07:00
parent 436faf8d1c
commit cded59acb7
2 changed files with 14 additions and 7 deletions
+1 -1
View File
@@ -751,7 +751,7 @@ mod tests {
/// been refused with `RecordGone`.
///
/// So the rule is the count, not a list of dangerous variants: **a frame
/// may carry at most one `Command::Model`**. The first fix refused only
/// may carry at most one document mutation**. The first fix refused only
/// the document-*replacing* case, which is the same bug seen through one
/// variant — `[LoadModel, TransposeAxes]` — instead of through the rule.
/// **Three** shapes are driven here, because a guard written for the one
+13 -6
View File
@@ -86,12 +86,19 @@ pub enum ClientFrame {
/// Every command is answered against the document the *frame* was
/// composed against, which is the only claim a client can construct — a
/// basis is published between batches and never during one. And
/// therefore **a frame may carry at most one [`Command::Model`]**: two
/// mutations could not both be honestly based, and the second would be
/// applied against a document its own sibling had already changed.
/// A frame that carries two is refused with
/// [`Rejection::FrameNotAtomic`]. View commands are not mutations, so
/// the batch a resolved keypress produces is unaffected.
/// therefore **a frame may carry at most one document mutation** — a
/// [`Command::Model`] or a [`Command::SwitchView`]. Two could not both be
/// honestly based, and the second would be applied against a document its
/// own sibling had already changed. A frame that carries two is refused
/// with [`Rejection::FrameNotAtomic`].
///
/// `SwitchView` counts although it carries no basis of its own, because
/// every model command except the two that name a record by surrogate
/// acts on the **active view**: `[SwitchView("Second"), TransposeAxes]`
/// would transpose `Second` rather than the view the transpose was
/// composed against. View commands are not mutations, so the batch a
/// locally-resolved keypress produces — a selection, its scroll offsets
/// and at most one edit — is unaffected.
Commands(Vec<Command>),
/// Write the document back to wherever this host keeps it
/// (improvise-368h).