docs,test: 1a review round 3 --- an overclaimed first, and a vacuous row
**"The first frontend→instance extension needing a gate in both
directions" was false**, and checking it took one grep: the v19 terminal
family and the v21 panel family each have an inbound variant AND an
outbound one, each gated. "First inbound-only extension" does not hold
either --- v7's `TripleDown` and v11's `Context` are inbound-only
`PointerKind` variants.
So the superlative is gone rather than weakened. The rustdoc now states
the PROPERTY and its consequence: the gate is producer-side and
receiver-side, and for an inbound variant the receiving half is the
load-bearing one, because withholding would otherwise be the peer's job
and a client built from this same crate can encode the discriminant
whatever it negotiated. The v19/v21 precedent is named so the shape does
not read as novel; what is unusual is only that this extension has no
outbound counterpart, so the receiver check is the whole of the daemon's
half.
**`multi_scalar_text_input_creates_no_typed_provenance` proved half of
what its name claimed.** It started from a fresh editor, where the
command chain is ALREADY empty, so asserting emptiness afterwards passed
whether or not `break_command_chain` ran --- the assertion could not
fail for the reason it existed.
Split in two. The record half keeps its name and its claim; the chain
half is a new row that PRIMES the chain first and then asserts it
cleared. Priming goes through 1a's own single-scalar path, because
`pmacs.command.invoke('buffer.self-insert')` cannot prime it: rotation
belongs to the dispatcher and invoking the command directly deliberately
never rotates --- the first attempt at this row failed on exactly that
precondition, which is a better outcome than passing on a chain that was
never live.
**M-1a-2 deletes `break_command_chain` from the multi-scalar branch and
fails the new row alone**; the other six stay green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
This commit is contained in:
parent
97da79f136
commit
b2a273b1fd
|
|
@ -1904,15 +1904,22 @@ pub enum ResourceBody {
|
|||
/// `PanelPointer`, the final v23 `FrontendEvent` variant, so no
|
||||
/// existing discriminant moves.
|
||||
///
|
||||
/// **This is the first FRONTEND→INSTANCE extension to need a gate in
|
||||
/// both directions**, and the reason is that the direction of travel is
|
||||
/// reversed: for an instance→frontend variant the daemon simply
|
||||
/// withholds, but here the *frontend* must withhold below
|
||||
/// [`TEXT_INPUT_MIN_VERSION`] **and** the daemon must refuse what a peer
|
||||
/// below it nonetheless sends. A client built from this crate can encode
|
||||
/// the variant whatever it negotiated, so the producer gate alone would
|
||||
/// leave a v6–v23 session able to drive an edit through a variant its
|
||||
/// own session never declared.
|
||||
/// **The gate is producer-side AND receiver-side**, and for an inbound
|
||||
/// variant the receiving half is the load-bearing one. An
|
||||
/// instance→frontend variant is gated by the daemon simply not sending
|
||||
/// it, which is entirely within the daemon's control; an inbound variant
|
||||
/// cannot be, because the withholding would be the *peer's* job and a
|
||||
/// client built from this same crate can encode the discriminant
|
||||
/// whatever it negotiated. So the daemon refuses `TextInput` from a
|
||||
/// session below [`TEXT_INPUT_MIN_VERSION`] rather than trusting the
|
||||
/// producer to withhold — otherwise a v6–v23 session could drive an edit
|
||||
/// through a variant its own session never declared.
|
||||
///
|
||||
/// This is not a new shape: the v19 terminal and v21 panel families
|
||||
/// already gate their own inbound events on the authenticated session's
|
||||
/// negotiated version. What is unusual here is only that the extension
|
||||
/// is **inbound-only** — there is no outbound counterpart to withhold,
|
||||
/// so the receiver check is the whole of the daemon's half.
|
||||
pub const PROTOCOL_VERSION: u32 = 24;
|
||||
|
||||
/// Protocol version placed in the daemon's server-first [`Hello`].
|
||||
|
|
@ -2096,10 +2103,9 @@ pub fn negotiated_session_version(frontend_offer: u32) -> u32 {
|
|||
/// both. [`ADVERTISED_PROTOCOL_VERSION`] does not move.
|
||||
///
|
||||
/// GUI arc Stage 1a: extended to `[6, ..., 24]` for
|
||||
/// [`FrontendEvent::TextInput`]. Additive, and gated in **both**
|
||||
/// directions rather than only daemon-side — see [`PROTOCOL_VERSION`]
|
||||
/// for why an inbound frontend→instance variant needs the receiving
|
||||
/// check too. [`ADVERTISED_PROTOCOL_VERSION`] does not move: a v23
|
||||
/// [`FrontendEvent::TextInput`]. Additive, and gated producer-side AND
|
||||
/// receiver-side — see [`PROTOCOL_VERSION`] for why an inbound variant
|
||||
/// cannot rely on the producer withholding. [`ADVERTISED_PROTOCOL_VERSION`] does not move: a v23
|
||||
/// frontend negotiates v23, never sends the variant, and keeps today's
|
||||
/// first-scalar behaviour.
|
||||
pub const SUPPORTED_PROTOCOL_VERSIONS: &[u32] = &[
|
||||
|
|
|
|||
|
|
@ -127,10 +127,38 @@ fn multi_scalar_text_input_creates_no_typed_provenance() {
|
|||
no_record,
|
||||
"a multi-scalar commit must not forge a typed-edit record"
|
||||
);
|
||||
let this_command: String = eval(&s, "return pmacs.editor.this_command() or ''");
|
||||
assert_ne!(
|
||||
this_command, "buffer.self-insert",
|
||||
"a multi-scalar commit is not a self-insert"
|
||||
}
|
||||
|
||||
/// §5 — a MULTI-scalar commit **breaks the command chain**, as a paste
|
||||
/// does.
|
||||
///
|
||||
/// **The chain is PRIMED first, and that is what makes the row
|
||||
/// discriminating.** Starting from a fresh editor the chain is already
|
||||
/// empty, so an assertion that it is empty afterwards passes whether or
|
||||
/// not `break_command_chain` is called — the first version of this row
|
||||
/// did exactly that and would have survived deleting the call.
|
||||
#[test]
|
||||
fn multi_scalar_text_input_breaks_a_live_command_chain() {
|
||||
let mut s = editor_with("");
|
||||
|
||||
// Prime it with 1a's OWN single-scalar path, which rotates to
|
||||
// `buffer.self-insert`. A programmatic
|
||||
// `pmacs.command.invoke('buffer.self-insert')` cannot prime it:
|
||||
// rotation belongs to the dispatcher, and invoking the command
|
||||
// directly deliberately never rotates or arms.
|
||||
s.dispatch_text_input(FID, "x");
|
||||
let primed: String = eval(&s, "return pmacs.editor.this_command() or ''");
|
||||
assert_eq!(
|
||||
primed, "buffer.self-insert",
|
||||
"precondition: the chain is live before the commit"
|
||||
);
|
||||
|
||||
s.dispatch_text_input(FID, "e\u{301}");
|
||||
|
||||
let after: Option<String> = eval(&s, "return pmacs.editor.this_command()");
|
||||
assert_eq!(
|
||||
after, None,
|
||||
"a multi-scalar commit is not a command and must clear the chain"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue