Keep the v21 panel wire dark for v20 clients
Reserve the additive v21 panel schema without advertising it in the server-first production handshake. Pin a real shipped-v20 client attach, make the two aggregate-budget ratchets exactly one byte over, and update the framing, coherence audit, handoff, and volatile lane record.
This commit is contained in:
parent
f82d91ed3a
commit
ab7c207904
10
COHERENCE.md
10
COHERENCE.md
|
|
@ -109,7 +109,7 @@ remain open to them.
|
|||
| 13 | Package lifecycle UX | **Resolution without lifecycle** | Mature resolver/lockfile; init-only install; no uninstall/disable/search |
|
||||
| 14 | Workbench primitives | **Partial (best trajectory)** | Listview is a real shared primitive; bottom panel landed (#155) |
|
||||
| 15 | Contextual affordances | **Weak** | Right-click menu only; code actions apply first-blindly; no git integration at all |
|
||||
| 16 | Semantic frontend | **Strong** | v6..=v20 negotiated protocol; degradation practiced; TUI/GPU share the model |
|
||||
| 16 | Semantic frontend | **Strong** | v6..=v21 schema support; production attach remains v20 during the dark panel slice; degradation practiced |
|
||||
| 17 | Distribution | **Missing** | CI is test-only; no binaries, channels, checksums, or update path |
|
||||
| 18 | Onboarding | **Missing** | No welcome, no tutorial; `C-h` deletes a word; `M-x` is the only door in |
|
||||
| 19 | Coherence acceptance tests | **Started** | `tests/journey_acceptance.rs` exists (steps 2, 3, 5); the other five scenarios are still unwritten |
|
||||
|
|
@ -1340,9 +1340,13 @@ facto privileged implementation.
|
|||
**Grade: strong — the healthiest concern in this document, and most of
|
||||
its asks are already practiced.**
|
||||
|
||||
- Versioned, negotiated protocol `SUPPORTED=[6..=20]` with deliberate
|
||||
- Versioned protocol schema `SUPPORTED=[6..=21]` with deliberate
|
||||
encoding-breaking bumps, both-frontends support required per bump,
|
||||
and byte-pin discipline for appended variants (handoff §4).
|
||||
and byte-pin discipline for appended variants (handoff §4). The v21
|
||||
bottom-panel family is reserved but dark in Stage 2B-1: because
|
||||
`Hello` is server-first, the production daemon still advertises v20
|
||||
so shipped v20 clients remain attachable; compatible v21 activation
|
||||
belongs to Stage 2B-3.
|
||||
- Two genuine frontends share the conceptual model; CRDT concurrent
|
||||
editing with presence across them; remote attach + reconnect.
|
||||
- **Graceful per-frontend degradation is practiced, not aspirational**:
|
||||
|
|
|
|||
|
|
@ -305,13 +305,15 @@ If it does not, stop and repair the remote/fetch configuration.
|
|||
never been enforced. Any CI job that compiles the `crdt` targets has to
|
||||
fix them first or it will be red on arrival.
|
||||
|
||||
## Bottom-panel lane (Arc 7) — 2B-1 GATED; PR #184 OPEN FOR REVIEW
|
||||
## Bottom-panel lane (Arc 7) — 2B-1 REVIEW FIX IN PROGRESS; PR #184 OPEN
|
||||
|
||||
Stage 1, the Stage 2 framing, and Stage 2A are on `main`. Framing
|
||||
revision 5's three-way split of 2B was explicitly approved on
|
||||
2026-07-27. **Stage 2B-1 is implemented, integrated with canonical
|
||||
`main` @ `7fd646d`, and fully gated at `c8895a8`; it has no remaining
|
||||
dependency. PR #184 is open and must not merge before user review.**
|
||||
2026-07-27; revision 6 records PR #184's review correction. **Stage
|
||||
2B-1 is implemented and integrated with canonical `main` @ `7fd646d`.
|
||||
The previous head was fully gated at `c8895a8`, but review round 2 found
|
||||
four issues and the corrected head must run the full gate again. PR
|
||||
#184 is open and must not merge before user review.**
|
||||
|
||||
- **Stage 2B-1 branch:** `bottom-panel-stage2b`, based on
|
||||
`githubsucks/main` @ `7fd646d` by merge because review had begun.
|
||||
|
|
@ -320,14 +322,27 @@ dependency. PR #184 is open and must not merge before user review.**
|
|||
and gate checkpoint is committed and pushed; nothing depends on a
|
||||
worktree or `/tmp`. PR #184:
|
||||
<https://github.com/levineuwirth/pmacs/pull/184>.
|
||||
- **Ships only the v21 wire layer:** the four wire shapes, version bump,
|
||||
shared cell-grid validator, and version-ladder move. It has no
|
||||
producer, consumer, or capability change; `panel_capable` stays
|
||||
`false`, so this slice changes no user-visible journey grade.
|
||||
- **Ships only the reserved v21 wire layer:** the four wire shapes,
|
||||
schema version, shared cell-grid validator, and accepted-version
|
||||
ladder move. The production daemon continues advertising v20 because
|
||||
its `Hello` is server-first; v21 activation belongs to 2B-3. This
|
||||
slice has no producer, consumer, or capability change;
|
||||
`panel_capable` stays `false`, so it changes no user-visible journey
|
||||
grade and existing v20 clients remain attachable.
|
||||
- **Review round 1 closed:** two P1s and one P2, all corrected at
|
||||
`9b364ad`: `PanelFrame` now identifies its buffer, the transport
|
||||
ratchet covers the actual attach path rather than a detached codec
|
||||
assertion, and shared grid bounds have one validator.
|
||||
- **Review round 2 found four issues; fixes are in progress:** the
|
||||
server-first `Hello` made the advertised v20↔v21 compatibility
|
||||
one-way; `COHERENCE.md` and `docs/agent-handoff.md` still named only
|
||||
v20 schema support; framing §9 named a nonexistent aggregate 2B
|
||||
suite instead of the three exact 2B slice suites; and the panel plus
|
||||
copied terminal "one byte over" fixtures were actually two bytes
|
||||
over. The correction keeps production advertisement at v20, adds a
|
||||
real-daemon existing-v20-client acceptance, updates all three durable
|
||||
records, names the exact slice suites, and asserts both rejecting
|
||||
fixtures are exactly `limit + 1`.
|
||||
- **The full gate found and corrected two 2B-1 omissions:** the
|
||||
statusline version ladder still pinned v20/rejected v21, and Vterm
|
||||
Stage 3 pinned v20 both structurally and in its real headless probe.
|
||||
|
|
@ -350,7 +365,7 @@ dependency. PR #184 is open and must not merge before user review.**
|
|||
`pmacs --gpu .` path, consumed the asynchronous dired snapshot, and
|
||||
retained the managed daemon before the wait so failure cleanup remains
|
||||
effective. The code integration auto-composed.
|
||||
- **The complete post-integration gate is green at `c8895a8`:**
|
||||
- **The previous complete post-integration gate was green at `c8895a8`:**
|
||||
formatting; strict workspace Clippy; library **1,849 passed + 3
|
||||
ignored default** and **2,034 passed + 4 ignored CRDT**; bottom-panel
|
||||
Stage 1 / 2A / 2B-1 **46 / 17 / 15**; folding Stage 2 **48**; GPU font
|
||||
|
|
@ -438,8 +453,9 @@ dependency. PR #184 is open and must not merge before user review.**
|
|||
`docs/agent-handoff.md` §1; the two round lessons are in §5.
|
||||
- Landed-docs follow-up merged as **#156** (`main` @ `d152120`,
|
||||
2026-07-25).
|
||||
- **Stage 2 framing: `docs/bottom-panel-stage2-framing.md` revision 5**
|
||||
is commit `56301ed` on branch `githubsucks/bottom-panel-stage2b`,
|
||||
- **Stage 2 framing: `docs/bottom-panel-stage2-framing.md` revision 6**
|
||||
is on branch `githubsucks/bottom-panel-stage2b` (revision 5 is commit
|
||||
`56301ed` there),
|
||||
worktree `../pmacs-bp-stage2b`. Revisions 1–4 remain on
|
||||
`githubsucks/bottom-panel-stage2-framing` (head `4fbd47f`, four
|
||||
framing commits, revision 4 at `49757e5`). Round 1 closed 2 blocking +
|
||||
|
|
@ -447,7 +463,9 @@ dependency. PR #184 is open and must not merge before user review.**
|
|||
round 2 closed 1 blocking + 2 high + 1 medium and decided both open
|
||||
items; round 3 closed 1 blocking + 1 high + 1 medium. No open items
|
||||
remain. Revision 5 adds no decision; it records the approved
|
||||
2B-1/2B-2/2B-3 implementation split. The
|
||||
2B-1/2B-2/2B-3 implementation split. Revision 6 corrects the
|
||||
server-first compatibility contract, durable protocol claims, exact
|
||||
acceptance-suite names, and `limit + 1` fixture. The
|
||||
parent framing `docs/bottom-panel-framing.md` (rev 4) remains
|
||||
authoritative, **including its acceptance criteria 37–55**.
|
||||
- Retained, carrying nothing unmerged: branch `bottom-panel` and worktree
|
||||
|
|
@ -456,12 +474,14 @@ dependency. PR #184 is open and must not merge before user review.**
|
|||
before the next branches:
|
||||
**2A** = classified §1.3 census routing + `paint_frame` per-window
|
||||
painter extraction (with the active-window auto-scroll preparation), no
|
||||
protocol change; **2B-1** = protocol **v21**
|
||||
protocol change; **2B-1** = reserved protocol schema **v21**, with
|
||||
production advertisement held at v20,
|
||||
(`InstanceMessage::PanelFrame` plus
|
||||
`FrontendEvent::{FrontendCellGeometry, PanelResizeRows, PanelPointer}`,
|
||||
gated both directions, each extended enum byte-pinned on its own
|
||||
previous final variant); **2B-2** = daemon panel projection and epoch
|
||||
machine; **2B-3** = the GPU band and negotiated `panel_capable` flip.
|
||||
machine; **2B-3** = compatible v21 activation, the GPU band, and the
|
||||
negotiated `panel_capable` flip.
|
||||
Stage 3 is the adopter default flip.
|
||||
- **Correction — this entry previously mis-stated the census contract.**
|
||||
It is **not** "route every consumer through `primary_document_window`".
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
# Agent handoff — cross-machine continuity
|
||||
|
||||
**Last updated: 2026-07-28, after the Journey/GPU directory-target
|
||||
ratchet (#183), following Journey Stage 1a (#182), which made directory
|
||||
**Last updated: 2026-07-28, during bottom-panel Stage 2B-1 PR #184
|
||||
review; the canonical landed base remains the Journey/GPU
|
||||
directory-target ratchet (#183), following Journey Stage 1a (#182),
|
||||
which made directory
|
||||
startup one coherent local/daemon/GPU path and incorporated the terminal
|
||||
configuration + copy mode landed-doc work (#180); following terminal
|
||||
copy mode (#178) — `C-c C-t`
|
||||
|
|
@ -58,9 +60,11 @@ commands, read `docs/active-work.md` immediately after this file.
|
|||
#165, the GPU terminal input fix #166, Lean 4 Stage 2 #161, the dired
|
||||
framing #164, COHERENCE.md #163, find-file #162, Lean 4 Stage 1 #160,
|
||||
minimap blank-slab #159, bottom-panel Stage 1 #155). Protocol unchanged
|
||||
at **v20** — bottom-panel Stage 2A deliberately carries no wire change;
|
||||
v21 arrives with Stage 2B. The bullets below describe the arcs in their
|
||||
own terms; this line is the head-of-`main` anchor.
|
||||
at **v20** — bottom-panel Stage 2A deliberately carries no wire change.
|
||||
The in-review Stage 2B-1 reserves the v21 schema but keeps the
|
||||
server-first production `Hello` at v20; compatible activation belongs
|
||||
to Stage 2B-3. The bullets below describe the arcs in their own terms;
|
||||
this line is the head-of-`main` anchor.
|
||||
- **`COHERENCE.md` is now required reading and a required framing input
|
||||
— #163.** It carries the product-coherence thesis, an audited
|
||||
scorecard, per-concern gaps, and §20's priority order, and it is the
|
||||
|
|
@ -395,9 +399,14 @@ commands, read `docs/active-work.md` immediately after this file.
|
|||
unchanged, which is the additivity gate for the `read_dir` change; M4
|
||||
121; required GPU 155; isolated-`XDG_CONFIG_HOME` workspace sweep
|
||||
3,205 across 93 suites. 15 claims bite-verified.
|
||||
- Protocol **v20** (`SUPPORTED=[6..=20]`; v16 = `ThemeFacts`, v17 =
|
||||
`FontFacts`, v18 = `StatuslineSegments`, v19 = terminal frames/events, v20 =
|
||||
the GPU initial-target semantic bootstrap family).
|
||||
- Canonical `main` is protocol **v20** (`SUPPORTED=[6..=20]`; v16 =
|
||||
`ThemeFacts`, v17 = `FontFacts`, v18 = `StatuslineSegments`, v19 =
|
||||
terminal frames/events, v20 = the GPU initial-target semantic
|
||||
bootstrap family). Bottom-panel Stage 2B-1's in-review schema is v21
|
||||
(`SUPPORTED=[6..=21]`), but its production daemon deliberately
|
||||
advertises v20: the handshake is server-first, so advertising 21
|
||||
would make shipped v20 GPU/TUI clients reject before
|
||||
`AttachRequest`. Stage 2B-3 owns compatible production activation.
|
||||
- **Bottom panel Stage 1 (window placement + TUI side windows) LANDED —
|
||||
#155** (`docs/bottom-panel-framing.md` rev 4; merge `e745068`; two review
|
||||
rounds). **No protocol change (still v20).** Arc 7's substrate: pmacs now
|
||||
|
|
@ -463,14 +472,18 @@ commands, read `docs/active-work.md` immediately after this file.
|
|||
required GPU 152; initial-target 14 CRDT; all three vterm suites; folding
|
||||
Stage 2 48. All 12 CI checks green at merge.
|
||||
- **Stage 2 (the GPU panel band) is FRAMED** —
|
||||
`docs/bottom-panel-stage2-framing.md` rev 5, four review rounds, no
|
||||
open items; the rev-5 implementation split was explicitly approved
|
||||
2026-07-27. It takes protocol **v21** and ships as four serial
|
||||
`docs/bottom-panel-stage2-framing.md` rev 6, four framing review
|
||||
rounds, no open framing items; the rev-5 implementation split was
|
||||
explicitly approved 2026-07-27 and rev 6 records PR #184's
|
||||
server-first compatibility and gate correction. It reserves
|
||||
protocol **v21** and ships as four serial
|
||||
implementation slices: **2A** classified census routing +
|
||||
per-window painter extraction (no wire change), **2B-1** the wire,
|
||||
**2B-2** the daemon projection and epoch machine, then **2B-3** the
|
||||
GPU band and negotiated `panel_capable` flip. Parent acceptance
|
||||
37–55 remains authoritative. Stage 3 is the adopter default flip.
|
||||
GPU band, compatible v21 activation, and negotiated
|
||||
`panel_capable` flip. Production attachment remains v20 through
|
||||
2B-1 and 2B-2. Parent acceptance 37–55 remains authoritative.
|
||||
Stage 3 is the adopter default flip.
|
||||
- **The §1.3 census is CLASSIFIED, not uniformly redirected.** Only the
|
||||
Projection class (#1–#12, #21–#22) routes through
|
||||
`primary_document_window`; focus/input (#13–#15, #23), focus chrome
|
||||
|
|
@ -1201,12 +1214,17 @@ buffer owns a path's recovery slot; only recover/discard release
|
|||
unclaimed crash data; adopt clears the old owner's skip cache.
|
||||
|
||||
**Protocol** — encoding-breaking bumps are deliberate and versioned. Canonical
|
||||
`main` is `[6..=20]`. v15 = `CompletionPopup` + `StatusFacts.message`; v16 =
|
||||
`main` is `[6..=20]`. The in-review bottom-panel 2B-1 schema extends support
|
||||
to `[6..=21]`, while `ADVERTISED_PROTOCOL_VERSION` stays 20 until 2B-3
|
||||
provides compatibility-preserving activation; the server-first `Hello`
|
||||
cannot advertise 21 without stranding existing v20 clients before
|
||||
`AttachRequest`. v15 = `CompletionPopup` + `StatusFacts.message`; v16 =
|
||||
`ThemeFacts`; v17 = `FontFacts`; v18 = `StatuslineSegments`; v19 = the vterm
|
||||
terminal family; v20 = semantic `SessionBootstrapRequest` plus appended
|
||||
`InitialTargetResult`. New wire surface ⇒ bump + both-frontends support +
|
||||
acceptance. An APPENDED variant must be guarded by a byte pin on the PREVIOUS
|
||||
final variant — its own round-trip cannot detect a discriminant shift.
|
||||
`InitialTargetResult`; v21 reserves the panel frame/event family. New wire
|
||||
surface ⇒ bump + both-frontends support + acceptance. An APPENDED variant
|
||||
must be guarded by a byte pin on the PREVIOUS final variant — its own
|
||||
round-trip cannot detect a discriminant shift.
|
||||
|
||||
**Fake LSP** (`src/bin/pmacs_fake_lsp.rs`) modes: `fullonly`,
|
||||
`rangeonly`, `rangeonly16` (UTF-16 + fail-closed bounds validation),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
# Bottom panel Stage 2 — the GPU panel band (framing)
|
||||
|
||||
**Revision 5 — APPROVED 2026-07-27; 2A merged, 2B-1 in progress.
|
||||
Ground truth: canonical `main` @ `c2d56ff`, protocol v20 on `main` and
|
||||
v21 on `bottom-panel-stage2b`.** Revisions 1–4 were
|
||||
pre-implementation; rev 5 records the three-way slice of Stage 2B
|
||||
(§0.0, §7.2, §9) after its first slice was already built.
|
||||
**Revision 6 — PR #184 review correction; the underlying Stage 2
|
||||
framing remains APPROVED 2026-07-27. 2A is merged and 2B-1 is under
|
||||
review. Ground truth: canonical `main` @ `7fd646d`, protocol v20 on
|
||||
`main`; `bottom-panel-stage2b` reserves the v21 schema while its
|
||||
server-first production handshake continues to advertise v20.**
|
||||
Revisions 1–4 were pre-implementation; rev 5 recorded the three-way
|
||||
slice of Stage 2B after its first slice was already built; rev 6
|
||||
corrects that slice's mixed-version and gate contracts.
|
||||
|
||||
Stage 1 (#155, merge `e745068`) gave pmacs window placement, window
|
||||
parameters, TUI side windows, the divider, and the adopter `display`
|
||||
|
|
@ -29,7 +32,33 @@ geometries), Q#BP16 (pointer transport), Q#BP17 (fold projection), and
|
|||
|
||||
## 0. Revision history
|
||||
|
||||
### 0.0 Rev 4 → rev 5 — the three-way slice of 2B (not a review round)
|
||||
### 0.0 Rev 5 → rev 6 — PR #184 review round 2, four findings closed
|
||||
|
||||
- **R6-1 (P1) — v21 is reserved, not advertised, in 2B-1.** The
|
||||
protocol's handshake is server-first. An existing v20 TUI or GPU
|
||||
frontend rejects a `Hello { protocol_version: 21 }` before it can
|
||||
send an `AttachRequest`, so rev 5's claim that a v21 daemon and v20
|
||||
peer "still negotiate 20" was impossible. 2B-1 therefore extends the
|
||||
schema and accepted-version ladder to v21 while the production daemon
|
||||
continues advertising v20. A real-daemon acceptance emulates the
|
||||
shipped v20 rejection point and then requires the attachment to reach
|
||||
its initial grid. **2B-3 owns both a compatibility-preserving
|
||||
activation mechanism and the production move to v21; it may not
|
||||
simply change the unsolicited `Hello` to 21.**
|
||||
- **R6-2 (P2) — durable protocol claims move with the wire.**
|
||||
`COHERENCE.md` and `docs/agent-handoff.md` now distinguish v21 schema
|
||||
support from the still-v20 production handshake.
|
||||
- **R6-3 (P2) — the gate contract names the actual decomposition.**
|
||||
§9 now names 2B-1's
|
||||
`bottom_panel_stage2b_protocol_acceptance` suite and the exact planned
|
||||
daemon/GPU suite names for 2B-2 and 2B-3 instead of the nonexistent
|
||||
`bottom_panel_stage2b_acceptance`.
|
||||
- **R6-4 (P2) — "one byte over" means exactly one.** The panel and
|
||||
copied terminal boundary fixtures replace a one-byte cluster with a
|
||||
two-byte cluster, and each independently asserts a total of
|
||||
`limit + 1`.
|
||||
|
||||
### 0.1 Rev 4 → rev 5 — the three-way slice of 2B (not a review round)
|
||||
|
||||
This revision changes no decision. It splits one approved
|
||||
implementation slice into three and reallocates the acceptance
|
||||
|
|
@ -68,18 +97,17 @@ criteria across them.
|
|||
2 and 3 are written.
|
||||
- **R5-4 — two of the three slices ship dark, deliberately.** Nothing
|
||||
in 2B-1 or 2B-2 is reachable by a user: `panel_capable` stays
|
||||
`false` for every negotiated semantic session until 2B-3, so a v21
|
||||
daemon and a v21 GPU frontend negotiate 21 and behave exactly as
|
||||
they do at v20. This is the same posture 2A took ("seam adoption
|
||||
that becomes load-bearing in 2B") and it carries the same
|
||||
obligation: **the version bump advertises a capability whose only
|
||||
distinguishing feature is unreachable until 2B-3 lands.** That is
|
||||
safe for compatibility — the variants are appended, the ladder is
|
||||
extended, and a v20 peer still negotiates 20 — but it means the arc
|
||||
must not stall between 2B-1 and 2B-3. Recorded here so a stall is
|
||||
visible as a decision rather than inherited as a default.
|
||||
`false` for every negotiated semantic session until 2B-3. Rev 5
|
||||
incorrectly described that posture as a production v21 negotiation
|
||||
that remained compatible with v20 clients; rev 6 R6-1 supersedes
|
||||
that claim. The actual dark posture keeps the server-first
|
||||
production handshake on v20 while the v21 schema is reserved. This
|
||||
is the same posture 2A took ("seam adoption that becomes
|
||||
load-bearing in 2B"), and it means the arc must not stall between
|
||||
2B-1 and 2B-3. Recorded here so a stall is visible as a decision
|
||||
rather than inherited as a default.
|
||||
|
||||
### 0.1 Round 3 (rev 3 → rev 4) — 1 blocking, 1 high, 1 medium, all closed
|
||||
### 0.2 Round 3 (rev 3 → rev 4) — 1 blocking, 1 high, 1 medium, all closed
|
||||
|
||||
- **R3-1 (blocker).** Rev 3's three-boundary model was right but its
|
||||
call-site table was wrong in five places, and each error was a real
|
||||
|
|
@ -107,7 +135,7 @@ criteria across them.
|
|||
`cell.attachment.is_some()` rejection. It is now classified — and
|
||||
**shared**, with the reasoning pinned.
|
||||
|
||||
### 0.2 Round 2 (rev 2 → rev 3) — 1 blocking, 2 high, 1 medium, all closed
|
||||
### 0.3 Round 2 (rev 2 → rev 3) — 1 blocking, 2 high, 1 medium, all closed
|
||||
|
||||
- **R2-1 (blocker).** Rev 2's "one document-bottom seam" conflated two
|
||||
boundaries that must **diverge** once a panel exists. Several sites it
|
||||
|
|
@ -134,7 +162,7 @@ criteria across them.
|
|||
- Both §8 open items are decided (§5.3): `BASE_DIVIDER_HEIGHT = 4.0` at
|
||||
scale 1.0, and `TEXT_TOP` stays unscaled.
|
||||
|
||||
### 0.3 Round 1 (rev 1 → rev 2) — 2 blocking, 3 high, 3 revision points, all closed
|
||||
### 0.4 Round 1 (rev 1 → rev 2) — 2 blocking, 3 high, 3 revision points, all closed
|
||||
|
||||
- **R1-1 (blocker).** Rev 1 said all 23 census reads route through
|
||||
`primary_document_window`. That contradicts Q#BP14, which routes only
|
||||
|
|
@ -181,9 +209,10 @@ criteria across them.
|
|||
| Byte pin `InstanceMessage::InitialTargetResult` | `pmacs-protocol/src/message.rs:1145` | Holds — still the enum's final variant |
|
||||
| Byte pin `FrontendEvent::TerminalPointer` | final variant of its enum | Holds |
|
||||
|
||||
**Protocol is still v20** (`pmacs-protocol/src/message.rs:1568`); no
|
||||
intervening PR bumped it. Q#BP9's conditional resolves: **Stage 2 is
|
||||
v21**, no reservation was taken and none was needed.
|
||||
**Protocol was still v20 at this re-scout**; no intervening PR had
|
||||
bumped it. Q#BP9's conditional resolved: **Stage 2 reserves v21**.
|
||||
Rev 6 R6-1 adds the server-first compatibility constraint discovered
|
||||
during 2B-1 review.
|
||||
|
||||
Fifteen PRs merged between the parent's last re-scout (`47581f4`) and
|
||||
this one: #149, #150, #152–#155, #158–#166. Nothing in the parent's
|
||||
|
|
@ -375,13 +404,18 @@ undedicated (Q#BP2c). "It receives no new events" is insufficient — if
|
|||
the daemon nevertheless places that frontend's window in a side panel
|
||||
it cannot render, the window becomes invisible. The gate is on
|
||||
placement, not only on transport. Parent acceptance 51 pins the mixed
|
||||
session.
|
||||
session. **The production daemon does not advertise v21 in 2B-1 or
|
||||
2B-2.** Because `Hello` is server-first, 2B-3 must add or prove a
|
||||
compatibility-preserving way to activate v21 before applying this rule;
|
||||
merely advertising 21 would strand already-shipped v20 clients before
|
||||
they can identify themselves.
|
||||
|
||||
## 4. Revisions to the parent framing
|
||||
|
||||
Only these; everything else stands.
|
||||
|
||||
- **Q#BP9 resolves to v21.**
|
||||
- **Q#BP9 resolves to the v21 schema, with production advertisement
|
||||
held at v20 until 2B-3 supplies compatible activation.**
|
||||
- **Q#BP15a's epoch ownership is specified** by §3.1's table and API
|
||||
split, replacing the parent's one-line "frontend-owned" statement.
|
||||
- **Q#BP8's statusline criterion splits** per §3.3: one read reroutes,
|
||||
|
|
@ -741,7 +775,10 @@ consumer, no capability change.
|
|||
becomes 21, `SUPPORTED_PROTOCOL_VERSIONS` accepts `6..=21` and
|
||||
rejects 22, and any test whose *name* encodes the old number is
|
||||
renamed. A ladder pin that passes across a bump was not pinning the
|
||||
version.
|
||||
version. **`ADVERTISED_PROTOCOL_VERSION` remains 20 in 2B-1 and
|
||||
2B-2** because the unsolicited `Hello` precedes any client version
|
||||
signal. A real daemon must remain attachable by a client whose
|
||||
supported range ends at 20.
|
||||
- **Shared bounds are aliased, not duplicated.** Every constant the
|
||||
terminal screen and the panel validator both enforce is one
|
||||
definition with the other as an alias, so truncation and validation
|
||||
|
|
@ -790,9 +827,11 @@ production negotiation until 2B-3.
|
|||
|
||||
#### 7.2.3 Slice 2B-3 — the GPU band and the capability flip
|
||||
|
||||
**Authority: `pmacs-gpu`, plus the negotiation rule.** This is the only
|
||||
slice a user can observe, and the only one that closes the journey
|
||||
divergence in §6.
|
||||
**Authority: `pmacs-gpu`, plus the compatibility-preserving negotiation
|
||||
activation.** This is the only slice a user can observe, and the only
|
||||
one that closes the journey divergence in §6. It must not advertise
|
||||
v21 in the server-first `Hello` until an existing v20 client can still
|
||||
attach.
|
||||
|
||||
- **46** (band + divider shrink the document text area by exactly their
|
||||
pixel height; carets, hits, and scroll geometry respect the reduced
|
||||
|
|
@ -838,7 +877,10 @@ divergence in §6.
|
|||
its chrome.
|
||||
- **A2B-5.** `panel_capable` is true only for a v21+ negotiated
|
||||
authenticated semantic session; a v20 semantic session is never
|
||||
**placed** in a side window, not merely denied the events.
|
||||
**placed** in a side window, not merely denied the events. The same
|
||||
acceptance must attach an actual v20 client to the production daemon
|
||||
after v21 activation, so the new path cannot pass by breaking the old
|
||||
handshake before placement is evaluated.
|
||||
|
||||
## 8. Open items
|
||||
|
||||
|
|
@ -866,17 +908,20 @@ stacked, and each is cut from `main`.
|
|||
installed.
|
||||
- **Stage 2B-1 — the v21 wire layer.** Branch `bottom-panel-stage2b`.
|
||||
The four wire shapes, the version bump, the shared cell-grid
|
||||
validator, and the version-ladder move. **No producer, no consumer,
|
||||
no capability change** — `panel_capable` stays `false`.
|
||||
validator, and the version-ladder move. The v21 schema is reserved
|
||||
while the production daemon continues advertising v20. **No
|
||||
producer, no consumer, no capability change** — `panel_capable`
|
||||
stays `false`.
|
||||
- **Stage 2B-2 — the daemon panel projection and epoch machine.** Cut
|
||||
from `main` after 2B-1 merges. Produces `PanelFrame` and owns
|
||||
stale-event rejection, exercised through a **test-only**
|
||||
panel-capable semantic view. Still no production flip.
|
||||
- **Stage 2B-3 — the GPU band and the negotiated flip.** Cut from
|
||||
`main` after 2B-2 merges. The three-boundary text-area split, the
|
||||
divider, pointer routing, and `panel_capable = true` for a v21+
|
||||
negotiated authenticated semantic session. **This is the slice that
|
||||
changes what a user sees**, and it repeats 2A's and 2B-2's relevant
|
||||
divider, pointer routing, the compatibility-preserving v21
|
||||
activation, and `panel_capable = true` for a v21+ negotiated
|
||||
authenticated semantic session. **This is the slice that changes
|
||||
what a user sees**, and it repeats 2A's and 2B-2's relevant
|
||||
assertions through the real capability flip.
|
||||
|
||||
**Each slice runs the full gate set below, not a subset of it.** A
|
||||
|
|
@ -889,9 +934,16 @@ Gates for each slice: the standing suite from `CLAUDE.md`, plus the **touched
|
|||
acceptance suites named explicitly** — the standing rule is to run the
|
||||
suites a change touches, and "standing suite" does not name them:
|
||||
|
||||
- `bottom_panel_stage1_acceptance` — the substrate both slices build on.
|
||||
- `bottom_panel_stage2a_acceptance` / `bottom_panel_stage2b_acceptance`
|
||||
— new, one per slice.
|
||||
- `bottom_panel_stage1_acceptance` — the substrate all four Stage 2
|
||||
slices build on.
|
||||
- `bottom_panel_stage2a_acceptance` — Stage 2A's classified census and
|
||||
painter extraction.
|
||||
- `bottom_panel_stage2b_protocol_acceptance` — Stage 2B-1's v21 schema,
|
||||
server-first v20 compatibility, byte pins, and shared validation.
|
||||
- `bottom_panel_stage2b_daemon_acceptance` — the exact suite name
|
||||
reserved for Stage 2B-2's projection and epoch machine.
|
||||
- `bottom_panel_stage2b_gpu_acceptance` — the exact suite name reserved
|
||||
for Stage 2B-3's band, compatible activation, and capability flip.
|
||||
- `statusline_segments_acceptance` — the fan-out target change (§3.3).
|
||||
- `m11_5_semantic_acceptance` — the semantic census (§3.2).
|
||||
- `gpu_initial_target_acceptance` — parent criterion 55.
|
||||
|
|
|
|||
|
|
@ -57,17 +57,18 @@ pub use cell::{
|
|||
pub use crdt::CrdtOp;
|
||||
pub use ids::{BufferId, ByteRange, FrontendId, Position};
|
||||
pub use message::{
|
||||
AdornmentContent, AdornmentPlacement, AttachRequest, BUILTIN_PAIR_CHARS, BlockAdornment,
|
||||
CompletionPopupRow, CursorState, Decoration, DecorationKind, DecorationSegment,
|
||||
FrontendCapabilities, FrontendEvent, GoodbyeReason, Hello, InitialTarget, InitialTargetResult,
|
||||
InlineAdornment, InstanceCapabilities, InstanceIdentity, InstanceMessage, InstanceSignal, Key,
|
||||
KeyEvent, LineNumberMode, MAX_INITIAL_TARGET_ERROR_BYTES, MAX_INITIAL_TARGET_PATH_BYTES,
|
||||
MAX_STATUSLINE_FACE_BYTES, MAX_STATUSLINE_PROVIDER_NAME_BYTES, MAX_STATUSLINE_PROVIDERS,
|
||||
MAX_STATUSLINE_SEGMENT_BYTES, MAX_STATUSLINE_TOTAL_TEXT_BYTES, MenuPromptRow, Modifiers,
|
||||
MouseButton, MouseEvent, MouseKind, NegotiatedCapabilities, PROTOCOL_VERSION, PointerKind,
|
||||
ResourceBody, SUPPORTED_PROTOCOL_VERSIONS, SelectionSnapshot, SessionBootstrapRequest,
|
||||
StatuslineSegment, StyleSegment, StyleSpan, ThemeFace, is_builtin_pair_char,
|
||||
is_modeline_face_name, is_supported_protocol_version, is_ui_face_name, negotiate_capabilities,
|
||||
ADVERTISED_PROTOCOL_VERSION, AdornmentContent, AdornmentPlacement, AttachRequest,
|
||||
BUILTIN_PAIR_CHARS, BlockAdornment, CompletionPopupRow, CursorState, Decoration,
|
||||
DecorationKind, DecorationSegment, FrontendCapabilities, FrontendEvent, GoodbyeReason, Hello,
|
||||
InitialTarget, InitialTargetResult, InlineAdornment, InstanceCapabilities, InstanceIdentity,
|
||||
InstanceMessage, InstanceSignal, Key, KeyEvent, LineNumberMode, MAX_INITIAL_TARGET_ERROR_BYTES,
|
||||
MAX_INITIAL_TARGET_PATH_BYTES, MAX_STATUSLINE_FACE_BYTES, MAX_STATUSLINE_PROVIDER_NAME_BYTES,
|
||||
MAX_STATUSLINE_PROVIDERS, MAX_STATUSLINE_SEGMENT_BYTES, MAX_STATUSLINE_TOTAL_TEXT_BYTES,
|
||||
MenuPromptRow, Modifiers, MouseButton, MouseEvent, MouseKind, NegotiatedCapabilities,
|
||||
PROTOCOL_VERSION, PointerKind, ResourceBody, SUPPORTED_PROTOCOL_VERSIONS, SelectionSnapshot,
|
||||
SessionBootstrapRequest, StatuslineSegment, StyleSegment, StyleSpan, ThemeFace,
|
||||
is_builtin_pair_char, is_modeline_face_name, is_supported_protocol_version, is_ui_face_name,
|
||||
negotiate_capabilities,
|
||||
};
|
||||
pub use panel::{MAX_PANEL_VISIBLE_CELLS, PanelFrame, PanelFrameError, PanelFramePayload};
|
||||
pub use terminal::{
|
||||
|
|
|
|||
|
|
@ -1665,6 +1665,16 @@ pub enum ResourceBody {
|
|||
/// window invisible.
|
||||
pub const PROTOCOL_VERSION: u32 = 21;
|
||||
|
||||
/// Protocol version placed in the daemon's server-first [`Hello`].
|
||||
///
|
||||
/// Bottom-panel Stage 2B-1 reserves the additive v21 wire family, but
|
||||
/// production attachment remains on v20 until the Stage 2B-3 capability
|
||||
/// activation can preserve compatibility with existing v20 frontends.
|
||||
/// Those frontends reject an unknown server-first version before they can
|
||||
/// send [`AttachRequest`], so advertising [`PROTOCOL_VERSION`] here would
|
||||
/// make the otherwise-dark protocol slice user-visible.
|
||||
pub const ADVERTISED_PROTOCOL_VERSION: u32 = 20;
|
||||
|
||||
/// T M10.5: the set of protocol versions a v1.0 binary accepts on
|
||||
/// the wire. v0.1 binaries only accepted `[1]`; v1.0 binaries accept
|
||||
/// `[1, 2]` so the version asymmetry the §sec:m10-backward-compat
|
||||
|
|
@ -1742,9 +1752,12 @@ pub const PROTOCOL_VERSION: u32 = 21;
|
|||
/// sessions send a bounded bootstrap envelope after `AttachRequest`; legacy
|
||||
/// and non-semantic sessions retain their existing handshake shape.
|
||||
///
|
||||
/// Bottom panel Stage 2 (Q#BP9): extended to `[6, ..., 21]`. v21 peers
|
||||
/// may exchange panel traffic; v20 peers interoperate with it simply
|
||||
/// absent, and are never placed in a side window.
|
||||
/// Bottom panel Stage 2 (Q#BP9): extended to `[6, ..., 21]`. Stage 2B-1
|
||||
/// reserves and validates the v21 wire while production daemons continue
|
||||
/// to send [`ADVERTISED_PROTOCOL_VERSION`] in their server-first
|
||||
/// [`Hello`]. The later capability-activation slice owns moving production
|
||||
/// negotiation to v21 without making existing v20 frontends reject the
|
||||
/// handshake.
|
||||
pub const SUPPORTED_PROTOCOL_VERSIONS: &[u32] =
|
||||
&[6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21];
|
||||
|
||||
|
|
@ -2126,7 +2139,10 @@ pub fn negotiate_capabilities(
|
|||
/// frontend will use as the `FrontendId` on every event it sends.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Hello {
|
||||
/// The instance's `PROTOCOL_VERSION`.
|
||||
/// The protocol version this attachment should use.
|
||||
///
|
||||
/// This can deliberately trail [`PROTOCOL_VERSION`] while an additive
|
||||
/// wire family is reserved but not yet activated in production.
|
||||
pub protocol_version: u32,
|
||||
/// `FrontendId` assigned to this attachment by the instance. The
|
||||
/// frontend stamps this onto subsequent events. v0.1 daemons start
|
||||
|
|
|
|||
|
|
@ -843,14 +843,14 @@ mod tests {
|
|||
let mut over = exact.clone();
|
||||
// One more byte of glyph, nothing else changed.
|
||||
let last = over.cells.len() - 1;
|
||||
over.cells[last] = cell_with(Glyph::Cluster(cluster_of_len(3).into_boxed_slice()));
|
||||
over.cells[last] = cell_with(Glyph::Cluster(cluster_of_len(2).into_boxed_slice()));
|
||||
|
||||
(exact, over)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maximum_legal_terminal_frame_encodes_below_the_transport_cap() {
|
||||
let (exact, _) = budget_boundary_frames();
|
||||
let (exact, over) = budget_boundary_frames();
|
||||
assert_eq!(exact.validate(), Ok(()));
|
||||
|
||||
let mut glyph_bytes = 0usize;
|
||||
|
|
@ -865,6 +865,20 @@ mod tests {
|
|||
glyph_bytes, MAX_TERMINAL_FRAME_GLYPH_BYTES,
|
||||
"the measured fixture must spend the whole aggregate budget"
|
||||
);
|
||||
let over_glyph_bytes = over
|
||||
.cells
|
||||
.iter()
|
||||
.map(|cell| match &cell.glyph {
|
||||
Glyph::Char(ch) => ch.len_utf8(),
|
||||
Glyph::Cluster(bytes) => bytes.len(),
|
||||
Glyph::Continuation => 0,
|
||||
})
|
||||
.sum::<usize>();
|
||||
assert_eq!(
|
||||
over_glyph_bytes,
|
||||
MAX_TERMINAL_FRAME_GLYPH_BYTES + 1,
|
||||
"the rejecting twin must be exactly one byte over the aggregate budget"
|
||||
);
|
||||
|
||||
let msg = InstanceMessage::TerminalFrame(exact);
|
||||
let bytes = postcard::to_allocvec(&msg).expect("encode");
|
||||
|
|
|
|||
|
|
@ -67,9 +67,9 @@ use crate::lockfile::{self, LockError, LockHandle};
|
|||
use crate::presence::{PresenceSnapshot, SessionRegistry};
|
||||
use crate::protocol::crossterm_translate::{key_to_crossterm, mouse_to_crossterm};
|
||||
use crate::protocol::{
|
||||
AttachRequest, FrontendEvent, FrontendId, GoodbyeReason, Hello, InitialTarget,
|
||||
InitialTargetResult, InstanceCapabilities, InstanceIdentity, InstanceMessage, InstanceSignal,
|
||||
MAX_INITIAL_TARGET_ERROR_BYTES, MAX_INITIAL_TARGET_PATH_BYTES, PROTOCOL_VERSION, PointerKind,
|
||||
ADVERTISED_PROTOCOL_VERSION, AttachRequest, FrontendEvent, FrontendId, GoodbyeReason, Hello,
|
||||
InitialTarget, InitialTargetResult, InstanceCapabilities, InstanceIdentity, InstanceMessage,
|
||||
InstanceSignal, MAX_INITIAL_TARGET_ERROR_BYTES, MAX_INITIAL_TARGET_PATH_BYTES, PointerKind,
|
||||
SelectionSnapshot, SessionBootstrapRequest,
|
||||
};
|
||||
use crate::socket_path::{SocketPathError, ensure_runtime_subdir};
|
||||
|
|
@ -712,7 +712,7 @@ fn per_attach_thread(
|
|||
// mismatch path without changing the default.
|
||||
let instance_caps_for_hello = instance_capabilities_with_env_override();
|
||||
let hello = Hello {
|
||||
protocol_version: PROTOCOL_VERSION,
|
||||
protocol_version: ADVERTISED_PROTOCOL_VERSION,
|
||||
assigned_frontend_id: frontend_id,
|
||||
instance_identity: daemon_state.build_identity(),
|
||||
instance_capabilities: instance_caps_for_hello.clone(),
|
||||
|
|
@ -739,7 +739,7 @@ fn per_attach_thread(
|
|||
let _ = write_message(
|
||||
&mut stream,
|
||||
&InstanceMessage::Goodbye(GoodbyeReason::VersionMismatch {
|
||||
server: PROTOCOL_VERSION,
|
||||
server: ADVERTISED_PROTOCOL_VERSION,
|
||||
client: req.protocol_version,
|
||||
}),
|
||||
);
|
||||
|
|
@ -3412,6 +3412,7 @@ fn apply_event(
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::protocol::PROTOCOL_VERSION;
|
||||
|
||||
#[test]
|
||||
fn daemon_state_starts_frontend_id_at_two() {
|
||||
|
|
|
|||
|
|
@ -5,17 +5,28 @@
|
|||
//! projection, the epoch state machine, and the GPU band are later
|
||||
//! slices of this stage and are not exercised here.
|
||||
|
||||
mod common;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use pmacs_protocol::cell::{Cell, CellCoord, CellSize, Color, Glyph, Style, UnderlineStyle};
|
||||
use pmacs_protocol::message::{FrontendEvent, InstanceMessage, Modifiers, MouseButton, MouseKind};
|
||||
use pmacs_protocol::message::{
|
||||
AttachRequest, FrontendEvent, Hello, InstanceMessage, Modifiers, MouseButton, MouseKind,
|
||||
};
|
||||
use pmacs_protocol::panel::{
|
||||
MAX_PANEL_VISIBLE_CELLS, PanelFrame, PanelFrameError, PanelFramePayload,
|
||||
};
|
||||
use pmacs_protocol::terminal::{
|
||||
MAX_TERMINAL_COLS, TerminalFrame, TerminalFrameError, TerminalProcessState,
|
||||
};
|
||||
use pmacs_protocol::transport::MAX_FRAME_BYTES;
|
||||
use pmacs_protocol::transport::{MAX_FRAME_BYTES, read_message, write_message};
|
||||
use pmacs_protocol::wire_grid::{MAX_WIRE_GRID_GLYPH_BYTES, MAX_WIRE_GRID_GRAPHEME_BYTES};
|
||||
use pmacs_protocol::{BufferId, FrontendId, PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS};
|
||||
use pmacs_protocol::{
|
||||
ADVERTISED_PROTOCOL_VERSION, BufferId, FrontendId, PROTOCOL_VERSION,
|
||||
SUPPORTED_PROTOCOL_VERSIONS,
|
||||
};
|
||||
|
||||
use common::daemon::{TestDaemon, build_default_caps};
|
||||
|
||||
fn cell(ch: char) -> Cell {
|
||||
Cell {
|
||||
|
|
@ -98,11 +109,54 @@ fn terminal_frame(rows: u32, cols: u32) -> TerminalFrame {
|
|||
fn the_panel_stage_takes_protocol_v21() {
|
||||
assert_eq!(PROTOCOL_VERSION, 21);
|
||||
assert!(SUPPORTED_PROTOCOL_VERSIONS.contains(&21));
|
||||
// v20 stays supported: a v20 peer interoperates with panel traffic
|
||||
// simply absent rather than being refused the handshake.
|
||||
// The wire family is reserved before it is activated: the production
|
||||
// server-first Hello must remain acceptable to already-shipped v20
|
||||
// clients throughout the dark protocol and daemon slices.
|
||||
assert_eq!(ADVERTISED_PROTOCOL_VERSION, 20);
|
||||
assert!(SUPPORTED_PROTOCOL_VERSIONS.contains(&20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_new_daemon_keeps_an_existing_v20_client_attachable() {
|
||||
let daemon = TestDaemon::spawn();
|
||||
let mut stream = daemon.connect();
|
||||
stream
|
||||
.set_read_timeout(Some(Duration::from_secs(5)))
|
||||
.expect("set handshake timeout");
|
||||
|
||||
// This is the rejection point in an already-shipped client: it reads the
|
||||
// daemon's unsolicited Hello before it is able to identify its own
|
||||
// supported range or send AttachRequest.
|
||||
let hello: Hello = read_message(&mut stream).expect("read daemon Hello");
|
||||
let v20_client_supported_versions = 6..=20;
|
||||
assert_eq!(hello.protocol_version, ADVERTISED_PROTOCOL_VERSION);
|
||||
assert!(
|
||||
v20_client_supported_versions.contains(&hello.protocol_version),
|
||||
"an existing v20 client would reject the server-first Hello"
|
||||
);
|
||||
|
||||
write_message(
|
||||
&mut stream,
|
||||
&AttachRequest {
|
||||
protocol_version: hello.protocol_version,
|
||||
frontend_capabilities: build_default_caps(),
|
||||
initial_size: CellSize::new(24, 80),
|
||||
},
|
||||
)
|
||||
.expect("write v20 AttachRequest");
|
||||
|
||||
assert!(
|
||||
matches!(
|
||||
read_message::<InstanceMessage>(&mut stream).expect("read initial grid"),
|
||||
InstanceMessage::CellDelta {
|
||||
full_grid: true,
|
||||
..
|
||||
}
|
||||
),
|
||||
"the daemon must establish the v20 session, not merely send an acceptable Hello"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_present_panel_frame_round_trips_with_both_epochs() {
|
||||
let frame = panel_frame(2, 3);
|
||||
|
|
@ -483,7 +537,7 @@ fn panel_budget_boundary_frames() -> (PanelFrame, PanelFrame) {
|
|||
let mut over = exact.clone();
|
||||
// One more byte of glyph, nothing else changed.
|
||||
let last = over.cells.len() - 1;
|
||||
over.cells[last] = maximal_cell(Glyph::Cluster(cluster_of_len(3).into_boxed_slice()));
|
||||
over.cells[last] = maximal_cell(Glyph::Cluster(cluster_of_len(2).into_boxed_slice()));
|
||||
|
||||
(exact, over)
|
||||
}
|
||||
|
|
@ -508,6 +562,20 @@ fn maximum_legal_panel_frame_encodes_below_the_transport_cap() {
|
|||
glyph_bytes, MAX_WIRE_GRID_GLYPH_BYTES,
|
||||
"the measured fixture must spend the whole aggregate budget"
|
||||
);
|
||||
let over_glyph_bytes = over
|
||||
.cells
|
||||
.iter()
|
||||
.map(|cell| match &cell.glyph {
|
||||
Glyph::Char(ch) => ch.len_utf8(),
|
||||
Glyph::Cluster(bytes) => bytes.len(),
|
||||
Glyph::Continuation => 0,
|
||||
})
|
||||
.sum::<usize>();
|
||||
assert_eq!(
|
||||
over_glyph_bytes,
|
||||
MAX_WIRE_GRID_GLYPH_BYTES + 1,
|
||||
"the rejecting twin must be exactly one byte over the aggregate budget"
|
||||
);
|
||||
|
||||
// One byte over is rejected, which is what makes `exact` maximal.
|
||||
assert!(matches!(
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ use tempfile::TempDir;
|
|||
#[cfg(feature = "crdt")]
|
||||
use pmacs::cell::CellSize;
|
||||
#[cfg(feature = "crdt")]
|
||||
use pmacs::protocol::{AttachRequest, PROTOCOL_VERSION};
|
||||
use pmacs::protocol::AttachRequest;
|
||||
use pmacs::protocol::{FrontendCapabilities, Hello};
|
||||
use pmacs::transport::read_message;
|
||||
#[cfg(feature = "crdt")]
|
||||
|
|
@ -294,7 +294,7 @@ pub fn attach_multi(daemon: &TestDaemon) -> (Hello, UnixStream) {
|
|||
.unwrap();
|
||||
let hello: Hello = read_message(&mut stream).expect("read Hello");
|
||||
let req = AttachRequest {
|
||||
protocol_version: PROTOCOL_VERSION,
|
||||
protocol_version: hello.protocol_version,
|
||||
frontend_capabilities: multi_frontend_caps(),
|
||||
initial_size: CellSize::new(24, 80),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -93,9 +93,9 @@ mod crdt {
|
|||
use pmacs::cell::CellSize;
|
||||
use pmacs::crdt::CrdtState;
|
||||
use pmacs::protocol::{
|
||||
AttachRequest, FrontendCapabilities, FrontendEvent, FrontendId, Hello, InitialTarget,
|
||||
InitialTargetResult, InstanceCapabilities, InstanceIdentity, InstanceMessage,
|
||||
PROTOCOL_VERSION, SessionBootstrapRequest,
|
||||
ADVERTISED_PROTOCOL_VERSION, AttachRequest, FrontendCapabilities, FrontendEvent,
|
||||
FrontendId, Hello, InitialTarget, InitialTargetResult, InstanceCapabilities,
|
||||
InstanceIdentity, InstanceMessage, PROTOCOL_VERSION, SessionBootstrapRequest,
|
||||
};
|
||||
use pmacs::transport::{read_message, write_message};
|
||||
|
||||
|
|
@ -244,11 +244,11 @@ mod crdt {
|
|||
.set_read_timeout(Some(Duration::from_secs(5)))
|
||||
.expect("set target frontend timeout");
|
||||
let hello: Hello = read_message(&mut stream).expect("target frontend Hello");
|
||||
assert_eq!(hello.protocol_version, PROTOCOL_VERSION);
|
||||
assert_eq!(hello.protocol_version, ADVERTISED_PROTOCOL_VERSION);
|
||||
write_message(
|
||||
&mut stream,
|
||||
&AttachRequest {
|
||||
protocol_version: PROTOCOL_VERSION,
|
||||
protocol_version: hello.protocol_version,
|
||||
frontend_capabilities: FrontendCapabilities {
|
||||
multi_frontend: true,
|
||||
crdt_replica: true,
|
||||
|
|
@ -583,7 +583,7 @@ mod crdt {
|
|||
facts
|
||||
.get("server_protocol_version")
|
||||
.and_then(|value| value.parse::<u32>().ok()),
|
||||
Some(PROTOCOL_VERSION)
|
||||
Some(ADVERTISED_PROTOCOL_VERSION)
|
||||
);
|
||||
assert_eq!(
|
||||
facts.get("spawned_daemon").map(String::as_str),
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ use pmacs::cell::Color;
|
|||
#[cfg(feature = "crdt")]
|
||||
use pmacs::overlay_color::color_for_slot;
|
||||
use pmacs::protocol::{
|
||||
AttachRequest, FrontendCapabilities, FrontendEvent, GoodbyeReason, Hello, InstanceMessage, Key,
|
||||
KeyEvent, Modifiers, PROTOCOL_VERSION,
|
||||
ADVERTISED_PROTOCOL_VERSION, AttachRequest, FrontendCapabilities, FrontendEvent, GoodbyeReason,
|
||||
Hello, InstanceMessage, Key, KeyEvent, Modifiers, PROTOCOL_VERSION,
|
||||
};
|
||||
use pmacs::transport::{read_message, write_message};
|
||||
|
||||
|
|
@ -52,9 +52,9 @@ use common::daemon::{
|
|||
/// Read the daemon's `Hello`, send our `AttachRequest`, return the Hello.
|
||||
fn do_handshake(stream: &mut UnixStream) -> Hello {
|
||||
let hello: Hello = read_message(stream).expect("read Hello");
|
||||
assert_eq!(hello.protocol_version, PROTOCOL_VERSION);
|
||||
assert_eq!(hello.protocol_version, ADVERTISED_PROTOCOL_VERSION);
|
||||
let req = AttachRequest {
|
||||
protocol_version: PROTOCOL_VERSION,
|
||||
protocol_version: hello.protocol_version,
|
||||
frontend_capabilities: build_default_caps(),
|
||||
initial_size: CellSize::new(24, 80),
|
||||
};
|
||||
|
|
@ -418,7 +418,7 @@ fn version_mismatch_clean_disconnect() {
|
|||
|
||||
// Read Hello.
|
||||
let hello: Hello = read_message(&mut stream).expect("Hello");
|
||||
assert_eq!(hello.protocol_version, PROTOCOL_VERSION);
|
||||
assert_eq!(hello.protocol_version, ADVERTISED_PROTOCOL_VERSION);
|
||||
|
||||
// Send AttachRequest with wrong protocol version.
|
||||
let req = AttachRequest {
|
||||
|
|
@ -431,7 +431,7 @@ fn version_mismatch_clean_disconnect() {
|
|||
// Expect Goodbye(VersionMismatch).
|
||||
match read_message::<InstanceMessage>(&mut stream) {
|
||||
Ok(InstanceMessage::Goodbye(GoodbyeReason::VersionMismatch { server, client })) => {
|
||||
assert_eq!(server, PROTOCOL_VERSION);
|
||||
assert_eq!(server, ADVERTISED_PROTOCOL_VERSION);
|
||||
assert_eq!(client, 999);
|
||||
}
|
||||
other => panic!("expected VersionMismatch Goodbye, got {other:?}"),
|
||||
|
|
@ -1145,9 +1145,9 @@ fn m10_10_production_attach_negotiates_crdt_replica() {
|
|||
|
||||
// Production handshake — NOT the test `attach_multi()` path.
|
||||
let hello: Hello = read_message(&mut stream).expect("read Hello");
|
||||
assert_eq!(hello.protocol_version, PROTOCOL_VERSION);
|
||||
assert_eq!(hello.protocol_version, ADVERTISED_PROTOCOL_VERSION);
|
||||
let req = AttachRequest {
|
||||
protocol_version: PROTOCOL_VERSION,
|
||||
protocol_version: hello.protocol_version,
|
||||
frontend_capabilities: pmacs::attach::build_capabilities(),
|
||||
initial_size: CellSize::new(24, 80),
|
||||
};
|
||||
|
|
@ -1183,9 +1183,9 @@ fn m10_10_production_attach_non_crdt_build_does_not_negotiate_crdt_replica() {
|
|||
stream
|
||||
.set_read_timeout(Some(Duration::from_secs(5)))
|
||||
.unwrap();
|
||||
let _hello: Hello = read_message(&mut stream).expect("read Hello");
|
||||
let hello: Hello = read_message(&mut stream).expect("read Hello");
|
||||
let req = AttachRequest {
|
||||
protocol_version: PROTOCOL_VERSION,
|
||||
protocol_version: hello.protocol_version,
|
||||
frontend_capabilities: pmacs::attach::build_capabilities(),
|
||||
initial_size: CellSize::new(24, 80),
|
||||
};
|
||||
|
|
@ -2171,7 +2171,7 @@ fn m10_10_f14_production_path_keystroke_flows_to_broadcast() {
|
|||
.unwrap();
|
||||
let hello_a: Hello = read_message(&mut stream_a).expect("A Hello");
|
||||
let req_a = AttachRequest {
|
||||
protocol_version: PROTOCOL_VERSION,
|
||||
protocol_version: hello_a.protocol_version,
|
||||
frontend_capabilities: pmacs::attach::build_capabilities(),
|
||||
initial_size: CellSize::new(24, 80),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ use nix::unistd::Pid;
|
|||
use tempfile::TempDir;
|
||||
|
||||
use pmacs::attach::PMACS_TEST_SSH_BIN;
|
||||
use pmacs::protocol::{Hello, PROTOCOL_VERSION};
|
||||
use pmacs::protocol::{ADVERTISED_PROTOCOL_VERSION, Hello};
|
||||
use pmacs::transport::read_message;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -385,7 +385,7 @@ fn daemon_attach_bridges_hello_from_existing_daemon() {
|
|||
// verbatim. (No AttachRequest sent — the daemon will hold the
|
||||
// attach slot until the bridge stdin closes below.)
|
||||
let hello: Hello = read_message(&mut bridge_stdout).expect("read Hello via bridge");
|
||||
assert_eq!(hello.protocol_version, PROTOCOL_VERSION);
|
||||
assert_eq!(hello.protocol_version, ADVERTISED_PROTOCOL_VERSION);
|
||||
|
||||
// Tear down: drop bridge stdin → bridge's stdin→socket copy sees
|
||||
// EOF, shuts down the socket write half, the daemon notices and
|
||||
|
|
@ -424,7 +424,7 @@ fn daemon_attach_auto_starts_missing_daemon() {
|
|||
// bound the socket and the bridge connected.
|
||||
let hello: Hello =
|
||||
read_message(&mut bridge_stdout).expect("read Hello via auto-started daemon");
|
||||
assert_eq!(hello.protocol_version, PROTOCOL_VERSION);
|
||||
assert_eq!(hello.protocol_version, ADVERTISED_PROTOCOL_VERSION);
|
||||
|
||||
// The lockfile must exist now: `acquire_lock` writes it on
|
||||
// daemon startup. (Existence of the lockfile is what proves
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ use tempfile::TempDir;
|
|||
|
||||
use pmacs::cell::CellSize;
|
||||
use pmacs::protocol::{
|
||||
AttachRequest, FrontendCapabilities, FrontendEvent, Hello, InstanceMessage, Key, KeyEvent,
|
||||
Modifiers, PROTOCOL_VERSION,
|
||||
ADVERTISED_PROTOCOL_VERSION, AttachRequest, FrontendCapabilities, FrontendEvent, Hello,
|
||||
InstanceMessage, Key, KeyEvent, Modifiers,
|
||||
};
|
||||
use pmacs::transport::{TransportError, read_message, write_message};
|
||||
|
||||
|
|
@ -158,9 +158,9 @@ fn build_default_caps() -> FrontendCapabilities {
|
|||
|
||||
fn do_handshake(stream: &mut UnixStream) -> Hello {
|
||||
let hello: Hello = read_message(stream).expect("read Hello");
|
||||
assert_eq!(hello.protocol_version, PROTOCOL_VERSION);
|
||||
assert_eq!(hello.protocol_version, ADVERTISED_PROTOCOL_VERSION);
|
||||
let req = AttachRequest {
|
||||
protocol_version: PROTOCOL_VERSION,
|
||||
protocol_version: hello.protocol_version,
|
||||
frontend_capabilities: build_default_caps(),
|
||||
initial_size: CellSize::new(24, 80),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ use std::time::{Duration, Instant};
|
|||
|
||||
use pmacs::cell::{Cell, CellSize, Glyph};
|
||||
use pmacs::protocol::{
|
||||
AttachRequest, FrontendEvent, FrontendId, Hello, InstanceMessage, Key, KeyEvent, Modifiers,
|
||||
PROTOCOL_VERSION,
|
||||
ADVERTISED_PROTOCOL_VERSION, AttachRequest, FrontendEvent, FrontendId, Hello, InstanceMessage,
|
||||
Key, KeyEvent, Modifiers,
|
||||
};
|
||||
use pmacs::transport::{read_message, write_message};
|
||||
|
||||
|
|
@ -75,11 +75,11 @@ fn attach(daemon: &TestDaemon) -> (Client, Grid) {
|
|||
.set_read_timeout(Some(Duration::from_secs(5)))
|
||||
.expect("set daemon handshake timeout");
|
||||
let hello: Hello = read_message(&mut stream).expect("read daemon Hello");
|
||||
assert_eq!(hello.protocol_version, PROTOCOL_VERSION);
|
||||
assert_eq!(hello.protocol_version, ADVERTISED_PROTOCOL_VERSION);
|
||||
write_message(
|
||||
&mut stream,
|
||||
&AttachRequest {
|
||||
protocol_version: PROTOCOL_VERSION,
|
||||
protocol_version: hello.protocol_version,
|
||||
frontend_capabilities: build_default_caps(),
|
||||
initial_size: CellSize::new(ROWS, COLS),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -716,8 +716,8 @@ fn a37_real_daemon_real_pty_and_headless_gpu_render_one_terminal_session() {
|
|||
|
||||
assert_eq!(
|
||||
facts.get("server_protocol_version").copied(),
|
||||
Some("21"),
|
||||
"the real daemon negotiated v21 with the real client: {text}"
|
||||
Some("20"),
|
||||
"the dark v21 wire slice must keep the real client on v20: {text}"
|
||||
);
|
||||
assert_eq!(
|
||||
facts.get("entered_terminal_mode").copied(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue