detached f791584

refactor(protocol): isolate binary wire encoding (#228)

* refactor(protocol): isolate binary wire encoding

* docs(architecture): record wire module ownership

* docs(architecture): correct wire layer map
commit
f791584fd55cd239e4484d7ada11e6b587ec64ec
author
bnema · · 2026-08-28 13:03 UTC
committer
GitHub · · 2026-08-28 13:03 UTC
parent
b21386d
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
137 files · +1868 -1790
M AGENTS.md +4 -2
@@ -37,7 +37,9 @@ Hexagonal boundaries are enforced by `boundary_test.go`.
3737 Layer map:
3838
3939 - `main.go` → `internal/app`: CLI parsing, wiring, daemon startup, hidden subcommands.
40-- `internal/ports`: interfaces and wire protocol.
40+- `internal/ports`: application and raw carriage interfaces.
41+- `internal/protocol`: typed transport-neutral messages and negotiated session policy.
42+- `internal/protocol/wire`: message IDs, frames, strict binary codecs, compression, and encoded bounds.
4143 - `internal/usecase/daemon`: sessions, tabs, panes, VT screens, renderer shadows, daemon features.
4244 - `internal/usecase/client`: raw-mode thin client; writes output bytes verbatim and interprets nothing.
4345 - `internal/adapters`: IPC, SSH stdio, PTY, terminal, and clock implementations.
@@ -61,7 +63,7 @@ Before touching daemon teardown paths, read the lock-ordering notes at the top o
6163
6264 ## Wire protocol
6365
64-Semantic presentation, route, and session-control values live in `internal/protocol`. Frame IDs and codecs live in `internal/ports/frame.go`, `internal/ports/wire.go`, `internal/ports/routes_wire.go`, and `internal/ports/wire_command.go`. Connection framing lives in `internal/adapters/ipc/transport.go`.
66+Typed messages and negotiated version live in `internal/protocol`. Message IDs, frames, strict codecs, compression, and encoded bounds live in `internal/protocol/wire`; raw carriage interfaces remain in `internal/ports`. Connection framing lives in concrete carriage adapters such as `internal/adapters/ipc`.
6567
6668 - IPC frames on a connection are 4-byte big-endian length, 1 type byte, then payload.
6769 - Client message types occupy `1–13`, `15`, `32–33`, and `35` (`MsgParkedRouteRequest`); server types occupy `16–23`, `25–31`, `34`, and `36` (`MsgParkedRouteResponse`). Types `14` and `24` remain reserved.
M internal/adapters/dgram/control_plane_test.go +5 -4
@@ -10,6 +10,7 @@ import (
1010 "time"
1111
1212 "github.com/bnema/vev/internal/ports"
13+ "github.com/bnema/vev/internal/protocol/wire"
1314 pdgram "github.com/bnema/vev/pkg/dgram"
1415 )
1516
@@ -81,7 +82,7 @@ func TestReadLoopDoesNotWriteACKSynchronously(t *testing.T) {
8182 a.diagnosticCh = make(chan Diagnostic, 1)
8283 a.mu.Unlock()
8384
84- if err := b.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("first")}); err != nil {
85+ if err := b.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("first")}); err != nil {
8586 t.Fatal(err)
8687 }
8788 awaitSignal(t, contacts, "first authenticated record")
@@ -92,7 +93,7 @@ func TestReadLoopDoesNotWriteACKSynchronously(t *testing.T) {
9293 awaitSignal(t, ackStarted, "blocked ACK")
9394
9495 clk.advance(time.Second)
95- if err := b.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("second")}); err != nil {
96+ if err := b.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("second")}); err != nil {
9697 t.Fatal(err)
9798 }
9899 awaitSignal(t, contacts, "second authenticated record while ACK write is blocked")
@@ -471,7 +472,7 @@ func TestHeartbeatContinuesDuringRetransmitStorm(t *testing.T) {
471472 a.mu.Lock()
472473 for seq := uint64(1); seq <= 64; seq++ {
473474 a.pending[seq] = &pending{
474- frame: ports.Frame{Type: ports.MsgInput, Payload: make([]byte, 24*a.mtu)},
475+ frame: wire.Frame{Type: wire.MsgInput, Payload: make([]byte, 24*a.mtu)},
475476 enqueued: now.Add(-time.Second), first: now.Add(-time.Second), last: now.Add(-time.Second),
476477 }
477478 }
@@ -515,7 +516,7 @@ func TestHealthChecksContinueDuringRetransmitStorm(t *testing.T) {
515516 now := clk.Now()
516517 a.mu.Lock()
517518 a.pending[1] = &pending{
518- frame: ports.Frame{Type: ports.MsgInput, Payload: make([]byte, 24*a.mtu)},
519+ frame: wire.Frame{Type: wire.MsgInput, Payload: make([]byte, 24*a.mtu)},
519520 enqueued: now.Add(-time.Second), first: now.Add(-time.Second), last: now.Add(-time.Second),
520521 }
521522 a.mu.Unlock()
M internal/adapters/dgram/diagnostics_test.go +3 -2
@@ -6,6 +6,7 @@ import (
66 "time"
77
88 "github.com/bnema/vev/internal/ports"
9+ "github.com/bnema/vev/internal/protocol/wire"
910 )
1011
1112 func TestDiagnosticSnapshotSeparatesProgressAges(t *testing.T) {
@@ -20,8 +21,8 @@ func TestDiagnosticSnapshotSeparatesProgressAges(t *testing.T) {
2021 retransmits: 64,
2122 reassemblyInflight: 2,
2223 pending: map[uint64]*pending{
23- 1: {frame: ports.Frame{Payload: make([]byte, 7)}},
24- 2: {frame: ports.Frame{Payload: make([]byte, 11)}},
24+ 1: {frame: wire.Frame{Payload: make([]byte, 7)}},
25+ 2: {frame: wire.Frame{Payload: make([]byte, 11)}},
2526 },
2627 diagnosticCh: make(chan Diagnostic, diagnosticBufferSize),
2728 done: make(chan struct{}),
M internal/adapters/dgram/network_sim_test.go +7 -6
@@ -12,6 +12,7 @@ import (
1212 "github.com/bnema/vev/internal/domain"
1313 "github.com/bnema/vev/internal/ports"
1414 "github.com/bnema/vev/internal/protocol"
15+ "github.com/bnema/vev/internal/protocol/wire"
1516 pdgram "github.com/bnema/vev/pkg/dgram"
1617 )
1718
@@ -454,7 +455,7 @@ func TestTransportFloodClassification(t *testing.T) {
454455 }
455456 b.mu.Unlock()
456457 primer := mustMarshalOutput(protocol.Output{Epoch: 1, Base: 0, New: 1, Size: domain.Size{Cols: 1, Rows: 1}, Full: true, Data: []byte("primer")})
457- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: primer}); err != nil {
458+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: primer}); err != nil {
458459 t.Fatal(err)
459460 }
460461 awaitSignal(t, primerWritten, "primer write")
@@ -537,7 +538,7 @@ func TestTransportFloodClassification(t *testing.T) {
537538 Size: domain.Size{Cols: 1, Rows: 1},
538539 Data: bytes.Repeat([]byte{byte(state + 1)}, outputDataBytes),
539540 })
540- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: payload}); err != nil {
541+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: payload}); err != nil {
541542 t.Fatal(err)
542543 }
543544 }
@@ -631,7 +632,7 @@ func TestTransportFloodClassification(t *testing.T) {
631632 for {
632633 select {
633634 case frame := <-b.in:
634- output, err := ports.UnmarshalOutput(frame.Payload)
635+ output, err := wire.UnmarshalOutput(frame.Payload)
635636 if err != nil {
636637 t.Fatal(err)
637638 }
@@ -795,7 +796,7 @@ func TestTransportFloodClassification(t *testing.T) {
795796 // A fresh record queues behind the sole data sender while control traffic
796797 // remains independent of congestion tokens and the blocked data write.
797798 go func() {
798- freshReturned <- a.Send(ports.Frame{Type: ports.MsgOutput, Payload: floodOutputPayload(floodRecordCount, 128)})
799+ freshReturned <- a.Send(wire.Frame{Type: wire.MsgOutput, Payload: floodOutputPayload(floodRecordCount, 128)})
799800 }()
800801 select {
801802 case err := <-freshReturned:
@@ -869,7 +870,7 @@ func TestFloodOutputPayloadIsIncrementalStateBearingOutput(t *testing.T) {
869870 const mtu = 128
870871 for state := range floodRecordCount {
871872 payload := floodOutputPayload(state, mtu)
872- output, err := ports.UnmarshalOutput(payload)
873+ output, err := wire.UnmarshalOutput(payload)
873874 if err != nil {
874875 t.Fatalf("state %d: %v", state, err)
875876 }
@@ -889,7 +890,7 @@ func TestFloodOutputPayloadIsIncrementalStateBearingOutput(t *testing.T) {
889890 func sendFloodOutputs(t *testing.T, sender *Transport, count, mtu int) {
890891 t.Helper()
891892 for state := range count {
892- if err := sender.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: floodOutputPayload(state, mtu)}); err != nil {
893+ if err := sender.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: floodOutputPayload(state, mtu)}); err != nil {
893894 t.Fatal(err)
894895 }
895896 }
M internal/adapters/dgram/output_pacer.go +4 -4
@@ -1,11 +1,11 @@
11 package dgram
22
3-import "github.com/bnema/vev/internal/ports"
3+import "github.com/bnema/vev/internal/protocol/wire"
44
55 type dataSendJob struct {
66 seq uint64
77 reliable bool
8- frame ports.Frame
8+ frame wire.Frame
99 done chan error
1010 }
1111
@@ -52,8 +52,8 @@ func (t *Transport) notifyOutputPacerLocked() {
5252 t.outputWake = make(chan struct{})
5353 }
5454
55-func shouldPaceOutput(f ports.Frame) bool {
56- return f.Type == ports.MsgOutput
55+func shouldPaceOutput(f wire.Frame) bool {
56+ return f.Type == wire.MsgOutput
5757 }
5858
5959 func (t *Transport) markPendingSent(seq uint64, reliable bool) {
M internal/adapters/dgram/proxy.go +6 -5
@@ -8,6 +8,7 @@ import (
88 "time"
99
1010 "github.com/bnema/vev/internal/ports"
11+ "github.com/bnema/vev/internal/protocol/wire"
1112 )
1213
1314 const (
@@ -50,7 +51,7 @@ type proxyCopyDirection struct {
5051 dst ports.Transport
5152 recvKind proxyCopyErrKind
5253 sendKind proxyCopyErrKind
53- transform func(ports.Frame) ports.Frame
54+ transform func(wire.Frame) wire.Frame
5455 retryRecoverable bool
5556 }
5657
@@ -211,15 +212,15 @@ func recoverableClientLink(t ports.Transport, err error) bool {
211212 // clampDatagramHelloOutputWindow enforces the datagram-safe state window at
212213 // the adapter boundary. Invalid Hello payloads pass through unchanged so the
213214 // daemon remains the single authority for strict decoding and version checks.
214-func clampDatagramHelloOutputWindow(f ports.Frame) ports.Frame {
215- if f.Type != ports.MsgHello {
215+func clampDatagramHelloOutputWindow(f wire.Frame) wire.Frame {
216+ if f.Type != wire.MsgHello {
216217 return f
217218 }
218- hello, err := ports.UnmarshalHello(f.Payload)
219+ hello, err := wire.UnmarshalHello(f.Payload)
219220 if err != nil {
220221 return f
221222 }
222223 hello.MaxOutputInFlight = 1
223- f.Payload = ports.MarshalHello(hello)
224+ f.Payload = wire.MarshalHello(hello)
224225 return f
225226 }
M internal/adapters/dgram/proxy_test.go +28 -27
@@ -13,11 +13,12 @@ import (
1313 "github.com/bnema/vev/internal/domain"
1414 "github.com/bnema/vev/internal/ports"
1515 "github.com/bnema/vev/internal/protocol"
16+ "github.com/bnema/vev/internal/protocol/wire"
1617 )
1718
1819 type fakeTransport struct {
1920 recv chan recvResult
20- sent chan ports.Frame
21+ sent chan wire.Frame
2122 sendErr chan error
2223 linkState ports.LinkState
2324 linkEvents chan ports.LinkEvent
@@ -26,14 +27,14 @@ type fakeTransport struct {
2627 }
2728
2829 type recvResult struct {
29- frame ports.Frame
30+ frame wire.Frame
3031 err error
3132 }
3233
3334 func newFakeTransport() *fakeTransport {
34- return &fakeTransport{recv: make(chan recvResult, 4), sent: make(chan ports.Frame, 4), sendErr: make(chan error, 4), linkEvents: make(chan ports.LinkEvent, 4), linkState: ports.LinkStateConnected, closed: make(chan struct{})}
35+ return &fakeTransport{recv: make(chan recvResult, 4), sent: make(chan wire.Frame, 4), sendErr: make(chan error, 4), linkEvents: make(chan ports.LinkEvent, 4), linkState: ports.LinkStateConnected, closed: make(chan struct{})}
3536 }
36-func (f *fakeTransport) Send(fr ports.Frame) error {
37+func (f *fakeTransport) Send(fr wire.Frame) error {
3738 select {
3839 case err := <-f.sendErr:
3940 return err
@@ -42,12 +43,12 @@ func (f *fakeTransport) Send(fr ports.Frame) error {
4243 f.sent <- fr
4344 return nil
4445 }
45-func (f *fakeTransport) Recv() (ports.Frame, error) {
46+func (f *fakeTransport) Recv() (wire.Frame, error) {
4647 select {
4748 case r := <-f.recv:
4849 return r.frame, r.err
4950 case <-f.closed:
50- return ports.Frame{}, io.EOF
51+ return wire.Frame{}, io.EOF
5152 }
5253 }
5354 func (f *fakeTransport) Close() error {
@@ -62,10 +63,10 @@ func TestProxyRuntimeCopiesFramesAndDaemonEOFIsTerminal(t *testing.T) {
6263 daemon := newFakeTransport()
6364 errCh := make(chan error, 1)
6465 go func() { errCh <- ProxyRuntime{Client: client, Daemon: daemon, IdleTTL: time.Hour}.Run(t.Context()) }()
65- client.recv <- recvResult{frame: ports.Frame{Type: ports.MsgInput, Payload: []byte("x")}}
66+ client.recv <- recvResult{frame: wire.Frame{Type: wire.MsgInput, Payload: []byte("x")}}
6667 select {
6768 case got := <-daemon.sent:
68- if got.Type != ports.MsgInput {
69+ if got.Type != wire.MsgInput {
6970 t.Fatalf("daemon got type %v", got.Type)
7071 }
7172 case <-time.After(time.Second):
@@ -90,11 +91,11 @@ func TestProxyRuntimeClampsDatagramHelloOutputWindowAndPreservesControl(t *testi
9091 errCh := make(chan error, 1)
9192 go func() { errCh <- ProxyRuntime{Client: client, Daemon: daemon, IdleTTL: time.Hour}.Run(t.Context()) }()
9293 hello := protocol.Hello{Version: protocol.Version, Intent: protocol.IntentAttach, Name: "work", Size: domain.Size{Cols: 80, Rows: 24}, MaxOutputInFlight: requested}
93- client.recv <- recvResult{frame: ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(hello)}}
94+ client.recv <- recvResult{frame: wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(hello)}}
9495
9596 select {
9697 case frame := <-daemon.sent:
97- got, err := ports.UnmarshalHello(frame.Payload)
98+ got, err := wire.UnmarshalHello(frame.Payload)
9899 if err != nil {
99100 t.Fatal(err)
100101 }
@@ -105,11 +106,11 @@ func TestProxyRuntimeClampsDatagramHelloOutputWindowAndPreservesControl(t *testi
105106 t.Fatal("timeout waiting for proxied Hello")
106107 }
107108
108- input := ports.Frame{Type: ports.MsgInput, Payload: []byte("still flowing")}
109- ack := ports.Frame{Type: ports.MsgAck, Payload: mustMarshalAck(protocol.Ack{Epoch: 1, State: 7})}
109+ input := wire.Frame{Type: wire.MsgInput, Payload: []byte("still flowing")}
110+ ack := wire.Frame{Type: wire.MsgAck, Payload: mustMarshalAck(protocol.Ack{Epoch: 1, State: 7})}
110111 client.recv <- recvResult{frame: input}
111112 client.recv <- recvResult{frame: ack}
112- for _, want := range []ports.Frame{input, ack} {
113+ for _, want := range []wire.Frame{input, ack} {
113114 select {
114115 case got := <-daemon.sent:
115116 if !reflect.DeepEqual(got, want) {
@@ -149,10 +150,10 @@ func TestProxyRuntimeRetriesRecoverableClientSendWithoutDroppingFrame(t *testing
149150 errCh <- ProxyRuntime{Client: client, Daemon: daemon, IdleTTL: time.Hour, RetryBackoff: time.Nanosecond}.Run(t.Context())
150151 }()
151152 client.sendErr <- tt.err
152- daemon.recv <- recvResult{frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte("preserved during outage")}}
153+ daemon.recv <- recvResult{frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte("preserved during outage")}}
153154 select {
154155 case got := <-client.sent:
155- if got.Type != ports.MsgOutput || string(got.Payload) != "preserved during outage" {
156+ if got.Type != wire.MsgOutput || string(got.Payload) != "preserved during outage" {
156157 t.Fatalf("client got %+v, want retried output", got)
157158 }
158159 case <-time.After(time.Second):
@@ -187,10 +188,10 @@ func TestProxyRuntimeSurvivesPendingFullAndPreservesOrder(t *testing.T) {
187188 const frames = 4
188189 for i := range frames {
189190 client.sendErr <- ErrPendingFull
190- daemon.recv <- recvResult{frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte{byte(i)}}}
191+ daemon.recv <- recvResult{frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte{byte(i)}}}
191192 select {
192193 case got := <-client.sent:
193- if got.Type != ports.MsgOutput || len(got.Payload) != 1 || got.Payload[0] != byte(i) {
194+ if got.Type != wire.MsgOutput || len(got.Payload) != 1 || got.Payload[0] != byte(i) {
194195 t.Fatalf("frame %d delivered out of order or dropped: %+v", i, got)
195196 }
196197 case <-time.After(time.Second):
@@ -378,7 +379,7 @@ func TestProxyRuntimeClientSendDeadWaitsForIdleTTL(t *testing.T) {
378379 }()
379380
380381 client.sendErr <- ErrLinkDead
381- daemon.recv <- recvResult{frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte("client is gone")}}
382+ daemon.recv <- recvResult{frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte("client is gone")}}
382383 waitForProxyManualTimers(t, clk, 1)
383384 select {
384385 case err := <-errCh:
@@ -443,14 +444,14 @@ func newRetryingOfflineTransport() *retryingOfflineTransport {
443444 return &retryingOfflineTransport{closed: make(chan struct{}), firstSend: make(chan struct{})}
444445 }
445446
446-func (t *retryingOfflineTransport) Send(ports.Frame) error {
447+func (t *retryingOfflineTransport) Send(wire.Frame) error {
447448 t.firstSendOnce.Do(func() { close(t.firstSend) })
448449 return errors.New("offline")
449450 }
450451
451-func (t *retryingOfflineTransport) Recv() (ports.Frame, error) {
452+func (t *retryingOfflineTransport) Recv() (wire.Frame, error) {
452453 <-t.closed
453- return ports.Frame{}, io.EOF
454+ return wire.Frame{}, io.EOF
454455 }
455456
456457 func (t *retryingOfflineTransport) Close() error {
@@ -474,7 +475,7 @@ func TestProxyCopierStopsRetryingWhenContextCanceled(t *testing.T) {
474475 close(done)
475476 }()
476477
477- daemon.recv <- recvResult{frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte("retry until cancel")}}
478+ daemon.recv <- recvResult{frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte("retry until cancel")}}
478479 select {
479480 case <-client.firstSend:
480481 case <-time.After(time.Second):
@@ -490,23 +491,23 @@ func TestProxyCopierStopsRetryingWhenContextCanceled(t *testing.T) {
490491
491492 func TestClampDatagramHelloOutputWindowPreservesProtocolValidation(t *testing.T) {
492493 t.Run("malformed payload remains malformed", func(t *testing.T) {
493- payload := append(ports.MarshalHello(protocol.Hello{Version: protocol.Version, Size: domain.Size{Cols: 80, Rows: 24}, MaxOutputInFlight: 8}), 0xff)
494- frame := ports.Frame{Type: ports.MsgHello, Payload: payload}
494+ payload := append(wire.MarshalHello(protocol.Hello{Version: protocol.Version, Size: domain.Size{Cols: 80, Rows: 24}, MaxOutputInFlight: 8}), 0xff)
495+ frame := wire.Frame{Type: wire.MsgHello, Payload: payload}
495496 got := clampDatagramHelloOutputWindow(frame)
496497 if !reflect.DeepEqual(got, frame) {
497498 t.Fatalf("malformed Hello changed: got %#v, want %#v", got, frame)
498499 }
499- if _, err := ports.UnmarshalHello(got.Payload); err == nil {
500+ if _, err := wire.UnmarshalHello(got.Payload); err == nil {
500501 t.Fatal("malformed Hello became decodable")
501502 }
502503 })
503504
504505 t.Run("mismatched version remains mismatched", func(t *testing.T) {
505506 wantVersion := protocol.Version + 1
506- frame := ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(protocol.Hello{
507+ frame := wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(protocol.Hello{
507508 Version: wantVersion, Size: domain.Size{Cols: 80, Rows: 24}, MaxOutputInFlight: 8,
508509 })}
509- got, err := ports.UnmarshalHello(clampDatagramHelloOutputWindow(frame).Payload)
510+ got, err := wire.UnmarshalHello(clampDatagramHelloOutputWindow(frame).Payload)
510511 if err != nil {
511512 t.Fatal(err)
512513 }
M internal/adapters/dgram/transport.go +5 -4
@@ -9,6 +9,7 @@ import (
99 "time"
1010
1111 "github.com/bnema/vev/internal/ports"
12+ "github.com/bnema/vev/internal/protocol/wire"
1213 pdgram "github.com/bnema/vev/pkg/dgram"
1314 )
1415
@@ -221,7 +222,7 @@ type Transport struct {
221222 recvMu sync.Mutex
222223 replay *pdgram.ReplayWindow
223224 reasm *pdgram.Reassembler
224- in chan ports.Frame
225+ in chan wire.Frame
225226 done chan struct{}
226227
227228 // Connection replacement takes writeMu, controlConnMu, then mu. Data writes
@@ -257,11 +258,11 @@ type Transport struct {
257258 deliverMu sync.Mutex
258259 deliverCond *sync.Cond
259260 nextRecvSeq uint64
260- recvBuf map[uint64]ports.Frame
261+ recvBuf map[uint64]wire.Frame
261262 }
262263
263264 type pending struct {
264- frame ports.Frame
265+ frame wire.Frame
265266 enqueued time.Time
266267 first time.Time
267268 last time.Time
@@ -273,7 +274,7 @@ type pending struct {
273274
274275 type retransmitRecord struct {
275276 seq uint64
276- f ports.Frame
277+ f wire.Frame
277278 }
278279
279280 type writeDeadlineState struct {
M internal/adapters/dgram/transport_health_test.go +11 -10
@@ -10,6 +10,7 @@ import (
1010 "time"
1111
1212 "github.com/bnema/vev/internal/ports"
13+ "github.com/bnema/vev/internal/protocol/wire"
1314 pdgram "github.com/bnema/vev/pkg/dgram"
1415 )
1516
@@ -32,7 +33,7 @@ func TestPortHopPreservesPendingReliableMessages(t *testing.T) {
3233 defer func() { _ = a.Close() }()
3334 defer func() { _ = b.Close() }()
3435
35- if err := a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("survive")}); err != nil {
36+ if err := a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("survive")}); err != nil {
3637 t.Fatal(err)
3738 }
3839 a.mu.Lock()
@@ -44,7 +45,7 @@ func TestPortHopPreservesPendingReliableMessages(t *testing.T) {
4445 }
4546 aPC.drop = nil
4647 got := recvWithin(t, b, time.Second)
47- if got.Type != ports.MsgInput || string(got.Payload) != "survive" {
48+ if got.Type != wire.MsgInput || string(got.Payload) != "survive" {
4849 t.Fatalf("got=%+v, want pending reliable message", got)
4950 }
5051 select {
@@ -151,8 +152,8 @@ func TestRecvUnblocksWhenPeerDeadAfter(t *testing.T) {
151152 func TestPendingAttemptsResetWhenLinkRecovers(t *testing.T) {
152153 tr, _ := newResendTestTransport(t, 10)
153154 tr.linkState = ports.LinkStateDegraded
154- tr.pending[1] = &pending{frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte("stale")}, attempts: 4}
155- tr.pending[2] = &pending{frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte("stale")}, attempts: 7}
155+ tr.pending[1] = &pending{frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte("stale")}, attempts: 4}
156+ tr.pending[2] = &pending{frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte("stale")}, attempts: 7}
156157
157158 tr.setLinkState(ports.LinkStateConnected, nil)
158159
@@ -200,12 +201,12 @@ func TestPendingReliableQueueReturnsWhenLinkDegrades(t *testing.T) {
200201 defer func() { _ = a.Close() }()
201202 defer func() { _ = b.Close() }()
202203
203- if err := a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("first")}); err != nil {
204+ if err := a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("first")}); err != nil {
204205 t.Fatal(err)
205206 }
206207 done := make(chan error, 2)
207- go func() { done <- a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("second")}) }()
208- go func() { done <- a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("third")}) }()
208+ go func() { done <- a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("second")}) }()
209+ go func() { done <- a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("third")}) }()
209210 waitForManualTimers(t, clk, 2)
210211 a.setLinkState(ports.LinkStateDegraded, nil)
211212 for i := range 2 {
@@ -313,7 +314,7 @@ func TestLinkHealthPendingStallWithFreshContactProbesOnceWithoutDying(t *testing
313314 now := time.Now()
314315 a.mu.Lock()
315316 a.health = healthTracker{lastPacket: now, lastRecord: now.Add(-time.Minute), lastProgress: now.Add(-time.Minute)}
316- a.pending[1] = &pending{frame: ports.Frame{Type: ports.MsgOutput}, enqueued: now.Add(-time.Minute), attempts: 3}
317+ a.pending[1] = &pending{frame: wire.Frame{Type: wire.MsgOutput}, enqueued: now.Add(-time.Minute), attempts: 3}
317318 a.mu.Unlock()
318319
319320 a.checkSilence()
@@ -347,7 +348,7 @@ func TestLinkHealthHeartbeatControlCannotMaskPendingACKStall(t *testing.T) {
347348 }
348349 defer func() { _ = tr.Close() }()
349350 defer func() { _ = bPC.Close() }()
350- if err := tr.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("unacked")}); err != nil {
351+ if err := tr.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("unacked")}); err != nil {
351352 t.Fatal(err)
352353 }
353354
@@ -417,7 +418,7 @@ func TestLinkHealthOnlyAckProgressThatRemovesPendingRestoresConnected(t *testing
417418 now := tr.clock.Now()
418419 tr.linkState = ports.LinkStateDegraded
419420 tr.health = healthTracker{lastPacket: now, lastRecord: now.Add(-time.Minute), lastProgress: now.Add(-time.Minute)}
420- tr.pending[1] = &pending{frame: ports.Frame{Type: ports.MsgOutput}, first: now, last: now}
421+ tr.pending[1] = &pending{frame: wire.Frame{Type: wire.MsgOutput}, first: now, last: now}
421422
422423 tr.handleRecord(ackRecord(1))
423424 if got := tr.LinkState(); got != ports.LinkStateConnected {
M internal/adapters/dgram/transport_lifecycle.go +6 -5
@@ -6,6 +6,7 @@ import (
66 "sync"
77
88 "github.com/bnema/vev/internal/ports"
9+ "github.com/bnema/vev/internal/protocol/wire"
910 pdgram "github.com/bnema/vev/pkg/dgram"
1011 )
1112
@@ -29,7 +30,7 @@ func NewTransportWithOptions(pc net.PacketConn, peer net.Addr, key []byte, sendD
2930 pending: make(map[uint64]*pending),
3031 replay: pdgram.NewReplayWindow(),
3132 reasm: pdgram.NewReassembler(),
32- in: make(chan ports.Frame, 32),
33+ in: make(chan wire.Frame, 32),
3334 done: make(chan struct{}),
3435 health: newHealthTracker(opts.Clock.Now()),
3536 lastAuthenticatedPacket: opts.Clock.Now(),
@@ -56,7 +57,7 @@ func NewTransportWithOptions(pc net.PacketConn, peer net.Addr, key []byte, sendD
5657 probeWait: make(map[uint64]chan struct{}),
5758 rebind: opts.RebindPacketConn,
5859 nextRecvSeq: 1,
59- recvBuf: make(map[uint64]ports.Frame),
60+ recvBuf: make(map[uint64]wire.Frame),
6061 }
6162 for _, opt := range runtimeOpts {
6263 if opt != nil {
@@ -90,7 +91,7 @@ func NewTransportWithOptions(pc net.PacketConn, peer net.Addr, key []byte, sendD
9091 return t, nil
9192 }
9293
93-func (t *Transport) Recv() (ports.Frame, error) {
94+func (t *Transport) Recv() (wire.Frame, error) {
9495 end := t.beginRuntimeOperation(ports.RuntimeAdapterReceiveStart, 0)
9596 select {
9697 case f := <-t.in:
@@ -102,11 +103,11 @@ func (t *Transport) Recv() (ports.Frame, error) {
102103 t.mu.Unlock()
103104 if err != nil {
104105 end(false)
105- return ports.Frame{}, err
106+ return wire.Frame{}, err
106107 }
107108 err = errors.New("dgram: closed")
108109 end(false)
109- return ports.Frame{}, err
110+ return wire.Frame{}, err
110111 }
111112 }
112113
M internal/adapters/dgram/transport_observability_test.go +4 -3
@@ -13,6 +13,7 @@ import (
1313
1414 "github.com/bnema/vev/internal/adapters/observability"
1515 "github.com/bnema/vev/internal/ports"
16+ "github.com/bnema/vev/internal/protocol/wire"
1617 )
1718
1819 func TestTransportObservabilityDgramFailedCloseEndsReceiveAndSend(t *testing.T) {
@@ -33,7 +34,7 @@ func TestTransportObservabilityDgramFailedCloseEndsReceiveAndSend(t *testing.T)
3334 if err != nil {
3435 t.Fatalf("NewTransportWithOptions() error = %v", err)
3536 }
36- if err := transport.Send(ports.Frame{Type: ports.MsgOutput, Payload: []byte("write failure")}); err == nil {
37+ if err := transport.Send(wire.Frame{Type: wire.MsgOutput, Payload: []byte("write failure")}); err == nil {
3738 t.Fatal("Send() error = nil on packet write failure")
3839 }
3940 recvDone := make(chan error, 1)
@@ -155,11 +156,11 @@ func TestTransportObservabilityDgramKeepsBehaviorClockSeparate(t *testing.T) {
155156 }
156157 defer func() { _ = b.Close() }()
157158
158- want := ports.Frame{Type: ports.MsgOutput, Payload: []byte("datagram carriage remains exact")}
159+ want := wire.Frame{Type: wire.MsgOutput, Payload: []byte("datagram carriage remains exact")}
159160 if err := a.Send(want); err != nil {
160161 t.Fatalf("Send() error = %v", err)
161162 }
162- gotCh := make(chan ports.Frame, 1)
163+ gotCh := make(chan wire.Frame, 1)
163164 errCh := make(chan error, 1)
164165 go func() {
165166 frame, recvErr := b.Recv()
M internal/adapters/dgram/transport_receive.go +8 -7
@@ -5,6 +5,7 @@ import (
55 "net"
66
77 "github.com/bnema/vev/internal/ports"
8+ "github.com/bnema/vev/internal/protocol/wire"
89 pdgram "github.com/bnema/vev/pkg/dgram"
910 )
1011
@@ -176,7 +177,7 @@ func (t *Transport) handleRecord(p []byte) {
176177 }
177178 }
178179
179-func (t *Transport) enqueueReliable(seq uint64, f ports.Frame) (ackSeq uint64, ack bool, queued bool) {
180+func (t *Transport) enqueueReliable(seq uint64, f wire.Frame) (ackSeq uint64, ack bool, queued bool) {
180181 t.deliverMu.Lock()
181182 defer t.deliverMu.Unlock()
182183 if seq < t.nextRecvSeq {
@@ -235,14 +236,14 @@ func (t *Transport) isClosed() bool {
235236 return t.closed
236237 }
237238
238-func (t *Transport) deliver(f ports.Frame) {
239+func (t *Transport) deliver(f wire.Frame) {
239240 select {
240241 case t.in <- f:
241242 case <-t.done:
242243 }
243244 }
244245
245-func encodeData(seq uint64, reliable bool, f ports.Frame) []byte {
246+func encodeData(seq uint64, reliable bool, f wire.Frame) []byte {
246247 b := make([]byte, dataRecordHeaderSize+len(f.Payload))
247248 b[0] = recData
248249 binary.BigEndian.PutUint64(b[1:9], seq)
@@ -253,12 +254,12 @@ func encodeData(seq uint64, reliable bool, f ports.Frame) []byte {
253254 copy(b[dataRecordHeaderSize:], f.Payload)
254255 return b
255256 }
256-func decodeData(b []byte) (uint64, bool, ports.Frame, bool) {
257+func decodeData(b []byte) (uint64, bool, wire.Frame, bool) {
257258 if len(b) < dataRecordHeaderSize || b[0] != recData {
258- return 0, false, ports.Frame{}, false
259+ return 0, false, wire.Frame{}, false
259260 }
260261 if b[11] != 0 {
261- return 0, false, ports.Frame{}, false
262+ return 0, false, wire.Frame{}, false
262263 }
263- return binary.BigEndian.Uint64(b[1:9]), b[9] == 1, ports.Frame{Type: ports.MsgType(b[10]), Payload: append([]byte(nil), b[dataRecordHeaderSize:]...)}, true
264+ return binary.BigEndian.Uint64(b[1:9]), b[9] == 1, wire.Frame{Type: wire.MsgType(b[10]), Payload: append([]byte(nil), b[dataRecordHeaderSize:]...)}, true
264265 }
M internal/adapters/dgram/transport_receive_test.go +18 -17
@@ -8,6 +8,7 @@ import (
88 "time"
99
1010 "github.com/bnema/vev/internal/ports"
11+ "github.com/bnema/vev/internal/protocol/wire"
1112 pdgram "github.com/bnema/vev/pkg/dgram"
1213 )
1314
@@ -35,11 +36,11 @@ func TestReliableDuplicateAckedButNotDeliveredTwice(t *testing.T) {
3536 t.Errorf("close b: %v", err)
3637 }
3738 }()
38- if err := a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("typed")}); err != nil {
39+ if err := a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("typed")}); err != nil {
3940 t.Fatal(err)
4041 }
4142 got := recvWithin(t, b, time.Second)
42- if got.Type != ports.MsgInput || string(got.Payload) != "typed" {
43+ if got.Type != wire.MsgInput || string(got.Payload) != "typed" {
4344 t.Fatalf("got=%+v", got)
4445 }
4546 eventually(t, time.Second, droppedAck.Load)
@@ -70,7 +71,7 @@ func TestAuthenticatedPeerChangeUpdatesPeerAndEmitsConnected(t *testing.T) {
7071 t.Fatalf("unauthenticated packet changed peer to %v", a.Peer())
7172 }
7273 c, _ := pdgram.NewCodec(key())
73- rec := encodeData(7, true, ports.Frame{Type: ports.MsgPing})
74+ rec := encodeData(7, true, wire.Frame{Type: wire.MsgPing})
7475 frags, _ := pdgram.FragmentPayload(9, rec, pdgram.DefaultMTU)
7576 raw, _ := pdgram.MarshalFragment(frags[0])
7677 aPC.in <- packet{c.Seal(2, 99, raw, nil), testAddr("evil")}
@@ -214,12 +215,12 @@ func TestAuthenticatedFragmentSeparatesContactProgressAndPeerEligibility(t *test
214215 }
215216
216217 func TestReliableReceiveBufferDoesNotDropAckedFutureFrame(t *testing.T) {
217- tr := &Transport{maxRecvBuffer: 1, nextRecvSeq: 1, recvBuf: make(map[uint64]ports.Frame)}
218- _, ack, queued := tr.enqueueReliable(2, ports.Frame{Type: ports.MsgOutput, Payload: []byte("future")})
218+ tr := &Transport{maxRecvBuffer: 1, nextRecvSeq: 1, recvBuf: make(map[uint64]wire.Frame)}
219+ _, ack, queued := tr.enqueueReliable(2, wire.Frame{Type: wire.MsgOutput, Payload: []byte("future")})
219220 if !ack || !queued {
220221 t.Fatalf("future enqueue ack=%v queued=%v, want true/true", ack, queued)
221222 }
222- _, ack, queued = tr.enqueueReliable(1, ports.Frame{Type: ports.MsgOutput, Payload: []byte("next")})
223+ _, ack, queued = tr.enqueueReliable(1, wire.Frame{Type: wire.MsgOutput, Payload: []byte("next")})
223224 if !ack || !queued {
224225 t.Fatalf("next enqueue ack=%v queued=%v, want true/true", ack, queued)
225226 }
@@ -229,19 +230,19 @@ func TestReliableReceiveBufferDoesNotDropAckedFutureFrame(t *testing.T) {
229230 }
230231
231232 func TestDataRecordWireEncoding(t *testing.T) {
232- got := encodeData(5, true, ports.Frame{Type: ports.MsgOutput, Payload: []byte("x")})
233- want := []byte{recData, 0, 0, 0, 0, 0, 0, 0, 5, 1, byte(ports.MsgOutput), 0, 'x'}
233+ got := encodeData(5, true, wire.Frame{Type: wire.MsgOutput, Payload: []byte("x")})
234+ want := []byte{recData, 0, 0, 0, 0, 0, 0, 0, 5, 1, byte(wire.MsgOutput), 0, 'x'}
234235 if !bytes.Equal(got, want) {
235236 t.Fatalf("encoded data=%v, want %v", got, want)
236237 }
237238 seq, reliable, frame, ok := decodeData(got)
238- if !ok || seq != 5 || !reliable || frame.Type != ports.MsgOutput || string(frame.Payload) != "x" {
239+ if !ok || seq != 5 || !reliable || frame.Type != wire.MsgOutput || string(frame.Payload) != "x" {
239240 t.Fatalf("decoded seq=%d reliable=%v frame=%+v ok=%v", seq, reliable, frame, ok)
240241 }
241242 }
242243
243244 func TestDataRecordWireDecodeRejectsUnknownFlags(t *testing.T) {
244- withTrailingGarbage := []byte{recData, 0, 0, 0, 0, 0, 0, 0, 5, 1, byte(ports.MsgOutput), 1, 'x'}
245+ withTrailingGarbage := []byte{recData, 0, 0, 0, 0, 0, 0, 0, 5, 1, byte(wire.MsgOutput), 1, 'x'}
245246 if _, _, _, ok := decodeData(withTrailingGarbage); ok {
246247 t.Fatal("decodeData accepted unknown data-record flags with trailing garbage")
247248 }
@@ -255,13 +256,13 @@ func TestReliableDeliveryPreservesInputSequenceOrder(t *testing.T) {
255256 defer func() { _ = b.Close() }()
256257
257258 for i := range 100 {
258- if err := a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte{byte(i)}}); err != nil {
259+ if err := a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte{byte(i)}}); err != nil {
259260 t.Fatal(err)
260261 }
261262 }
262263 for i := range 100 {
263264 got := recvWithin(t, b, time.Second)
264- if got.Type != ports.MsgInput || len(got.Payload) != 1 || got.Payload[0] != byte(i) {
265+ if got.Type != wire.MsgInput || len(got.Payload) != 1 || got.Payload[0] != byte(i) {
265266 t.Fatalf("frame %d delivered out of order: %+v", i, got)
266267 }
267268 }
@@ -275,7 +276,7 @@ func TestAckProcessingContinuesWhenConsumerBackpressured(t *testing.T) {
275276 defer func() { _ = b.Close() }()
276277
277278 for i := range cap(b.in) + 20 {
278- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte{byte(i)}}); err != nil {
279+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte{byte(i)}}); err != nil {
279280 t.Fatal(err)
280281 }
281282 }
@@ -296,9 +297,9 @@ func TestAckProcessingContinuesWhenConsumerBackpressured(t *testing.T) {
296297 }
297298
298299 func TestReliableReceiverAcksOnlyHighestContiguousSequence(t *testing.T) {
299- tr := &Transport{maxRecvBuffer: maxRecvBuffer, nextRecvSeq: 1, recvBuf: make(map[uint64]ports.Frame)}
300+ tr := &Transport{maxRecvBuffer: maxRecvBuffer, nextRecvSeq: 1, recvBuf: make(map[uint64]wire.Frame)}
300301
301- ackSeq, ack, queued := tr.enqueueReliable(3, ports.Frame{Type: ports.MsgOutput, Payload: []byte("third")})
302+ ackSeq, ack, queued := tr.enqueueReliable(3, wire.Frame{Type: wire.MsgOutput, Payload: []byte("third")})
302303 if !ack || !queued {
303304 t.Fatalf("out-of-order enqueue ack=%v queued=%v, want true/true", ack, queued)
304305 }
@@ -306,7 +307,7 @@ func TestReliableReceiverAcksOnlyHighestContiguousSequence(t *testing.T) {
306307 t.Fatalf("out-of-order ack=%d, want highest contiguous %d", got, want)
307308 }
308309
309- ackSeq, ack, queued = tr.enqueueReliable(1, ports.Frame{Type: ports.MsgOutput, Payload: []byte("first")})
310+ ackSeq, ack, queued = tr.enqueueReliable(1, wire.Frame{Type: wire.MsgOutput, Payload: []byte("first")})
310311 if !ack || !queued {
311312 t.Fatalf("first enqueue ack=%v queued=%v, want true/true", ack, queued)
312313 }
@@ -314,7 +315,7 @@ func TestReliableReceiverAcksOnlyHighestContiguousSequence(t *testing.T) {
314315 t.Fatalf("first contiguous ack=%d, want %d", got, want)
315316 }
316317
317- ackSeq, ack, queued = tr.enqueueReliable(2, ports.Frame{Type: ports.MsgOutput, Payload: []byte("second")})
318+ ackSeq, ack, queued = tr.enqueueReliable(2, wire.Frame{Type: wire.MsgOutput, Payload: []byte("second")})
318319 if !ack || !queued {
319320 t.Fatalf("gap fill enqueue ack=%v queued=%v, want true/true", ack, queued)
320321 }
M internal/adapters/dgram/transport_replay_test.go +3 -3
@@ -3,13 +3,13 @@ package dgram
33 import (
44 "testing"
55
6- "github.com/bnema/vev/internal/ports"
6+ "github.com/bnema/vev/internal/protocol/wire"
77 "github.com/bnema/vev/internal/testutil/replaytest"
88 "github.com/stretchr/testify/require"
99 )
1010
1111 func TestTransportReplay(t *testing.T) {
12- replaytest.Run(t, func(t *testing.T, frames []ports.Frame) []ports.Frame {
12+ replaytest.Run(t, func(t *testing.T, frames []wire.Frame) []wire.Frame {
1313 link := newSimulatedLink(fixedClock{}, packetPolicy{})
1414 leftPC, rightPC := newPairWithCapacity(link, 32)
1515 left, err := NewTransport(leftPC, testAddr("b"), key(), 1, 2)
@@ -18,7 +18,7 @@ func TestTransportReplay(t *testing.T) {
1818 require.NoError(t, err)
1919 t.Cleanup(func() { _ = left.Close(); _ = right.Close() })
2020
21- got := make([]ports.Frame, 0, len(frames))
21+ got := make([]wire.Frame, 0, len(frames))
2222 for _, frame := range frames {
2323 require.NoError(t, left.Send(frame))
2424 link.flush(rightPC)
M internal/adapters/dgram/transport_retransmit_test.go +21 -20
@@ -7,11 +7,12 @@ import (
77 "time"
88
99 "github.com/bnema/vev/internal/ports"
10+ "github.com/bnema/vev/internal/protocol/wire"
1011 )
1112
1213 func TestResendSkipsQueuedOutputBeforeFirstWireAttempt(t *testing.T) {
1314 tr, writes := newResendTestTransport(t, 10)
14- tr.pending[1] = &pending{frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte("queued")}}
15+ tr.pending[1] = &pending{frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte("queued")}}
1516
1617 tr.resendPending()
1718
@@ -27,7 +28,7 @@ func TestResendSkipsOutputDuringFirstWireAttempt(t *testing.T) {
2728 tr, writes := newResendTestTransport(t, 10)
2829 now := tr.clock.Now()
2930 tr.pending[1] = &pending{
30- frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte("sending")},
31+ frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte("sending")},
3132 first: now.Add(-time.Second),
3233 last: now.Add(-time.Second),
3334 initialInFlight: true,
@@ -68,11 +69,11 @@ func TestReliableInputRetransmitsUntilAck(t *testing.T) {
6869 t.Errorf("close b: %v", err)
6970 }
7071 }()
71- if err := a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("typed")}); err != nil {
72+ if err := a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("typed")}); err != nil {
7273 t.Fatal(err)
7374 }
7475 got := recvWithin(t, b, time.Second)
75- if got.Type != ports.MsgInput || string(got.Payload) != "typed" || !dropped.Load() {
76+ if got.Type != wire.MsgInput || string(got.Payload) != "typed" || !dropped.Load() {
7677 t.Fatalf("got=%+v dropped=%v", got, dropped.Load())
7778 }
7879 }
@@ -95,11 +96,11 @@ func TestOutputRetransmitsUntilAck(t *testing.T) {
9596 t.Errorf("close b: %v", err)
9697 }
9798 }()
98- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte("state1")}); err != nil {
99+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte("state1")}); err != nil {
99100 t.Fatal(err)
100101 }
101102 got := recvWithin(t, b, time.Second)
102- if got.Type != ports.MsgOutput || string(got.Payload) != "state1" || !dropped.Load() {
103+ if got.Type != wire.MsgOutput || string(got.Payload) != "state1" || !dropped.Load() {
103104 t.Fatalf("got=%+v dropped=%v", got, dropped.Load())
104105 }
105106 }
@@ -124,7 +125,7 @@ func TestImmediateLoopbackAckSamplesStampedFinalWrite(t *testing.T) {
124125 t.Error("ACK did not retire pending frame before WriteTo returned")
125126 }
126127
127- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte("state")}); err != nil {
128+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte("state")}); err != nil {
128129 t.Fatal(err)
129130 }
130131 eventually(t, time.Second, func() bool {
@@ -155,14 +156,14 @@ func TestOutputDependencyChainRetransmitsDroppedPredecessors(t *testing.T) {
155156 defer func() { _ = b.Close() }()
156157
157158 for _, payload := range []string{"state1", "state2", "state3"} {
158- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte(payload)}); err != nil {
159+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte(payload)}); err != nil {
159160 t.Fatal(err)
160161 }
161162 }
162163
163164 for _, want := range []string{"state1", "state2", "state3"} {
164165 got := recvWithin(t, b, time.Second)
165- if got.Type != ports.MsgOutput || string(got.Payload) != want {
166+ if got.Type != wire.MsgOutput || string(got.Payload) != want {
166167 t.Fatalf("got=%+v, want ordered prerequisite output %q", got, want)
167168 }
168169 }
@@ -175,7 +176,7 @@ func TestResendPendingCapsBurst(t *testing.T) {
175176 tr, writes := newResendTestTransport(t, 2)
176177 now := tr.clock.Now()
177178 for i := range 5 {
178- tr.pending[uint64(i+1)] = &pending{frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte{byte(i)}}, last: now.Add(-time.Second)}
179+ tr.pending[uint64(i+1)] = &pending{frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte{byte(i)}}, last: now.Add(-time.Second)}
179180 }
180181
181182 tr.resendPending()
@@ -189,7 +190,7 @@ func TestRetransmitBatchReducesCongestionWindowOnce(t *testing.T) {
189190 now := tr.clock.Now()
190191 for seq := uint64(1); seq <= 3; seq++ {
191192 tr.pending[seq] = &pending{
192- frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte{byte(seq)}},
193+ frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte{byte(seq)}},
193194 last: now.Add(-time.Second),
194195 }
195196 }
@@ -209,7 +210,7 @@ func TestACKBeforeQueuedRetransmitDoesNotReduceCongestionWindow(t *testing.T) {
209210 tr, writes := newResendTestTransport(t, 1)
210211 now := tr.clock.Now()
211212 tr.pending[1] = &pending{
212- frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte("acked")},
213+ frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte("acked")},
213214 first: now.Add(-time.Second),
214215 last: now.Add(-time.Second),
215216 wireBytes: tr.mtu,
@@ -244,7 +245,7 @@ func TestOversizedRetransmitHonorsPacketBudget(t *testing.T) {
244245 tr.clock = clk
245246 tr.done = make(chan struct{})
246247 tr.pending[1] = &pending{
247- frame: ports.Frame{Type: ports.MsgOutput, Payload: make([]byte, 24*tr.mtu)},
248+ frame: wire.Frame{Type: wire.MsgOutput, Payload: make([]byte, 24*tr.mtu)},
248249 last: clk.now.Add(-time.Second),
249250 }
250251 done := make(chan struct{})
@@ -274,9 +275,9 @@ func TestOversizedRetransmitHonorsPacketBudget(t *testing.T) {
274275 func TestResendPendingUsesExponentialBackoffAndMaxDelay(t *testing.T) {
275276 tr, writes := newResendTestTransport(t, 10)
276277 now := tr.clock.Now()
277- tr.pending[1] = &pending{frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte("too-soon")}, last: now.Add(-150 * time.Millisecond), attempts: 1}
278- tr.pending[2] = &pending{frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte("ready")}, last: now.Add(-250 * time.Millisecond), attempts: 1}
279- tr.pending[3] = &pending{frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte("capped")}, last: now.Add(-time.Second), attempts: 10}
278+ tr.pending[1] = &pending{frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte("too-soon")}, last: now.Add(-150 * time.Millisecond), attempts: 1}
279+ tr.pending[2] = &pending{frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte("ready")}, last: now.Add(-250 * time.Millisecond), attempts: 1}
280+ tr.pending[3] = &pending{frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte("capped")}, last: now.Add(-time.Second), attempts: 10}
280281
281282 tr.resendPending()
282283 if got := writes.Load(); got != 2 {
@@ -297,7 +298,7 @@ func TestCumulativeAckReleasesAllEarlierPendingFrames(t *testing.T) {
297298 tr, _ := newResendTestTransport(t, 10)
298299 now := tr.clock.Now()
299300 for seq := uint64(1); seq <= 3; seq++ {
300- tr.pending[seq] = &pending{frame: ports.Frame{Type: ports.MsgOutput, Payload: []byte{byte(seq)}}, first: now, last: now}
301+ tr.pending[seq] = &pending{frame: wire.Frame{Type: wire.MsgOutput, Payload: []byte{byte(seq)}}, first: now, last: now}
301302 }
302303
303304 tr.handleRecord(ackRecord(3))
@@ -314,14 +315,14 @@ func TestRTTEstimatorUpdatesRTOFromAckSample(t *testing.T) {
314315 tr.resendAfter = 100 * time.Millisecond
315316 tr.maxResendAfter = time.Second
316317 tr.rto = tr.resendAfter
317- tr.pending[1] = &pending{frame: ports.Frame{Type: ports.MsgOutput}, first: clk.now.Add(-200 * time.Millisecond), last: clk.now.Add(-200 * time.Millisecond)}
318+ tr.pending[1] = &pending{frame: wire.Frame{Type: wire.MsgOutput}, first: clk.now.Add(-200 * time.Millisecond), last: clk.now.Add(-200 * time.Millisecond)}
318319
319320 tr.handleRecord(ackRecord(1))
320321
321322 if tr.rto <= tr.resendAfter {
322323 t.Fatalf("rto=%v, want above initial resend after RTT sample", tr.rto)
323324 }
324- tr.pending[2] = &pending{frame: ports.Frame{Type: ports.MsgOutput}, first: clk.now, last: clk.now.Add(-(tr.rto - time.Millisecond))}
325+ tr.pending[2] = &pending{frame: wire.Frame{Type: wire.MsgOutput}, first: clk.now, last: clk.now.Add(-(tr.rto - time.Millisecond))}
325326 tr.resendPending()
326327 if got := writes.Load(); got != 0 {
327328 t.Fatalf("writes before rto=%d, want 0", got)
@@ -340,7 +341,7 @@ func TestRTTEstimatorIgnoresRetransmittedFramesAfterRecovery(t *testing.T) {
340341 tr.resendAfter = 100 * time.Millisecond
341342 tr.maxResendAfter = time.Second
342343 tr.rto = tr.resendAfter
343- tr.pending[1] = &pending{frame: ports.Frame{Type: ports.MsgOutput}, first: clk.now.Add(-200 * time.Millisecond), last: clk.now.Add(-tr.resendAfter)}
344+ tr.pending[1] = &pending{frame: wire.Frame{Type: wire.MsgOutput}, first: clk.now.Add(-200 * time.Millisecond), last: clk.now.Add(-tr.resendAfter)}
344345 tr.resendPending()
345346 tr.linkState = ports.LinkStateOffline
346347
M internal/adapters/dgram/transport_send.go +5 -4
@@ -6,16 +6,17 @@ import (
66 "time"
77
88 "github.com/bnema/vev/internal/ports"
9+ "github.com/bnema/vev/internal/protocol/wire"
910 )
1011
11-func (t *Transport) Send(f ports.Frame) error {
12+func (t *Transport) Send(f wire.Frame) error {
1213 end := t.beginRuntimeOperation(ports.RuntimeAdapterSendStart, uint64(len(f.Payload)))
1314 err := t.send(f, false)
1415 end(err == nil)
1516 return err
1617 }
1718
18-func (t *Transport) SendAsync(f ports.Frame) error {
19+func (t *Transport) SendAsync(f wire.Frame) error {
1920 end := t.beginRuntimeOperation(ports.RuntimeAdapterSendStart, uint64(len(f.Payload)))
2021 err := t.send(f, true)
2122 end(err == nil)
@@ -24,9 +25,9 @@ func (t *Transport) SendAsync(f ports.Frame) error {
2425
2526 // SendSynchronous owns queue reservation, fragment pacing, per-write deadlines,
2627 // and close cancellation. Callers must not layer a shorter preflight timer over it.
27-func (t *Transport) SendSynchronous(f ports.Frame) error { return t.Send(f) }
28+func (t *Transport) SendSynchronous(f wire.Frame) error { return t.Send(f) }
2829
29-func (t *Transport) send(f ports.Frame, async bool) error {
30+func (t *Transport) send(f wire.Frame, async bool) error {
3031 reliable := true
3132 if err := t.lockOutboundSlot(reliable); err != nil {
3233 return err
M internal/adapters/dgram/transport_send_test.go +31 -31
@@ -8,8 +8,8 @@ import (
88 "time"
99
1010 "github.com/bnema/vev/internal/domain"
11- "github.com/bnema/vev/internal/ports"
1211 "github.com/bnema/vev/internal/protocol"
12+ "github.com/bnema/vev/internal/protocol/wire"
1313 pdgram "github.com/bnema/vev/pkg/dgram"
1414 "github.com/stretchr/testify/require"
1515 )
@@ -26,7 +26,7 @@ func TestPendingWireBytesMatchSealedDatagrams(t *testing.T) {
2626 defer func() { _ = a.Close() }()
2727
2828 maxFragmentData := mtu - pdgram.HeaderSize - a.codec.Overhead() - 16
29- frame := ports.Frame{Type: ports.MsgOutput, Payload: make([]byte, maxFragmentData-dataRecordHeaderSize+1)}
29+ frame := wire.Frame{Type: wire.MsgOutput, Payload: make([]byte, maxFragmentData-dataRecordHeaderSize+1)}
3030 if err := a.Send(frame); err != nil {
3131 t.Fatal(err)
3232 }
@@ -69,7 +69,7 @@ func TestSendAsyncTransfersFragmentedRecordToBytePacedSender(t *testing.T) {
6969 defer func() { _ = a.Close() }()
7070
7171 maxFragmentData := mtu - pdgram.HeaderSize - a.codec.Overhead()
72- frame := ports.Frame{Type: ports.MsgOutput, Payload: make([]byte, 2*maxFragmentData)}
72+ frame := wire.Frame{Type: wire.MsgOutput, Payload: make([]byte, 2*maxFragmentData)}
7373 if err := a.SendAsync(frame); err != nil {
7474 t.Fatal(err)
7575 }
@@ -92,12 +92,12 @@ func TestSmallOutputsShareInitialByteBurst(t *testing.T) {
9292 defer func() { _ = a.Close() }()
9393
9494 for i := range 3 {
95- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte{byte(i)}}); err != nil {
95+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte{byte(i)}}); err != nil {
9696 t.Fatal(err)
9797 }
9898 }
9999 eventuallyPacketCount(t, bPC, 3)
100- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte("next")}); err != nil {
100+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte("next")}); err != nil {
101101 t.Fatal(err)
102102 }
103103 eventuallyPacketCount(t, bPC, 4)
@@ -112,12 +112,12 @@ func TestOutputPacingSendsAtMostOneOversizedFramePerTick(t *testing.T) {
112112 }
113113 defer func() { _ = a.Close() }()
114114
115- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte("prime")}); err != nil {
115+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte("prime")}); err != nil {
116116 t.Fatal(err)
117117 }
118118 large := mustMarshalOutput(protocol.Output{Epoch: 1, Base: 1, New: 2, Size: domain.Size{Cols: 1, Rows: 1}, Data: make([]byte, 24*a.mtu)})
119119 for range 2 {
120- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: large}); err != nil {
120+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: large}); err != nil {
121121 t.Fatal(err)
122122 }
123123 }
@@ -152,7 +152,7 @@ func TestPacedInitialSendTimestampsFinalFragmentCompletion(t *testing.T) {
152152 t.Fatal(err)
153153 }
154154 defer func() { _ = a.Close() }()
155- large := ports.Frame{Type: ports.MsgOutput, Payload: make([]byte, 24*a.mtu)}
155+ large := wire.Frame{Type: wire.MsgOutput, Payload: make([]byte, 24*a.mtu)}
156156 done := make(chan error, 1)
157157 go func() { done <- a.Send(large) }()
158158 start := clk.Now()
@@ -209,7 +209,7 @@ func TestOwnedSynchronousMaxSideEffectCompletesThroughConcurrentPacedWork(t *tes
209209 a.srtt = 500 * time.Millisecond
210210 a.mu.Unlock()
211211 for i := range 8 {
212- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte{byte(i)}}); err != nil {
212+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte{byte(i)}}); err != nil {
213213 t.Fatal(err)
214214 }
215215 }
@@ -217,13 +217,13 @@ func TestOwnedSynchronousMaxSideEffectCompletesThroughConcurrentPacedWork(t *tes
217217 a.mu.Lock()
218218 for _, seq := range []uint64{100, 101} {
219219 a.pending[seq] = &pending{
220- frame: ports.Frame{Type: ports.MsgOutput, Payload: make([]byte, 20*a.mtu)},
220+ frame: wire.Frame{Type: wire.MsgOutput, Payload: make([]byte, 20*a.mtu)},
221221 enqueued: now.Add(-time.Second), first: now.Add(-time.Second), last: now.Add(-time.Second),
222222 }
223223 }
224224 a.mu.Unlock()
225225 go a.resendPending()
226- sideEffect := ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{Epoch: 1, Size: domain.Size{Cols: 1, Rows: 1}, Data: make([]byte, 100*1024)})}
226+ sideEffect := wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{Epoch: 1, Size: domain.Size{Cols: 1, Rows: 1}, Data: make([]byte, 100*1024)})}
227227 done := make(chan error, 1)
228228 go func() { done <- a.SendSynchronous(sideEffect) }()
229229
@@ -256,12 +256,12 @@ func TestInputWaitsForDequeuedOutputBatch(t *testing.T) {
256256
257257 a.writeMu.Lock()
258258 for _, payload := range []string{"first", "queued"} {
259- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte(payload)}); err != nil {
259+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte(payload)}); err != nil {
260260 t.Fatal(err)
261261 }
262262 }
263263 done := make(chan error, 1)
264- go func() { done <- a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("typed")}) }()
264+ go func() { done <- a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("typed")}) }()
265265 select {
266266 case err := <-done:
267267 a.writeMu.Unlock()
@@ -295,7 +295,7 @@ func TestPacedControlQueueFailureClosesTransport(t *testing.T) {
295295 t.Fatal(err)
296296 }
297297 inputDone := make(chan error, 1)
298- go func() { inputDone <- a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("typed")}) }()
298+ go func() { inputDone <- a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("typed")}) }()
299299 if err := <-inputDone; err == nil {
300300 t.Fatal("data send succeeded after paced wire failure")
301301 }
@@ -328,10 +328,10 @@ func TestRemoveQueuedPendingResolvesSynchronousWaiters(t *testing.T) {
328328 func TestCloseUnblocksQueuedSynchronousFrames(t *testing.T) {
329329 frames := []struct {
330330 name string
331- frame ports.Frame
331+ frame wire.Frame
332332 }{
333- {name: "control", frame: ports.Frame{Type: ports.MsgPing}},
334- {name: "terminal side effect", frame: ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{Epoch: 1, Size: domain.Size{Cols: 1, Rows: 1}, Data: []byte("osc")})}},
333+ {name: "control", frame: wire.Frame{Type: wire.MsgPing}},
334+ {name: "terminal side effect", frame: wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{Epoch: 1, Size: domain.Size{Cols: 1, Rows: 1}, Data: []byte("osc")})}},
335335 }
336336 for _, tt := range frames {
337337 t.Run(tt.name, func(t *testing.T) {
@@ -341,7 +341,7 @@ func TestCloseUnblocksQueuedSynchronousFrames(t *testing.T) {
341341 if err != nil {
342342 t.Fatal(err)
343343 }
344- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: make([]byte, 3*a.mtu)}); err != nil {
344+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: make([]byte, 3*a.mtu)}); err != nil {
345345 t.Fatal(err)
346346 }
347347 done := make(chan error, 1)
@@ -371,7 +371,7 @@ func TestOutputPaceFailureClosesTransportWithWriteError(t *testing.T) {
371371 if err := bPC.Close(); err != nil {
372372 t.Fatal(err)
373373 }
374- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte("fails")}); err != nil {
374+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte("fails")}); err != nil {
375375 t.Fatal(err)
376376 }
377377
@@ -380,7 +380,7 @@ func TestOutputPaceFailureClosesTransportWithWriteError(t *testing.T) {
380380 case <-time.After(time.Second):
381381 t.Fatal("transport remained open after paced output write failed")
382382 }
383- if err := a.Send(ports.Frame{Type: ports.MsgInput}); err == nil || err.Error() != "peer closed" {
383+ if err := a.Send(wire.Frame{Type: wire.MsgInput}); err == nil || err.Error() != "peer closed" {
384384 t.Fatalf("send after paced write failure err=%v, want peer closed", err)
385385 }
386386 }
@@ -394,24 +394,24 @@ func TestInputAndControlRemainOrderedInBoundedPacedQueue(t *testing.T) {
394394 }
395395 defer func() { _ = a.Close() }()
396396
397- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte("output")}); err != nil {
397+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte("output")}); err != nil {
398398 t.Fatal(err)
399399 }
400- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte("queued")}); err != nil {
400+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte("queued")}); err != nil {
401401 t.Fatal(err)
402402 }
403403 eventuallyPacketCount(t, bPC, 2)
404404 inputDone := make(chan error, 1)
405- go func() { inputDone <- a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("typed")}) }()
405+ go func() { inputDone <- a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("typed")}) }()
406406 eventuallyPacketCount(t, bPC, 3)
407407 if err := <-inputDone; err != nil {
408408 t.Fatal(err)
409409 }
410- if err := a.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte("queued-before-control")}); err != nil {
410+ if err := a.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte("queued-before-control")}); err != nil {
411411 t.Fatal(err)
412412 }
413413 controlDone := make(chan error, 1)
414- go func() { controlDone <- a.Send(ports.Frame{Type: ports.MsgPing}) }()
414+ go func() { controlDone <- a.Send(wire.Frame{Type: wire.MsgPing}) }()
415415 eventuallyPacketCount(t, bPC, 5)
416416 if err := <-controlDone; err != nil {
417417 t.Fatal(err)
@@ -431,7 +431,7 @@ func TestSendWriteTimeoutBoundsBlockedPacketConn(t *testing.T) {
431431 defer func() { _ = tr.Close() }()
432432
433433 done := make(chan error, 1)
434- go func() { done <- tr.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("blocked")}) }()
434+ go func() { done <- tr.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("blocked")}) }()
435435 select {
436436 case err := <-done:
437437 if err == nil {
@@ -460,11 +460,11 @@ func TestPendingReliableQueueBackpressuresUntilAck(t *testing.T) {
460460 defer func() { _ = a.Close() }()
461461 defer func() { _ = b.Close() }()
462462
463- if err := a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("first")}); err != nil {
463+ if err := a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("first")}); err != nil {
464464 t.Fatal(err)
465465 }
466466 done := make(chan error, 1)
467- go func() { done <- a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("second")}) }()
467+ go func() { done <- a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("second")}) }()
468468
469469 select {
470470 case err := <-done:
@@ -498,11 +498,11 @@ func TestPendingReliableQueueReturnsErrPendingFullWhileConnected(t *testing.T) {
498498 defer func() { _ = a.Close() }()
499499 defer func() { _ = b.Close() }()
500500
501- if err := a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("first")}); err != nil {
501+ if err := a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("first")}); err != nil {
502502 t.Fatal(err)
503503 }
504504 done := make(chan error, 1)
505- go func() { done <- a.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("second")}) }()
505+ go func() { done <- a.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("second")}) }()
506506 waitForManualTimers(t, clk, 4)
507507
508508 clk.advance(50 * time.Millisecond)
@@ -530,7 +530,7 @@ func TestWritePacketDeadlineUsesTransportClock(t *testing.T) {
530530 }
531531 defer func() { _ = tr.Close() }()
532532
533- if err := tr.Send(ports.Frame{Type: ports.MsgInput, Payload: []byte("deadline")}); err != nil {
533+ if err := tr.Send(wire.Frame{Type: wire.MsgInput, Payload: []byte("deadline")}); err != nil {
534534 t.Fatal(err)
535535 }
536536 got, _ := pc.deadline.Load().(time.Time)
M internal/adapters/dgram/transport_test_helpers_test.go +19 -18
@@ -10,6 +10,7 @@ import (
1010 "time"
1111
1212 "github.com/bnema/vev/internal/ports"
13+ "github.com/bnema/vev/internal/protocol/wire"
1314 pdgram "github.com/bnema/vev/pkg/dgram"
1415 )
1516
@@ -309,15 +310,15 @@ func probeRecord(kind byte, id uint64) []byte {
309310 }
310311
311312 func TestReliableRecvBufferBoundedForFarFutureSequences(t *testing.T) {
312- tr := &Transport{nextRecvSeq: 1, recvBuf: make(map[uint64]ports.Frame), maxRecvBuffer: maxRecvBuffer, done: make(chan struct{})}
313+ tr := &Transport{nextRecvSeq: 1, recvBuf: make(map[uint64]wire.Frame), maxRecvBuffer: maxRecvBuffer, done: make(chan struct{})}
313314 tr.deliverCond = sync.NewCond(&tr.deliverMu)
314315 for i := range maxRecvBuffer + 100 {
315- tr.enqueueReliable(uint64(1000+i), ports.Frame{Type: ports.MsgOutput, Payload: []byte{byte(i)}})
316+ tr.enqueueReliable(uint64(1000+i), wire.Frame{Type: wire.MsgOutput, Payload: []byte{byte(i)}})
316317 }
317318 if got := len(tr.recvBuf); got != maxRecvBuffer {
318319 t.Fatalf("recvBuf len=%d, want %d", got, maxRecvBuffer)
319320 }
320- tr.enqueueReliable(1, ports.Frame{Type: ports.MsgOutput, Payload: []byte("next")})
321+ tr.enqueueReliable(1, wire.Frame{Type: wire.MsgOutput, Payload: []byte("next")})
321322 if _, ok := tr.recvBuf[1]; !ok {
322323 t.Fatalf("next expected sequence was dropped when far-future buffer was full")
323324 }
@@ -329,13 +330,13 @@ func TestReliableRecvBufferBoundedForFarFutureSequences(t *testing.T) {
329330 func TestOutputPrerequisitesRemainInFullReceiveBuffer(t *testing.T) {
330331 tr := &Transport{
331332 nextRecvSeq: 1,
332- recvBuf: map[uint64]ports.Frame{2: {Type: ports.MsgOutput, Payload: []byte("stale")}},
333+ recvBuf: map[uint64]wire.Frame{2: {Type: wire.MsgOutput, Payload: []byte("stale")}},
333334 maxRecvBuffer: 1,
334335 done: make(chan struct{}),
335336 }
336337 tr.deliverCond = sync.NewCond(&tr.deliverMu)
337338
338- ackSeq, ack, queued := tr.enqueueReliable(3, ports.Frame{Type: ports.MsgOutput, Payload: []byte("replacement")})
339+ ackSeq, ack, queued := tr.enqueueReliable(3, wire.Frame{Type: wire.MsgOutput, Payload: []byte("replacement")})
339340 if ack || queued || ackSeq != 0 {
340341 t.Fatalf("replacement ackSeq=%d ack=%v queued=%v, want output rejected while prerequisite buffer is full", ackSeq, ack, queued)
341342 }
@@ -353,22 +354,22 @@ func TestReliableFullRecvBufferDoesNotAckDroppedFarFutureFrame(t *testing.T) {
353354 if err != nil {
354355 t.Fatal(err)
355356 }
356- tr := &Transport{pc: aPC, peer: testAddr("b"), codec: codec, sendDir: 1, mtu: pdgram.DefaultMTU, nextRecvSeq: 1, recvBuf: make(map[uint64]ports.Frame), maxRecvBuffer: maxRecvBuffer, clock: realClock{}, done: make(chan struct{})}
357+ tr := &Transport{pc: aPC, peer: testAddr("b"), codec: codec, sendDir: 1, mtu: pdgram.DefaultMTU, nextRecvSeq: 1, recvBuf: make(map[uint64]wire.Frame), maxRecvBuffer: maxRecvBuffer, clock: realClock{}, done: make(chan struct{})}
357358 tr.deliverCond = sync.NewCond(&tr.deliverMu)
358359 defer func() { _ = aPC.Close() }()
359360 defer func() { _ = bPC.Close() }()
360361
361362 for i := range maxRecvBuffer {
362- tr.recvBuf[uint64(1000+i)] = ports.Frame{Type: ports.MsgOutput, Payload: []byte{byte(i)}}
363+ tr.recvBuf[uint64(1000+i)] = wire.Frame{Type: wire.MsgOutput, Payload: []byte{byte(i)}}
363364 }
364- tr.handleRecord(encodeData(9999, true, ports.Frame{Type: ports.MsgOutput, Payload: []byte("dropped")}))
365+ tr.handleRecord(encodeData(9999, true, wire.Frame{Type: wire.MsgOutput, Payload: []byte("dropped")}))
365366 select {
366367 case pkt := <-bPC.in:
367368 t.Fatalf("unexpected ACK packet for dropped frame: %x", pkt.b)
368369 case <-time.After(25 * time.Millisecond):
369370 }
370371
371- tr.handleRecord(encodeData(1, true, ports.Frame{Type: ports.MsgOutput, Payload: []byte("next")}))
372+ tr.handleRecord(encodeData(1, true, wire.Frame{Type: wire.MsgOutput, Payload: []byte("next")}))
372373 select {
373374 case <-bPC.in:
374375 case <-time.After(time.Second):
@@ -376,27 +377,27 @@ func TestReliableFullRecvBufferDoesNotAckDroppedFarFutureFrame(t *testing.T) {
376377 }
377378 }
378379
379-func recvMaybe(tr *Transport, d time.Duration) (ports.Frame, bool) {
380- ch := make(chan ports.Frame, 1)
380+func recvMaybe(tr *Transport, d time.Duration) (wire.Frame, bool) {
381+ ch := make(chan wire.Frame, 1)
381382 go func() { f, _ := tr.Recv(); ch <- f }()
382383 select {
383384 case f := <-ch:
384385 return f, true
385386 case <-time.After(d):
386- return ports.Frame{}, false
387+ return wire.Frame{}, false
387388 }
388389 }
389390
390-func recvWithin(t *testing.T, tr *Transport, d time.Duration) ports.Frame {
391+func recvWithin(t *testing.T, tr *Transport, d time.Duration) wire.Frame {
391392 t.Helper()
392- ch := make(chan ports.Frame, 1)
393+ ch := make(chan wire.Frame, 1)
393394 go func() { f, _ := tr.Recv(); ch <- f }()
394395 select {
395396 case f := <-ch:
396397 return f
397398 case <-time.After(d):
398399 t.Fatal("timeout")
399- return ports.Frame{}
400+ return wire.Frame{}
400401 }
401402 }
402403
@@ -412,10 +413,10 @@ func waitPeer(t *testing.T, tr *Transport, want string) {
412413 t.Fatalf("peer=%v, want %s", tr.Peer(), want)
413414 }
414415
415-func recvErrWithin(t *testing.T, tr *Transport, d time.Duration) (ports.Frame, error) {
416+func recvErrWithin(t *testing.T, tr *Transport, d time.Duration) (wire.Frame, error) {
416417 t.Helper()
417418 type result struct {
418- f ports.Frame
419+ f wire.Frame
419420 err error
420421 }
421422 ch := make(chan result, 1)
@@ -428,6 +429,6 @@ func recvErrWithin(t *testing.T, tr *Transport, d time.Duration) (ports.Frame, e
428429 return r.f, r.err
429430 case <-time.After(d):
430431 t.Fatal("timeout")
431- return ports.Frame{}, nil
432+ return wire.Frame{}, nil
432433 }
433434 }
M internal/adapters/dgram/wire_test_helpers_test.go +3 -3
@@ -1,12 +1,12 @@
11 package dgram
22
33 import (
4- "github.com/bnema/vev/internal/ports"
54 "github.com/bnema/vev/internal/protocol"
5+ "github.com/bnema/vev/internal/protocol/wire"
66 )
77
88 func mustMarshalOutput(m protocol.Output) []byte {
9- payload, err := ports.MarshalOutput(m)
9+ payload, err := wire.MarshalOutput(m)
1010 if err != nil {
1111 panic(err)
1212 }
@@ -14,7 +14,7 @@ func mustMarshalOutput(m protocol.Output) []byte {
1414 }
1515
1616 func mustMarshalAck(m protocol.Ack) []byte {
17- payload, err := ports.MarshalAck(m)
17+ payload, err := wire.MarshalAck(m)
1818 if err != nil {
1919 panic(err)
2020 }
M internal/adapters/ipc/listener_test.go +3 -3
@@ -9,7 +9,7 @@ import (
99 "strings"
1010 "testing"
1111
12- "github.com/bnema/vev/internal/ports"
12+ "github.com/bnema/vev/internal/protocol/wire"
1313 )
1414
1515 func TestListenAcceptRoundTrip(t *testing.T) {
@@ -43,7 +43,7 @@ func TestListenAcceptRoundTrip(t *testing.T) {
4343 }
4444 defer func() { _ = conn.Close() }()
4545 client := NewTransport(conn)
46- errCh <- client.Send(ports.Frame{Type: ports.MsgPing})
46+ errCh <- client.Send(wire.Frame{Type: wire.MsgPing})
4747 }()
4848
4949 transport, err := ln.Accept()
@@ -56,7 +56,7 @@ func TestListenAcceptRoundTrip(t *testing.T) {
5656 if err != nil {
5757 t.Fatalf("Recv() error = %v", err)
5858 }
59- if got.Type != ports.MsgPing {
59+ if got.Type != wire.MsgPing {
6060 t.Fatalf("Recv() type = %v, want MsgPing", got.Type)
6161 }
6262 if err := <-errCh; err != nil {
M internal/adapters/ipc/transport.go +13 -12
@@ -8,6 +8,7 @@ import (
88 "sync"
99
1010 "github.com/bnema/vev/internal/ports"
11+ "github.com/bnema/vev/internal/protocol/wire"
1112 )
1213
1314 // frameHeaderLen is the size, in bytes, of the length prefix that precedes
@@ -19,7 +20,7 @@ const frameHeaderLen = 4
1920 var ErrZeroLengthFrame = errors.New("ipc: zero-length frame")
2021
2122 // ErrFrameTooLarge is returned by Recv when a frame's length field exceeds
22-// ports.MaxFrameLen, and by Send when a payload is too large to encode.
23+// wire.MaxFrameLen, and by Send when a payload is too large to encode.
2324 var ErrFrameTooLarge = errors.New("ipc: frame exceeds maximum length")
2425
2526 // ErrBackpressure means the bounded IPC egress queue is full. Callers using
@@ -103,7 +104,7 @@ type sendRequest struct {
103104 }
104105
105106 // Send queues f for the sole writer and waits for its wire attempt.
106-func (t *unixTransport) Send(f ports.Frame) error {
107+func (t *unixTransport) Send(f wire.Frame) error {
107108 data, err := marshalFrame(f)
108109 if err != nil {
109110 return err
@@ -124,7 +125,7 @@ func (t *unixTransport) Send(f ports.Frame) error {
124125
125126 // SendAsync accepts f for ordered background transmission without waiting for
126127 // a socket write. The queue is bounded and ownership includes a payload copy.
127-func (t *unixTransport) SendAsync(f ports.Frame) error {
128+func (t *unixTransport) SendAsync(f wire.Frame) error {
128129 data, err := marshalFrame(f)
129130 if err != nil {
130131 return err
@@ -137,9 +138,9 @@ func (t *unixTransport) SendAsync(f ports.Frame) error {
137138 return nil
138139 }
139140
140-func marshalFrame(f ports.Frame) ([]byte, error) {
141+func marshalFrame(f wire.Frame) ([]byte, error) {
141142 n := 1 + len(f.Payload) // type + payload
142- if n > ports.MaxFrameLen {
143+ if n > wire.MaxFrameLen {
143144 return nil, ErrFrameTooLarge
144145 }
145146 buf := make([]byte, frameHeaderLen+n)
@@ -256,22 +257,22 @@ func writeAll(w io.Writer, data []byte) error {
256257 // Recv reads one frame: a 4-byte length, then a body of that many bytes
257258 // (type byte + payload). It blocks until a full frame arrives, the
258259 // connection is closed (io.EOF), or an error occurs.
259-func (t *unixTransport) Recv() (ports.Frame, error) {
260+func (t *unixTransport) Recv() (wire.Frame, error) {
260261 end := t.beginOperation(ports.RuntimeAdapterReceiveStart, 0)
261262 var hdr [frameHeaderLen]byte
262263 if _, err := io.ReadFull(t.conn, hdr[:]); err != nil {
263264 end(false)
264- return ports.Frame{}, err
265+ return wire.Frame{}, err
265266 }
266267
267268 n := binary.BigEndian.Uint32(hdr[:])
268269 if n == 0 {
269270 end(false)
270- return ports.Frame{}, ErrZeroLengthFrame
271+ return wire.Frame{}, ErrZeroLengthFrame
271272 }
272- if n > ports.MaxFrameLen {
273+ if n > wire.MaxFrameLen {
273274 end(false)
274- return ports.Frame{}, ErrFrameTooLarge
275+ return wire.Frame{}, ErrFrameTooLarge
275276 }
276277
277278 if cap(t.readBuf) < int(n) {
@@ -281,7 +282,7 @@ func (t *unixTransport) Recv() (ports.Frame, error) {
281282 }
282283 if _, err := io.ReadFull(t.conn, t.readBuf); err != nil {
283284 end(false)
284- return ports.Frame{}, err
285+ return wire.Frame{}, err
285286 }
286287
287288 var payload []byte
@@ -290,7 +291,7 @@ func (t *unixTransport) Recv() (ports.Frame, error) {
290291 copy(payload, t.readBuf[1:])
291292 }
292293 end(true)
293- return ports.Frame{Type: ports.MsgType(t.readBuf[0]), Payload: payload}, nil
294+ return wire.Frame{Type: wire.MsgType(t.readBuf[0]), Payload: payload}, nil
294295 }
295296
296297 func (t *unixTransport) beginOperation(start ports.RuntimeMarkKind, bytes uint64) func(bool) {
M internal/adapters/ipc/transport_benchmark_test.go +4 -4
@@ -7,14 +7,14 @@ import (
77 "testing"
88 "time"
99
10- "github.com/bnema/vev/internal/ports"
10+ "github.com/bnema/vev/internal/protocol/wire"
1111 )
1212
1313 func BenchmarkTransportSend(b *testing.B) {
1414 conn := discardConn{}
1515 tr := NewTransport(conn)
1616 payload := []byte("payload payload payload")
17- frame := ports.Frame{Type: ports.MsgOutput, Payload: payload}
17+ frame := wire.Frame{Type: wire.MsgOutput, Payload: payload}
1818
1919 b.ReportAllocs()
2020 for b.Loop() {
@@ -26,7 +26,7 @@ func BenchmarkTransportSend(b *testing.B) {
2626
2727 func BenchmarkTransportRecvReuse(b *testing.B) {
2828 payload := []byte("payload payload payload")
29- encoded := encodeBenchmarkFrame(ports.Frame{Type: ports.MsgOutput, Payload: payload})
29+ encoded := encodeBenchmarkFrame(wire.Frame{Type: wire.MsgOutput, Payload: payload})
3030 recv := &unixTransport{conn: &loopingReaderConn{data: encoded}}
3131
3232 b.ReportAllocs()
@@ -37,7 +37,7 @@ func BenchmarkTransportRecvReuse(b *testing.B) {
3737 }
3838 }
3939
40-func encodeBenchmarkFrame(f ports.Frame) []byte {
40+func encodeBenchmarkFrame(f wire.Frame) []byte {
4141 n := 1 + len(f.Payload)
4242 buf := make([]byte, frameHeaderLen+n)
4343 binary.BigEndian.PutUint32(buf[:frameHeaderLen], uint32(n))
M internal/adapters/ipc/transport_observability_test.go +3 -2
@@ -12,6 +12,7 @@ import (
1212
1313 "github.com/bnema/vev/internal/adapters/observability"
1414 "github.com/bnema/vev/internal/ports"
15+ "github.com/bnema/vev/internal/protocol/wire"
1516 )
1617
1718 func TestTransportObservabilityIPCEOFAndCloseEndFailedSpans(t *testing.T) {
@@ -55,7 +56,7 @@ func TestTransportObservabilityIPCEOFAndCloseEndFailedSpans(t *testing.T) {
5556 left, right := net.Pipe()
5657 transport := NewTransport(left, WithRuntimeObserver(reporter))
5758 _ = right.Close()
58- if err := transport.Send(ports.Frame{Type: ports.MsgOutput, Payload: []byte("failed")}); err == nil {
59+ if err := transport.Send(wire.Frame{Type: wire.MsgOutput, Payload: []byte("failed")}); err == nil {
5960 t.Fatal("Send() error = nil after peer close")
6061 }
6162 _ = transport.Close()
@@ -152,7 +153,7 @@ func TestTransportObservabilityIPCMarksCarriageWithoutChangingBytes(t *testing.T
152153 defer func() { _ = right.Close() }()
153154 client := NewTransport(left, WithRuntimeObserver(reporter))
154155 server := NewTransport(right, WithRuntimeObserver(reporter))
155- want := ports.Frame{Type: ports.MsgOutput, Payload: []byte("wire bytes stay exact")}
156+ want := wire.Frame{Type: wire.MsgOutput, Payload: []byte("wire bytes stay exact")}
156157
157158 var wg sync.WaitGroup
158159 wg.Go(func() {
M internal/adapters/ipc/transport_replay_test.go +3 -3
@@ -5,12 +5,12 @@ import (
55 "sync"
66 "testing"
77
8- "github.com/bnema/vev/internal/ports"
8+ "github.com/bnema/vev/internal/protocol/wire"
99 "github.com/bnema/vev/internal/testutil/replaytest"
1010 )
1111
1212 func TestTransportReplay(t *testing.T) {
13- replaytest.Run(t, func(t *testing.T, frames []ports.Frame) []ports.Frame {
13+ replaytest.Run(t, func(t *testing.T, frames []wire.Frame) []wire.Frame {
1414 left, right := net.Pipe()
1515 t.Cleanup(func() { _ = left.Close(); _ = right.Close() })
1616 sender, receiver := NewTransport(left), NewTransport(right)
@@ -24,7 +24,7 @@ func TestTransportReplay(t *testing.T) {
2424 }
2525 }
2626 })
27- got := make([]ports.Frame, 0, len(frames))
27+ got := make([]wire.Frame, 0, len(frames))
2828 for range frames {
2929 frame, err := receiver.Recv()
3030 if err != nil {
M internal/adapters/ipc/transport_test.go +27 -26
@@ -14,6 +14,7 @@ import (
1414 "github.com/bnema/vev/internal/domain"
1515 "github.com/bnema/vev/internal/ports"
1616 "github.com/bnema/vev/internal/protocol"
17+ "github.com/bnema/vev/internal/protocol/wire"
1718 )
1819
1920 func TestTransportSendRecvBothDirections(t *testing.T) {
@@ -24,8 +25,8 @@ func TestTransportSendRecvBothDirections(t *testing.T) {
2425 client := NewTransport(c1)
2526 server := NewTransport(c2)
2627
27- clientToServer := ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(protocol.Hello{Version: 1, Intent: protocol.IntentNew, Name: "w0"})}
28- serverToClient := ports.Frame{Type: ports.MsgWelcome, Payload: ports.MarshalWelcome(protocol.Welcome{SessionID: "s1", SessionName: "main"})}
28+ clientToServer := wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(protocol.Hello{Version: 1, Intent: protocol.IntentNew, Name: "w0"})}
29+ serverToClient := wire.Frame{Type: wire.MsgWelcome, Payload: wire.MarshalWelcome(protocol.Welcome{SessionID: "s1", SessionName: "main"})}
2930
3031 var wg sync.WaitGroup
3132 wg.Go(func() {
@@ -97,12 +98,12 @@ func TestTransportLargeFrameNearCap(t *testing.T) {
9798 server := NewTransport(c2)
9899
99100 // Largest payload whose frame length (1 + len(payload)) still fits
100- // exactly at ports.MaxFrameLen.
101- payload := make([]byte, ports.MaxFrameLen-1)
101+ // exactly at wire.MaxFrameLen.
102+ payload := make([]byte, wire.MaxFrameLen-1)
102103 for i := range payload {
103104 payload[i] = byte(i)
104105 }
105- want := ports.Frame{Type: ports.MsgOutput, Payload: payload}
106+ want := wire.Frame{Type: wire.MsgOutput, Payload: payload}
106107
107108 var wg sync.WaitGroup
108109 wg.Go(func() {
@@ -135,13 +136,13 @@ func TestTransportManySmallFramesBackToBack(t *testing.T) {
135136 server := NewTransport(c2)
136137
137138 const count = 200
138- frames := make([]ports.Frame, count)
139+ frames := make([]wire.Frame, count)
139140 for i := range frames {
140- frames[i] = ports.Frame{Type: ports.MsgPing, Payload: nil}
141+ frames[i] = wire.Frame{Type: wire.MsgPing, Payload: nil}
141142 }
142143 // Vary a couple to prove ordering and content are both preserved.
143- frames[7] = ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("hop")})}
144- frames[150] = ports.Frame{Type: ports.MsgResize, Payload: mustMarshalResize(protocol.Resize{Size: domain.Size{Cols: 5, Rows: 6}})}
144+ frames[7] = wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("hop")})}
145+ frames[150] = wire.Frame{Type: wire.MsgResize, Payload: mustMarshalResize(protocol.Resize{Size: domain.Size{Cols: 5, Rows: 6}})}
145146
146147 var wg sync.WaitGroup
147148 wg.Go(func() {
@@ -178,7 +179,7 @@ func TestTransportRecvOversizeFrame(t *testing.T) {
178179
179180 go func() {
180181 var hdr [4]byte
181- binary.BigEndian.PutUint32(hdr[:], ports.MaxFrameLen+1)
182+ binary.BigEndian.PutUint32(hdr[:], wire.MaxFrameLen+1)
182183 _, _ = c1.Write(hdr[:])
183184 }()
184185
@@ -228,8 +229,8 @@ func TestTransportSendOversizePayloadRejected(t *testing.T) {
228229 defer func() { _ = c2.Close() }()
229230
230231 client := NewTransport(c1)
231- payload := make([]byte, ports.MaxFrameLen) // +1 byte type => exceeds max
232- err := client.Send(ports.Frame{Type: ports.MsgOutput, Payload: payload})
232+ payload := make([]byte, wire.MaxFrameLen) // +1 byte type => exceeds max
233+ err := client.Send(wire.Frame{Type: wire.MsgOutput, Payload: payload})
233234 if !errors.Is(err, ErrFrameTooLarge) {
234235 t.Fatalf("client.Send() error = %v, want ErrFrameTooLarge", err)
235236 }
@@ -257,7 +258,7 @@ func TestTransportAsyncEgressPreservesWelcomeBeforeOutput(t *testing.T) {
257258 t.Fatal("IPC transport does not implement AsyncTransport")
258259 }
259260
260- welcome := ports.Frame{Type: ports.MsgWelcome, Payload: []byte("welcome")}
261+ welcome := wire.Frame{Type: wire.MsgWelcome, Payload: []byte("welcome")}
261262 sent := make(chan error, 1)
262263 go func() { sent <- server.Send(welcome) }()
263264 got, err := client.Recv()
@@ -277,16 +278,16 @@ func TestTransportAsyncEgressPreservesWelcomeBeforeOutput(t *testing.T) {
277278 }
278279
279280 payload := []byte("first")
280- if err := async.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: payload}); err != nil {
281+ if err := async.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: payload}); err != nil {
281282 t.Fatalf("SendAsync first output: %v", err)
282283 }
283284 payload[0] = 'X' // async ownership must not retain caller memory.
284- if err := async.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte("second")}); err != nil {
285+ if err := async.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte("second")}); err != nil {
285286 t.Fatalf("SendAsync second output: %v", err)
286287 }
287- for _, want := range []ports.Frame{
288- {Type: ports.MsgOutput, Payload: []byte("first")},
289- {Type: ports.MsgOutput, Payload: []byte("second")},
288+ for _, want := range []wire.Frame{
289+ {Type: wire.MsgOutput, Payload: []byte("first")},
290+ {Type: wire.MsgOutput, Payload: []byte("second")},
290291 } {
291292 got, err := client.Recv()
292293 if err != nil {
@@ -322,7 +323,7 @@ func TestTransportSendWaitsForEgressCapacity(t *testing.T) {
322323 }
323324
324325 queued := fillAsyncEgress(t, transport, async, timeout)
325- want := ports.Frame{Type: ports.MsgOutput, Payload: []byte("synchronous")}
326+ want := wire.Frame{Type: wire.MsgOutput, Payload: []byte("synchronous")}
326327 sent := make(chan error, 1)
327328 go func() { sent <- server.Send(want) }()
328329 waitForEgressSender(t, transport, timeout)
@@ -371,7 +372,7 @@ func TestTransportCloseInterruptsSendWaitingForEgressCapacity(t *testing.T) {
371372 fillAsyncEgress(t, transport, async, timeout)
372373
373374 sent := make(chan error, 1)
374- go func() { sent <- server.Send(ports.Frame{Type: ports.MsgOutput, Payload: []byte("synchronous")}) }()
375+ go func() { sent <- server.Send(wire.Frame{Type: wire.MsgOutput, Payload: []byte("synchronous")}) }()
375376 waitForEgressSender(t, transport, timeout)
376377
377378 closeTransport(t, server)
@@ -421,18 +422,18 @@ func closeTransport(t *testing.T, transport ports.Transport) {
421422 }
422423 }
423424
424-func fillAsyncEgress(t *testing.T, transport *unixTransport, async ports.AsyncTransport, timeout time.Duration) []ports.Frame {
425+func fillAsyncEgress(t *testing.T, transport *unixTransport, async ports.AsyncTransport, timeout time.Duration) []wire.Frame {
425426 t.Helper()
426427
427- first := ports.Frame{Type: ports.MsgOutput, Payload: []byte{0}}
428+ first := wire.Frame{Type: wire.MsgOutput, Payload: []byte{0}}
428429 if err := async.SendAsync(first); err != nil {
429430 t.Fatalf("SendAsync blocked frame: %v", err)
430431 }
431432 waitForEgressDequeue(t, transport, timeout)
432433
433- queued := []ports.Frame{first}
434+ queued := []wire.Frame{first}
434435 for i := 1; ; i++ {
435- frame := ports.Frame{Type: ports.MsgOutput, Payload: []byte{byte(i)}}
436+ frame := wire.Frame{Type: wire.MsgOutput, Payload: []byte{byte(i)}}
436437 err := async.SendAsync(frame)
437438 switch {
438439 case err == nil:
@@ -479,12 +480,12 @@ func TestTransportAsyncEgressIsBoundedAndCloseInterruptsWorkers(t *testing.T) {
479480 }
480481
481482 // The first write blocks in net.Pipe because the peer never drains it.
482- if err := async.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte("blocked")}); err != nil {
483+ if err := async.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte("blocked")}); err != nil {
483484 t.Fatalf("SendAsync blocked frame: %v", err)
484485 }
485486 var backpressure error
486487 for range sendQueueCapacity + 2 {
487- err := async.SendAsync(ports.Frame{Type: ports.MsgOutput, Payload: []byte("queued")})
488+ err := async.SendAsync(wire.Frame{Type: wire.MsgOutput, Payload: []byte("queued")})
488489 if errors.Is(err, ErrBackpressure) {
489490 backpressure = err
490491 break
M internal/adapters/ipc/wire_test_helpers_test.go +2 -2
@@ -1,12 +1,12 @@
11 package ipc
22
33 import (
4- "github.com/bnema/vev/internal/ports"
54 "github.com/bnema/vev/internal/protocol"
5+ "github.com/bnema/vev/internal/protocol/wire"
66 )
77
88 func mustMarshalResize(m protocol.Resize) []byte {
9- payload, err := ports.MarshalResize(m)
9+ payload, err := wire.MarshalResize(m)
1010 if err != nil {
1111 panic(err)
1212 }
M internal/adapters/remote/preview.go +3 -2
@@ -12,6 +12,7 @@ import (
1212 "github.com/bnema/vev/internal/domain"
1313 "github.com/bnema/vev/internal/ports"
1414 "github.com/bnema/vev/internal/protocol"
15+ "github.com/bnema/vev/internal/protocol/wire"
1516 )
1617
1718 const (
@@ -42,7 +43,7 @@ func NewPreviewClient() ports.RemotePreviewClient {
4243
4344 func (c *PreviewClient) Preview(ctx context.Context, target domain.RemoteSessionTarget, width, height uint16) (protocol.RemotePreview, error) {
4445 request := protocol.RemotePreviewRequest{Version: protocol.RemotePreviewSchemaVersion, Target: target, Width: width, Height: height}
45- payload := ports.MarshalRemotePreviewRequest(request)
46+ payload := wire.MarshalRemotePreviewRequest(request)
4647 if payload == nil {
4748 return protocol.RemotePreview{}, protocol.ErrInvalidRemotePreviewRequest
4849 }
@@ -78,7 +79,7 @@ func (c *PreviewClient) Preview(ctx context.Context, target domain.RemoteSession
7879 if stdout.overflow || stderr.overflow {
7980 return protocol.RemotePreview{}, errRemotePreviewTooLarge
8081 }
81- preview, err := ports.UnmarshalRemotePreview(stdout.Bytes())
82+ preview, err := wire.UnmarshalRemotePreview(stdout.Bytes())
8283 if err != nil {
8384 return protocol.RemotePreview{}, err
8485 }
M internal/adapters/remote/preview_test.go +4 -4
@@ -15,8 +15,8 @@ import (
1515 renderer "github.com/bnema/vev-vt"
1616 "github.com/bnema/vev/internal/adapters/sshstdio"
1717 "github.com/bnema/vev/internal/domain"
18- "github.com/bnema/vev/internal/ports"
1918 "github.com/bnema/vev/internal/protocol"
19+ "github.com/bnema/vev/internal/protocol/wire"
2020 )
2121
2222 func previewClientTargetForTest() domain.RemoteSessionTarget {
@@ -37,7 +37,7 @@ func TestRemotePreviewClientBuildsExactSSHCommandAndDecodesResponse(t *testing.T
3737 LifecycleID: lifecycle, TabID: target.LiveTabID, Revision: 7, Width: 1, Height: 1,
3838 Cells: []renderer.Cell{{Rune: 'x', Style: renderer.DefaultStyle()}},
3939 }
40- payload := ports.MarshalRemotePreview(want)
40+ payload := wire.MarshalRemotePreview(want)
4141 require.NotNil(t, payload)
4242
4343 var gotPath string
@@ -57,7 +57,7 @@ func TestRemotePreviewClientBuildsExactSSHCommandAndDecodesResponse(t *testing.T
5757 if err != nil {
5858 t.Fatalf("decode request: %v", err)
5959 }
60- request, err := ports.UnmarshalRemotePreviewRequest(requestPayload)
60+ request, err := wire.UnmarshalRemotePreviewRequest(requestPayload)
6161 if err != nil {
6262 t.Fatalf("decode request payload: %v", err)
6363 }
@@ -96,7 +96,7 @@ func TestRemotePreviewClientRejectsMismatchedResponseIdentity(t *testing.T) {
9696 t.Run(tt.name, func(t *testing.T) {
9797 preview := valid
9898 tt.mutate(&preview)
99- payload := ports.MarshalRemotePreview(preview)
99+ payload := wire.MarshalRemotePreview(preview)
100100 require.NotNil(t, payload)
101101
102102 client := &PreviewClient{command: func(ctx context.Context, _ string, _ ...string) *exec.Cmd {
M internal/adapters/sshstdio/transport.go +9 -8
@@ -19,10 +19,11 @@ import (
1919 "time"
2020
2121 "github.com/bnema/vev/internal/ports"
22+ "github.com/bnema/vev/internal/protocol/wire"
2223 )
2324
2425 const (
25- maxFrameLen = ports.MaxFrameLen
26+ maxFrameLen = wire.MaxFrameLen
2627 frameHeaderLen = 4
2728 sshCloseTimeout = 3 * time.Second
2829 )
@@ -260,7 +261,7 @@ func (r processStdinReader) Read(dst []byte) (int, error) {
260261 return r.pump.read(r.done, dst)
261262 }
262263
263-func (t *transport) Send(f ports.Frame) error {
264+func (t *transport) Send(f wire.Frame) error {
264265 end := t.beginOperation(ports.RuntimeAdapterSendStart, uint64(len(f.Payload)))
265266 n := 1 + len(f.Payload)
266267 if n > maxFrameLen {
@@ -280,23 +281,23 @@ func (t *transport) Send(f ports.Frame) error {
280281 return err
281282 }
282283
283-func (t *transport) Recv() (ports.Frame, error) {
284+func (t *transport) Recv() (wire.Frame, error) {
284285 end := t.beginOperation(ports.RuntimeAdapterReceiveStart, 0)
285286 var hdr [frameHeaderLen]byte
286287 if _, err := io.ReadFull(t.r, hdr[:]); err != nil {
287288 err = t.mapEOFError(err)
288289 end(false)
289- return ports.Frame{}, err
290+ return wire.Frame{}, err
290291 }
291292
292293 n := binary.BigEndian.Uint32(hdr[:])
293294 if n == 0 {
294295 end(false)
295- return ports.Frame{}, ErrZeroLengthFrame
296+ return wire.Frame{}, ErrZeroLengthFrame
296297 }
297298 if n > maxFrameLen {
298299 end(false)
299- return ports.Frame{}, ErrFrameTooLarge
300+ return wire.Frame{}, ErrFrameTooLarge
300301 }
301302
302303 if cap(t.readBuf) < int(n) {
@@ -307,12 +308,12 @@ func (t *transport) Recv() (ports.Frame, error) {
307308 if _, err := io.ReadFull(t.r, t.readBuf); err != nil {
308309 err = t.mapEOFError(err)
309310 end(false)
310- return ports.Frame{}, err
311+ return wire.Frame{}, err
311312 }
312313
313314 payload := append([]byte(nil), t.readBuf[1:]...)
314315 end(true)
315- return ports.Frame{Type: ports.MsgType(t.readBuf[0]), Payload: payload}, nil
316+ return wire.Frame{Type: wire.MsgType(t.readBuf[0]), Payload: payload}, nil
316317 }
317318
318319 func (t *transport) beginOperation(start ports.RuntimeMarkKind, bytes uint64) func(bool) {
M internal/adapters/sshstdio/transport_observability_test.go +2 -1
@@ -13,6 +13,7 @@ import (
1313
1414 "github.com/bnema/vev/internal/adapters/observability"
1515 "github.com/bnema/vev/internal/ports"
16+ "github.com/bnema/vev/internal/protocol/wire"
1617 )
1718
1819 func TestTransportObservabilitySSHStdioEOFEndsReceive(t *testing.T) {
@@ -212,7 +213,7 @@ func TestTransportObservabilitySSHStdioPreservesCarriage(t *testing.T) {
212213 reporter := ports.NewSerializedRuntimeObserver(observer, 64)
213214 defer reporter.Close()
214215
215- frame := ports.Frame{Type: ports.MsgOutput, Payload: []byte("stdio bytes stay exact")}
216+ frame := wire.Frame{Type: wire.MsgOutput, Payload: []byte("stdio bytes stay exact")}
216217 var observed, baseline bytes.Buffer
217218 if err := NewTransport(bytes.NewReader(nil), &observed, nil, WithRuntimeObserver(reporter)).Send(frame); err != nil {
218219 t.Fatalf("observed Send() error = %v", err)
M internal/adapters/sshstdio/transport_replay_test.go +3 -3
@@ -5,12 +5,12 @@ import (
55 "sync"
66 "testing"
77
8- "github.com/bnema/vev/internal/ports"
8+ "github.com/bnema/vev/internal/protocol/wire"
99 "github.com/bnema/vev/internal/testutil/replaytest"
1010 )
1111
1212 func TestTransportReplay(t *testing.T) {
13- replaytest.Run(t, func(t *testing.T, frames []ports.Frame) []ports.Frame {
13+ replaytest.Run(t, func(t *testing.T, frames []wire.Frame) []wire.Frame {
1414 clientRead, serverWrite := io.Pipe()
1515 serverRead, clientWrite := io.Pipe()
1616 sender := NewTransport(clientRead, clientWrite, func() error { return clientWrite.Close() })
@@ -26,7 +26,7 @@ func TestTransportReplay(t *testing.T) {
2626 }
2727 }
2828 })
29- got := make([]ports.Frame, 0, len(frames))
29+ got := make([]wire.Frame, 0, len(frames))
3030 for range frames {
3131 frame, err := receiver.Recv()
3232 if err != nil {
M internal/adapters/sshstdio/transport_test.go +15 -15
@@ -14,8 +14,8 @@ import (
1414 "time"
1515
1616 "github.com/bnema/vev/internal/domain"
17- "github.com/bnema/vev/internal/ports"
1817 "github.com/bnema/vev/internal/protocol"
18+ "github.com/bnema/vev/internal/protocol/wire"
1919 )
2020
2121 func TestBuildCommandUsesExecArgs(t *testing.T) {
@@ -114,7 +114,7 @@ func TestCloseInterruptsBlockedSend(t *testing.T) {
114114 transport := NewTransport(nil, writer, nil)
115115
116116 sendDone := make(chan error, 1)
117- go func() { sendDone <- transport.Send(ports.Frame{Type: ports.MsgOutput, Payload: []byte("blocked")}) }()
117+ go func() { sendDone <- transport.Send(wire.Frame{Type: wire.MsgOutput, Payload: []byte("blocked")}) }()
118118 <-writer.started
119119 if err := transport.Close(); err != nil {
120120 t.Fatalf("Close() error = %v", err)
@@ -166,7 +166,7 @@ func TestTransportRoundTripAndVersionMismatchFrame(t *testing.T) {
166166 t.Errorf("server Recv: %v", err)
167167 return
168168 }
169- got, err := ports.UnmarshalHello(f.Payload)
169+ got, err := wire.UnmarshalHello(f.Payload)
170170 if err != nil {
171171 t.Errorf("UnmarshalHello: %v", err)
172172 return
@@ -174,20 +174,20 @@ func TestTransportRoundTripAndVersionMismatchFrame(t *testing.T) {
174174 if got.Version == protocol.Version {
175175 t.Errorf("test did not send a mismatched version")
176176 }
177- _ = server.Send(ports.Frame{Type: ports.MsgError, Payload: ports.MarshalErrorMsg(protocol.ErrorMsg{Code: protocol.ErrVersionMismatch, Text: "protocol version mismatch"})})
177+ _ = server.Send(wire.Frame{Type: wire.MsgError, Payload: wire.MarshalErrorMsg(protocol.ErrorMsg{Code: protocol.ErrVersionMismatch, Text: "protocol version mismatch"})})
178178 }()
179179
180- if err := client.Send(ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(hello)}); err != nil {
180+ if err := client.Send(wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(hello)}); err != nil {
181181 t.Fatalf("client Send: %v", err)
182182 }
183183 reply, err := client.Recv()
184184 if err != nil {
185185 t.Fatalf("client Recv: %v", err)
186186 }
187- if reply.Type != ports.MsgError {
187+ if reply.Type != wire.MsgError {
188188 t.Fatalf("reply type = %d, want MsgError", reply.Type)
189189 }
190- em, err := ports.UnmarshalErrorMsg(reply.Payload)
190+ em, err := wire.UnmarshalErrorMsg(reply.Payload)
191191 if err != nil {
192192 t.Fatalf("UnmarshalErrorMsg: %v", err)
193193 }
@@ -205,35 +205,35 @@ func TestTransportRejectsZeroLengthFrame(t *testing.T) {
205205 }
206206
207207 func TestTransportUsesCanonicalFrameMaximum(t *testing.T) {
208- boundaryPayload := make([]byte, ports.MaxFrameLen-1)
208+ boundaryPayload := make([]byte, wire.MaxFrameLen-1)
209209 boundaryWire := &bytes.Buffer{}
210210 boundarySend := NewTransport(nil, boundaryWire, nil)
211- if err := boundarySend.Send(ports.Frame{Type: ports.MsgOutput, Payload: boundaryPayload}); err != nil {
211+ if err := boundarySend.Send(wire.Frame{Type: wire.MsgOutput, Payload: boundaryPayload}); err != nil {
212212 t.Fatalf("boundary Send error = %v", err)
213213 }
214- if got := binary.BigEndian.Uint32(boundaryWire.Bytes()[:frameHeaderLen]); got != ports.MaxFrameLen {
215- t.Fatalf("boundary frame length = %d, want %d", got, ports.MaxFrameLen)
214+ if got := binary.BigEndian.Uint32(boundaryWire.Bytes()[:frameHeaderLen]); got != wire.MaxFrameLen {
215+ t.Fatalf("boundary frame length = %d, want %d", got, wire.MaxFrameLen)
216216 }
217217 boundaryRecv := NewTransport(bytes.NewReader(boundaryWire.Bytes()), io.Discard, nil)
218218 boundaryFrame, err := boundaryRecv.Recv()
219219 if err != nil {
220220 t.Fatalf("boundary Recv error = %v", err)
221221 }
222- if boundaryFrame.Type != ports.MsgOutput || len(boundaryFrame.Payload) != len(boundaryPayload) {
223- t.Fatalf("boundary frame = type %d, payload %d bytes; want type %d, payload %d bytes", boundaryFrame.Type, len(boundaryFrame.Payload), ports.MsgOutput, len(boundaryPayload))
222+ if boundaryFrame.Type != wire.MsgOutput || len(boundaryFrame.Payload) != len(boundaryPayload) {
223+ t.Fatalf("boundary frame = type %d, payload %d bytes; want type %d, payload %d bytes", boundaryFrame.Type, len(boundaryFrame.Payload), wire.MsgOutput, len(boundaryPayload))
224224 }
225225 if !bytes.Equal(boundaryFrame.Payload, boundaryPayload) {
226226 t.Fatal("boundary payload was corrupted")
227227 }
228228
229229 send := NewTransport(nil, io.Discard, nil)
230- err = send.Send(ports.Frame{Type: ports.MsgOutput, Payload: make([]byte, ports.MaxFrameLen)})
230+ err = send.Send(wire.Frame{Type: wire.MsgOutput, Payload: make([]byte, wire.MaxFrameLen)})
231231 if !errors.Is(err, ErrFrameTooLarge) {
232232 t.Fatalf("Send error = %v, want ErrFrameTooLarge", err)
233233 }
234234
235235 var header [frameHeaderLen]byte
236- binary.BigEndian.PutUint32(header[:], ports.MaxFrameLen+1)
236+ binary.BigEndian.PutUint32(header[:], wire.MaxFrameLen+1)
237237 recv := NewTransport(bytes.NewReader(header[:]), io.Discard, nil)
238238 _, err = recv.Recv()
239239 if !errors.Is(err, ErrFrameTooLarge) {
M internal/app/cmd.go +6 -5
@@ -14,6 +14,7 @@ import (
1414 "github.com/bnema/vev/internal/adapters/ipc"
1515 "github.com/bnema/vev/internal/ports"
1616 "github.com/bnema/vev/internal/protocol"
17+ "github.com/bnema/vev/internal/protocol/wire"
1718 commandusecase "github.com/bnema/vev/internal/usecase/command"
1819 "github.com/bnema/vev/internal/usecase/daemon"
1920 )
@@ -170,7 +171,7 @@ func runCmdWithDeps(ctx context.Context, invocation cmdInvocation, deps cmdDeps)
170171 return usagef("--self requires running inside a vev pane")
171172 }
172173
173- if _, err := ports.MarshalCommandRequest(request); errors.Is(err, ports.ErrTooManyCommandArgs) {
174+ if _, err := wire.MarshalCommandRequest(request); errors.Is(err, wire.ErrTooManyCommandArgs) {
174175 return usagef("too many command arguments")
175176 } else if err != nil {
176177 return fmt.Errorf("encoding command: %w", err)
@@ -190,12 +191,12 @@ func runCmdWithDeps(ctx context.Context, invocation cmdInvocation, deps cmdDeps)
190191 const generation = uint64(1)
191192 requestID, outcome := tracker.Publish(generation)
192193 request.RequestID = requestID
193- payload, err := ports.MarshalCommandRequest(request)
194+ payload, err := wire.MarshalCommandRequest(request)
194195 if err != nil {
195196 tracker.Remove(requestID, generation)
196197 return fmt.Errorf("encoding command: %w", err)
197198 }
198- if err := transport.Send(ports.Frame{Type: ports.MsgCommand, Payload: payload}); err != nil {
199+ if err := transport.Send(wire.Frame{Type: wire.MsgCommand, Payload: payload}); err != nil {
199200 tracker.Remove(requestID, generation)
200201 return &exitCoded{code: 3, err: fmt.Errorf("sending command: %w", err)}
201202 }
@@ -205,11 +206,11 @@ func runCmdWithDeps(ctx context.Context, invocation cmdInvocation, deps cmdDeps)
205206 tracker.Fail(requestID, generation, fmt.Errorf("reading command reply: %w", recvErr))
206207 return
207208 }
208- if reply.Type != ports.MsgCommandResult {
209+ if reply.Type != wire.MsgCommandResult {
209210 tracker.Fail(requestID, generation, fmt.Errorf("unexpected command reply type %d", reply.Type))
210211 return
211212 }
212- result, decodeErr := ports.UnmarshalCommandResult(reply.Payload)
213+ result, decodeErr := wire.UnmarshalCommandResult(reply.Payload)
213214 if decodeErr != nil {
214215 tracker.Fail(requestID, generation, fmt.Errorf("decoding command reply: %w", decodeErr))
215216 return
M internal/app/cmd_test.go +21 -20
@@ -14,6 +14,7 @@ import (
1414 "github.com/bnema/vev/internal/ports"
1515 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1616 "github.com/bnema/vev/internal/protocol"
17+ "github.com/bnema/vev/internal/protocol/wire"
1718 "github.com/bnema/vev/internal/usecase/daemon"
1819 "github.com/stretchr/testify/mock"
1920 "github.com/stretchr/testify/require"
@@ -141,14 +142,14 @@ func TestMoveCmdPreservesPositionalArguments(t *testing.T) {
141142 t.Run(tt.slug, func(t *testing.T) {
142143 parsed, err := parseArgs(append([]string{"cmd", tt.slug}, tt.args...))
143144 require.NoError(t, err)
144- transport := &cmdTestTransport{recv: ports.Frame{Type: ports.MsgCommandResult, Payload: ports.MarshalCommandResult(protocol.CommandResult{OK: true})}}
145+ transport := &cmdTestTransport{recv: wire.Frame{Type: wire.MsgCommandResult, Payload: wire.MarshalCommandResult(protocol.CommandResult{OK: true})}}
145146 require.NoError(t, runCmdWithDeps(context.Background(), parsed.cmd, cmdDeps{
146147 stdout: io.Discard,
147148 getenv: func(string) string { return "" },
148149 dial: func(context.Context, string) (ports.Transport, error) { return transport, nil },
149150 }))
150151 require.Len(t, transport.sent, 1)
151- request, err := ports.UnmarshalCommandRequest(transport.sent[0].Payload)
152+ request, err := wire.UnmarshalCommandRequest(transport.sent[0].Payload)
152153 require.NoError(t, err)
153154 require.Equal(t, tt.slug, request.Slug)
154155 require.Equal(t, tt.args, request.Args)
@@ -158,7 +159,7 @@ func TestMoveCmdPreservesPositionalArguments(t *testing.T) {
158159 }
159160
160161 func TestRemoteCatalogCommandEnsuresDaemon(t *testing.T) {
161- transport := &cmdTestTransport{recv: ports.Frame{Type: ports.MsgCommandResult, Payload: ports.MarshalCommandResult(protocol.CommandResult{OK: true})}}
162+ transport := &cmdTestTransport{recv: wire.Frame{Type: wire.MsgCommandResult, Payload: wire.MarshalCommandResult(protocol.CommandResult{OK: true})}}
162163 ensureCalls := 0
163164 err := runCmdWithDeps(context.Background(), cmdInvocation{slug: "remote-catalog", jsonOut: true}, cmdDeps{
164165 stdout: io.Discard,
@@ -184,7 +185,7 @@ func TestMoveCmdInvalidArgumentResultExitsTwo(t *testing.T) {
184185 {slug: "move-tab", args: []string{"work", "extra"}},
185186 } {
186187 t.Run(invocation.slug+"/"+strconv.Itoa(len(invocation.args)), func(t *testing.T) {
187- transport := &cmdTestTransport{recv: ports.Frame{Type: ports.MsgCommandResult, Payload: ports.MarshalCommandResult(protocol.CommandResult{
188+ transport := &cmdTestTransport{recv: wire.Frame{Type: wire.MsgCommandResult, Payload: wire.MarshalCommandResult(protocol.CommandResult{
188189 Code: protocol.ErrInvalidCommandArgs, Text: "invalid command arguments",
189190 })}}
190191 err := runCmdWithDeps(context.Background(), invocation, cmdDeps{
@@ -207,14 +208,14 @@ func TestTargetPaneCmdsParseAndUseSelfTarget(t *testing.T) {
207208 t.Run(slug, func(t *testing.T) {
208209 invocation, err := parseArgs([]string{"cmd", "--self", slug})
209210 require.NoError(t, err)
210- transport := &cmdTestTransport{recv: ports.Frame{Type: ports.MsgCommandResult, Payload: ports.MarshalCommandResult(protocol.CommandResult{OK: true})}}
211+ transport := &cmdTestTransport{recv: wire.Frame{Type: wire.MsgCommandResult, Payload: wire.MarshalCommandResult(protocol.CommandResult{OK: true})}}
211212 require.NoError(t, runCmdWithDeps(context.Background(), invocation.cmd, cmdDeps{
212213 stdout: io.Discard,
213214 getenv: func(string) string { return "session=work,tab=t_abc,pane=p_def" },
214215 dial: func(context.Context, string) (ports.Transport, error) { return transport, nil },
215216 }))
216217 require.Len(t, transport.sent, 1)
217- request, err := ports.UnmarshalCommandRequest(transport.sent[0].Payload)
218+ request, err := wire.UnmarshalCommandRequest(transport.sent[0].Payload)
218219 require.NoError(t, err)
219220 require.Equal(t, slug, request.Slug)
220221 require.True(t, request.Self)
@@ -226,7 +227,7 @@ func TestTargetPaneCmdsParseAndUseSelfTarget(t *testing.T) {
226227 }
227228
228229 func TestRunCmdBuildsOneShotTargetedRequest(t *testing.T) {
229- transport := &cmdTestTransport{recv: ports.Frame{Type: ports.MsgCommandResult, Payload: ports.MarshalCommandResult(protocol.CommandResult{OK: true, Output: "done"})}}
230+ transport := &cmdTestTransport{recv: wire.Frame{Type: wire.MsgCommandResult, Payload: wire.MarshalCommandResult(protocol.CommandResult{OK: true, Output: "done"})}}
230231 out := new(strings.Builder)
231232 err := runCmdWithDeps(context.Background(), cmdInvocation{slug: "split-right", self: true}, cmdDeps{
232233 stdout: out,
@@ -239,11 +240,11 @@ func TestRunCmdBuildsOneShotTargetedRequest(t *testing.T) {
239240 if len(transport.sent) != 1 || transport.recvCalls != 1 || transport.closeCalls != 1 {
240241 t.Fatalf("transport calls: sent=%d recv=%d close=%d", len(transport.sent), transport.recvCalls, transport.closeCalls)
241242 }
242- req, err := ports.UnmarshalCommandRequest(transport.sent[0].Payload)
243+ req, err := wire.UnmarshalCommandRequest(transport.sent[0].Payload)
243244 if err != nil {
244245 t.Fatalf("decode request: %v", err)
245246 }
246- if transport.sent[0].Type != ports.MsgCommand || req.Slug != "split-right" || !req.Self || req.TargetSession != "old" || req.TargetTab != "t_abc" || req.TargetPane != "p_def" {
247+ if transport.sent[0].Type != wire.MsgCommand || req.Slug != "split-right" || !req.Self || req.TargetSession != "old" || req.TargetTab != "t_abc" || req.TargetPane != "p_def" {
247248 t.Fatalf("request = type %d %+v", transport.sent[0].Type, req)
248249 }
249250 if out.String() != "done\n" {
@@ -252,7 +253,7 @@ func TestRunCmdBuildsOneShotTargetedRequest(t *testing.T) {
252253 }
253254
254255 func TestRunCmdVEVWithoutSelfUsesIDsOnlyAsSessionLocator(t *testing.T) {
255- transport := &cmdTestTransport{recv: ports.Frame{Type: ports.MsgCommandResult, Payload: ports.MarshalCommandResult(protocol.CommandResult{OK: true})}}
256+ transport := &cmdTestTransport{recv: wire.Frame{Type: wire.MsgCommandResult, Payload: wire.MarshalCommandResult(protocol.CommandResult{OK: true})}}
256257 err := runCmdWithDeps(context.Background(), cmdInvocation{slug: "split-right"}, cmdDeps{
257258 stdout: io.Discard,
258259 getenv: func(string) string { return "session=old,tab=t_abc,pane=p_def" },
@@ -261,7 +262,7 @@ func TestRunCmdVEVWithoutSelfUsesIDsOnlyAsSessionLocator(t *testing.T) {
261262 if err != nil {
262263 t.Fatalf("runCmdWithDeps: %v", err)
263264 }
264- req, err := ports.UnmarshalCommandRequest(transport.sent[0].Payload)
265+ req, err := wire.UnmarshalCommandRequest(transport.sent[0].Payload)
265266 if err != nil {
266267 t.Fatalf("decode request: %v", err)
267268 }
@@ -271,7 +272,7 @@ func TestRunCmdVEVWithoutSelfUsesIDsOnlyAsSessionLocator(t *testing.T) {
271272 }
272273
273274 func TestRunCmdExplicitSessionDoesNotUseEnvIDsWithoutSelf(t *testing.T) {
274- transport := &cmdTestTransport{recv: ports.Frame{Type: ports.MsgCommandResult, Payload: ports.MarshalCommandResult(protocol.CommandResult{OK: true})}}
275+ transport := &cmdTestTransport{recv: wire.Frame{Type: wire.MsgCommandResult, Payload: wire.MarshalCommandResult(protocol.CommandResult{OK: true})}}
275276 err := runCmdWithDeps(context.Background(), cmdInvocation{slug: "new-tab", session: "current"}, cmdDeps{
276277 stdout: io.Discard,
277278 getenv: func(string) string { return "session=old,tab=t_abc,pane=p_def" },
@@ -280,7 +281,7 @@ func TestRunCmdExplicitSessionDoesNotUseEnvIDsWithoutSelf(t *testing.T) {
280281 if err != nil {
281282 t.Fatalf("runCmdWithDeps: %v", err)
282283 }
283- req, err := ports.UnmarshalCommandRequest(transport.sent[0].Payload)
284+ req, err := wire.UnmarshalCommandRequest(transport.sent[0].Payload)
284285 if err != nil {
285286 t.Fatalf("decode request: %v", err)
286287 }
@@ -343,7 +344,7 @@ func TestRunCmdTimesOutPendingRequest(t *testing.T) {
343344 if ExitCode(err) != 3 || !errors.Is(err, daemon.ErrCommandRequestTimeout) {
344345 t.Fatalf("timeout error=%v code=%d", err, ExitCode(err))
345346 }
346- request, decodeErr := ports.UnmarshalCommandRequest(transport.sent[0].Payload)
347+ request, decodeErr := wire.UnmarshalCommandRequest(transport.sent[0].Payload)
347348 if decodeErr != nil || request.RequestID == 0 {
348349 t.Fatalf("request=%+v decode=%v, want non-zero request ID", request, decodeErr)
349350 }
@@ -351,7 +352,7 @@ func TestRunCmdTimesOutPendingRequest(t *testing.T) {
351352
352353 func TestRunCmdZeroRequestIDReplyReturnsDaemonError(t *testing.T) {
353354 const want = "daemon error"
354- transport := &cmdTestTransport{recv: ports.Frame{Type: ports.MsgCommandResult, Payload: ports.MarshalCommandResult(protocol.CommandResult{
355+ transport := &cmdTestTransport{recv: wire.Frame{Type: wire.MsgCommandResult, Payload: wire.MarshalCommandResult(protocol.CommandResult{
355356 Code: protocol.ErrInternal, Text: want,
356357 })}}
357358 err := runCmdWithDeps(context.Background(), cmdInvocation{slug: "split-right"}, cmdDeps{
@@ -415,7 +416,7 @@ func TestRunCmdClassifiesDaemonCommandErrors(t *testing.T) {
415416 }
416417 for _, tt := range tests {
417418 t.Run(tt.name, func(t *testing.T) {
418- transport := &cmdTestTransport{recv: ports.Frame{Type: ports.MsgCommandResult, Payload: ports.MarshalCommandResult(tt.result)}}
419+ transport := &cmdTestTransport{recv: wire.Frame{Type: wire.MsgCommandResult, Payload: wire.MarshalCommandResult(tt.result)}}
419420 err := runCmdWithDeps(context.Background(), cmdInvocation{slug: "split-right"}, cmdDeps{
420421 stdout: io.Discard,
421422 getenv: func(string) string { return "" },
@@ -429,8 +430,8 @@ func TestRunCmdClassifiesDaemonCommandErrors(t *testing.T) {
429430 }
430431
431432 type cmdTestTransport struct {
432- sent []ports.Frame
433- recv ports.Frame
433+ sent []wire.Frame
434+ recv wire.Frame
434435 recvErr error
435436 recvCalls int
436437 closeCalls int
@@ -438,12 +439,12 @@ type cmdTestTransport struct {
438439 recvGate <-chan struct{}
439440 }
440441
441-func (t *cmdTestTransport) Send(frame ports.Frame) error {
442+func (t *cmdTestTransport) Send(frame wire.Frame) error {
442443 t.sent = append(t.sent, frame)
443444 return nil
444445 }
445446
446-func (t *cmdTestTransport) Recv() (ports.Frame, error) {
447+func (t *cmdTestTransport) Recv() (wire.Frame, error) {
447448 t.recvCalls++
448449 if t.recvStarted != nil {
449450 close(t.recvStarted)
M internal/app/daemon_integration_test.go +38 -37
@@ -29,6 +29,7 @@ import (
2929 "github.com/bnema/vev/internal/persist"
3030 "github.com/bnema/vev/internal/ports"
3131 "github.com/bnema/vev/internal/protocol"
32+ "github.com/bnema/vev/internal/protocol/wire"
3233 "github.com/bnema/vev/internal/usecase/daemon"
3334 "github.com/bnema/vev/internal/usecase/recovery"
3435 )
@@ -64,10 +65,10 @@ func startDaemonInDir(t *testing.T, dir string, opts ...daemon.Option) (string,
6465
6566 // pump wires a background Recv pump to a transport so tests can await frames
6667 // with a timeout (ports.Transport has no read deadline).
67-type pump struct{ ch chan ports.Frame }
68+type pump struct{ ch chan wire.Frame }
6869
6970 func recvPump(tr ports.Transport) *pump {
70- p := &pump{ch: make(chan ports.Frame, 128)}
71+ p := &pump{ch: make(chan wire.Frame, 128)}
7172 go func() {
7273 for {
7374 f, err := tr.Recv()
@@ -92,12 +93,12 @@ func attachWithEnvironment(t *testing.T, dir string, intent uint8, name string,
9293 tr, err := ipc.DialContext(context.Background(), dir)
9394 require.NoError(t, err)
9495 hello := protocol.Hello{Version: protocol.Version, Intent: intent, Name: name, Size: sz, TermEnv: "xterm-256color", TrueColor: true, Env: env}
95- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(hello)}))
96+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(hello)}))
9697 p := recvPump(tr)
9798 select {
9899 case f, ok := <-p.ch:
99100 require.True(t, ok, "connection closed before welcome")
100- require.Equal(t, ports.MsgWelcome, f.Type)
101+ require.Equal(t, wire.MsgWelcome, f.Type)
101102 case <-time.After(3 * time.Second):
102103 t.Fatal("timed out waiting for welcome")
103104 }
@@ -109,11 +110,11 @@ func listRemoteSessions(t *testing.T, dir string) protocol.Sessions {
109110 tr, err := ipc.DialContext(context.Background(), dir)
110111 require.NoError(t, err)
111112 defer func() { _ = tr.Close() }()
112- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgList, Payload: ports.MarshalList(protocol.List{})}))
113+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgList, Payload: wire.MarshalList(protocol.List{})}))
113114 f, err := tr.Recv()
114115 require.NoError(t, err)
115- require.Equal(t, ports.MsgSessions, f.Type)
116- sessions, err := ports.UnmarshalSessions(f.Payload)
116+ require.Equal(t, wire.MsgSessions, f.Type)
117+ sessions, err := wire.UnmarshalSessions(f.Payload)
117118 require.NoError(t, err)
118119 return sessions
119120 }
@@ -124,7 +125,7 @@ func killAll(dir string) error {
124125 return err
125126 }
126127 defer func() { _ = tr.Close() }()
127- if err := tr.Send(ports.Frame{Type: ports.MsgKill, Payload: ports.MarshalKill(protocol.Kill{All: true})}); err != nil {
128+ if err := tr.Send(wire.Frame{Type: wire.MsgKill, Payload: wire.MarshalKill(protocol.Kill{All: true})}); err != nil {
128129 return err
129130 }
130131 _, err = tr.Recv()
@@ -153,8 +154,8 @@ func awaitScreenText(t *testing.T, p *pump, sz domain.Size, want string) string
153154 if !ok {
154155 t.Fatalf("connection closed before %q appeared; screen=%q", want, screenText(screen))
155156 }
156- if f.Type == ports.MsgOutput {
157- o, err := ports.UnmarshalOutput(f.Payload)
157+ if f.Type == wire.MsgOutput {
158+ o, err := wire.UnmarshalOutput(f.Payload)
158159 require.NoError(t, err)
159160 screen.Write(o.Data)
160161 text := screenText(screen)
@@ -178,8 +179,8 @@ func assertNoTextAfterInput(t *testing.T, p *pump, sz domain.Size, absent string
178179 if !ok {
179180 return
180181 }
181- if f.Type == ports.MsgOutput {
182- o, err := ports.UnmarshalOutput(f.Payload)
182+ if f.Type == wire.MsgOutput {
183+ o, err := wire.UnmarshalOutput(f.Payload)
183184 require.NoError(t, err)
184185 screen.Write(o.Data)
185186 require.NotContains(t, screenText(screen), absent)
@@ -214,7 +215,7 @@ func shellFixture(t *testing.T, label string) string {
214215 func assertChildEnvironment(t *testing.T, tr ports.Transport, p *pump, sz domain.Size, wantTestEnv, wantShell, wantRuntimeDir, wantWayland string) {
215216 t.Helper()
216217 command := "printf '\\033[2J\\033[H'; printf 'VEV_TEST_ENV=%s SHELL=%s XDG_RUNTIME_DIR=%s WAYLAND_DISPLAY=%s TERM=%s COLORTERM=%s TERM_PROGRAM=%s VEV_PREFIX=%.24s\\n' \"$VEV_TEST_ENV\" \"${SHELL##*/}\" \"$XDG_RUNTIME_DIR\" \"$WAYLAND_DISPLAY\" \"$TERM\" \"$COLORTERM\" \"$TERM_PROGRAM\" \"$VEV\"\n"
217- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte(command)})}))
218+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte(command)})}))
218219 text := awaitScreenText(t, p, sz, "TERM_PROGRAM=vev")
219220 for _, want := range []string{
220221 "VEV_TEST_ENV=" + wantTestEnv,
@@ -255,9 +256,9 @@ func TestIntegration_AttachEnvironmentRefreshesFuturePTYChildren(t *testing.T) {
255256 // The first shell was already running, so it retains its original environment.
256257 assertChildEnvironment(t, tr2, p2, sz, "first", firstShell, "/run/first", "wayland-first")
257258
258- require.NoError(t, tr2.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("\x1b ")})}))
259+ require.NoError(t, tr2.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("\x1b ")})}))
259260 awaitText(t, p2, sz, "Commands")
260- require.NoError(t, tr2.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("CNT\r")})}))
261+ require.NoError(t, tr2.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("CNT\r")})}))
261262 awaitText(t, p2, sz, "SHELL_COMMAND=second")
262263 assertChildEnvironment(t, tr2, p2, sz, "second", secondShell, "/run/second", "wayland-second")
263264 }
@@ -288,17 +289,17 @@ func TestIntegration_TwoAttachmentsReceiveSharedMutationPTYOutput(t *testing.T)
288289
289290 // The second attachment mutates shared tab state. Its newly opened PTY must
290291 // also be rendered to that attachment, not only to the coordinator primary.
291- require.NoError(t, tr2.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("\x1b ")})}))
292+ require.NoError(t, tr2.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("\x1b ")})}))
292293 awaitText(t, p2, sz, "Commands")
293- require.NoError(t, tr2.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("CNT\r")})}))
294+ require.NoError(t, tr2.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("CNT\r")})}))
294295 awaitText(t, p2, sz, "SHELL_COMMAND=second")
295296
296297 // Each attachment owns its selected tab. Move the first attachment to the
297298 // new PTY before releasing its gate, so the fresh PTY output must reach both
298299 // views rather than only being recovered by a later first paint.
299- require.NoError(t, tr1.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("\x1b2")})}))
300+ require.NoError(t, tr1.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("\x1b2")})}))
300301 awaitText(t, p1, sz, "SHELL_COMMAND=second")
301- require.NoError(t, tr1.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("go\n")})}))
302+ require.NoError(t, tr1.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("go\n")})}))
302303 awaitText(t, p2, sz, sharedOutput)
303304 awaitText(t, p1, sz, sharedOutput)
304305 }
@@ -320,7 +321,7 @@ func TestIntegration_InputRoundtrip(t *testing.T) {
320321 tr, p := attach(t, dir, protocol.IntentEphemeral, "", sz)
321322 defer func() { _ = tr.Close() }()
322323
323- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("PINGPONG\n")})}))
324+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("PINGPONG\n")})}))
324325 awaitText(t, p, sz, "PINGPONG")
325326 }
326327
@@ -332,10 +333,10 @@ func TestIntegration_CommandPaletteCreatesTab(t *testing.T) {
332333 defer func() { _ = tr.Close() }()
333334 awaitText(t, p, sz, "READY")
334335
335- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("\x1b ")})}))
336+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("\x1b ")})}))
336337 awaitText(t, p, sz, "Commands")
337338
338- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("CNT\r")})}))
339+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("CNT\r")})}))
339340 // Tab labels are enriched with the focused pane's title; with no process
340341 // inspector wired in this test, that title falls back to the shell's
341342 // basename ("sh").
@@ -352,24 +353,24 @@ func TestIntegration_CommandPaletteRenamesEphemeralSession(t *testing.T) {
352353 text := awaitScreenText(t, p, sz, "READY")
353354 require.Contains(t, text, " 0* ")
354355
355- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("\x1b ")})}))
356+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("\x1b ")})}))
356357 awaitText(t, p, sz, "Commands")
357358
358- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("RNS\r")})}))
359+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("RNS\r")})}))
359360 awaitText(t, p, sz, "Rename session")
360361
361- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("\x7fwork\r")})}))
362+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("\x7fwork\r")})}))
362363 text = awaitScreenText(t, p, sz, " work ")
363364 require.NotContains(t, text, "work*")
364365
365366 listTr, err := ipc.DialContext(context.Background(), dir)
366367 require.NoError(t, err)
367368 defer func() { _ = listTr.Close() }()
368- require.NoError(t, listTr.Send(ports.Frame{Type: ports.MsgList, Payload: ports.MarshalList(protocol.List{})}))
369+ require.NoError(t, listTr.Send(wire.Frame{Type: wire.MsgList, Payload: wire.MarshalList(protocol.List{})}))
369370 f, err := listTr.Recv()
370371 require.NoError(t, err)
371- require.Equal(t, ports.MsgSessions, f.Type)
372- sessions, err := ports.UnmarshalSessions(f.Payload)
372+ require.Equal(t, wire.MsgSessions, f.Type)
373+ sessions, err := wire.UnmarshalSessions(f.Payload)
373374 require.NoError(t, err)
374375 require.NotEmpty(t, sessions.Sessions)
375376 require.Equal(t, "work", sessions.Sessions[0].Name)
@@ -385,7 +386,7 @@ func TestIntegration_AltCWithoutPaletteDoesNotCreateTab(t *testing.T) {
385386 text := awaitScreenText(t, p, sz, "READY")
386387 require.NotContains(t, text, " 1 2 ")
387388
388- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("\x1bc")})}))
389+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("\x1bc")})}))
389390 assertNoTextAfterInput(t, p, sz, " 1 2 ")
390391 }
391392
@@ -802,7 +803,7 @@ func publishRestorableCheckpoint(t *testing.T, stateDir string, repository *snap
802803 go func() { served <- d.Serve(ctx, listener) }()
803804
804805 tr, _ := attach(t, runtimeDir, protocol.IntentNew, name, domain.Size{Cols: 80, Rows: 24})
805- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("checkpoint me\n")})}))
806+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("checkpoint me\n")})}))
806807 require.Eventually(t, func() bool {
807808 record, ok, _ := opened.Catalogue.Record(name)
808809 return ok && record.Committed != nil && record.DegradedReason == ""
@@ -888,7 +889,7 @@ func runBlockedSnapshotWriterShutdown(t *testing.T) blockedSnapshotWriterShutdow
888889
889890 awaitLifecycleStage(t, listenerReady, "daemon listener")
890891 tr, _ := attach(t, runtimeDir, protocol.IntentNew, "work", domain.Size{Cols: 80, Rows: 24})
891- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte("dirty state\n")})}))
892+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte("dirty state\n")})}))
892893 awaitLifecycleStage(t, repository.entered, "snapshot publication")
893894 require.NoError(t, tr.Close())
894895 require.Eventually(t, func() bool {
@@ -1240,11 +1241,11 @@ func (c *lifecycleControlledCatalogue) Close() error {
12401241
12411242 type integrationTransport struct{}
12421243
1243-func (*integrationTransport) Send(ports.Frame) error { return nil }
1244-func (*integrationTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
1245-func (*integrationTransport) Close() error { return nil }
1246-func (*integrationTransport) LocalAddr() net.Addr { return nil }
1247-func (*integrationTransport) RemoteAddr() net.Addr { return nil }
1244+func (*integrationTransport) Send(wire.Frame) error { return nil }
1245+func (*integrationTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
1246+func (*integrationTransport) Close() error { return nil }
1247+func (*integrationTransport) LocalAddr() net.Addr { return nil }
1248+func (*integrationTransport) RemoteAddr() net.Addr { return nil }
12481249
12491250 func TestIntegration_KillAllShutsDownDaemon(t *testing.T) {
12501251 sz := domain.Size{Cols: 80, Rows: 24}
@@ -1258,7 +1259,7 @@ func TestIntegration_KillAllShutsDownDaemon(t *testing.T) {
12581259 killTr, err := ipc.DialContext(context.Background(), dir)
12591260 require.NoError(t, err)
12601261 defer func() { _ = killTr.Close() }()
1261- require.NoError(t, killTr.Send(ports.Frame{Type: ports.MsgKill, Payload: ports.MarshalKill(protocol.Kill{All: true})}))
1262+ require.NoError(t, killTr.Send(wire.Frame{Type: wire.MsgKill, Payload: wire.MarshalKill(protocol.Kill{All: true})}))
12621263
12631264 _, err = killTr.Recv()
12641265 require.ErrorIs(t, err, io.EOF)
M internal/app/final_acceptance_test.go +17 -16
@@ -9,16 +9,17 @@ import (
99 "github.com/bnema/vev/internal/domain"
1010 "github.com/bnema/vev/internal/ports"
1111 "github.com/bnema/vev/internal/protocol"
12+ "github.com/bnema/vev/internal/protocol/wire"
1213 "github.com/bnema/vev/internal/usecase/daemon"
1314 "github.com/stretchr/testify/require"
1415 )
1516
1617 func sendAcceptanceInput(t *testing.T, tr ports.Transport, data string) {
1718 t.Helper()
18- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte(data)})}))
19+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte(data)})}))
1920 }
2021
21-func awaitAcceptanceFrame(t *testing.T, p *pump, typ ports.MsgType, predicate func(ports.Frame) bool) ports.Frame {
22+func awaitAcceptanceFrame(t *testing.T, p *pump, typ wire.MsgType, predicate func(wire.Frame) bool) wire.Frame {
2223 t.Helper()
2324 deadline := time.NewTimer(5 * time.Second)
2425 defer deadline.Stop()
@@ -37,35 +38,35 @@ func awaitAcceptanceFrame(t *testing.T, p *pump, typ ports.MsgType, predicate fu
3738
3839 func awaitAcceptanceCommand(t *testing.T, p *pump, requestID uint64) protocol.CommandResult {
3940 t.Helper()
40- frame := awaitAcceptanceFrame(t, p, ports.MsgCommandResult, func(frame ports.Frame) bool {
41- result, err := ports.UnmarshalCommandResult(frame.Payload)
41+ frame := awaitAcceptanceFrame(t, p, wire.MsgCommandResult, func(frame wire.Frame) bool {
42+ result, err := wire.UnmarshalCommandResult(frame.Payload)
4243 require.NoError(t, err)
4344 return result.RequestID == requestID
4445 })
45- result, err := ports.UnmarshalCommandResult(frame.Payload)
46+ result, err := wire.UnmarshalCommandResult(frame.Payload)
4647 require.NoError(t, err)
4748 return result
4849 }
4950
5051 func awaitAcceptanceOutput(t *testing.T, p *pump, predicate func(protocol.Output) bool) protocol.Output {
5152 t.Helper()
52- frame := awaitAcceptanceFrame(t, p, ports.MsgOutput, func(frame ports.Frame) bool {
53- output, err := ports.UnmarshalOutput(frame.Payload)
53+ frame := awaitAcceptanceFrame(t, p, wire.MsgOutput, func(frame wire.Frame) bool {
54+ output, err := wire.UnmarshalOutput(frame.Payload)
5455 require.NoError(t, err)
5556 return predicate(output)
5657 })
57- output, err := ports.UnmarshalOutput(frame.Payload)
58+ output, err := wire.UnmarshalOutput(frame.Payload)
5859 require.NoError(t, err)
5960 return output
6061 }
6162
6263 func awaitAcceptanceCommandResult(t *testing.T, tr ports.Transport, p *pump, requestID uint64, slug string) protocol.CommandResult {
6364 t.Helper()
64- payload, err := ports.MarshalCommandRequest(protocol.CommandRequest{
65+ payload, err := wire.MarshalCommandRequest(protocol.CommandRequest{
6566 Version: protocol.Version, RequestID: requestID, Attached: true, Slug: slug,
6667 })
6768 require.NoError(t, err)
68- require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgCommand, Payload: payload}))
69+ require.NoError(t, tr.Send(wire.Frame{Type: wire.MsgCommand, Payload: payload}))
6970 return awaitAcceptanceCommand(t, p, requestID)
7071 }
7172
@@ -114,25 +115,25 @@ func TestAcceptanceTwoLocalAttachmentsKeepViewsOverTransports(t *testing.T) {
114115 // The resize keeps its attachment-local output stream while recalculating
115116 // shared PTY/content geometry from the latest valid attachment claim; the
116117 // peer stream remains independent.
117- resizePayload, err := ports.MarshalResize(protocol.Resize{Size: domain.Size{Cols: 100, Rows: 30}})
118+ resizePayload, err := wire.MarshalResize(protocol.Resize{Size: domain.Size{Cols: 100, Rows: 30}})
118119 require.NoError(t, err)
119- require.NoError(t, first.Send(ports.Frame{Type: ports.MsgResize, Payload: resizePayload}))
120+ require.NoError(t, first.Send(wire.Frame{Type: wire.MsgResize, Payload: resizePayload}))
120121 resized := awaitAcceptanceOutput(t, firstPump, func(output protocol.Output) bool { return output.Size == (domain.Size{Cols: 100, Rows: 30}) })
121122 require.Equal(t, domain.Size{Cols: 100, Rows: 30}, resized.Size)
122123 assertNoTextAfterInput(t, secondPump, size, "FIRST_PANE_INPUT")
123124
124- require.NoError(t, first.Send(ports.Frame{Type: ports.MsgOutputResetRequest, Payload: ports.MarshalOutputResetRequest(protocol.OutputResetRequest{})}))
125+ require.NoError(t, first.Send(wire.Frame{Type: wire.MsgOutputResetRequest, Payload: wire.MarshalOutputResetRequest(protocol.OutputResetRequest{})}))
125126 reset := awaitAcceptanceOutput(t, firstPump, func(output protocol.Output) bool { return output.Base == 0 && output.Full })
126127 require.True(t, reset.Full)
127128 assertNoTextAfterInput(t, secondPump, size, "FIRST_PANE_INPUT")
128129
129- require.NoError(t, first.Send(ports.Frame{Type: ports.MsgDetach, Payload: ports.MarshalDetach(protocol.Detach{})}))
130+ require.NoError(t, first.Send(wire.Frame{Type: wire.MsgDetach, Payload: wire.MarshalDetach(protocol.Detach{})}))
130131 deadline := time.NewTimer(5 * time.Second)
131132 defer deadline.Stop()
132133 for {
133134 select {
134135 case frame := <-firstPump.ch:
135- if frame.Type == ports.MsgDetached {
136+ if frame.Type == wire.MsgDetached {
136137 goto detached
137138 }
138139 case <-deadline.C:
@@ -173,7 +174,7 @@ func TestAcceptanceAttachedCommandUsesItsConnectionOnly(t *testing.T) {
173174 }
174175
175176 drained:
176- require.NoError(t, first.Send(ports.Frame{Type: ports.MsgOutputResetRequest, Payload: ports.MarshalOutputResetRequest(protocol.OutputResetRequest{})}))
177+ require.NoError(t, first.Send(wire.Frame{Type: wire.MsgOutputResetRequest, Payload: wire.MarshalOutputResetRequest(protocol.OutputResetRequest{})}))
177178 awaitAcceptanceOutput(t, firstPump, func(output protocol.Output) bool { return output.Full && output.Base == 0 })
178179 assertNoTextAfterInput(t, secondPump, size, "Commands")
179180 }
M internal/app/remote_final_acceptance_test.go +12 -11
@@ -16,6 +16,7 @@ import (
1616 "github.com/bnema/vev/internal/ports"
1717 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1818 "github.com/bnema/vev/internal/protocol"
19+ "github.com/bnema/vev/internal/protocol/wire"
1920 "github.com/bnema/vev/internal/usecase/client"
2021 "github.com/stretchr/testify/require"
2122 )
@@ -58,17 +59,17 @@ func serveAcceptanceRemote(tr ports.Transport, target string, handoff protocol.A
5859 return
5960 }
6061 if !welcomed {
61- if frame.Type != ports.MsgHello {
62+ if frame.Type != wire.MsgHello {
6263 continue
6364 }
64- if err := tr.Send(ports.Frame{Type: ports.MsgWelcome, Payload: ports.MarshalWelcome(protocol.Welcome{SessionID: target})}); err != nil {
65+ if err := tr.Send(wire.Frame{Type: wire.MsgWelcome, Payload: wire.MarshalWelcome(protocol.Welcome{SessionID: target})}); err != nil {
6566 return
6667 }
6768 welcomed = true
6869 continue
6970 }
7071 if handoff.Endpoint != "" {
71- _ = tr.Send(ports.Frame{Type: ports.MsgAttachTarget, Payload: ports.MarshalAttachTarget(handoff)})
72+ _ = tr.Send(wire.Frame{Type: wire.MsgAttachTarget, Payload: wire.MarshalAttachTarget(handoff)})
7273 for {
7374 if _, err := tr.Recv(); err != nil {
7475 return
@@ -79,16 +80,16 @@ func serveAcceptanceRemote(tr ports.Transport, target string, handoff protocol.A
7980 if outputs != nil {
8081 outputs <- string(data)
8182 }
82- payload, err := ports.MarshalOutput(protocol.Output{
83+ payload, err := wire.MarshalOutput(protocol.Output{
8384 Epoch: 1, New: 1, Full: true, Size: domain.Size{Cols: 80, Rows: 24}, Data: data,
8485 })
8586 if err != nil {
8687 return
8788 }
88- if err := tr.Send(ports.Frame{Type: ports.MsgOutput, Payload: payload}); err != nil {
89+ if err := tr.Send(wire.Frame{Type: wire.MsgOutput, Payload: payload}); err != nil {
8990 return
9091 }
91- _ = tr.Send(ports.Frame{Type: ports.MsgDetached, Payload: ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach})})
92+ _ = tr.Send(wire.Frame{Type: wire.MsgDetached, Payload: wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach})})
9293 for {
9394 if _, err := tr.Recv(); err != nil {
9495 return
@@ -150,18 +151,18 @@ func TestAcceptanceRemoteDirectAndPickerUseOnlyRemoteTransports(t *testing.T) {
150151 handoff: protocol.AttachTarget{Endpoint: "picked.example", Session: "picked", Intent: protocol.IntentAttach},
151152 }).Dial(context.Background())
152153 require.NoError(t, err)
153- require.NoError(t, wireTransport.Send(ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(protocol.Hello{
154+ require.NoError(t, wireTransport.Send(wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(protocol.Hello{
154155 Version: protocol.Version, Intent: protocol.IntentAttach, Name: "work", Size: domain.Size{Cols: 80, Rows: 24},
155156 })}))
156157 welcome, err := wireTransport.Recv()
157158 require.NoError(t, err)
158- require.Equal(t, ports.MsgWelcome, welcome.Type)
159- require.NoError(t, wireTransport.Send(ports.Frame{Type: ports.MsgTheme, Payload: ports.MarshalTheme(protocol.Theme{})}))
159+ require.Equal(t, wire.MsgWelcome, welcome.Type)
160+ require.NoError(t, wireTransport.Send(wire.Frame{Type: wire.MsgTheme, Payload: wire.MarshalTheme(protocol.Theme{})}))
160161 attachTarget, err := wireTransport.Recv()
161162 require.NoError(t, err)
162- target, err := ports.UnmarshalAttachTarget(attachTarget.Payload)
163+ target, err := wire.UnmarshalAttachTarget(attachTarget.Payload)
163164 require.NoError(t, err)
164- require.Equal(t, ports.MsgAttachTarget, attachTarget.Type)
165+ require.Equal(t, wire.MsgAttachTarget, attachTarget.Type)
165166 require.Equal(t, "picked.example", target.Endpoint)
166167 require.NoError(t, wireTransport.Close())
167168
M internal/app/remote_preview.go +8 -8
@@ -8,8 +8,8 @@ import (
88 "os"
99
1010 "github.com/bnema/vev/internal/adapters/ipc"
11- "github.com/bnema/vev/internal/ports"
1211 "github.com/bnema/vev/internal/protocol"
12+ "github.com/bnema/vev/internal/protocol/wire"
1313 )
1414
1515 // runRemotePreview is the hidden SSH-side carriage. The request is encoded in
@@ -20,7 +20,7 @@ func runRemotePreview(ctx context.Context, encoded string) error {
2020 if err != nil {
2121 return fmt.Errorf("vev: invalid remote preview request: %w", err)
2222 }
23- if _, err := ports.UnmarshalRemotePreviewRequest(request); err != nil {
23+ if _, err := wire.UnmarshalRemotePreviewRequest(request); err != nil {
2424 return fmt.Errorf("vev: invalid remote preview request: %w", err)
2525 }
2626 transport, err := ipc.DialContext(ctx, ipc.SocketDir())
@@ -31,17 +31,17 @@ func runRemotePreview(ctx context.Context, encoded string) error {
3131 return writeRemotePreviewStatus(protocol.RemotePreviewUnavailable)
3232 }
3333 defer func() { _ = transport.Close() }()
34- if err := transport.Send(ports.Frame{Type: ports.MsgRemotePreviewRequest, Payload: request}); err != nil {
34+ if err := transport.Send(wire.Frame{Type: wire.MsgRemotePreviewRequest, Payload: request}); err != nil {
3535 return fmt.Errorf("vev: sending remote preview request: %w", err)
3636 }
3737 frameCh := make(chan struct {
38- frame ports.Frame
38+ frame wire.Frame
3939 err error
4040 }, 1)
4141 go func() {
4242 frame, err := transport.Recv()
4343 frameCh <- struct {
44- frame ports.Frame
44+ frame wire.Frame
4545 err error
4646 }{frame: frame, err: err}
4747 }()
@@ -53,10 +53,10 @@ func runRemotePreview(ctx context.Context, encoded string) error {
5353 if result.err != nil {
5454 return fmt.Errorf("vev: receiving remote preview: %w", result.err)
5555 }
56- if result.frame.Type != ports.MsgRemotePreviewResponse {
56+ if result.frame.Type != wire.MsgRemotePreviewResponse {
5757 return fmt.Errorf("vev: unexpected remote preview response %d", result.frame.Type)
5858 }
59- if _, err := ports.UnmarshalRemotePreview(result.frame.Payload); err != nil {
59+ if _, err := wire.UnmarshalRemotePreview(result.frame.Payload); err != nil {
6060 return fmt.Errorf("vev: malformed remote preview response: %w", err)
6161 }
6262 _, err := os.Stdout.Write(result.frame.Payload)
@@ -65,7 +65,7 @@ func runRemotePreview(ctx context.Context, encoded string) error {
6565 }
6666
6767 func writeRemotePreviewStatus(status protocol.RemotePreviewStatus) error {
68- payload := ports.MarshalRemotePreview(protocol.RemotePreview{Version: protocol.RemotePreviewSchemaVersion, Status: status})
68+ payload := wire.MarshalRemotePreview(protocol.RemotePreview{Version: protocol.RemotePreviewSchemaVersion, Status: status})
6969 if payload == nil {
7070 return errors.New("vev: failed to encode remote preview status")
7171 }
M internal/app/remote_test.go +5 -4
@@ -12,6 +12,7 @@ import (
1212 "github.com/bnema/vev/internal/ports"
1313 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1414 "github.com/bnema/vev/internal/protocol"
15+ "github.com/bnema/vev/internal/protocol/wire"
1516 "github.com/bnema/vev/internal/usecase/client"
1617 "github.com/stretchr/testify/mock"
1718 "github.com/stretchr/testify/require"
@@ -56,15 +57,15 @@ func TestRemoteHostDepsDefaultsStore(t *testing.T) {
5657
5758 func TestDecodeSessionListErrorReply(t *testing.T) {
5859 t.Run("valid", func(t *testing.T) {
59- _, err := decodeSessionListReply(ports.Frame{
60- Type: ports.MsgError,
61- Payload: ports.MarshalErrorMsg(protocol.ErrorMsg{Text: "denied"}),
60+ _, err := decodeSessionListReply(wire.Frame{
61+ Type: wire.MsgError,
62+ Payload: wire.MarshalErrorMsg(protocol.ErrorMsg{Text: "denied"}),
6263 })
6364 require.EqualError(t, err, "vev: denied")
6465 })
6566
6667 t.Run("malformed", func(t *testing.T) {
67- _, err := decodeSessionListReply(ports.Frame{Type: ports.MsgError, Payload: []byte{0}})
68+ _, err := decodeSessionListReply(wire.Frame{Type: wire.MsgError, Payload: []byte{0}})
6869 require.ErrorContains(t, err, "vev: decoding error reply")
6970 })
7071 }
M internal/app/run.go +16 -15
@@ -47,6 +47,7 @@ import (
4747 "github.com/bnema/vev/internal/platform"
4848 "github.com/bnema/vev/internal/ports"
4949 "github.com/bnema/vev/internal/protocol"
50+ "github.com/bnema/vev/internal/protocol/wire"
5051 "github.com/bnema/vev/internal/usecase/client"
5152 "github.com/bnema/vev/internal/usecase/daemon"
5253 "github.com/bnema/vev/internal/usecase/recovery"
@@ -1077,12 +1078,12 @@ func createDetachedLocalSession(ctx context.Context, name string) error {
10771078 cwd = ""
10781079 }
10791080 hello := detachedLocalHello(name, cwd)
1080- if err := transport.Send(ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(hello)}); err != nil {
1081+ if err := transport.Send(wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(hello)}); err != nil {
10811082 return fmt.Errorf("vev: creating detached session: %w", err)
10821083 }
10831084
10841085 type detachedReply struct {
1085- frame ports.Frame
1086+ frame wire.Frame
10861087 err error
10871088 }
10881089 replyCh := make(chan detachedReply, 1)
@@ -1100,10 +1101,10 @@ func createDetachedLocalSession(ctx context.Context, name string) error {
11001101 return fmt.Errorf("vev: awaiting detached session creation: %w", reply.err)
11011102 }
11021103 switch reply.frame.Type {
1103- case ports.MsgWelcome:
1104+ case wire.MsgWelcome:
11041105 return nil
1105- case ports.MsgError:
1106- em, derr := ports.UnmarshalErrorMsg(reply.frame.Payload)
1106+ case wire.MsgError:
1107+ em, derr := wire.UnmarshalErrorMsg(reply.frame.Payload)
11071108 if derr != nil {
11081109 return fmt.Errorf("vev: decoding error reply: %w", derr)
11091110 }
@@ -1126,7 +1127,7 @@ func requestDaemonStop(ctx context.Context) error {
11261127 return errors.Join(errors.New("vev: no daemon running"), owner.Release())
11271128 }
11281129 defer func() { _ = transport.Close() }()
1129- if err := transport.Send(ports.Frame{Type: ports.MsgKill, Payload: ports.MarshalKill(protocol.Kill{All: true})}); err != nil {
1130+ if err := transport.Send(wire.Frame{Type: wire.MsgKill, Payload: wire.MarshalKill(protocol.Kill{All: true})}); err != nil {
11301131 return fmt.Errorf("vev: requesting daemon stop: %w", err)
11311132 }
11321133 if _, err := transport.Recv(); err != nil && !errors.Is(err, io.EOF) {
@@ -1482,7 +1483,7 @@ func listLocalSessions(ctx context.Context) (_ []protocol.SessionInfo, retErr er
14821483 }
14831484 defer func() { _ = transport.Close() }()
14841485
1485- if err := transport.Send(ports.Frame{Type: ports.MsgList, Payload: ports.MarshalList(protocol.List{})}); err != nil {
1486+ if err := transport.Send(wire.Frame{Type: wire.MsgList, Payload: wire.MarshalList(protocol.List{})}); err != nil {
14861487 return nil, fmt.Errorf("vev: requesting session list: %w", err)
14871488 }
14881489 reply, err := transport.Recv()
@@ -1492,18 +1493,18 @@ func listLocalSessions(ctx context.Context) (_ []protocol.SessionInfo, retErr er
14921493 return decodeSessionListReply(reply)
14931494 }
14941495
1495-func decodeSessionListReply(reply ports.Frame) ([]protocol.SessionInfo, error) {
1496- if reply.Type == ports.MsgError {
1497- em, err := ports.UnmarshalErrorMsg(reply.Payload)
1496+func decodeSessionListReply(reply wire.Frame) ([]protocol.SessionInfo, error) {
1497+ if reply.Type == wire.MsgError {
1498+ em, err := wire.UnmarshalErrorMsg(reply.Payload)
14981499 if err != nil {
14991500 return nil, fmt.Errorf("vev: decoding error reply: %w", err)
15001501 }
15011502 return nil, fmt.Errorf("vev: %s", em.Text)
15021503 }
1503- if reply.Type != ports.MsgSessions {
1504+ if reply.Type != wire.MsgSessions {
15041505 return nil, fmt.Errorf("vev: unexpected reply type %d to list", reply.Type)
15051506 }
1506- sessions, err := ports.UnmarshalSessions(reply.Payload)
1507+ sessions, err := wire.UnmarshalSessions(reply.Payload)
15071508 if err != nil {
15081509 return nil, fmt.Errorf("vev: decoding session list: %w", err)
15091510 }
@@ -1596,7 +1597,7 @@ func runKill(ctx context.Context, name string, all, daemon bool) (retErr error)
15961597 }
15971598 defer func() { _ = transport.Close() }()
15981599
1599- if err := transport.Send(ports.Frame{Type: ports.MsgKill, Payload: ports.MarshalKill(protocol.Kill{Name: name, All: all || daemon})}); err != nil {
1600+ if err := transport.Send(wire.Frame{Type: wire.MsgKill, Payload: wire.MarshalKill(protocol.Kill{Name: name, All: all || daemon})}); err != nil {
16001601 return fmt.Errorf("vev: requesting kill: %w", err)
16011602 }
16021603 reply, err := transport.Recv()
@@ -1609,8 +1610,8 @@ func runKill(ctx context.Context, name string, all, daemon bool) (retErr error)
16091610 }
16101611 return fmt.Errorf("vev: reading kill reply: %w", err)
16111612 }
1612- if reply.Type == ports.MsgError {
1613- em, _ := ports.UnmarshalErrorMsg(reply.Payload)
1613+ if reply.Type == wire.MsgError {
1614+ em, _ := wire.UnmarshalErrorMsg(reply.Payload)
16141615 return fmt.Errorf("vev: %s", em.Text)
16151616 }
16161617 printKillSuccess(name, all, daemon)
M internal/ports/mocks/transport_mock.go +43 -42
@@ -8,6 +8,7 @@ import (
88 "context"
99
1010 "github.com/bnema/vev/internal/ports"
11+ "github.com/bnema/vev/internal/protocol/wire"
1112 mock "github.com/stretchr/testify/mock"
1213 )
1314
@@ -83,22 +84,22 @@ func (_c *MockTransport_Close_Call) RunAndReturn(run func() error) *MockTranspor
8384 }
8485
8586 // Recv provides a mock function for the type MockTransport
86-func (_mock *MockTransport) Recv() (ports.Frame, error) {
87+func (_mock *MockTransport) Recv() (wire.Frame, error) {
8788 ret := _mock.Called()
8889
8990 if len(ret) == 0 {
9091 panic("no return value specified for Recv")
9192 }
9293
93- var r0 ports.Frame
94+ var r0 wire.Frame
9495 var r1 error
95- if returnFunc, ok := ret.Get(0).(func() (ports.Frame, error)); ok {
96+ if returnFunc, ok := ret.Get(0).(func() (wire.Frame, error)); ok {
9697 return returnFunc()
9798 }
98- if returnFunc, ok := ret.Get(0).(func() ports.Frame); ok {
99+ if returnFunc, ok := ret.Get(0).(func() wire.Frame); ok {
99100 r0 = returnFunc()
100101 } else {
101- r0 = ret.Get(0).(ports.Frame)
102+ r0 = ret.Get(0).(wire.Frame)
102103 }
103104 if returnFunc, ok := ret.Get(1).(func() error); ok {
104105 r1 = returnFunc()
@@ -125,18 +126,18 @@ func (_c *MockTransport_Recv_Call) Run(run func()) *MockTransport_Recv_Call {
125126 return _c
126127 }
127128
128-func (_c *MockTransport_Recv_Call) Return(frame ports.Frame, err error) *MockTransport_Recv_Call {
129+func (_c *MockTransport_Recv_Call) Return(frame wire.Frame, err error) *MockTransport_Recv_Call {
129130 _c.Call.Return(frame, err)
130131 return _c
131132 }
132133
133-func (_c *MockTransport_Recv_Call) RunAndReturn(run func() (ports.Frame, error)) *MockTransport_Recv_Call {
134+func (_c *MockTransport_Recv_Call) RunAndReturn(run func() (wire.Frame, error)) *MockTransport_Recv_Call {
134135 _c.Call.Return(run)
135136 return _c
136137 }
137138
138139 // Send provides a mock function for the type MockTransport
139-func (_mock *MockTransport) Send(frame ports.Frame) error {
140+func (_mock *MockTransport) Send(frame wire.Frame) error {
140141 ret := _mock.Called(frame)
141142
142143 if len(ret) == 0 {
@@ -144,7 +145,7 @@ func (_mock *MockTransport) Send(frame ports.Frame) error {
144145 }
145146
146147 var r0 error
147- if returnFunc, ok := ret.Get(0).(func(ports.Frame) error); ok {
148+ if returnFunc, ok := ret.Get(0).(func(wire.Frame) error); ok {
148149 r0 = returnFunc(frame)
149150 } else {
150151 r0 = ret.Error(0)
@@ -158,16 +159,16 @@ type MockTransport_Send_Call struct {
158159 }
159160
160161 // Send is a helper method to define mock.On call
161-// - frame ports.Frame
162+// - frame wire.Frame
162163 func (_e *MockTransport_Expecter) Send(frame any) *MockTransport_Send_Call {
163164 return &MockTransport_Send_Call{Call: _e.mock.On("Send", frame)}
164165 }
165166
166-func (_c *MockTransport_Send_Call) Run(run func(frame ports.Frame)) *MockTransport_Send_Call {
167+func (_c *MockTransport_Send_Call) Run(run func(frame wire.Frame)) *MockTransport_Send_Call {
167168 _c.Call.Run(func(args mock.Arguments) {
168- var arg0 ports.Frame
169+ var arg0 wire.Frame
169170 if args[0] != nil {
170- arg0 = args[0].(ports.Frame)
171+ arg0 = args[0].(wire.Frame)
171172 }
172173 run(
173174 arg0,
@@ -181,7 +182,7 @@ func (_c *MockTransport_Send_Call) Return(err error) *MockTransport_Send_Call {
181182 return _c
182183 }
183184
184-func (_c *MockTransport_Send_Call) RunAndReturn(run func(frame ports.Frame) error) *MockTransport_Send_Call {
185+func (_c *MockTransport_Send_Call) RunAndReturn(run func(frame wire.Frame) error) *MockTransport_Send_Call {
185186 _c.Call.Return(run)
186187 return _c
187188 }
@@ -291,22 +292,22 @@ func (_c *MockDatagramTransport_DatagramTransport_Call) RunAndReturn(run func())
291292 }
292293
293294 // Recv provides a mock function for the type MockDatagramTransport
294-func (_mock *MockDatagramTransport) Recv() (ports.Frame, error) {
295+func (_mock *MockDatagramTransport) Recv() (wire.Frame, error) {
295296 ret := _mock.Called()
296297
297298 if len(ret) == 0 {
298299 panic("no return value specified for Recv")
299300 }
300301
301- var r0 ports.Frame
302+ var r0 wire.Frame
302303 var r1 error
303- if returnFunc, ok := ret.Get(0).(func() (ports.Frame, error)); ok {
304+ if returnFunc, ok := ret.Get(0).(func() (wire.Frame, error)); ok {
304305 return returnFunc()
305306 }
306- if returnFunc, ok := ret.Get(0).(func() ports.Frame); ok {
307+ if returnFunc, ok := ret.Get(0).(func() wire.Frame); ok {
307308 r0 = returnFunc()
308309 } else {
309- r0 = ret.Get(0).(ports.Frame)
310+ r0 = ret.Get(0).(wire.Frame)
310311 }
311312 if returnFunc, ok := ret.Get(1).(func() error); ok {
312313 r1 = returnFunc()
@@ -333,18 +334,18 @@ func (_c *MockDatagramTransport_Recv_Call) Run(run func()) *MockDatagramTranspor
333334 return _c
334335 }
335336
336-func (_c *MockDatagramTransport_Recv_Call) Return(frame ports.Frame, err error) *MockDatagramTransport_Recv_Call {
337+func (_c *MockDatagramTransport_Recv_Call) Return(frame wire.Frame, err error) *MockDatagramTransport_Recv_Call {
337338 _c.Call.Return(frame, err)
338339 return _c
339340 }
340341
341-func (_c *MockDatagramTransport_Recv_Call) RunAndReturn(run func() (ports.Frame, error)) *MockDatagramTransport_Recv_Call {
342+func (_c *MockDatagramTransport_Recv_Call) RunAndReturn(run func() (wire.Frame, error)) *MockDatagramTransport_Recv_Call {
342343 _c.Call.Return(run)
343344 return _c
344345 }
345346
346347 // Send provides a mock function for the type MockDatagramTransport
347-func (_mock *MockDatagramTransport) Send(frame ports.Frame) error {
348+func (_mock *MockDatagramTransport) Send(frame wire.Frame) error {
348349 ret := _mock.Called(frame)
349350
350351 if len(ret) == 0 {
@@ -352,7 +353,7 @@ func (_mock *MockDatagramTransport) Send(frame ports.Frame) error {
352353 }
353354
354355 var r0 error
355- if returnFunc, ok := ret.Get(0).(func(ports.Frame) error); ok {
356+ if returnFunc, ok := ret.Get(0).(func(wire.Frame) error); ok {
356357 r0 = returnFunc(frame)
357358 } else {
358359 r0 = ret.Error(0)
@@ -366,16 +367,16 @@ type MockDatagramTransport_Send_Call struct {
366367 }
367368
368369 // Send is a helper method to define mock.On call
369-// - frame ports.Frame
370+// - frame wire.Frame
370371 func (_e *MockDatagramTransport_Expecter) Send(frame any) *MockDatagramTransport_Send_Call {
371372 return &MockDatagramTransport_Send_Call{Call: _e.mock.On("Send", frame)}
372373 }
373374
374-func (_c *MockDatagramTransport_Send_Call) Run(run func(frame ports.Frame)) *MockDatagramTransport_Send_Call {
375+func (_c *MockDatagramTransport_Send_Call) Run(run func(frame wire.Frame)) *MockDatagramTransport_Send_Call {
375376 _c.Call.Run(func(args mock.Arguments) {
376- var arg0 ports.Frame
377+ var arg0 wire.Frame
377378 if args[0] != nil {
378- arg0 = args[0].(ports.Frame)
379+ arg0 = args[0].(wire.Frame)
379380 }
380381 run(
381382 arg0,
@@ -389,7 +390,7 @@ func (_c *MockDatagramTransport_Send_Call) Return(err error) *MockDatagramTransp
389390 return _c
390391 }
391392
392-func (_c *MockDatagramTransport_Send_Call) RunAndReturn(run func(frame ports.Frame) error) *MockDatagramTransport_Send_Call {
393+func (_c *MockDatagramTransport_Send_Call) RunAndReturn(run func(frame wire.Frame) error) *MockDatagramTransport_Send_Call {
393394 _c.Call.Return(run)
394395 return _c
395396 }
@@ -422,7 +423,7 @@ func (_m *MockAsyncTransport) EXPECT() *MockAsyncTransport_Expecter {
422423 }
423424
424425 // SendAsync provides a mock function for the type MockAsyncTransport
425-func (_mock *MockAsyncTransport) SendAsync(frame ports.Frame) error {
426+func (_mock *MockAsyncTransport) SendAsync(frame wire.Frame) error {
426427 ret := _mock.Called(frame)
427428
428429 if len(ret) == 0 {
@@ -430,7 +431,7 @@ func (_mock *MockAsyncTransport) SendAsync(frame ports.Frame) error {
430431 }
431432
432433 var r0 error
433- if returnFunc, ok := ret.Get(0).(func(ports.Frame) error); ok {
434+ if returnFunc, ok := ret.Get(0).(func(wire.Frame) error); ok {
434435 r0 = returnFunc(frame)
435436 } else {
436437 r0 = ret.Error(0)
@@ -444,16 +445,16 @@ type MockAsyncTransport_SendAsync_Call struct {
444445 }
445446
446447 // SendAsync is a helper method to define mock.On call
447-// - frame ports.Frame
448+// - frame wire.Frame
448449 func (_e *MockAsyncTransport_Expecter) SendAsync(frame any) *MockAsyncTransport_SendAsync_Call {
449450 return &MockAsyncTransport_SendAsync_Call{Call: _e.mock.On("SendAsync", frame)}
450451 }
451452
452-func (_c *MockAsyncTransport_SendAsync_Call) Run(run func(frame ports.Frame)) *MockAsyncTransport_SendAsync_Call {
453+func (_c *MockAsyncTransport_SendAsync_Call) Run(run func(frame wire.Frame)) *MockAsyncTransport_SendAsync_Call {
453454 _c.Call.Run(func(args mock.Arguments) {
454- var arg0 ports.Frame
455+ var arg0 wire.Frame
455456 if args[0] != nil {
456- arg0 = args[0].(ports.Frame)
457+ arg0 = args[0].(wire.Frame)
457458 }
458459 run(
459460 arg0,
@@ -467,7 +468,7 @@ func (_c *MockAsyncTransport_SendAsync_Call) Return(err error) *MockAsyncTranspo
467468 return _c
468469 }
469470
470-func (_c *MockAsyncTransport_SendAsync_Call) RunAndReturn(run func(frame ports.Frame) error) *MockAsyncTransport_SendAsync_Call {
471+func (_c *MockAsyncTransport_SendAsync_Call) RunAndReturn(run func(frame wire.Frame) error) *MockAsyncTransport_SendAsync_Call {
471472 _c.Call.Return(run)
472473 return _c
473474 }
@@ -500,7 +501,7 @@ func (_m *MockOwnedSynchronousTransport) EXPECT() *MockOwnedSynchronousTransport
500501 }
501502
502503 // SendSynchronous provides a mock function for the type MockOwnedSynchronousTransport
503-func (_mock *MockOwnedSynchronousTransport) SendSynchronous(frame ports.Frame) error {
504+func (_mock *MockOwnedSynchronousTransport) SendSynchronous(frame wire.Frame) error {
504505 ret := _mock.Called(frame)
505506
506507 if len(ret) == 0 {
@@ -508,7 +509,7 @@ func (_mock *MockOwnedSynchronousTransport) SendSynchronous(frame ports.Frame) e
508509 }
509510
510511 var r0 error
511- if returnFunc, ok := ret.Get(0).(func(ports.Frame) error); ok {
512+ if returnFunc, ok := ret.Get(0).(func(wire.Frame) error); ok {
512513 r0 = returnFunc(frame)
513514 } else {
514515 r0 = ret.Error(0)
@@ -522,16 +523,16 @@ type MockOwnedSynchronousTransport_SendSynchronous_Call struct {
522523 }
523524
524525 // SendSynchronous is a helper method to define mock.On call
525-// - frame ports.Frame
526+// - frame wire.Frame
526527 func (_e *MockOwnedSynchronousTransport_Expecter) SendSynchronous(frame any) *MockOwnedSynchronousTransport_SendSynchronous_Call {
527528 return &MockOwnedSynchronousTransport_SendSynchronous_Call{Call: _e.mock.On("SendSynchronous", frame)}
528529 }
529530
530-func (_c *MockOwnedSynchronousTransport_SendSynchronous_Call) Run(run func(frame ports.Frame)) *MockOwnedSynchronousTransport_SendSynchronous_Call {
531+func (_c *MockOwnedSynchronousTransport_SendSynchronous_Call) Run(run func(frame wire.Frame)) *MockOwnedSynchronousTransport_SendSynchronous_Call {
531532 _c.Call.Run(func(args mock.Arguments) {
532- var arg0 ports.Frame
533+ var arg0 wire.Frame
533534 if args[0] != nil {
534- arg0 = args[0].(ports.Frame)
535+ arg0 = args[0].(wire.Frame)
535536 }
536537 run(
537538 arg0,
@@ -545,7 +546,7 @@ func (_c *MockOwnedSynchronousTransport_SendSynchronous_Call) Return(err error)
545546 return _c
546547 }
547548
548-func (_c *MockOwnedSynchronousTransport_SendSynchronous_Call) RunAndReturn(run func(frame ports.Frame) error) *MockOwnedSynchronousTransport_SendSynchronous_Call {
549+func (_c *MockOwnedSynchronousTransport_SendSynchronous_Call) RunAndReturn(run func(frame wire.Frame) error) *MockOwnedSynchronousTransport_SendSynchronous_Call {
549550 _c.Call.Return(run)
550551 return _c
551552 }
M internal/ports/transport.go +9 -5
@@ -1,13 +1,17 @@
11 package ports
22
3-import "context"
3+import (
4+ "context"
5+
6+ "github.com/bnema/vev/internal/protocol/wire"
7+)
48
59 // Transport is a framed message channel over a single connection. Close must
610 // be safe to call concurrently with Send and Recv, and must unblock active
711 // Send and Recv calls.
812 type Transport interface {
9- Send(Frame) error
10- Recv() (Frame, error) // blocking; io.EOF on close
13+ Send(wire.Frame) error
14+ Recv() (wire.Frame, error) // blocking; io.EOF on close
1115 Close() error
1216 }
1317
@@ -22,13 +26,13 @@ type DatagramTransport interface {
2226 // returns once the adapter owns the frame; Send retains its synchronous wire-
2327 // attempt contract. Daemon paint output may use this capability to pipeline.
2428 type AsyncTransport interface {
25- SendAsync(Frame) error
29+ SendAsync(wire.Frame) error
2630 }
2731
2832 // OwnedSynchronousTransport owns the complete bounded synchronous operation,
2933 // including adapter queues, pacing, write deadlines, and close cancellation.
3034 type OwnedSynchronousTransport interface {
31- SendSynchronous(Frame) error
35+ SendSynchronous(wire.Frame) error
3236 }
3337
3438 // Dialer establishes outbound Transport connections.
R internal/ports/frame.go → internal/protocol/wire/frame.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package ports
1+package wire
22
33 // MaxFrameLen is the largest permitted frame length, including the type byte
44 // and excluding the four-byte length prefix.
R internal/ports/frame_test.go → internal/protocol/wire/frame_test.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package ports
1+package wire
22
33 import (
44 "testing"
R internal/ports/navigation_test.go → internal/protocol/wire/navigation_test.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package ports
1+package wire
22
33 import (
44 "encoding/hex"
R internal/ports/output_compression_test.go → internal/protocol/wire/output_compression_test.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package ports
1+package wire
22
33 import (
44 "bytes"
R internal/ports/remote_preview_hardening_test.go → internal/protocol/wire/remote_preview_hardening_test.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package ports
1+package wire
22
33 import (
44 "bytes"
R internal/ports/remote_preview_test.go → internal/protocol/wire/remote_preview_test.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package ports
1+package wire
22
33 import (
44 "testing"
R internal/ports/remote_target_wire_test.go → internal/protocol/wire/remote_target_wire_test.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package ports
1+package wire
22
33 import (
44 "bytes"
R internal/ports/routes_test.go → internal/protocol/wire/routes_test.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package ports
1+package wire
22
33 import (
44 "encoding/hex"
R internal/ports/routes_wire.go → internal/protocol/wire/routes_wire.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package ports
1+package wire
22
33 import (
44 "fmt"
R internal/ports/same_peer_switch_test.go → internal/protocol/wire/same_peer_switch_test.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package ports
1+package wire
22
33 import (
44 "bytes"
R internal/ports/wire.go → internal/protocol/wire/wire.go +3 -6 renamed, 99% similar
@@ -1,10 +1,7 @@
1-// Wire codecs for vev's IPC message payloads. They live in ports (alongside
2-// Frame and the MsgType constants) because wire encoding is protocol
3-// surface, not I/O: everything here is pure, stdlib-only byte marshalling.
4-// Adapters keep what actually performs I/O (framing over a connection,
5-// listening on a socket) and import these types for the payloads they carry.
1+// Package wire owns vev's binary frame representation and strict payload
2+// codecs. It performs no I/O; carriage adapters transport Frame values.
63
7-package ports
4+package wire
85
96 import (
107 "bytes"
R internal/ports/wire_command.go → internal/protocol/wire/wire_command.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package ports
1+package wire
22
33 import (
44 "encoding/binary"
R internal/ports/wire_final_test.go → internal/protocol/wire/wire_final_test.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package ports
1+package wire
22
33 import (
44 "bytes"
R internal/ports/wire_test.go → internal/protocol/wire/wire_test.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package ports
1+package wire
22
33 import (
44 "bytes"
M internal/testutil/replaytest/replay.go +12 -12
@@ -8,31 +8,31 @@ import (
88 "testing"
99
1010 "github.com/bnema/vev/internal/domain"
11- "github.com/bnema/vev/internal/ports"
1211 "github.com/bnema/vev/internal/protocol"
12+ "github.com/bnema/vev/internal/protocol/wire"
1313 )
1414
1515 func mustMarshalOutput(m protocol.Output) []byte {
16- payload, err := ports.MarshalOutput(m)
16+ payload, err := wire.MarshalOutput(m)
1717 if err != nil {
1818 panic(err)
1919 }
2020 return payload
2121 }
2222
23-var transcript = []ports.Frame{
24- {Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{Epoch: 1, Base: 0, New: 1, Size: domain.Size{Cols: 80, Rows: 24}, Full: true, Data: []byte("\x1b[2J\x1b[Hone\r\ntwo")})},
25- {Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{Epoch: 1, Base: 1, New: 2, Echo: 7, Size: domain.Size{Cols: 80, Rows: 24}, Data: []byte("\x1b[2;1HTWO")})},
23+var transcript = []wire.Frame{
24+ {Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{Epoch: 1, Base: 0, New: 1, Size: domain.Size{Cols: 80, Rows: 24}, Full: true, Data: []byte("\x1b[2J\x1b[Hone\r\ntwo")})},
25+ {Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{Epoch: 1, Base: 1, New: 2, Echo: 7, Size: domain.Size{Cols: 80, Rows: 24}, Data: []byte("\x1b[2;1HTWO")})},
2626 }
2727
2828 // Transcript returns a deep copy of the canonical replay transcript so an
2929 // adapter cannot mutate the fixture observed by another adapter.
30-func Transcript() []ports.Frame {
30+func Transcript() []wire.Frame {
3131 return cloneFrames(transcript)
3232 }
3333
3434 // Exchange carries already-composed frames through one transport adapter.
35-type Exchange func(t *testing.T, frames []ports.Frame) []ports.Frame
35+type Exchange func(t *testing.T, frames []wire.Frame) []wire.Frame
3636
3737 // Run verifies that an adapter preserves the canonical transcript exactly.
3838 func Run(t *testing.T, exchange Exchange) {
@@ -50,12 +50,12 @@ func Run(t *testing.T, exchange Exchange) {
5050 t.Errorf("frame %d payload = %x, want byte-for-byte %x", i, got[i].Payload, want[i].Payload)
5151 continue
5252 }
53- gotOutput, err := ports.UnmarshalOutput(got[i].Payload)
53+ gotOutput, err := wire.UnmarshalOutput(got[i].Payload)
5454 if err != nil {
5555 t.Errorf("frame %d output decode: %v", i, err)
5656 continue
5757 }
58- wantOutput, err := ports.UnmarshalOutput(want[i].Payload)
58+ wantOutput, err := wire.UnmarshalOutput(want[i].Payload)
5959 if err != nil {
6060 t.Fatalf("canonical frame %d output decode: %v", i, err)
6161 }
@@ -65,10 +65,10 @@ func Run(t *testing.T, exchange Exchange) {
6565 }
6666 }
6767
68-func cloneFrames(frames []ports.Frame) []ports.Frame {
69- cloned := make([]ports.Frame, len(frames))
68+func cloneFrames(frames []wire.Frame) []wire.Frame {
69+ cloned := make([]wire.Frame, len(frames))
7070 for i, frame := range frames {
71- cloned[i] = ports.Frame{Type: frame.Type, Payload: append([]byte(nil), frame.Payload...)}
71+ cloned[i] = wire.Frame{Type: frame.Type, Payload: append([]byte(nil), frame.Payload...)}
7272 }
7373 return cloned
7474 }
M internal/usecase/client/ack_queue_test.go +26 -26
@@ -7,20 +7,20 @@ import (
77 "testing"
88 "time"
99
10- "github.com/bnema/vev/internal/ports"
10+ "github.com/bnema/vev/internal/protocol/wire"
1111 "github.com/stretchr/testify/require"
1212 )
1313
14-type ackRecordingTransport struct{ sent chan ports.Frame }
14+type ackRecordingTransport struct{ sent chan wire.Frame }
1515
16-func (t *ackRecordingTransport) Send(f ports.Frame) error { t.sent <- f; return nil }
17-func (t *ackRecordingTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
18-func (t *ackRecordingTransport) Close() error { return nil }
16+func (t *ackRecordingTransport) Send(f wire.Frame) error { t.sent <- f; return nil }
17+func (t *ackRecordingTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
18+func (t *ackRecordingTransport) Close() error { return nil }
1919
2020 func TestCumulativeAckBypassesFullNormalSendQueue(t *testing.T) {
21- normal := make(chan ports.Frame, sendQueueDepth)
21+ normal := make(chan wire.Frame, sendQueueDepth)
2222 for range cap(normal) {
23- normal <- ports.Frame{Type: ports.MsgInput}
23+ normal <- wire.Frame{Type: wire.MsgInput}
2424 }
2525 const epoch = 7
2626 acks := newCumulativeAckQueue()
@@ -30,13 +30,13 @@ func TestCumulativeAckBypassesFullNormalSendQueue(t *testing.T) {
3030
3131 ctx, cancel := context.WithCancel(context.Background())
3232 defer cancel()
33- tr := &ackRecordingTransport{sent: make(chan ports.Frame, sendQueueDepth+1)}
33+ tr := &ackRecordingTransport{sent: make(chan wire.Frame, sendQueueDepth+1)}
3434 errCh := make(chan error, 1)
3535 go runSender(ctx, cancel, tr, nil, nil, normal, nil, acks, errCh, slog.Default())
3636
3737 first := <-tr.sent
38- require.Equal(t, ports.MsgAck, first.Type)
39- ack, err := ports.UnmarshalAck(first.Payload)
38+ require.Equal(t, wire.MsgAck, first.Type)
39+ ack, err := wire.UnmarshalAck(first.Payload)
4040 require.NoError(t, err)
4141 require.Equal(t, uint64(epoch), ack.Epoch)
4242 require.Equal(t, uint64(maxUnackedOutputStatesForTest), ack.State)
@@ -45,21 +45,21 @@ func TestCumulativeAckBypassesFullNormalSendQueue(t *testing.T) {
4545 func TestSamePeerSwitchControlBypassesHeldInput(t *testing.T) {
4646 ctx, cancel := context.WithCancel(context.Background())
4747 defer cancel()
48- normal := make(chan ports.Frame, 1)
49- control := make(chan ports.Frame, 1)
48+ normal := make(chan wire.Frame, 1)
49+ control := make(chan wire.Frame, 1)
5050 gate := newSamePeerInputGate()
5151 gate.setPaused(true)
5252 held := make(chan struct{})
5353 gate.afterInputHeld = func() { close(held) }
54- transport := &ackRecordingTransport{sent: make(chan ports.Frame, 2)}
54+ transport := &ackRecordingTransport{sent: make(chan wire.Frame, 2)}
5555 acks := newCumulativeAckQueue()
5656 errs := make(chan error, 1)
5757 go runSender(ctx, cancel, transport, control, nil, normal, gate, acks, errs, slog.Default())
5858
59- normal <- ports.Frame{Type: ports.MsgInput, Payload: []byte("held")}
59+ normal <- wire.Frame{Type: wire.MsgInput, Payload: []byte("held")}
6060 awaitSenderSignal(t, held)
61- control <- ports.Frame{Type: ports.MsgSamePeerSwitchRequest, Payload: []byte("switch")}
62- if got := awaitSenderFrame(t, transport.sent); got.Type != ports.MsgSamePeerSwitchRequest {
61+ control <- wire.Frame{Type: wire.MsgSamePeerSwitchRequest, Payload: []byte("switch")}
62+ if got := awaitSenderFrame(t, transport.sent); got.Type != wire.MsgSamePeerSwitchRequest {
6363 t.Fatalf("first frame = %d, want same-peer switch request", got.Type)
6464 }
6565 select {
@@ -68,7 +68,7 @@ func TestSamePeerSwitchControlBypassesHeldInput(t *testing.T) {
6868 default:
6969 }
7070 gate.setPaused(false)
71- if got := awaitSenderFrame(t, transport.sent); got.Type != ports.MsgInput {
71+ if got := awaitSenderFrame(t, transport.sent); got.Type != wire.MsgInput {
7272 t.Fatalf("released frame = %d, want input", got.Type)
7373 }
7474 }
@@ -76,14 +76,14 @@ func TestSamePeerSwitchControlBypassesHeldInput(t *testing.T) {
7676 func TestSenderBarrierLeavesPausedInputQueued(t *testing.T) {
7777 ctx, cancel := context.WithCancel(context.Background())
7878 defer cancel()
79- normal := make(chan ports.Frame, 1)
79+ normal := make(chan wire.Frame, 1)
8080 barriers := make(chan chan struct{})
8181 gate := newSamePeerInputGate()
8282 gate.setPaused(true)
83- transport := &ackRecordingTransport{sent: make(chan ports.Frame, 1)}
83+ transport := &ackRecordingTransport{sent: make(chan wire.Frame, 1)}
8484 go runSender(ctx, cancel, transport, nil, barriers, normal, gate, newCumulativeAckQueue(), make(chan error, 1), slog.Default())
8585
86- normal <- ports.Frame{Type: ports.MsgInput, Payload: []byte("held")}
86+ normal <- wire.Frame{Type: wire.MsgInput, Payload: []byte("held")}
8787 barrierDone := make(chan struct{})
8888 barriers <- barrierDone
8989 awaitSenderSignal(t, barrierDone)
@@ -94,7 +94,7 @@ func TestSenderBarrierLeavesPausedInputQueued(t *testing.T) {
9494 }
9595
9696 gate.setPaused(false)
97- if got := awaitSenderFrame(t, transport.sent); got.Type != ports.MsgInput {
97+ if got := awaitSenderFrame(t, transport.sent); got.Type != wire.MsgInput {
9898 t.Fatalf("released frame = %d, want input", got.Type)
9999 }
100100 }
@@ -104,14 +104,14 @@ func TestSenderBarrierFlushesPendingAck(t *testing.T) {
104104 defer cancel()
105105 acks := newCumulativeAckQueue()
106106 acks.offer(3, 9)
107- transport := &ackRecordingTransport{sent: make(chan ports.Frame, 1)}
107+ transport := &ackRecordingTransport{sent: make(chan wire.Frame, 1)}
108108 barriers := make(chan chan struct{})
109- go runSender(ctx, cancel, transport, nil, barriers, make(chan ports.Frame, 1), newSamePeerInputGate(), acks, make(chan error, 1), slog.Default())
109+ go runSender(ctx, cancel, transport, nil, barriers, make(chan wire.Frame, 1), newSamePeerInputGate(), acks, make(chan error, 1), slog.Default())
110110
111111 done := make(chan struct{})
112112 barriers <- done
113113 frame := awaitSenderFrame(t, transport.sent)
114- require.Equal(t, ports.MsgAck, frame.Type)
114+ require.Equal(t, wire.MsgAck, frame.Type)
115115 awaitSenderSignal(t, done)
116116 }
117117
@@ -124,14 +124,14 @@ func awaitSenderSignal(t *testing.T, signal <-chan struct{}) {
124124 }
125125 }
126126
127-func awaitSenderFrame(t *testing.T, frames <-chan ports.Frame) ports.Frame {
127+func awaitSenderFrame(t *testing.T, frames <-chan wire.Frame) wire.Frame {
128128 t.Helper()
129129 select {
130130 case frame := <-frames:
131131 return frame
132132 case <-time.After(time.Second):
133133 t.Fatal("sender did not emit frame")
134- return ports.Frame{}
134+ return wire.Frame{}
135135 }
136136 }
137137
M internal/usecase/client/client.go +74 -73
@@ -24,6 +24,7 @@ import (
2424 "github.com/bnema/vev/internal/domain"
2525 "github.com/bnema/vev/internal/ports"
2626 "github.com/bnema/vev/internal/protocol"
27+ "github.com/bnema/vev/internal/protocol/wire"
2728 "github.com/bnema/vev/internal/usecase/theme"
2829 )
2930
@@ -1243,14 +1244,14 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
12431244 Remote: remote,
12441245 }
12451246 if err := sendHandshake(func() error {
1246- return transport.Send(ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(hello)})
1247+ return transport.Send(wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(hello)})
12471248 }); err != nil {
12481249 return handshakeFailure("sending hello", err)
12491250 }
12501251 ms.helloSent = true
12511252
12521253 // 2. Await Welcome or a typed rejection.
1253- var reply ports.Frame
1254+ var reply wire.Frame
12541255 if err := boundedHandshakeOperationWithTransition(handshakeCtx, transport, func() error {
12551256 var recvErr error
12561257 reply, recvErr = transport.Recv()
@@ -1271,8 +1272,8 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
12711272 }
12721273 }
12731274 switch reply.Type {
1274- case ports.MsgWelcome:
1275- welcome, derr := ports.UnmarshalWelcome(reply.Payload)
1275+ case wire.MsgWelcome:
1276+ welcome, derr := wire.UnmarshalWelcome(reply.Payload)
12761277 if derr != nil {
12771278 return result(false, fmt.Errorf("vev: decoding welcome: %w", derr))
12781279 }
@@ -1286,8 +1287,8 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
12861287 remember()
12871288 }
12881289 }
1289- case ports.MsgError:
1290- em, derr := ports.UnmarshalErrorMsg(reply.Payload)
1290+ case wire.MsgError:
1291+ em, derr := wire.UnmarshalErrorMsg(reply.Payload)
12911292 if derr != nil {
12921293 return result(false, fmt.Errorf("vev: decoding error reply: %w", derr))
12931294 }
@@ -1319,30 +1320,30 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
13191320 return welcomedResult(fmt.Errorf("vev: committing route identity: %w", commitErr))
13201321 }
13211322 }
1322- snapshotPayload, err := ports.MarshalRecentRouteSnapshot(a.runner.ledger.snapshot())
1323+ snapshotPayload, err := wire.MarshalRecentRouteSnapshot(a.runner.ledger.snapshot())
13231324 if err != nil {
13241325 return welcomedResult(fmt.Errorf("vev: encoding route snapshot: %w", err))
13251326 }
1326- attentionPayload, err := ports.MarshalRouteAttentionSubscription(a.runner.ledger.attentionSubscription())
1327+ attentionPayload, err := wire.MarshalRouteAttentionSubscription(a.runner.ledger.attentionSubscription())
13271328 if err != nil {
13281329 return welcomedResult(fmt.Errorf("vev: encoding route attention subscription: %w", err))
13291330 }
13301331 if err := sendHandshake(func() error {
1331- if err := transport.Send(ports.Frame{Type: ports.MsgRecentRouteSnapshot, Payload: snapshotPayload}); err != nil {
1332+ if err := transport.Send(wire.Frame{Type: wire.MsgRecentRouteSnapshot, Payload: snapshotPayload}); err != nil {
13321333 return err
13331334 }
1334- return transport.Send(ports.Frame{Type: ports.MsgRouteAttentionSubscription, Payload: attentionPayload})
1335+ return transport.Send(wire.Frame{Type: wire.MsgRouteAttentionSubscription, Payload: attentionPayload})
13351336 }); err != nil {
13361337 return welcomedResult(fmt.Errorf("vev: publishing route snapshot: %w", err))
13371338 }
13381339 }
13391340 if a.runner.routeFailure != nil {
1340- payload, err := ports.MarshalRouteNavigationFailure(*a.runner.routeFailure)
1341+ payload, err := wire.MarshalRouteNavigationFailure(*a.runner.routeFailure)
13411342 if err != nil {
13421343 return welcomedResult(fmt.Errorf("vev: encoding route navigation failure: %w", err))
13431344 }
13441345 if err := sendHandshake(func() error {
1345- return transport.Send(ports.Frame{Type: ports.MsgRouteNavigationFailure, Payload: payload})
1346+ return transport.Send(wire.Frame{Type: wire.MsgRouteNavigationFailure, Payload: payload})
13461347 }); err != nil {
13471348 return welcomedResult(fmt.Errorf("vev: publishing route navigation failure: %w", err))
13481349 }
@@ -1369,8 +1370,8 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
13691370 input.start()
13701371 defer input.stop()
13711372 }
1372- sendCh := make(chan ports.Frame, sendQueueDepth)
1373- controlCh := make(chan ports.Frame, 1)
1373+ sendCh := make(chan wire.Frame, sendQueueDepth)
1374+ controlCh := make(chan wire.Frame, 1)
13741375 barrierCh := make(chan chan struct{})
13751376 inputGate := newSamePeerInputGate()
13761377 ackQueue := newCumulativeAckQueue()
@@ -1393,7 +1394,7 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
13931394 }
13941395 }
13951396 publish := func(snapshot protocol.Theme) error {
1396- frame := ports.Frame{Type: ports.MsgTheme, Payload: ports.MarshalTheme(snapshot)}
1397+ frame := wire.Frame{Type: wire.MsgTheme, Payload: wire.MarshalTheme(snapshot)}
13971398 // The initial cleared snapshot is sent before the sender exists. This
13981399 // keeps the generation publication ordered before the first query and
13991400 // avoids leaving a queued Theme behind an immediate detach.
@@ -1503,11 +1504,11 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
15031504 // Before the asynchronous sender starts, preserve transport ordering by
15041505 // requesting the reset synchronously after the initial Theme publication.
15051506 resetErr := sendHandshake(func() error {
1506- payload, err := ports.MarshalResize(protocol.Resize{Size: geometry.Size, PixelWidth: geometry.PixelWidth, PixelHeight: geometry.PixelHeight})
1507+ payload, err := wire.MarshalResize(protocol.Resize{Size: geometry.Size, PixelWidth: geometry.PixelWidth, PixelHeight: geometry.PixelHeight})
15071508 if err != nil {
15081509 return err
15091510 }
1510- return transport.Send(ports.Frame{Type: ports.MsgResize, Payload: payload})
1511+ return transport.Send(wire.Frame{Type: wire.MsgResize, Payload: payload})
15111512 })
15121513 if resetErr != nil {
15131514 resetErr = handshakeContextError(ctx, handshakeTimedOut, resetErr)
@@ -1629,12 +1630,12 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
16291630 return fmt.Errorf("reading terminal geometry for parked route: %w", err)
16301631 }
16311632 geometry = geometry.NormalizePixels()
1632- payload, err := ports.MarshalResize(protocol.Resize{Size: geometry.Size, PixelWidth: geometry.PixelWidth, PixelHeight: geometry.PixelHeight})
1633+ payload, err := wire.MarshalResize(protocol.Resize{Size: geometry.Size, PixelWidth: geometry.PixelWidth, PixelHeight: geometry.PixelHeight})
16331634 if err != nil {
16341635 return fmt.Errorf("encoding terminal size for parked route: %w", err)
16351636 }
16361637 select {
1637- case controlCh <- ports.Frame{Type: ports.MsgResize, Payload: payload}:
1638+ case controlCh <- wire.Frame{Type: wire.MsgResize, Payload: payload}:
16381639 return nil
16391640 case <-loopCtx.Done():
16401641 return context.Canceled
@@ -1646,12 +1647,12 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
16461647 }
16471648 nextParkedRequestID++
16481649 request := protocol.ParkedRouteRequest{RequestID: nextParkedRequestID, LeaseID: leaseID, Action: action, Target: target}
1649- payload := ports.MarshalParkedRouteRequest(request)
1650+ payload := wire.MarshalParkedRouteRequest(request)
16501651 if payload == nil {
16511652 return errors.New("vev: invalid parked-route request")
16521653 }
16531654 select {
1654- case controlCh <- ports.Frame{Type: ports.MsgParkedRouteRequest, Payload: payload}:
1655+ case controlCh <- wire.Frame{Type: wire.MsgParkedRouteRequest, Payload: payload}:
16551656 parkedPending = &parkedRoutePending{
16561657 requestID: request.RequestID, action: action, leaseID: leaseID,
16571658 timer: clk.NewTimer(protocol.HandshakeTimeout),
@@ -1726,12 +1727,12 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
17261727 return fmt.Errorf("reading terminal geometry for reconnect reconciliation: %w", serr)
17271728 }
17281729 geometry = geometry.NormalizePixels()
1729- payload, err := ports.MarshalResize(protocol.Resize{Size: geometry.Size, PixelWidth: geometry.PixelWidth, PixelHeight: geometry.PixelHeight})
1730+ payload, err := wire.MarshalResize(protocol.Resize{Size: geometry.Size, PixelWidth: geometry.PixelWidth, PixelHeight: geometry.PixelHeight})
17301731 if err != nil {
17311732 return fmt.Errorf("encoding terminal size for reconnect reconciliation: %w", err)
17321733 }
17331734 select {
1734- case sendCh <- ports.Frame{Type: ports.MsgResize, Payload: payload}:
1735+ case sendCh <- wire.Frame{Type: wire.MsgResize, Payload: payload}:
17351736 awaitingReconnectReset = true
17361737 return nil
17371738 case <-loopCtx.Done():
@@ -1742,17 +1743,17 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
17421743 if a.runner.ledger == nil {
17431744 return errors.New("vev: route ledger unavailable")
17441745 }
1745- snapshotPayload, err := ports.MarshalRecentRouteSnapshot(a.runner.ledger.snapshot())
1746+ snapshotPayload, err := wire.MarshalRecentRouteSnapshot(a.runner.ledger.snapshot())
17461747 if err != nil {
17471748 return err
17481749 }
1749- attentionPayload, err := ports.MarshalRouteAttentionSubscription(a.runner.ledger.attentionSubscription())
1750+ attentionPayload, err := wire.MarshalRouteAttentionSubscription(a.runner.ledger.attentionSubscription())
17501751 if err != nil {
17511752 return err
17521753 }
1753- for _, frame := range []ports.Frame{
1754- {Type: ports.MsgRecentRouteSnapshot, Payload: snapshotPayload},
1755- {Type: ports.MsgRouteAttentionSubscription, Payload: attentionPayload},
1754+ for _, frame := range []wire.Frame{
1755+ {Type: wire.MsgRecentRouteSnapshot, Payload: snapshotPayload},
1756+ {Type: wire.MsgRouteAttentionSubscription, Payload: attentionPayload},
17561757 } {
17571758 select {
17581759 case sendCh <- frame:
@@ -1763,7 +1764,7 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
17631764 return nil
17641765 }
17651766 sendRouteFailure := func(action protocol.RouteNavigationAction, code protocol.RouteFailureCode) error {
1766- payload, err := ports.MarshalRouteNavigationFailure(protocol.RouteNavigationFailure{
1767+ payload, err := wire.MarshalRouteNavigationFailure(protocol.RouteNavigationFailure{
17671768 Key: action.Key,
17681769 Generation: action.Generation,
17691770 Code: code,
@@ -1772,7 +1773,7 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
17721773 return err
17731774 }
17741775 select {
1775- case sendCh <- ports.Frame{Type: ports.MsgRouteNavigationFailure, Payload: payload}:
1776+ case sendCh <- wire.Frame{Type: wire.MsgRouteNavigationFailure, Payload: payload}:
17761777 return nil
17771778 case <-loopCtx.Done():
17781779 return context.Canceled
@@ -1827,7 +1828,7 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
18271828 return welcomedResult(fmt.Errorf("dismissing reconnect toast: %w", err))
18281829 }
18291830 select {
1830- case sendCh <- ports.Frame{Type: ports.MsgClientNotice, Payload: ports.MarshalClientNotice(protocol.ClientNotice{Action: protocol.ClientNoticeLinkConnected})}:
1831+ case sendCh <- wire.Frame{Type: wire.MsgClientNotice, Payload: wire.MarshalClientNotice(protocol.ClientNotice{Action: protocol.ClientNoticeLinkConnected})}:
18311832 case <-loopCtx.Done():
18321833 return welcomedResult(nil)
18331834 }
@@ -1836,7 +1837,7 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
18361837 if ev.State == ports.LinkStateDegraded {
18371838 log.Warn("UDP link degraded")
18381839 select {
1839- case sendCh <- ports.Frame{Type: ports.MsgClientNotice, Payload: ports.MarshalClientNotice(protocol.ClientNotice{Action: protocol.ClientNoticeLinkDegraded})}:
1840+ case sendCh <- wire.Frame{Type: wire.MsgClientNotice, Payload: wire.MarshalClientNotice(protocol.ClientNotice{Action: protocol.ClientNoticeLinkDegraded})}:
18401841 case <-loopCtx.Done():
18411842 return welcomedResult(nil)
18421843 }
@@ -1884,8 +1885,8 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
18841885 return welcomedResult(fmt.Errorf("vev: receiving from daemon: %w", r.err))
18851886 }
18861887 switch r.frame.Type {
1887- case ports.MsgOutput:
1888- o, derr := ports.UnmarshalOutput(r.frame.Payload)
1888+ case wire.MsgOutput:
1889+ o, derr := wire.UnmarshalOutput(r.frame.Payload)
18891890 if derr != nil {
18901891 return welcomedResult(fmt.Errorf("vev: decoding output: %w", derr))
18911892 }
@@ -1896,7 +1897,7 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
18961897 // repeated gaps while that reset is in flight.
18971898 if !outputResetRequested {
18981899 select {
1899- case sendCh <- ports.Frame{Type: ports.MsgOutputResetRequest, Payload: ports.MarshalOutputResetRequest(protocol.OutputResetRequest{})}:
1900+ case sendCh <- wire.Frame{Type: wire.MsgOutputResetRequest, Payload: wire.MarshalOutputResetRequest(protocol.OutputResetRequest{})}:
19001901 outputResetRequested = true
19011902 case <-loopCtx.Done():
19021903 return loopCanceledResult()
@@ -1946,8 +1947,8 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
19461947 log.Debug("received first output")
19471948 endHandshake()
19481949 }
1949- case ports.MsgAttachTarget:
1950- target, derr := ports.UnmarshalAttachTarget(r.frame.Payload)
1950+ case wire.MsgAttachTarget:
1951+ target, derr := wire.UnmarshalAttachTarget(r.frame.Payload)
19511952 if derr != nil {
19521953 return welcomedResult(fmt.Errorf("vev: decoding attach target: %w", derr))
19531954 }
@@ -1966,7 +1967,7 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
19661967 }
19671968 nextSamePeerRequestID++
19681969 request := a.runner.ledger.samePeerHandoff(request, target)
1969- payload, err := ports.MarshalSamePeerSwitchRequest(protocol.SamePeerSwitchRequest{
1970+ payload, err := wire.MarshalSamePeerSwitchRequest(protocol.SamePeerSwitchRequest{
19701971 RequestID: nextSamePeerRequestID,
19711972 Target: *target.ExactTarget,
19721973 PreferredTabID: request.PreferredTabID,
@@ -1977,7 +1978,7 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
19771978 samePeerSwitch = &samePeerSwitchPending{requestID: nextSamePeerRequestID, target: *target.ExactTarget}
19781979 inputGate.setPaused(true)
19791980 select {
1980- case controlCh <- ports.Frame{Type: ports.MsgSamePeerSwitchRequest, Payload: payload}:
1981+ case controlCh <- wire.Frame{Type: wire.MsgSamePeerSwitchRequest, Payload: payload}:
19811982 continue
19821983 case <-loopCtx.Done():
19831984 return loopCanceledResult()
@@ -1986,8 +1987,8 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
19861987 handoff := welcomedResult(nil)
19871988 handoff.target = &target
19881989 return handoff
1989- case ports.MsgParkedRouteResponse:
1990- response, derr := ports.UnmarshalParkedRouteResponse(r.frame.Payload)
1990+ case wire.MsgParkedRouteResponse:
1991+ response, derr := wire.UnmarshalParkedRouteResponse(r.frame.Payload)
19911992 if derr != nil {
19921993 return welcomedResult(fmt.Errorf("vev: decoding parked-route response: %w", derr))
19931994 }
@@ -2033,8 +2034,8 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
20332034 return fallback
20342035 }
20352036 }
2036- case ports.MsgNavigationAction:
2037- directive, derr := ports.UnmarshalNavigationDirective(r.frame.Payload)
2037+ case wire.MsgNavigationAction:
2038+ directive, derr := wire.UnmarshalNavigationDirective(r.frame.Payload)
20382039 if derr != nil {
20392040 return welcomedResult(fmt.Errorf("vev: decoding navigation action: %w", derr))
20402041 }
@@ -2052,8 +2053,8 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
20522053 navigation := welcomedResult(nil)
20532054 navigation.action = directive.Action
20542055 return navigation
2055- case ports.MsgNavigateRecentRoute:
2056- action, derr := ports.UnmarshalRouteNavigationAction(r.frame.Payload)
2056+ case wire.MsgNavigateRecentRoute:
2057+ action, derr := wire.UnmarshalRouteNavigationAction(r.frame.Payload)
20572058 if derr != nil {
20582059 return welcomedResult(fmt.Errorf("vev: decoding route navigation action: %w", derr))
20592060 }
@@ -2076,8 +2077,8 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
20762077 navigation := welcomedResult(nil)
20772078 navigation.routeAction = &action
20782079 return navigation
2079- case ports.MsgCommittedRouteIdentity:
2080- identity, derr := ports.UnmarshalCommittedRouteIdentity(r.frame.Payload)
2080+ case wire.MsgCommittedRouteIdentity:
2081+ identity, derr := wire.UnmarshalCommittedRouteIdentity(r.frame.Payload)
20812082 if derr != nil {
20822083 return welcomedResult(fmt.Errorf("vev: decoding committed route identity: %w", derr))
20832084 }
@@ -2099,8 +2100,8 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
20992100 samePeerSwitch = nil
21002101 inputGate.setPaused(false)
21012102 }
2102- case ports.MsgSamePeerSwitchFailure:
2103- failure, derr := ports.UnmarshalSamePeerSwitchFailure(r.frame.Payload)
2103+ case wire.MsgSamePeerSwitchFailure:
2104+ failure, derr := wire.UnmarshalSamePeerSwitchFailure(r.frame.Payload)
21042105 if derr != nil {
21052106 return welcomedResult(fmt.Errorf("vev: decoding same-peer switch failure: %w", derr))
21062107 }
@@ -2111,7 +2112,7 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
21112112 transitionWaitingFull = true
21122113 if !outputResetRequested {
21132114 select {
2114- case sendCh <- ports.Frame{Type: ports.MsgOutputResetRequest, Payload: ports.MarshalOutputResetRequest(protocol.OutputResetRequest{})}:
2115+ case sendCh <- wire.Frame{Type: wire.MsgOutputResetRequest, Payload: wire.MarshalOutputResetRequest(protocol.OutputResetRequest{})}:
21152116 outputResetRequested = true
21162117 case <-loopCtx.Done():
21172118 return loopCanceledResult()
@@ -2121,8 +2122,8 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
21212122 transition.stop()
21222123 }
21232124 }
2124- case ports.MsgRoutePosition:
2125- position, derr := ports.UnmarshalRoutePosition(r.frame.Payload)
2125+ case wire.MsgRoutePosition:
2126+ position, derr := wire.UnmarshalRoutePosition(r.frame.Payload)
21262127 if derr != nil {
21272128 return welcomedResult(fmt.Errorf("vev: decoding route position: %w", derr))
21282129 }
@@ -2133,14 +2134,14 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
21332134 return welcomedResult(fmt.Errorf("vev: remembering route position: %w", derr))
21342135 }
21352136 routePosition = cloneRoutePosition(&position)
2136- case ports.MsgRouteNavigationFailure:
2137- failure, derr := ports.UnmarshalRouteNavigationFailure(r.frame.Payload)
2137+ case wire.MsgRouteNavigationFailure:
2138+ failure, derr := wire.UnmarshalRouteNavigationFailure(r.frame.Payload)
21382139 if derr != nil {
21392140 return welcomedResult(fmt.Errorf("vev: decoding route navigation failure: %w", derr))
21402141 }
21412142 log.Warn("route navigation rejected", "key", failure.Key, "generation", failure.Generation, "code", failure.Code)
2142- case ports.MsgDetached:
2143- d, derr := ports.UnmarshalDetached(r.frame.Payload)
2143+ case wire.MsgDetached:
2144+ d, derr := wire.UnmarshalDetached(r.frame.Payload)
21442145 if derr != nil {
21452146 return welcomedResult(fmt.Errorf("vev: decoding detached: %w", derr))
21462147 }
@@ -2151,7 +2152,7 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
21512152 log.Warn("detached by daemon", "reason", d.Reason)
21522153 }
21532154 return welcomedResult(detachedResult(d.Reason))
2154- case ports.MsgPong:
2155+ case wire.MsgPong:
21552156 // Liveness reply; nothing to do.
21562157 default:
21572158 // Unknown/out-of-band message types are ignored so a newer
@@ -2164,7 +2165,7 @@ func (a *attachAttempt) run(ctx context.Context) attachResult {
21642165 // recvResult carries one framed message (or a read error) from the receive
21652166 // pump to the main loop.
21662167 type recvResult struct {
2167- frame ports.Frame
2168+ frame wire.Frame
21682169 err error
21692170 }
21702171
@@ -2227,9 +2228,9 @@ func (q *cumulativeAckQueue) take() (uint64, uint64) {
22272228
22282229 // runSender preserves normal-frame order while allowing switch control and
22292230 // output ACKs to make progress when raw input is held for a same-peer switch.
2230-func runSender(ctx context.Context, cancel context.CancelFunc, transport ports.Transport, control <-chan ports.Frame, barriers <-chan chan struct{}, in <-chan ports.Frame, inputGate *samePeerInputGate, acks *cumulativeAckQueue, errCh chan<- error, log *slog.Logger) {
2231+func runSender(ctx context.Context, cancel context.CancelFunc, transport ports.Transport, control <-chan wire.Frame, barriers <-chan chan struct{}, in <-chan wire.Frame, inputGate *samePeerInputGate, acks *cumulativeAckQueue, errCh chan<- error, log *slog.Logger) {
22312232 defer log.Debug("sender pump exited")
2232- send := func(f ports.Frame) bool {
2233+ send := func(f wire.Frame) bool {
22332234 if err := transport.Send(f); err != nil {
22342235 select {
22352236 case errCh <- err:
@@ -2245,7 +2246,7 @@ func runSender(ctx context.Context, cancel context.CancelFunc, transport ports.T
22452246 if state == 0 {
22462247 return true
22472248 }
2248- payload, err := ports.MarshalAck(protocol.Ack{Epoch: epoch, State: state})
2249+ payload, err := wire.MarshalAck(protocol.Ack{Epoch: epoch, State: state})
22492250 if err != nil {
22502251 select {
22512252 case errCh <- fmt.Errorf("encoding output ACK: %w", err):
@@ -2254,7 +2255,7 @@ func runSender(ctx context.Context, cancel context.CancelFunc, transport ports.T
22542255 cancel()
22552256 return false
22562257 }
2257- return send(ports.Frame{Type: ports.MsgAck, Payload: payload})
2258+ return send(wire.Frame{Type: wire.MsgAck, Payload: payload})
22582259 }
22592260 flushPending := func() bool {
22602261 for {
@@ -2286,7 +2287,7 @@ func runSender(ctx context.Context, cancel context.CancelFunc, transport ports.T
22862287 close(done)
22872288 return true
22882289 }
2289- var heldInput *ports.Frame
2290+ var heldInput *wire.Frame
22902291 for {
22912292 if heldInput != nil {
22922293 paused, changed := inputGate.snapshot()
@@ -2353,7 +2354,7 @@ func runSender(ctx context.Context, cancel context.CancelFunc, transport ports.T
23532354 return
23542355 default:
23552356 }
2356- if frame.Type == ports.MsgInput || frame.Type == ports.MsgImagePush {
2357+ if frame.Type == wire.MsgInput || frame.Type == wire.MsgImagePush {
23572358 if paused, _ := inputGate.snapshot(); paused {
23582359 held := frame
23592360 heldInput = &held
@@ -2725,7 +2726,7 @@ type stdinPump struct {
27252726 in io.Reader // compatibility for isolated scanner tests
27262727 input *terminalInputPump
27272728 consumer uint64
2728- out chan<- ports.Frame
2729+ out chan<- wire.Frame
27292730 clock ports.Clock
27302731 clipboard ports.ClipboardReader
27312732 logger *slog.Logger
@@ -2746,7 +2747,7 @@ func (p *stdinPump) run() {
27462747 var undeliveredMu sync.Mutex
27472748 var undelivered []byte
27482749 sendOK.Store(true)
2749- send := func(frame ports.Frame) bool {
2750+ send := func(frame wire.Frame) bool {
27502751 if !sendOK.Load() {
27512752 return false
27522753 }
@@ -2785,7 +2786,7 @@ func (p *stdinPump) run() {
27852786 return
27862787 }
27872788 inputSeq++
2788- if !send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{InputSeq: inputSeq, Data: append([]byte(nil), data...)})}) {
2789+ if !send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{InputSeq: inputSeq, Data: append([]byte(nil), data...)})}) {
27892790 undeliveredMu.Lock()
27902791 undelivered = append(undelivered, data...)
27912792 undeliveredMu.Unlock()
@@ -2799,11 +2800,11 @@ func (p *stdinPump) run() {
27992800 reader: p.clipboard,
28002801 log: p.logger,
28012802 sendNotice: func(action uint8) {
2802- send(ports.Frame{Type: ports.MsgClientNotice, Payload: ports.MarshalClientNotice(protocol.ClientNotice{Action: action})})
2803+ send(wire.Frame{Type: wire.MsgClientNotice, Payload: wire.MarshalClientNotice(protocol.ClientNotice{Action: action})})
28032804 },
28042805 sendImage: func(mime string, data []byte) {
28052806 inputSeq++
2806- send(ports.Frame{Type: ports.MsgImagePush, Payload: ports.MarshalImagePush(protocol.ImagePush{InputSeq: inputSeq, Mime: mime, Data: data})})
2807+ send(wire.Frame{Type: wire.MsgImagePush, Payload: wire.MarshalImagePush(protocol.ImagePush{InputSeq: inputSeq, Mime: mime, Data: data})})
28072808 },
28082809 next: coalescer.Scan,
28092810 }
@@ -2945,7 +2946,7 @@ func (p *stdinPump) run() {
29452946 disarmMarkerDeadline()
29462947 markers.flush(sink)
29472948 select {
2948- case p.out <- ports.Frame{Type: ports.MsgDetach, Payload: ports.MarshalDetach(protocol.Detach{})}:
2949+ case p.out <- wire.Frame{Type: wire.MsgDetach, Payload: wire.MarshalDetach(protocol.Detach{})}:
29492950 case <-p.ctx.Done():
29502951 }
29512952 p.cancel()
@@ -2966,7 +2967,7 @@ func (p *stdinPump) run() {
29662967 // runResize forwards coalesced terminal resize events to the daemon. It
29672968 // tolerates an already-closed resize channel (which the terminal adapter
29682969 // hands back when restore ran before ResizeEvents was first called).
2969-func runResize(ctx context.Context, events <-chan domain.Geometry, out chan<- ports.Frame, sendLease *foregroundSendLease, log *slog.Logger) {
2970+func runResize(ctx context.Context, events <-chan domain.Geometry, out chan<- wire.Frame, sendLease *foregroundSendLease, log *slog.Logger) {
29702971 defer log.Debug("resize pump exited")
29712972 for {
29722973 select {
@@ -2975,12 +2976,12 @@ func runResize(ctx context.Context, events <-chan domain.Geometry, out chan<- po
29752976 return
29762977 }
29772978 geometry = geometry.NormalizePixels()
2978- payload, err := ports.MarshalResize(protocol.Resize{Size: geometry.Size, PixelWidth: geometry.PixelWidth, PixelHeight: geometry.PixelHeight})
2979+ payload, err := wire.MarshalResize(protocol.Resize{Size: geometry.Size, PixelWidth: geometry.PixelWidth, PixelHeight: geometry.PixelHeight})
29792980 if err != nil {
29802981 log.Error("encoding terminal resize", "error", err)
29812982 continue
29822983 }
2983- frame := ports.Frame{Type: ports.MsgResize, Payload: payload}
2984+ frame := wire.Frame{Type: wire.MsgResize, Payload: payload}
29842985 if !sendLease.send(func() bool {
29852986 select {
29862987 case out <- frame:
M internal/usecase/client/client_test.go +198 -197
@@ -19,6 +19,7 @@ import (
1919 "github.com/bnema/vev/internal/ports"
2020 portsmocks "github.com/bnema/vev/internal/ports/mocks"
2121 "github.com/bnema/vev/internal/protocol"
22+ "github.com/bnema/vev/internal/protocol/wire"
2223 "github.com/bnema/vev/internal/usecase/client"
2324 )
2425
@@ -117,7 +118,7 @@ func (t *reconnectTestTimer) Stop() bool {
117118
118119 // recvItem is one scripted Recv result.
119120 type recvItem struct {
120- f ports.Frame
121+ f wire.Frame
121122 err error
122123 wait <-chan struct{}
123124 }
@@ -135,26 +136,26 @@ func scriptRecv(tr *portsmocks.MockTransport, items ...recvItem) func() {
135136 ch <- it
136137 }
137138 done := make(chan struct{})
138- tr.EXPECT().Recv().RunAndReturn(func() (ports.Frame, error) {
139+ tr.EXPECT().Recv().RunAndReturn(func() (wire.Frame, error) {
139140 select {
140141 case it := <-ch:
141142 if it.wait != nil {
142143 select {
143144 case <-it.wait:
144145 case <-done:
145- return ports.Frame{}, io.EOF
146+ return wire.Frame{}, io.EOF
146147 }
147148 }
148149 return it.f, it.err
149150 case <-done:
150- return ports.Frame{}, io.EOF
151+ return wire.Frame{}, io.EOF
151152 }
152153 }).Maybe()
153154 return func() { close(done) }
154155 }
155156
156-func frameOf(t ports.MsgType, payload []byte) ports.Frame {
157- return ports.Frame{Type: t, Payload: payload}
157+func frameOf(t wire.MsgType, payload []byte) wire.Frame {
158+ return wire.Frame{Type: t, Payload: payload}
158159 }
159160
160161 // blockingReader blocks on Read until closed, then returns EOF. Stands in
@@ -234,8 +235,8 @@ func newHappyTerminal(t *testing.T, out *bytes.Buffer, restoreCount *atomic.Int3
234235 return tm, in
235236 }
236237
237-func isType(typ ports.MsgType) any {
238- return mock.MatchedBy(func(f ports.Frame) bool { return f.Type == typ })
238+func isType(typ wire.MsgType) any {
239+ return mock.MatchedBy(func(f wire.Frame) bool { return f.Type == typ })
239240 }
240241
241242 // transportDialer adapts one already-open test transport to the Runner API.
@@ -339,13 +340,13 @@ func TestRunBoundsPreWelcomeOperations(t *testing.T) {
339340 return io.ErrClosedPipe
340341 }
341342 if tt.phase == "send" {
342- tr.EXPECT().Send(isType(ports.MsgHello)).RunAndReturn(func(ports.Frame) error {
343+ tr.EXPECT().Send(isType(wire.MsgHello)).RunAndReturn(func(wire.Frame) error {
343344 return block()
344345 }).Once()
345346 } else {
346- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
347- tr.EXPECT().Recv().RunAndReturn(func() (ports.Frame, error) {
348- return ports.Frame{}, block()
347+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
348+ tr.EXPECT().Recv().RunAndReturn(func() (wire.Frame, error) {
349+ return wire.Frame{}, block()
349350 }).Once()
350351 }
351352 dialer := portsmocks.NewMockDialer(t)
@@ -384,7 +385,7 @@ func TestRunBoundsPreWelcomeOperations(t *testing.T) {
384385 case <-time.After(time.Second):
385386 t.Fatal("handshake operation leaked after Run returned")
386387 }
387- tr.AssertNotCalled(t, "Send", isType(ports.MsgTheme))
388+ tr.AssertNotCalled(t, "Send", isType(wire.MsgTheme))
388389 })
389390 }
390391 }
@@ -401,17 +402,17 @@ func TestAttachHelloIncludesTrueColor(t *testing.T) {
401402
402403 gotHello := make(chan protocol.Hello, 1)
403404 tr := portsmocks.NewMockTransport(t)
404- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
405- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
406- tr.EXPECT().Send(isType(ports.MsgHello)).RunAndReturn(func(f ports.Frame) error {
407- hello, err := ports.UnmarshalHello(f.Payload)
405+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
406+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
407+ tr.EXPECT().Send(isType(wire.MsgHello)).RunAndReturn(func(f wire.Frame) error {
408+ hello, err := wire.UnmarshalHello(f.Payload)
408409 require.NoError(t, err)
409410 gotHello <- hello
410411 return nil
411412 }).Once()
412413 unblock := scriptRecv(tr,
413- recvItem{f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
414- recvItem{f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
414+ recvItem{f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
415+ recvItem{f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
415416 )
416417 defer unblock()
417418 tr.EXPECT().Close().Return(nil).Once()
@@ -434,14 +435,14 @@ func TestAttachPublishesCommittedRouteSnapshot(t *testing.T) {
434435 defer term.in.unblock()
435436 lifecycle := domain.SessionLifecycleID{9, 8, 7}
436437 tr := &recordingTransport{recvs: []recvItem{
437- {f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{
438+ {f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{
438439 SessionID: "s1",
439440 SessionName: "main",
440441 CommittedIdentity: &protocol.CommittedRouteIdentity{
441442 Target: protocol.ExactSessionTarget{LifecycleID: lifecycle, SessionName: "main"},
442443 },
443444 }))},
444- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
445+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
445446 }}
446447
447448 err := runTestClient(context.Background(), attachTestDependencies(tr, term, realClock{}), client.AttachRequest{
@@ -458,14 +459,14 @@ func TestAttachPublishesCommittedRouteSnapshot(t *testing.T) {
458459 foundSubscription := false
459460 for _, frame := range tr.Sends() {
460461 switch frame.Type {
461- case ports.MsgRecentRouteSnapshot:
462+ case wire.MsgRecentRouteSnapshot:
462463 var err error
463- snapshot, err = ports.UnmarshalRecentRouteSnapshot(frame.Payload)
464+ snapshot, err = wire.UnmarshalRecentRouteSnapshot(frame.Payload)
464465 require.NoError(t, err)
465466 foundSnapshot = true
466- case ports.MsgRouteAttentionSubscription:
467+ case wire.MsgRouteAttentionSubscription:
467468 var err error
468- subscription, err = ports.UnmarshalRouteAttentionSubscription(frame.Payload)
469+ subscription, err = wire.UnmarshalRouteAttentionSubscription(frame.Payload)
469470 require.NoError(t, err)
470471 foundSubscription = true
471472 }
@@ -484,8 +485,8 @@ func TestLocalOnlyHandoffBetweenLocalSessionsKeepsClientRunning(t *testing.T) {
484485
485486 firstLifecycle := domain.SessionLifecycleID{1}
486487 secondLifecycle := domain.SessionLifecycleID{2}
487- welcome := func(name string, lifecycle domain.SessionLifecycleID) ports.Frame {
488- return frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{
488+ welcome := func(name string, lifecycle domain.SessionLifecycleID) wire.Frame {
489+ return frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{
489490 SessionID: name, SessionName: name, ResumeToken: 1, Capabilities: protocol.CapabilityResume,
490491 CommittedIdentity: &protocol.CommittedRouteIdentity{
491492 Target: protocol.ExactSessionTarget{LifecycleID: lifecycle, SessionName: name},
@@ -495,12 +496,12 @@ func TestLocalOnlyHandoffBetweenLocalSessionsKeepsClientRunning(t *testing.T) {
495496 secondTarget := protocol.ExactSessionTarget{LifecycleID: secondLifecycle, SessionName: "second"}
496497 first := &recordingTransport{recvs: []recvItem{
497498 {f: welcome("first", firstLifecycle)},
498- {f: frameOf(ports.MsgAttachTarget, ports.MarshalAttachTarget(protocol.AttachTarget{
499+ {f: frameOf(wire.MsgAttachTarget, wire.MarshalAttachTarget(protocol.AttachTarget{
499500 Session: "second", Intent: protocol.IntentAttach, ExactTarget: &secondTarget,
500501 EnvironmentPolicy: protocol.EnvironmentPolicyDaemonOwned, SamePeer: true,
501502 }))},
502- {f: frameOf(ports.MsgCommittedRouteIdentity, mustMarshalCommittedIdentity(protocol.CommittedRouteIdentity{Target: secondTarget}))},
503- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
503+ {f: frameOf(wire.MsgCommittedRouteIdentity, mustMarshalCommittedIdentity(protocol.CommittedRouteIdentity{Target: secondTarget}))},
504+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
504505 }}
505506 dialer := &sequenceDialer{trs: []ports.Transport{first}}
506507
@@ -530,7 +531,7 @@ func TestAttachHelloPreservesCompleteAttachRequest(t *testing.T) {
530531 }
531532 tr := &recordingTransport{recvs: []recvItem{
532533 {f: welcomeFrame(0)},
533- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
534+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
534535 }}
535536
536537 require.NoError(t, runTestClient(context.Background(), attachTestDependencies(tr, term, realClock{}), request))
@@ -550,8 +551,8 @@ func TestStoppedLocalHandoffDialsReplacementTransport(t *testing.T) {
550551 sourceLifecycle := domain.SessionLifecycleID{1}
551552 targetLifecycle := domain.SessionLifecycleID{2}
552553 target := protocol.ExactSessionTarget{LifecycleID: targetLifecycle, SessionName: "stopped"}
553- welcome := func(name string, lifecycle domain.SessionLifecycleID) ports.Frame {
554- return frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{
554+ welcome := func(name string, lifecycle domain.SessionLifecycleID) wire.Frame {
555+ return frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{
555556 SessionID: name, SessionName: name, ResumeToken: 1, Capabilities: protocol.CapabilityResume,
556557 CommittedIdentity: &protocol.CommittedRouteIdentity{
557558 Target: protocol.ExactSessionTarget{LifecycleID: lifecycle, SessionName: name},
@@ -560,14 +561,14 @@ func TestStoppedLocalHandoffDialsReplacementTransport(t *testing.T) {
560561 }
561562 first := &recordingTransport{recvs: []recvItem{
562563 {f: welcome("source", sourceLifecycle)},
563- {f: frameOf(ports.MsgAttachTarget, ports.MarshalAttachTarget(protocol.AttachTarget{
564+ {f: frameOf(wire.MsgAttachTarget, wire.MarshalAttachTarget(protocol.AttachTarget{
564565 Session: "stopped", Intent: protocol.IntentAttach, ExactTarget: &target,
565566 EnvironmentPolicy: protocol.EnvironmentPolicyDaemonOwned,
566567 }))},
567568 }}
568569 second := &recordingTransport{recvs: []recvItem{
569570 {f: welcome("stopped", targetLifecycle)},
570- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
571+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
571572 }}
572573 dialer := &sequenceDialer{trs: []ports.Transport{first, second}}
573574
@@ -587,8 +588,8 @@ func TestAcceptedHomeActionUsesCapturedRouteAfterRequestMetadataRebase(t *testin
587588 defer term.in.unblock()
588589 localLifecycle := domain.SessionLifecycleID{1}
589590 remoteLifecycle := domain.SessionLifecycleID{2}
590- welcome := func(name string, lifecycle domain.SessionLifecycleID) ports.Frame {
591- return frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{
591+ welcome := func(name string, lifecycle domain.SessionLifecycleID) wire.Frame {
592+ return frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{
592593 SessionID: name, SessionName: name, ResumeToken: 1, Capabilities: protocol.CapabilityResume,
593594 CommittedIdentity: &protocol.CommittedRouteIdentity{Target: protocol.ExactSessionTarget{LifecycleID: lifecycle, SessionName: name}},
594595 }))
@@ -599,7 +600,7 @@ func TestAcceptedHomeActionUsesCapturedRouteAfterRequestMetadataRebase(t *testin
599600 }
600601 local1 := &recordingTransport{recvs: []recvItem{
601602 {f: welcome("local", localLifecycle)},
602- {f: frameOf(ports.MsgAttachTarget, ports.MarshalAttachTarget(protocol.AttachTarget{
603+ {f: frameOf(wire.MsgAttachTarget, wire.MarshalAttachTarget(protocol.AttachTarget{
603604 Endpoint: "remote", Session: "work", Intent: protocol.IntentAttach,
604605 RemoteTarget: &remoteTarget, EnvironmentPolicy: protocol.EnvironmentPolicyDaemonOwned,
605606 }))},
@@ -610,7 +611,7 @@ func TestAcceptedHomeActionUsesCapturedRouteAfterRequestMetadataRebase(t *testin
610611 }}
611612 local2 := &recordingTransport{recvs: []recvItem{
612613 {f: welcome("local", localLifecycle)},
613- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
614+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
614615 }}
615616 localDialer := &sequenceDialer{trs: []ports.Transport{local1, local2}}
616617 remoteDialer := &sequenceDialer{trs: []ports.Transport{remote}}
@@ -632,8 +633,8 @@ func TestAcceptedHomeActionUsesCapturedRouteAfterRequestMetadataRebase(t *testin
632633 require.Equal(t, protocol.StartupOverlaySessionPicker, helloFromSend(t, local2).StartupOverlay)
633634 }
634635
635-func hybridWelcomeFrame(name string, lifecycle domain.SessionLifecycleID) ports.Frame {
636- return frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{
636+func hybridWelcomeFrame(name string, lifecycle domain.SessionLifecycleID) wire.Frame {
637+ return frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{
637638 SessionID: name, SessionName: name, ResumeToken: 1, Capabilities: protocol.CapabilityResume,
638639 CommittedIdentity: &protocol.CommittedRouteIdentity{Target: protocol.ExactSessionTarget{LifecycleID: lifecycle, SessionName: name}},
639640 }))
@@ -642,7 +643,7 @@ func hybridWelcomeFrame(name string, lifecycle domain.SessionLifecycleID) ports.
642643 func hybridLocalBootstrap(lifecycle domain.SessionLifecycleID, target domain.RemoteSessionTarget) *recordingTransport {
643644 return &recordingTransport{recvs: []recvItem{
644645 {f: hybridWelcomeFrame("local", lifecycle)},
645- {f: frameOf(ports.MsgAttachTarget, ports.MarshalAttachTarget(protocol.AttachTarget{
646+ {f: frameOf(wire.MsgAttachTarget, wire.MarshalAttachTarget(protocol.AttachTarget{
646647 Endpoint: target.Endpoint, Session: target.SessionName, Intent: protocol.IntentAttach,
647648 RemoteTarget: &target, EnvironmentPolicy: protocol.EnvironmentPolicyDaemonOwned,
648649 }))},
@@ -669,16 +670,16 @@ func hybridPickerDependencies(localDialer ports.Dialer, term ports.Terminal, clo
669670 return deps
670671 }
671672
672-func hybridParkedRequestHandler(requests chan<- protocol.ParkedRouteRequest, requestErrors chan<- error, signals map[protocol.ParkedRouteAction]chan struct{}) func(ports.Frame) {
673+func hybridParkedRequestHandler(requests chan<- protocol.ParkedRouteRequest, requestErrors chan<- error, signals map[protocol.ParkedRouteAction]chan struct{}) func(wire.Frame) {
673674 once := make(map[protocol.ParkedRouteAction]*sync.Once, len(signals))
674675 for action := range signals {
675676 once[action] = &sync.Once{}
676677 }
677- return func(frame ports.Frame) {
678- if frame.Type != ports.MsgParkedRouteRequest {
678+ return func(frame wire.Frame) {
679+ if frame.Type != wire.MsgParkedRouteRequest {
679680 return
680681 }
681- request, err := ports.UnmarshalParkedRouteRequest(frame.Payload)
682+ request, err := wire.UnmarshalParkedRouteRequest(frame.Payload)
682683 if err != nil {
683684 if requestErrors != nil {
684685 requestErrors <- err
@@ -717,11 +718,11 @@ func TestHybridPickerSameHostSwitchReusesRemoteTransport(t *testing.T) {
717718 remote := &recordingTransport{recvs: []recvItem{
718719 {f: hybridWelcomeFrame("source", remoteSourceLifecycle)},
719720 {f: navigationDirectiveFrame(protocol.NavigationOpenHomePicker)},
720- {f: frameOf(ports.MsgParkedRouteResponse, ports.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 1, Status: protocol.ParkedRouteReady})), wait: parkSent},
721- {f: frameOf(ports.MsgCommittedRouteIdentity, mustMarshalCommittedIdentity(protocol.CommittedRouteIdentity{Target: protocol.ExactSessionTarget{LifecycleID: remoteTargetLifecycle, SessionName: "target"}})), wait: switchSent},
722- {f: frameOf(ports.MsgParkedRouteResponse, ports.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 2, Status: protocol.ParkedRouteSwitched}))},
723- {f: frameOf(ports.MsgOutput, mustMarshalOutput(protocol.Output{Epoch: 2, New: 1, Size: domain.Size{Cols: 80, Rows: 24}, Full: true, Data: []byte("target frame")}))},
724- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
721+ {f: frameOf(wire.MsgParkedRouteResponse, wire.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 1, Status: protocol.ParkedRouteReady})), wait: parkSent},
722+ {f: frameOf(wire.MsgCommittedRouteIdentity, mustMarshalCommittedIdentity(protocol.CommittedRouteIdentity{Target: protocol.ExactSessionTarget{LifecycleID: remoteTargetLifecycle, SessionName: "target"}})), wait: switchSent},
723+ {f: frameOf(wire.MsgParkedRouteResponse, wire.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 2, Status: protocol.ParkedRouteSwitched}))},
724+ {f: frameOf(wire.MsgOutput, mustMarshalOutput(protocol.Output{Epoch: 2, New: 1, Size: domain.Size{Cols: 80, Rows: 24}, Full: true, Data: []byte("target frame")}))},
725+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
725726 }}
726727 remote.onSend = hybridParkedRequestHandler(parkedRequests, parkedRequestErrors, map[protocol.ParkedRouteAction]chan struct{}{
727728 protocol.ParkedRoutePrepare: parkSent,
@@ -729,14 +730,14 @@ func TestHybridPickerSameHostSwitchReusesRemoteTransport(t *testing.T) {
729730 })
730731 localPicker := &recordingTransport{recvs: []recvItem{
731732 {f: hybridWelcomeFrame("local", localLifecycle)},
732- {f: frameOf(ports.MsgAttachTarget, ports.MarshalAttachTarget(protocol.AttachTarget{
733+ {f: frameOf(wire.MsgAttachTarget, wire.MarshalAttachTarget(protocol.AttachTarget{
733734 Endpoint: "remote", Session: "target", Intent: protocol.IntentAttach,
734735 RemoteTarget: &targetTarget, EnvironmentPolicy: protocol.EnvironmentPolicyDaemonOwned,
735736 }))},
736737 }}
737738 var resizeOnce sync.Once
738- localPicker.onSend = func(frame ports.Frame) {
739- if frame.Type == ports.MsgHello {
739+ localPicker.onSend = func(frame wire.Frame) {
740+ if frame.Type == wire.MsgHello {
740741 resizeOnce.Do(func() { term.setSize(domain.Size{Cols: 100, Rows: 40}) })
741742 }
742743 }
@@ -763,10 +764,10 @@ func TestHybridPickerSameHostSwitchReusesRemoteTransport(t *testing.T) {
763764 frames := remote.Sends()
764765 switchIndex := -1
765766 for i, frame := range frames {
766- if frame.Type != ports.MsgParkedRouteRequest {
767+ if frame.Type != wire.MsgParkedRouteRequest {
767768 continue
768769 }
769- request, requestErr := ports.UnmarshalParkedRouteRequest(frame.Payload)
770+ request, requestErr := wire.UnmarshalParkedRouteRequest(frame.Payload)
770771 require.NoError(t, requestErr)
771772 if request.Action == protocol.ParkedRouteSwitch {
772773 switchIndex = i
@@ -774,8 +775,8 @@ func TestHybridPickerSameHostSwitchReusesRemoteTransport(t *testing.T) {
774775 }
775776 }
776777 require.Positive(t, switchIndex)
777- require.Equal(t, ports.MsgResize, frames[switchIndex-1].Type, "current size must precede the parked switch")
778- resize, resizeErr := ports.UnmarshalResize(frames[switchIndex-1].Payload)
778+ require.Equal(t, wire.MsgResize, frames[switchIndex-1].Type, "current size must precede the parked switch")
779+ resize, resizeErr := wire.UnmarshalResize(frames[switchIndex-1].Payload)
779780 require.NoError(t, resizeErr)
780781 require.Equal(t, domain.Size{Cols: 100, Rows: 40}, resize.Size)
781782 require.Equal(t, int32(1), remoteDialer.calls.Load(), "same-host picker switch must retain the authenticated remote transport")
@@ -803,8 +804,8 @@ func TestHybridPickerExpiredSwitchFallsBackToNewDial(t *testing.T) {
803804 sourceRemote := &recordingTransport{recvs: []recvItem{
804805 {f: hybridWelcomeFrame("source", sourceLifecycle)},
805806 {f: navigationDirectiveFrame(protocol.NavigationOpenHomePicker)},
806- {f: frameOf(ports.MsgParkedRouteResponse, ports.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 1, Status: protocol.ParkedRouteReady})), wait: prepareSent},
807- {f: frameOf(ports.MsgParkedRouteResponse, ports.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 2, Status: protocol.ParkedRouteExpired})), wait: switchSent},
807+ {f: frameOf(wire.MsgParkedRouteResponse, wire.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 1, Status: protocol.ParkedRouteReady})), wait: prepareSent},
808+ {f: frameOf(wire.MsgParkedRouteResponse, wire.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 2, Status: protocol.ParkedRouteExpired})), wait: switchSent},
808809 }}
809810 sourceRemote.onSend = hybridParkedRequestHandler(nil, parkedRequestErrors, map[protocol.ParkedRouteAction]chan struct{}{
810811 protocol.ParkedRoutePrepare: prepareSent,
@@ -812,14 +813,14 @@ func TestHybridPickerExpiredSwitchFallsBackToNewDial(t *testing.T) {
812813 })
813814 localPicker := &recordingTransport{recvs: []recvItem{
814815 {f: hybridWelcomeFrame("local", localLifecycle)},
815- {f: frameOf(ports.MsgAttachTarget, ports.MarshalAttachTarget(protocol.AttachTarget{
816+ {f: frameOf(wire.MsgAttachTarget, wire.MarshalAttachTarget(protocol.AttachTarget{
816817 Endpoint: "remote", Session: "target", Intent: protocol.IntentAttach,
817818 RemoteTarget: &targetTarget, EnvironmentPolicy: protocol.EnvironmentPolicyDaemonOwned,
818819 }))},
819820 }}
820821 targetRemote := &recordingTransport{recvs: []recvItem{
821822 {f: hybridWelcomeFrame("target", targetLifecycle)},
822- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
823+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
823824 }}
824825 localDialer := &sequenceDialer{trs: []ports.Transport{localInitial, localPicker}}
825826 remoteDialer := &sequenceDialer{trs: []ports.Transport{
@@ -951,10 +952,10 @@ func TestHybridPickerBackResumesRetainedRemoteTransport(t *testing.T) {
951952 remote := &recordingTransport{recvs: []recvItem{
952953 {f: hybridWelcomeFrame("work", remoteLifecycle)},
953954 {f: navigationDirectiveFrame(protocol.NavigationOpenHomePicker)},
954- {f: frameOf(ports.MsgParkedRouteResponse, ports.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 1, Status: protocol.ParkedRouteReady})), wait: parkSent},
955- {f: frameOf(ports.MsgParkedRouteResponse, ports.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 2, Status: protocol.ParkedRouteResumed})), wait: resumeSent},
956- {f: frameOf(ports.MsgOutput, mustMarshalOutput(protocol.Output{Epoch: 1, New: 1, Size: domain.Size{Cols: 80, Rows: 24}, Full: true, Data: []byte("source frame")}))},
957- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
955+ {f: frameOf(wire.MsgParkedRouteResponse, wire.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 1, Status: protocol.ParkedRouteReady})), wait: parkSent},
956+ {f: frameOf(wire.MsgParkedRouteResponse, wire.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 2, Status: protocol.ParkedRouteResumed})), wait: resumeSent},
957+ {f: frameOf(wire.MsgOutput, mustMarshalOutput(protocol.Output{Epoch: 1, New: 1, Size: domain.Size{Cols: 80, Rows: 24}, Full: true, Data: []byte("source frame")}))},
958+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
958959 }}
959960 remote.onSend = hybridParkedRequestHandler(nil, parkedRequestErrors, map[protocol.ParkedRouteAction]chan struct{}{
960961 protocol.ParkedRoutePrepare: parkSent,
@@ -1002,7 +1003,7 @@ func TestHybridPickerDifferentHostFallsBackToNewRemoteDial(t *testing.T) {
10021003 sourceRemote := &recordingTransport{recvs: []recvItem{
10031004 {f: hybridWelcomeFrame("source", sourceLifecycle)},
10041005 {f: navigationDirectiveFrame(protocol.NavigationOpenHomePicker)},
1005- {f: frameOf(ports.MsgParkedRouteResponse, ports.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 1, Status: protocol.ParkedRouteReady})), wait: parkSent},
1006+ {f: frameOf(wire.MsgParkedRouteResponse, wire.MarshalParkedRouteResponse(protocol.ParkedRouteResponse{RequestID: 1, Status: protocol.ParkedRouteReady})), wait: parkSent},
10061007 {err: io.EOF, wait: keepSourceOpen},
10071008 }}
10081009 sourceRemote.onSend = hybridParkedRequestHandler(nil, parkedRequestErrors, map[protocol.ParkedRouteAction]chan struct{}{
@@ -1010,14 +1011,14 @@ func TestHybridPickerDifferentHostFallsBackToNewRemoteDial(t *testing.T) {
10101011 })
10111012 localPicker := &recordingTransport{recvs: []recvItem{
10121013 {f: hybridWelcomeFrame("local", localLifecycle)},
1013- {f: frameOf(ports.MsgAttachTarget, ports.MarshalAttachTarget(protocol.AttachTarget{
1014+ {f: frameOf(wire.MsgAttachTarget, wire.MarshalAttachTarget(protocol.AttachTarget{
10141015 Endpoint: targetTarget.Endpoint, Session: targetTarget.SessionName, Intent: protocol.IntentAttach,
10151016 RemoteTarget: &targetTarget, EnvironmentPolicy: protocol.EnvironmentPolicyDaemonOwned,
10161017 }))},
10171018 }}
10181019 targetRemote := &recordingTransport{recvs: []recvItem{
10191020 {f: hybridWelcomeFrame("target", targetLifecycle)},
1020- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
1021+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
10211022 }}
10221023 localDialer := &sequenceDialer{trs: []ports.Transport{localInitial, localPicker}}
10231024 sourceDialer := &sequenceDialer{trs: []ports.Transport{markedDatagramTransport{Transport: sourceRemote}}}
@@ -1048,8 +1049,8 @@ func TestRouteNavigationPreservesRemoteHomePickerAcrossLocalReturn(t *testing.T)
10481049 remoteLifecycle := domain.SessionLifecycleID{2, 2, 2}
10491050 newLifecycle := domain.SessionLifecycleID{3, 3, 3}
10501051 remoteNewLifecycle := domain.SessionLifecycleID{4, 4, 4}
1051- welcome := func(name string, lifecycle domain.SessionLifecycleID, token uint64) ports.Frame {
1052- return frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{
1052+ welcome := func(name string, lifecycle domain.SessionLifecycleID, token uint64) wire.Frame {
1053+ return frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{
10531054 SessionID: name + "-id",
10541055 SessionName: name,
10551056 ResumeToken: token,
@@ -1073,14 +1074,14 @@ func TestRouteNavigationPreservesRemoteHomePickerAcrossLocalReturn(t *testing.T)
10731074
10741075 local1 := &recordingTransport{recvs: []recvItem{
10751076 {f: welcome("test", localLifecycle, 11)},
1076- {f: frameOf(ports.MsgAttachTarget, ports.MarshalAttachTarget(remoteHandoff))},
1077+ {f: frameOf(wire.MsgAttachTarget, wire.MarshalAttachTarget(remoteHandoff))},
10771078 }}
10781079 local2 := &recordingTransport{recvs: []recvItem{
10791080 {f: welcome("test", localLifecycle, 11)},
1080- {f: frameOf(ports.MsgCommittedRouteIdentity, mustMarshalCommittedIdentity(protocol.CommittedRouteIdentity{
1081+ {f: frameOf(wire.MsgCommittedRouteIdentity, mustMarshalCommittedIdentity(protocol.CommittedRouteIdentity{
10811082 Target: protocol.ExactSessionTarget{LifecycleID: newLifecycle, SessionName: "new"},
10821083 }))},
1083- {f: frameOf(ports.MsgNavigateRecentRoute, mustMarshalRouteAction(protocol.RouteNavigationAction{SnapshotGeneration: 4, Key: 2, Generation: 2}))},
1084+ {f: frameOf(wire.MsgNavigateRecentRoute, mustMarshalRouteAction(protocol.RouteNavigationAction{SnapshotGeneration: 4, Key: 2, Generation: 2}))},
10841085 }}
10851086 local3 := &recordingTransport{recvs: []recvItem{
10861087 {f: welcome("test", localLifecycle, 11)},
@@ -1088,21 +1089,21 @@ func TestRouteNavigationPreservesRemoteHomePickerAcrossLocalReturn(t *testing.T)
10881089 }}
10891090 remote1 := &recordingTransport{recvs: []recvItem{
10901091 {f: welcome("remote-manual", remoteLifecycle, 22)},
1091- {f: frameOf(ports.MsgNavigateRecentRoute, mustMarshalRouteAction(protocol.RouteNavigationAction{SnapshotGeneration: 2, Key: 1, Generation: 1}))},
1092+ {f: frameOf(wire.MsgNavigateRecentRoute, mustMarshalRouteAction(protocol.RouteNavigationAction{SnapshotGeneration: 2, Key: 1, Generation: 1}))},
10921093 }}
10931094 remote2 := &recordingTransport{recvs: []recvItem{
10941095 {f: welcome("remote-manual", remoteLifecycle, 22)},
1095- {f: frameOf(ports.MsgCommittedRouteIdentity, mustMarshalCommittedIdentity(protocol.CommittedRouteIdentity{
1096+ {f: frameOf(wire.MsgCommittedRouteIdentity, mustMarshalCommittedIdentity(protocol.CommittedRouteIdentity{
10961097 Target: protocol.ExactSessionTarget{LifecycleID: remoteNewLifecycle, SessionName: "remote-new"},
10971098 }))},
1098- {f: frameOf(ports.MsgRoutePosition, mustMarshalRoutePosition(protocol.RoutePosition{
1099+ {f: frameOf(wire.MsgRoutePosition, mustMarshalRoutePosition(protocol.RoutePosition{
10991100 Target: protocol.ExactSessionTarget{LifecycleID: remoteNewLifecycle, SessionName: "remote-new"}, ActiveTabID: "tab-2",
11001101 }))},
11011102 {f: navigationDirectiveFrame(protocol.NavigationOpenHomePicker)},
11021103 }}
11031104 remote3 := &recordingTransport{recvs: []recvItem{
11041105 {f: welcome("remote-new", remoteNewLifecycle, 22)},
1105- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
1106+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
11061107 }}
11071108 localDialer := &sequenceDialer{trs: []ports.Transport{local1, local2, local3}}
11081109 remoteDialer := &sequenceDialer{trs: []ports.Transport{remote1, remote2, remote3}}
@@ -1129,16 +1130,16 @@ func TestRouteNavigationPreservesRemoteHomePickerAcrossLocalReturn(t *testing.T)
11291130 require.Equal(t, &protocol.ExactSessionTarget{LifecycleID: remoteNewLifecycle, SessionName: "remote-new"}, remoteNewHello.ExactTarget)
11301131 }
11311132
1132-func navigationDirectiveFrame(action protocol.NavigationAction) ports.Frame {
1133+func navigationDirectiveFrame(action protocol.NavigationAction) wire.Frame {
11331134 directive := protocol.NavigationDirective{Action: action}
11341135 if action == protocol.NavigationOpenHomePicker {
11351136 directive.LeaseID = protocol.ParkedRouteLeaseID{1}
11361137 }
1137- return frameOf(ports.MsgNavigationAction, ports.MarshalNavigationDirective(directive))
1138+ return frameOf(wire.MsgNavigationAction, wire.MarshalNavigationDirective(directive))
11381139 }
11391140
11401141 func mustMarshalCommittedIdentity(identity protocol.CommittedRouteIdentity) []byte {
1141- payload, err := ports.MarshalCommittedRouteIdentity(identity)
1142+ payload, err := wire.MarshalCommittedRouteIdentity(identity)
11421143 if err != nil {
11431144 panic(err)
11441145 }
@@ -1146,7 +1147,7 @@ func mustMarshalCommittedIdentity(identity protocol.CommittedRouteIdentity) []by
11461147 }
11471148
11481149 func mustMarshalRouteAction(action protocol.RouteNavigationAction) []byte {
1149- payload, err := ports.MarshalRouteNavigationAction(action)
1150+ payload, err := wire.MarshalRouteNavigationAction(action)
11501151 if err != nil {
11511152 panic(err)
11521153 }
@@ -1154,7 +1155,7 @@ func mustMarshalRouteAction(action protocol.RouteNavigationAction) []byte {
11541155 }
11551156
11561157 func mustMarshalRoutePosition(position protocol.RoutePosition) []byte {
1157- payload, err := ports.MarshalRoutePosition(position)
1158+ payload, err := wire.MarshalRoutePosition(position)
11581159 if err != nil {
11591160 panic(err)
11601161 }
@@ -1170,11 +1171,11 @@ func TestAttachTargetHandoffReturnsValidatedTargetAndClosesTransport(t *testing.
11701171
11711172 target := protocol.AttachTarget{Endpoint: "remote.example", Session: "work", Intent: protocol.IntentAttach}
11721173 tr := portsmocks.NewMockTransport(t)
1173- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1174- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
1174+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1175+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
11751176 unblock := scriptRecv(tr,
1176- recvItem{f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "local"}))},
1177- recvItem{f: frameOf(ports.MsgAttachTarget, ports.MarshalAttachTarget(target))},
1177+ recvItem{f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "local"}))},
1178+ recvItem{f: frameOf(wire.MsgAttachTarget, wire.MarshalAttachTarget(target))},
11781179 )
11791180 defer unblock()
11801181 tr.EXPECT().Close().Return(nil).Once()
@@ -1197,10 +1198,10 @@ func TestAttachHelloIncludesCompleteLocalEnvironment(t *testing.T) {
11971198
11981199 gotHello := make(chan protocol.Hello, 1)
11991200 tr := portsmocks.NewMockTransport(t)
1200- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1201- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1202- tr.EXPECT().Send(isType(ports.MsgHello)).RunAndReturn(func(f ports.Frame) error {
1203- hello, err := ports.UnmarshalHello(f.Payload)
1201+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1202+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1203+ tr.EXPECT().Send(isType(wire.MsgHello)).RunAndReturn(func(f wire.Frame) error {
1204+ hello, err := wire.UnmarshalHello(f.Payload)
12041205 if err != nil {
12051206 return err
12061207 }
@@ -1208,8 +1209,8 @@ func TestAttachHelloIncludesCompleteLocalEnvironment(t *testing.T) {
12081209 return nil
12091210 }).Once()
12101211 unblock := scriptRecv(tr,
1211- recvItem{f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1212- recvItem{f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
1212+ recvItem{f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1213+ recvItem{f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
12131214 )
12141215 defer unblock()
12151216 tr.EXPECT().Close().Return(nil).Once()
@@ -1226,17 +1227,17 @@ func TestAttachHelloRequestsSingleOutputForDatagramTransport(t *testing.T) {
12261227 defer in.unblock()
12271228
12281229 tr := portsmocks.NewMockTransport(t)
1229- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1230- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1231- tr.EXPECT().Send(isType(ports.MsgHello)).RunAndReturn(func(f ports.Frame) error {
1232- hello, err := ports.UnmarshalHello(f.Payload)
1230+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1231+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1232+ tr.EXPECT().Send(isType(wire.MsgHello)).RunAndReturn(func(f wire.Frame) error {
1233+ hello, err := wire.UnmarshalHello(f.Payload)
12331234 require.NoError(t, err)
12341235 require.Equal(t, uint8(1), hello.MaxOutputInFlight)
12351236 return nil
12361237 }).Once()
12371238 unblock := scriptRecv(tr,
1238- recvItem{f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1239- recvItem{f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
1239+ recvItem{f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1240+ recvItem{f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
12401241 )
12411242 defer unblock()
12421243 tr.EXPECT().Close().Return(nil).Once()
@@ -1252,13 +1253,13 @@ func TestAttachHappyPath(t *testing.T) {
12521253 defer in.unblock()
12531254
12541255 tr := portsmocks.NewMockTransport(t)
1255- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1256- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1257- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
1256+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1257+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1258+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
12581259 unblock := scriptRecv(tr,
1259- recvItem{f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1", SessionName: "main"}))},
1260- recvItem{f: frameOf(ports.MsgOutput, mustMarshalOutput(protocol.Output{Epoch: 1, Size: domain.Size{Cols: 1, Rows: 1}, Data: []byte("hello world")}))},
1261- recvItem{f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
1260+ recvItem{f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1", SessionName: "main"}))},
1261+ recvItem{f: frameOf(wire.MsgOutput, mustMarshalOutput(protocol.Output{Epoch: 1, Size: domain.Size{Cols: 1, Rows: 1}, Data: []byte("hello world")}))},
1262+ recvItem{f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
12621263 )
12631264 defer unblock()
12641265 tr.EXPECT().Close().Return(nil).Once()
@@ -1275,9 +1276,9 @@ func TestAttachVersionMismatch(t *testing.T) {
12751276 // EnterRaw must NOT be called on the error-before-welcome path.
12761277
12771278 tr := portsmocks.NewMockTransport(t)
1278- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
1279+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
12791280 tr.EXPECT().Recv().Return(
1280- frameOf(ports.MsgError, ports.MarshalErrorMsg(protocol.ErrorMsg{Code: protocol.ErrVersionMismatch, Text: "version mismatch"})),
1281+ frameOf(wire.MsgError, wire.MarshalErrorMsg(protocol.ErrorMsg{Code: protocol.ErrVersionMismatch, Text: "version mismatch"})),
12811282 nil,
12821283 ).Once()
12831284 tr.EXPECT().Close().Return(nil).Once()
@@ -1287,7 +1288,7 @@ func TestAttachVersionMismatch(t *testing.T) {
12871288 var pe *client.ProtocolError
12881289 require.True(t, errors.As(err, &pe), "want *client.ProtocolError, got %T", err)
12891290 require.Equal(t, protocol.ErrVersionMismatch, pe.Code)
1290- tr.AssertNotCalled(t, "Send", isType(ports.MsgTheme))
1291+ tr.AssertNotCalled(t, "Send", isType(wire.MsgTheme))
12911292 }
12921293
12931294 func TestAttachRestoredOnRecvErrorMidStream(t *testing.T) {
@@ -1298,12 +1299,12 @@ func TestAttachRestoredOnRecvErrorMidStream(t *testing.T) {
12981299 defer in.unblock()
12991300
13001301 tr := portsmocks.NewMockTransport(t)
1301- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1302- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1303- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
1302+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1303+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1304+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
13041305 boom := errors.New("connection reset")
13051306 unblock := scriptRecv(tr,
1306- recvItem{f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1307+ recvItem{f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
13071308 recvItem{err: boom},
13081309 )
13091310 defer unblock()
@@ -1322,11 +1323,11 @@ func TestAttachDaemonVanishedOnEOF(t *testing.T) {
13221323 defer in.unblock()
13231324
13241325 tr := portsmocks.NewMockTransport(t)
1325- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1326- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1327- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
1326+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1327+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1328+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
13281329 unblock := scriptRecv(tr,
1329- recvItem{f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1330+ recvItem{f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
13301331 recvItem{err: io.EOF},
13311332 )
13321333 defer unblock()
@@ -1356,35 +1357,35 @@ func TestAttachStdinForwardsSGRMouseReportAsSingleFrame(t *testing.T) {
13561357 gotInput := make(chan []byte, 2)
13571358 allowDetach := make(chan struct{})
13581359 tr := portsmocks.NewMockTransport(t)
1359- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1360- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1361- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
1362- tr.EXPECT().Send(isType(ports.MsgInput)).RunAndReturn(func(f ports.Frame) error {
1363- in, err := ports.UnmarshalInput(f.Payload)
1360+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1361+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1362+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
1363+ tr.EXPECT().Send(isType(wire.MsgInput)).RunAndReturn(func(f wire.Frame) error {
1364+ in, err := wire.UnmarshalInput(f.Payload)
13641365 require.NoError(t, err)
13651366 gotInput <- in.Data
13661367 close(allowDetach)
13671368 return nil
13681369 }).Once()
1369- welcome := frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))
1370- detached := frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))
1370+ welcome := frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))
1371+ detached := frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))
13711372 recvCh := make(chan recvItem, 1)
13721373 recvCh <- recvItem{f: welcome}
13731374 closed := make(chan struct{})
1374- tr.EXPECT().Recv().RunAndReturn(func() (ports.Frame, error) {
1375+ tr.EXPECT().Recv().RunAndReturn(func() (wire.Frame, error) {
13751376 select {
13761377 case it := <-recvCh:
13771378 return it.f, it.err
13781379 case <-allowDetach:
13791380 select {
13801381 case <-closed:
1381- return ports.Frame{}, io.EOF
1382+ return wire.Frame{}, io.EOF
13821383 default:
13831384 close(closed)
13841385 return detached, nil
13851386 }
13861387 case <-closed:
1387- return ports.Frame{}, io.EOF
1388+ return wire.Frame{}, io.EOF
13881389 }
13891390 }).Maybe()
13901391 tr.EXPECT().Close().Return(nil).Once()
@@ -1418,35 +1419,35 @@ func TestAttachStdinCoalescesSplitBracketedPaste(t *testing.T) {
14181419 gotInput := make(chan []byte, 1)
14191420 allowDetach := make(chan struct{})
14201421 tr := portsmocks.NewMockTransport(t)
1421- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1422- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1423- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
1424- tr.EXPECT().Send(isType(ports.MsgInput)).RunAndReturn(func(f ports.Frame) error {
1425- in, err := ports.UnmarshalInput(f.Payload)
1422+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1423+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1424+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
1425+ tr.EXPECT().Send(isType(wire.MsgInput)).RunAndReturn(func(f wire.Frame) error {
1426+ in, err := wire.UnmarshalInput(f.Payload)
14261427 require.NoError(t, err)
14271428 gotInput <- in.Data
14281429 close(allowDetach)
14291430 return nil
14301431 }).Once()
1431- welcome := frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))
1432- detached := frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))
1432+ welcome := frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))
1433+ detached := frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))
14331434 recvCh := make(chan recvItem, 1)
14341435 recvCh <- recvItem{f: welcome}
14351436 closed := make(chan struct{})
1436- tr.EXPECT().Recv().RunAndReturn(func() (ports.Frame, error) {
1437+ tr.EXPECT().Recv().RunAndReturn(func() (wire.Frame, error) {
14371438 select {
14381439 case it := <-recvCh:
14391440 return it.f, it.err
14401441 case <-allowDetach:
14411442 select {
14421443 case <-closed:
1443- return ports.Frame{}, io.EOF
1444+ return wire.Frame{}, io.EOF
14441445 default:
14451446 close(closed)
14461447 return detached, nil
14471448 }
14481449 case <-closed:
1449- return ports.Frame{}, io.EOF
1450+ return wire.Frame{}, io.EOF
14501451 }
14511452 }).Maybe()
14521453 tr.EXPECT().Close().Return(nil).Once()
@@ -1473,24 +1474,24 @@ func TestAttachForwardsResize(t *testing.T) {
14731474 var firstRecvOnce sync.Once
14741475
14751476 tr := portsmocks.NewMockTransport(t)
1476- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1477- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1478- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
1477+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1478+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1479+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
14791480 // The resize frame is forwarded via the sender goroutine.
14801481 gotResize := make(chan protocol.Resize, 1)
1481- tr.EXPECT().Send(isType(ports.MsgResize)).RunAndReturn(func(f ports.Frame) error {
1482- r, _ := ports.UnmarshalResize(f.Payload)
1482+ tr.EXPECT().Send(isType(wire.MsgResize)).RunAndReturn(func(f wire.Frame) error {
1483+ r, _ := wire.UnmarshalResize(f.Payload)
14831484 gotResize <- r
14841485 close(detachAfterResize)
14851486 return nil
14861487 }).Once()
14871488
1488- welcome := frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))
1489- detached := frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))
1489+ welcome := frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))
1490+ detached := frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))
14901491 recvCh := make(chan recvItem, 2)
14911492 recvCh <- recvItem{f: welcome}
14921493 done := make(chan struct{})
1493- tr.EXPECT().Recv().RunAndReturn(func() (ports.Frame, error) {
1494+ tr.EXPECT().Recv().RunAndReturn(func() (wire.Frame, error) {
14941495 firstRecvOnce.Do(func() { close(firstRecv) })
14951496 select {
14961497 case it := <-recvCh:
@@ -1499,13 +1500,13 @@ func TestAttachForwardsResize(t *testing.T) {
14991500 // Deliver the detach once the resize has been observed.
15001501 select {
15011502 case <-done:
1502- return ports.Frame{}, io.EOF
1503+ return wire.Frame{}, io.EOF
15031504 default:
15041505 close(done)
15051506 return detached, nil
15061507 }
15071508 case <-done:
1508- return ports.Frame{}, io.EOF
1509+ return wire.Frame{}, io.EOF
15091510 }
15101511 }).Maybe()
15111512 tr.EXPECT().Close().Return(nil).Once()
@@ -1534,12 +1535,12 @@ func TestRunRestoresRawModeAfterAttachError(t *testing.T) {
15341535 defer in.unblock()
15351536
15361537 tr := portsmocks.NewMockTransport(t)
1537- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1538- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1539- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
1538+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1539+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
1540+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
15401541 boom := errors.New("connection reset")
15411542 unblock := scriptRecv(tr,
1542- recvItem{f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1543+ recvItem{f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
15431544 recvItem{err: boom},
15441545 )
15451546 defer unblock()
@@ -1558,9 +1559,9 @@ func TestRunDoesNotEnterRawBeforePreWelcomeError(t *testing.T) {
15581559 // EnterRaw must NOT be called when the daemon rejects Hello before Welcome.
15591560
15601561 tr := portsmocks.NewMockTransport(t)
1561- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
1562+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
15621563 tr.EXPECT().Recv().Return(
1563- frameOf(ports.MsgError, ports.MarshalErrorMsg(protocol.ErrorMsg{Code: protocol.ErrVersionMismatch, Text: "version mismatch"})),
1564+ frameOf(wire.MsgError, wire.MarshalErrorMsg(protocol.ErrorMsg{Code: protocol.ErrVersionMismatch, Text: "version mismatch"})),
15641565 nil,
15651566 ).Once()
15661567 tr.EXPECT().Close().Return(nil).Once()
@@ -1571,7 +1572,7 @@ func TestRunDoesNotEnterRawBeforePreWelcomeError(t *testing.T) {
15711572 require.Error(t, err)
15721573 var pe *client.ProtocolError
15731574 require.True(t, errors.As(err, &pe), "want *client.ProtocolError, got %T", err)
1574- tr.AssertNotCalled(t, "Send", isType(ports.MsgTheme))
1575+ tr.AssertNotCalled(t, "Send", isType(wire.MsgTheme))
15751576 }
15761577
15771578 func TestRunPhaseASingleAttempt(t *testing.T) {
@@ -1604,14 +1605,14 @@ func (d *sequenceDialer) Dial(context.Context) (ports.Transport, error) {
16041605 type recordingTransport struct {
16051606 mu sync.Mutex
16061607 recvs []recvItem
1607- sends []ports.Frame
1608+ sends []wire.Frame
16081609 closed atomic.Int32
16091610 stall <-chan struct{}
1610- onSend func(ports.Frame)
1611+ onSend func(wire.Frame)
16111612 onClose func()
16121613 }
16131614
1614-func (t *recordingTransport) Send(f ports.Frame) error {
1615+func (t *recordingTransport) Send(f wire.Frame) error {
16151616 t.mu.Lock()
16161617 t.sends = append(t.sends, f)
16171618 onSend := t.onSend
@@ -1622,18 +1623,18 @@ func (t *recordingTransport) Send(f ports.Frame) error {
16221623 return nil
16231624 }
16241625
1625-func (t *recordingTransport) Sends() []ports.Frame {
1626+func (t *recordingTransport) Sends() []wire.Frame {
16261627 t.mu.Lock()
16271628 defer t.mu.Unlock()
1628- return append([]ports.Frame(nil), t.sends...)
1629+ return append([]wire.Frame(nil), t.sends...)
16291630 }
16301631
1631-func (t *recordingTransport) Recv() (ports.Frame, error) {
1632+func (t *recordingTransport) Recv() (wire.Frame, error) {
16321633 if len(t.recvs) == 0 {
16331634 if t.stall != nil {
16341635 <-t.stall
16351636 }
1636- return ports.Frame{}, io.EOF
1637+ return wire.Frame{}, io.EOF
16371638 }
16381639 it := t.recvs[0]
16391640 t.recvs = t.recvs[1:]
@@ -1692,14 +1693,14 @@ func helloFromSend(t *testing.T, tr *recordingTransport) protocol.Hello {
16921693 t.Helper()
16931694 sends := tr.Sends()
16941695 require.NotEmpty(t, sends)
1695- require.Equal(t, ports.MsgHello, sends[0].Type)
1696- h, err := ports.UnmarshalHello(sends[0].Payload)
1696+ require.Equal(t, wire.MsgHello, sends[0].Type)
1697+ h, err := wire.UnmarshalHello(sends[0].Payload)
16971698 require.NoError(t, err)
16981699 return h
16991700 }
17001701
1701-func welcomeFrame(token uint64) ports.Frame {
1702- return frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1", SessionName: "main", ResumeToken: token, Capabilities: protocol.CapabilityResume}))
1702+func welcomeFrame(token uint64) wire.Frame {
1703+ return frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1", SessionName: "main", ResumeToken: token, Capabilities: protocol.CapabilityResume}))
17031704 }
17041705
17051706 func TestRunReconnectsWithRotatedTokenAndSameClientID(t *testing.T) {
@@ -1707,7 +1708,7 @@ func TestRunReconnectsWithRotatedTokenAndSameClientID(t *testing.T) {
17071708 defer term.in.unblock()
17081709 tr1 := &recordingTransport{recvs: []recvItem{{f: welcomeFrame(11)}, {err: io.EOF}}}
17091710 tr2 := &recordingTransport{recvs: []recvItem{{f: welcomeFrame(22)}, {err: io.EOF}}}
1710- tr3 := &recordingTransport{recvs: []recvItem{{f: welcomeFrame(33)}, {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))}}}
1711+ tr3 := &recordingTransport{recvs: []recvItem{{f: welcomeFrame(33)}, {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))}}}
17111712 d := &sequenceDialer{trs: []ports.Transport{tr1, tr2, tr3}}
17121713 clock := &reconnectTestClock{}
17131714 result := make(chan error, 1)
@@ -1741,20 +1742,20 @@ func TestReconnectRestoresLastPublishedRouteTab(t *testing.T) {
17411742 defer term.in.unblock()
17421743 lifecycle := domain.SessionLifecycleID{7, 8, 9}
17431744 target := protocol.ExactSessionTarget{LifecycleID: lifecycle, SessionName: "work"}
1744- welcome := func(token uint64) ports.Frame {
1745- return frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{
1745+ welcome := func(token uint64) wire.Frame {
1746+ return frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{
17461747 SessionID: "work", SessionName: "work", ResumeToken: token, Capabilities: protocol.CapabilityResume,
17471748 CommittedIdentity: &protocol.CommittedRouteIdentity{Target: target},
17481749 }))
17491750 }
17501751 tr1 := &recordingTransport{recvs: []recvItem{
17511752 {f: welcome(11)},
1752- {f: frameOf(ports.MsgRoutePosition, mustMarshalRoutePosition(protocol.RoutePosition{Target: target, ActiveTabID: "tab-2"}))},
1753+ {f: frameOf(wire.MsgRoutePosition, mustMarshalRoutePosition(protocol.RoutePosition{Target: target, ActiveTabID: "tab-2"}))},
17531754 {err: io.EOF},
17541755 }}
17551756 tr2 := &recordingTransport{recvs: []recvItem{
17561757 {f: welcome(22)},
1757- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
1758+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
17581759 }}
17591760 dialer := &sequenceDialer{trs: []ports.Transport{tr1, tr2}}
17601761 clock := &reconnectTestClock{}
@@ -1780,8 +1781,8 @@ func TestRemoteReconnectDropsStalePickerTargetAfterDaemonSessionSwitch(t *testin
17801781 Endpoint: "remote", DisplayOrigin: "remote", LifecycleID: oldLifecycle,
17811782 SessionName: "old", LiveTabID: "tab-1",
17821783 }
1783- welcome := func(name string, lifecycle domain.SessionLifecycleID, token uint64) ports.Frame {
1784- return frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{
1784+ welcome := func(name string, lifecycle domain.SessionLifecycleID, token uint64) wire.Frame {
1785+ return frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{
17851786 SessionID: name, SessionName: name, ResumeToken: token, Capabilities: protocol.CapabilityResume,
17861787 CommittedIdentity: &protocol.CommittedRouteIdentity{
17871788 Target: protocol.ExactSessionTarget{LifecycleID: lifecycle, SessionName: name},
@@ -1790,14 +1791,14 @@ func TestRemoteReconnectDropsStalePickerTargetAfterDaemonSessionSwitch(t *testin
17901791 }
17911792 tr1 := &recordingTransport{recvs: []recvItem{
17921793 {f: welcome("old", oldLifecycle, 11)},
1793- {f: frameOf(ports.MsgCommittedRouteIdentity, mustMarshalCommittedIdentity(protocol.CommittedRouteIdentity{
1794+ {f: frameOf(wire.MsgCommittedRouteIdentity, mustMarshalCommittedIdentity(protocol.CommittedRouteIdentity{
17941795 Target: protocol.ExactSessionTarget{LifecycleID: newLifecycle, SessionName: "new"},
17951796 }))},
17961797 {err: io.EOF},
17971798 }}
17981799 tr2 := &recordingTransport{recvs: []recvItem{
17991800 {f: welcome("new", newLifecycle, 22)},
1800- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
1801+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
18011802 }}
18021803 dialer := &sequenceDialer{trs: []ports.Transport{tr1, tr2}}
18031804 clock := &reconnectTestClock{}
@@ -1838,8 +1839,8 @@ func TestAttachRememberRemoteHost(t *testing.T) {
18381839 },
18391840 remember: func() error { return nil },
18401841 recv: []recvItem{
1841- {f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1842- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
1842+ {f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1843+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
18431844 },
18441845 wantCalled: true,
18451846 },
@@ -1852,8 +1853,8 @@ func TestAttachRememberRemoteHost(t *testing.T) {
18521853 },
18531854 remember: func() error { return errors.New("disk full") },
18541855 recv: []recvItem{
1855- {f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1856- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
1856+ {f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1857+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
18571858 },
18581859 wantCalled: true,
18591860 },
@@ -1869,8 +1870,8 @@ func TestAttachRememberRemoteHost(t *testing.T) {
18691870 return nil
18701871 },
18711872 recv: []recvItem{
1872- {f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1873- {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
1873+ {f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1874+ {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
18741875 },
18751876 },
18761877 {
@@ -1885,7 +1886,7 @@ func TestAttachRememberRemoteHost(t *testing.T) {
18851886 return nil
18861887 },
18871888 recv: []recvItem{
1888- {f: frameOf(ports.MsgError, ports.MarshalErrorMsg(protocol.ErrorMsg{Code: protocol.ErrVersionMismatch, Text: "version mismatch"}))},
1889+ {f: frameOf(wire.MsgError, wire.MarshalErrorMsg(protocol.ErrorMsg{Code: protocol.ErrVersionMismatch, Text: "version mismatch"}))},
18891890 },
18901891 preWelcomeReject: true,
18911892 wantErr: true,
@@ -1899,7 +1900,7 @@ func TestAttachRememberRemoteHost(t *testing.T) {
18991900 },
19001901 remember: func() error { return nil },
19011902 recv: []recvItem{
1902- {f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
1903+ {f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
19031904 {err: errors.New("connection reset")},
19041905 },
19051906 wantCalled: true,
@@ -1940,9 +1941,9 @@ func TestAttachRememberRemoteHost(t *testing.T) {
19401941
19411942 tr := portsmocks.NewMockTransport(t)
19421943 if !tt.preWelcomeReject {
1943- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
1944+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
19441945 }
1945- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
1946+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
19461947 if tt.preWelcomeReject {
19471948 tr.EXPECT().Recv().Return(tt.recv[0].f, tt.recv[0].err).Once()
19481949 } else {
@@ -1984,7 +1985,7 @@ func TestRunRememberRemoteHostAtMostOnceAcrossReconnects(t *testing.T) {
19841985 learnerDone := make(chan struct{})
19851986
19861987 tr1 := &recordingTransport{recvs: []recvItem{{f: welcomeFrame(11)}, {err: io.EOF}}}
1987- tr2 := &recordingTransport{recvs: []recvItem{{f: welcomeFrame(22)}, {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))}}}
1988+ tr2 := &recordingTransport{recvs: []recvItem{{f: welcomeFrame(22)}, {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))}}}
19881989 d := &sequenceDialer{trs: []ports.Transport{tr1, tr2}}
19891990
19901991 deps := testDependencies(d, term, realClock{}, nil, nil)
@@ -2033,11 +2034,11 @@ func TestAttachRememberRemoteHostDoesNotBlockAfterWelcome(t *testing.T) {
20332034 learnerDone := make(chan struct{})
20342035
20352036 tr := portsmocks.NewMockTransport(t)
2036- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
2037- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
2037+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
2038+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
20382039 unblock := scriptRecv(tr,
2039- recvItem{f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
2040- recvItem{f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
2040+ recvItem{f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
2041+ recvItem{f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
20412042 )
20422043 defer unblock()
20432044 tr.EXPECT().Close().Return(nil).Once()
@@ -2114,11 +2115,11 @@ func TestRunRestoresTerminalBeforeWaitingForLearner(t *testing.T) {
21142115 learnerDone := make(chan struct{})
21152116
21162117 tr := portsmocks.NewMockTransport(t)
2117- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
2118- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
2118+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
2119+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
21192120 unblock := scriptRecv(tr,
2120- recvItem{f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
2121- recvItem{f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
2121+ recvItem{f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
2122+ recvItem{f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
21222123 )
21232124 defer unblock()
21242125 tr.EXPECT().Close().Return(nil).Once()
@@ -2211,11 +2212,11 @@ func TestRunReturnsWhenRemoteHostLearnerStalls(t *testing.T) {
22112212 }).Maybe()
22122213
22132214 tr := portsmocks.NewMockTransport(t)
2214- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
2215- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
2215+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
2216+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
22162217 unblock := scriptRecv(tr,
2217- recvItem{f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
2218- recvItem{f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
2218+ recvItem{f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
2219+ recvItem{f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
22192220 )
22202221 defer unblock()
22212222 tr.EXPECT().Close().Return(nil).Once()
@@ -2267,7 +2268,7 @@ func TestRunReturnsWhenRemoteHostLearnerStalls(t *testing.T) {
22672268 func TestRunDoesNotRetryTerminalDetachedError(t *testing.T) {
22682269 term := newRunTerminal()
22692270 defer term.in.unblock()
2270- tr := &recordingTransport{recvs: []recvItem{{f: welcomeFrame(11)}, {f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonSessionKilled}))}}}
2271+ tr := &recordingTransport{recvs: []recvItem{{f: welcomeFrame(11)}, {f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonSessionKilled}))}}}
22712272 d := &sequenceDialer{trs: []ports.Transport{tr}}
22722273
22732274 err := runTestClient(context.Background(), testDependencies(d, term, realClock{}, nil, nil), client.AttachRequest{Intent: protocol.IntentAttach, SessionName: "main", Remote: false})
M internal/usecase/client/clipboard_intercept_test.go +25 -24
@@ -15,6 +15,7 @@ import (
1515 "github.com/bnema/vev/internal/ports"
1616 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1717 "github.com/bnema/vev/internal/protocol"
18+ "github.com/bnema/vev/internal/protocol/wire"
1819 "github.com/bnema/vev/internal/usecase/client"
1920 )
2021
@@ -44,11 +45,11 @@ func runRemoteClipboardTest(t *testing.T, remote bool, clip ports.ClipboardReade
4445 allowDetach := make(chan struct{})
4546
4647 tr := portsmocks.NewMockTransport(t)
47- tr.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
48- tr.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
49- tr.EXPECT().Send(isType(ports.MsgResize)).Return(nil).Maybe()
50- tr.EXPECT().Send(isType(ports.MsgInput)).RunAndReturn(func(f ports.Frame) error {
51- in, err := ports.UnmarshalInput(f.Payload)
48+ tr.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
49+ tr.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
50+ tr.EXPECT().Send(isType(wire.MsgResize)).Return(nil).Maybe()
51+ tr.EXPECT().Send(isType(wire.MsgInput)).RunAndReturn(func(f wire.Frame) error {
52+ in, err := wire.UnmarshalInput(f.Payload)
5253 require.NoError(t, err)
5354 gotInput <- append([]byte(nil), in.Data...)
5455 if bytes.IndexByte(in.Data, triggerByte) >= 0 {
@@ -56,33 +57,33 @@ func runRemoteClipboardTest(t *testing.T, remote bool, clip ports.ClipboardReade
5657 }
5758 return nil
5859 }).Maybe()
59- tr.EXPECT().Send(isType(ports.MsgImagePush)).RunAndReturn(func(f ports.Frame) error {
60- ip, err := ports.UnmarshalImagePush(f.Payload)
60+ tr.EXPECT().Send(isType(wire.MsgImagePush)).RunAndReturn(func(f wire.Frame) error {
61+ ip, err := wire.UnmarshalImagePush(f.Payload)
6162 require.NoError(t, err)
6263 gotImage <- ip
6364 return nil
6465 }).Maybe()
65- tr.EXPECT().Send(isType(ports.MsgClientNotice)).Return(nil).Maybe()
66+ tr.EXPECT().Send(isType(wire.MsgClientNotice)).Return(nil).Maybe()
6667
67- welcome := frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))
68- detached := frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))
68+ welcome := frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))
69+ detached := frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))
6970 recvCh := make(chan recvItem, 1)
7071 recvCh <- recvItem{f: welcome}
7172 closed := make(chan struct{})
72- tr.EXPECT().Recv().RunAndReturn(func() (ports.Frame, error) {
73+ tr.EXPECT().Recv().RunAndReturn(func() (wire.Frame, error) {
7374 select {
7475 case it := <-recvCh:
7576 return it.f, it.err
7677 case <-allowDetach:
7778 select {
7879 case <-closed:
79- return ports.Frame{}, io.EOF
80+ return wire.Frame{}, io.EOF
8081 default:
8182 close(closed)
8283 return detached, nil
8384 }
8485 case <-closed:
85- return ports.Frame{}, io.EOF
86+ return wire.Frame{}, io.EOF
8687 }
8788 }).Maybe()
8889 tr.EXPECT().Close().Return(nil).Once()
@@ -142,17 +143,17 @@ func TestRunRemoteClipboardCtrlVWithNoImageForwardsCtrlV(t *testing.T) {
142143 }
143144
144145 type clipboardToastLifecycleTransport struct {
145- recv chan ports.Frame
146- sends chan ports.Frame
146+ recv chan wire.Frame
147+ sends chan wire.Frame
147148 }
148149
149-func (t *clipboardToastLifecycleTransport) Send(f ports.Frame) error {
150+func (t *clipboardToastLifecycleTransport) Send(f wire.Frame) error {
150151 t.sends <- f
151152 return nil
152153 }
153154
154-func (t *clipboardToastLifecycleTransport) Recv() (ports.Frame, error) { return <-t.recv, nil }
155-func (*clipboardToastLifecycleTransport) Close() error { return nil }
155+func (t *clipboardToastLifecycleTransport) Recv() (wire.Frame, error) { return <-t.recv, nil }
156+func (*clipboardToastLifecycleTransport) Close() error { return nil }
156157
157158 type clipboardToastLifecycleDialer struct{ transport ports.Transport }
158159
@@ -175,8 +176,8 @@ func TestRunRemoteClipboardFailureNotifiesDaemonAndWritesOutputVerbatim(t *testi
175176
176177 clipboard := portsmocks.NewMockClipboardReader(t)
177178 clipboard.EXPECT().ReadImage(mock.Anything).Return("", nil, errors.New("read failed")).Once()
178- transport := &clipboardToastLifecycleTransport{recv: make(chan ports.Frame, 8), sends: make(chan ports.Frame, 16)}
179- transport.recv <- frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))
179+ transport := &clipboardToastLifecycleTransport{recv: make(chan wire.Frame, 8), sends: make(chan wire.Frame, 16)}
180+ transport.recv <- frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))
180181
181182 result := make(chan error, 1)
182183 go func() {
@@ -187,10 +188,10 @@ func TestRunRemoteClipboardFailureNotifiesDaemonAndWritesOutputVerbatim(t *testi
187188 for !gotNotice {
188189 select {
189190 case sent := <-transport.sends:
190- if sent.Type != ports.MsgClientNotice {
191+ if sent.Type != wire.MsgClientNotice {
191192 continue
192193 }
193- notice, err := ports.UnmarshalClientNotice(sent.Payload)
194+ notice, err := wire.UnmarshalClientNotice(sent.Payload)
194195 require.NoError(t, err)
195196 require.Equal(t, protocol.ClientNoticeClipboardFallback, notice.Action)
196197 gotNotice = true
@@ -199,8 +200,8 @@ func TestRunRemoteClipboardFailureNotifiesDaemonAndWritesOutputVerbatim(t *testi
199200 }
200201 }
201202 beforeOutput := out.String()
202- transport.recv <- frameOf(ports.MsgOutput, mustMarshalOutput(protocol.Output{Epoch: 1, Base: 0, New: 1, Size: domain.Size{Cols: 1, Rows: 1}, Full: true, Data: []byte("incremental")}))
203- transport.recv <- frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))
203+ transport.recv <- frameOf(wire.MsgOutput, mustMarshalOutput(protocol.Output{Epoch: 1, Base: 0, New: 1, Size: domain.Size{Cols: 1, Rows: 1}, Full: true, Data: []byte("incremental")}))
204+ transport.recv <- frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))
204205
205206 select {
206207 case err := <-result:
M internal/usecase/client/palette_attach_generation_test.go +14 -13
@@ -17,6 +17,7 @@ import (
1717 "github.com/bnema/vev/internal/domain"
1818 "github.com/bnema/vev/internal/ports"
1919 "github.com/bnema/vev/internal/protocol"
20+ "github.com/bnema/vev/internal/protocol/wire"
2021 )
2122
2223 // attachPaletteClock deliberately exposes each independently-created timer.
@@ -115,11 +116,11 @@ func (*attachPaletteTerminalHarness) Flush() error { r
115116
116117 type attachPaletteTransport struct {
117118 mu sync.Mutex
118- frames []ports.Frame
119+ frames []wire.Frame
119120 themes []protocol.Theme
120121 themeCh chan protocol.Theme
121122 inputCh chan protocol.Input
122- detached chan ports.Frame
123+ detached chan wire.Frame
123124 themeSendErr error
124125 welcomed bool
125126 }
@@ -128,20 +129,20 @@ func newAttachPaletteTransport() *attachPaletteTransport {
128129 return &attachPaletteTransport{
129130 themeCh: make(chan protocol.Theme, 16),
130131 inputCh: make(chan protocol.Input, 16),
131- detached: make(chan ports.Frame, 1),
132+ detached: make(chan wire.Frame, 1),
132133 }
133134 }
134-func (t *attachPaletteTransport) Send(frame ports.Frame) error {
135+func (t *attachPaletteTransport) Send(frame wire.Frame) error {
135136 t.mu.Lock()
136137 t.frames = append(t.frames, frame)
137138 themeSendErr := t.themeSendErr
138139 t.mu.Unlock()
139140 switch frame.Type {
140- case ports.MsgTheme:
141+ case wire.MsgTheme:
141142 if themeSendErr != nil {
142143 return themeSendErr
143144 }
144- got, err := ports.UnmarshalTheme(frame.Payload)
145+ got, err := wire.UnmarshalTheme(frame.Payload)
145146 if err != nil {
146147 return err
147148 }
@@ -149,8 +150,8 @@ func (t *attachPaletteTransport) Send(frame ports.Frame) error {
149150 t.themes = append(t.themes, got)
150151 t.mu.Unlock()
151152 t.themeCh <- got
152- case ports.MsgInput:
153- got, err := ports.UnmarshalInput(frame.Payload)
153+ case wire.MsgInput:
154+ got, err := wire.UnmarshalInput(frame.Payload)
154155 if err != nil {
155156 return err
156157 }
@@ -158,12 +159,12 @@ func (t *attachPaletteTransport) Send(frame ports.Frame) error {
158159 }
159160 return nil
160161 }
161-func (t *attachPaletteTransport) Recv() (ports.Frame, error) {
162+func (t *attachPaletteTransport) Recv() (wire.Frame, error) {
162163 t.mu.Lock()
163164 if !t.welcomed {
164165 t.welcomed = true
165166 t.mu.Unlock()
166- return ports.Frame{Type: ports.MsgWelcome, Payload: ports.MarshalWelcome(protocol.Welcome{SessionID: "s"})}, nil
167+ return wire.Frame{Type: wire.MsgWelcome, Payload: wire.MarshalWelcome(protocol.Welcome{SessionID: "s"})}, nil
167168 }
168169 t.mu.Unlock()
169170 return <-t.detached, nil
@@ -239,7 +240,7 @@ func (h *attachPaletteHarness) nextTimer(t *testing.T) *attachPaletteTimer {
239240 }
240241 func (h *attachPaletteHarness) detach(t *testing.T) {
241242 t.Helper()
242- h.transport.detached <- ports.Frame{Type: ports.MsgDetached, Payload: ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach})}
243+ h.transport.detached <- wire.Frame{Type: wire.MsgDetached, Payload: wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach})}
243244 result := <-h.done
244245 require.NoError(t, result.err)
245246 select {
@@ -325,7 +326,7 @@ func TestAttachReconnectPreservesStandaloneEscapeBeforeClaimRevocation(t *testin
325326
326327 // Detach starts reconnect teardown. The residual must already be owned by
327328 // the lifecycle reader at the precise point the old claim is revoked.
328- first.transport.detached <- ports.Frame{Type: ports.MsgDetached, Payload: ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach})}
329+ first.transport.detached <- wire.Frame{Type: wire.MsgDetached, Payload: wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach})}
329330 require.Equal(t, []byte("\x1b"), <-revoked)
330331 release()
331332 require.NoError(t, (<-first.done).err)
@@ -437,7 +438,7 @@ func TestStdinPumpSameReadSchemeAndOldMarkerCannotAdvanceReplacement(t *testing.
437438 // notification and publish its replacement generation before the scanner
438439 // can deliver the old marker from the same terminal Read.
439440 events := make(chan paletteGenerationEvent)
440- out := make(chan ports.Frame, 1)
441+ out := make(chan wire.Frame, 1)
441442 schemeHandled := make(chan struct{})
442443 pump := &stdinPump{
443444 ctx: ctx,
M internal/usecase/client/palette_attach_test.go +9 -8
@@ -15,6 +15,7 @@ import (
1515 "github.com/bnema/vev/internal/domain"
1616 "github.com/bnema/vev/internal/ports"
1717 "github.com/bnema/vev/internal/protocol"
18+ "github.com/bnema/vev/internal/protocol/wire"
1819 )
1920
2021 type paletteAttachClock struct{}
@@ -78,18 +79,18 @@ func (t *paletteAttachTerminal) Flush() error { return n
7879
7980 type paletteAttachTransport struct {
8081 mu sync.Mutex
81- sent []ports.Frame
82+ sent []wire.Frame
8283 themes []protocol.Theme
8384 finals int
8485 finalSet chan struct{}
8586 }
8687
87-func (t *paletteAttachTransport) Send(frame ports.Frame) error {
88+func (t *paletteAttachTransport) Send(frame wire.Frame) error {
8889 t.mu.Lock()
8990 defer t.mu.Unlock()
9091 t.sent = append(t.sent, frame)
91- if frame.Type == ports.MsgTheme {
92- theme, err := ports.UnmarshalTheme(frame.Payload)
92+ if frame.Type == wire.MsgTheme {
93+ theme, err := wire.UnmarshalTheme(frame.Payload)
9394 if err != nil {
9495 return err
9596 }
@@ -104,15 +105,15 @@ func (t *paletteAttachTransport) Send(frame ports.Frame) error {
104105 return nil
105106 }
106107
107-func (t *paletteAttachTransport) Recv() (ports.Frame, error) {
108+func (t *paletteAttachTransport) Recv() (wire.Frame, error) {
108109 t.mu.Lock()
109110 first := len(t.sent) == 1
110111 t.mu.Unlock()
111112 if first {
112- return ports.Frame{Type: ports.MsgWelcome, Payload: ports.MarshalWelcome(protocol.Welcome{SessionID: "s"})}, nil
113+ return wire.Frame{Type: wire.MsgWelcome, Payload: wire.MarshalWelcome(protocol.Welcome{SessionID: "s"})}, nil
113114 }
114115 <-t.finalSet
115- return ports.Frame{Type: ports.MsgDetached, Payload: ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach})}, nil
116+ return wire.Frame{Type: wire.MsgDetached, Payload: wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach})}, nil
116117 }
117118 func (*paletteAttachTransport) Close() error { return nil }
118119
@@ -155,7 +156,7 @@ func TestAttachPublishesOnlyClearedAndDefinitiveInitialPalette(t *testing.T) {
155156 transport.mu.Lock()
156157 defer transport.mu.Unlock()
157158 require.Len(t, transport.sent, 3)
158- require.Equal(t, []ports.MsgType{ports.MsgHello, ports.MsgTheme, ports.MsgTheme}, []ports.MsgType{
159+ require.Equal(t, []wire.MsgType{wire.MsgHello, wire.MsgTheme, wire.MsgTheme}, []wire.MsgType{
159160 transport.sent[0].Type, transport.sent[1].Type, transport.sent[2].Type,
160161 }, "the protocol requires Hello before the two palette publications")
161162 require.Len(t, transport.themes, 2)
M internal/usecase/client/reconnect_toast_test.go +61 -60
@@ -21,6 +21,7 @@ import (
2121 "github.com/bnema/vev/internal/ports"
2222 portsmocks "github.com/bnema/vev/internal/ports/mocks"
2323 "github.com/bnema/vev/internal/protocol"
24+ "github.com/bnema/vev/internal/protocol/wire"
2425 )
2526
2627 type reconnectToastTerminalHarness struct {
@@ -129,20 +130,20 @@ func (r *reconnectToastOutputRecorder) String() string {
129130 return r.all.String()
130131 }
131132
132-func reconnectToastWelcome(token uint64) ports.Frame {
133+func reconnectToastWelcome(token uint64) wire.Frame {
133134 return reconnectToastWelcomeNamed("", token)
134135 }
135136
136-func reconnectToastWelcomeNamed(name string, token uint64) ports.Frame {
137- return ports.Frame{Type: ports.MsgWelcome, Payload: ports.MarshalWelcome(protocol.Welcome{SessionID: "s1", SessionName: name, ResumeToken: token, Capabilities: protocol.CapabilityResume})}
137+func reconnectToastWelcomeNamed(name string, token uint64) wire.Frame {
138+ return wire.Frame{Type: wire.MsgWelcome, Payload: wire.MarshalWelcome(protocol.Welcome{SessionID: "s1", SessionName: name, ResumeToken: token, Capabilities: protocol.CapabilityResume})}
138139 }
139140
140-func reconnectToastDetach(reason uint8) ports.Frame {
141- return ports.Frame{Type: ports.MsgDetached, Payload: ports.MarshalDetached(protocol.Detached{Reason: reason})}
141+func reconnectToastDetach(reason uint8) wire.Frame {
142+ return wire.Frame{Type: wire.MsgDetached, Payload: wire.MarshalDetached(protocol.Detached{Reason: reason})}
142143 }
143144
144145 type reconnectToastRecv struct {
145- frame ports.Frame
146+ frame wire.Frame
146147 err error
147148 }
148149
@@ -163,7 +164,7 @@ func (d *reconnectToastSequenceDialer) Dial(context.Context) (ports.Transport, e
163164 type reconnectToastRecordingTransport struct {
164165 mu sync.Mutex
165166 recvs []reconnectToastRecv
166- sends []ports.Frame
167+ sends []wire.Frame
167168 closed bool
168169 }
169170
@@ -173,7 +174,7 @@ func newReconnectToastRecordingTransport(recvs ...reconnectToastRecv) *reconnect
173174
174175 type reconnectToastSentFrames struct {
175176 mu sync.Mutex
176- frames []ports.Frame
177+ frames []wire.Frame
177178 changed chan struct{}
178179 }
179180
@@ -181,7 +182,7 @@ func newReconnectToastSentFrames() *reconnectToastSentFrames {
181182 return &reconnectToastSentFrames{changed: make(chan struct{}, 1)}
182183 }
183184
184-func (s *reconnectToastSentFrames) record(frame ports.Frame) {
185+func (s *reconnectToastSentFrames) record(frame wire.Frame) {
185186 s.mu.Lock()
186187 s.frames = append(s.frames, frame)
187188 s.mu.Unlock()
@@ -191,7 +192,7 @@ func (s *reconnectToastSentFrames) record(frame ports.Frame) {
191192 }
192193 }
193194
194-func (s *reconnectToastSentFrames) find(match func(ports.Frame) bool) (ports.Frame, bool) {
195+func (s *reconnectToastSentFrames) find(match func(wire.Frame) bool) (wire.Frame, bool) {
195196 s.mu.Lock()
196197 defer s.mu.Unlock()
197198 for _, frame := range s.frames {
@@ -199,7 +200,7 @@ func (s *reconnectToastSentFrames) find(match func(ports.Frame) bool) (ports.Fra
199200 return frame, true
200201 }
201202 }
202- return ports.Frame{}, false
203+ return wire.Frame{}, false
203204 }
204205
205206 type reconnectToastLinkTransport struct {
@@ -220,12 +221,12 @@ func newReconnectToastLinkTransport() *reconnectToastLinkTransport {
220221 }
221222 }
222223
223-func (t *reconnectToastLinkTransport) Send(f ports.Frame) error {
224+func (t *reconnectToastLinkTransport) Send(f wire.Frame) error {
224225 t.sends.record(f)
225- if f.Type != ports.MsgClientNotice {
226+ if f.Type != wire.MsgClientNotice {
226227 return nil
227228 }
228- notice, err := ports.UnmarshalClientNotice(f.Payload)
229+ notice, err := wire.UnmarshalClientNotice(f.Payload)
229230 if err != nil {
230231 return err
231232 }
@@ -233,7 +234,7 @@ func (t *reconnectToastLinkTransport) Send(f ports.Frame) error {
233234 return nil
234235 }
235236
236-func (t *reconnectToastLinkTransport) Recv() (ports.Frame, error) {
237+func (t *reconnectToastLinkTransport) Recv() (wire.Frame, error) {
237238 recv := <-t.recvCh
238239 return recv.frame, recv.err
239240 }
@@ -244,18 +245,18 @@ func (t *reconnectToastLinkTransport) LinkEvents() <-chan ports.LinkEvent { retu
244245
245246 func (t *reconnectToastLinkTransport) LinkState() ports.LinkState { return t.state }
246247
247-func (t *reconnectToastRecordingTransport) Send(f ports.Frame) error {
248+func (t *reconnectToastRecordingTransport) Send(f wire.Frame) error {
248249 t.mu.Lock()
249250 defer t.mu.Unlock()
250251 t.sends = append(t.sends, f)
251252 return nil
252253 }
253254
254-func (t *reconnectToastRecordingTransport) Recv() (ports.Frame, error) {
255+func (t *reconnectToastRecordingTransport) Recv() (wire.Frame, error) {
255256 t.mu.Lock()
256257 defer t.mu.Unlock()
257258 if len(t.recvs) == 0 {
258- return ports.Frame{}, io.EOF
259+ return wire.Frame{}, io.EOF
259260 }
260261 recv := t.recvs[0]
261262 t.recvs = t.recvs[1:]
@@ -269,10 +270,10 @@ func (t *reconnectToastRecordingTransport) Close() error {
269270 return nil
270271 }
271272
272-func (t *reconnectToastRecordingTransport) sentFrames() []ports.Frame {
273+func (t *reconnectToastRecordingTransport) sentFrames() []wire.Frame {
273274 t.mu.Lock()
274275 defer t.mu.Unlock()
275- return append([]ports.Frame(nil), t.sends...)
276+ return append([]wire.Frame(nil), t.sends...)
276277 }
277278
278279 func (t *reconnectToastRecordingTransport) wasClosed() bool {
@@ -288,9 +289,9 @@ func assertReconnectToastAttemptPublishesOnlyClearedTheme(t *testing.T, tr *reco
288289 t.Helper()
289290 frames := tr.sentFrames()
290291 require.GreaterOrEqual(t, len(frames), 2)
291- require.Equal(t, ports.MsgHello, frames[0].Type)
292- require.Equal(t, ports.MsgTheme, frames[1].Type)
293- theme, err := ports.UnmarshalTheme(frames[1].Payload)
292+ require.Equal(t, wire.MsgHello, frames[0].Type)
293+ require.Equal(t, wire.MsgTheme, frames[1].Type)
294+ theme, err := wire.UnmarshalTheme(frames[1].Payload)
294295 require.NoError(t, err)
295296 require.False(t, theme.HasForeground)
296297 require.False(t, theme.HasBackground)
@@ -302,8 +303,8 @@ func reconnectToastHelloFromSend(t *testing.T, tr *reconnectToastRecordingTransp
302303 t.Helper()
303304 frames := tr.sentFrames()
304305 require.NotEmpty(t, frames)
305- require.Equal(t, ports.MsgHello, frames[0].Type)
306- hello, err := ports.UnmarshalHello(frames[0].Payload)
306+ require.Equal(t, wire.MsgHello, frames[0].Type)
307+ hello, err := wire.UnmarshalHello(frames[0].Payload)
307308 require.NoError(t, err)
308309 return hello
309310 }
@@ -394,7 +395,7 @@ func TestProbingToastReconcilesDaemonOutputAndDismissal(t *testing.T) {
394395 require.Contains(t, firstToast, "┌")
395396 require.Contains(t, firstToast, "probing UDP path")
396397
397- tr.recvCh <- reconnectToastRecv{frame: ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
398+ tr.recvCh <- reconnectToastRecv{frame: wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
398399 Epoch: 1,
399400 Base: 0,
400401 New: 2,
@@ -413,7 +414,7 @@ func TestProbingToastReconcilesDaemonOutputAndDismissal(t *testing.T) {
413414
414415 beforeAwaitingReset := out.String()
415416 beforeStatelessFlushes := flushes.Load()
416- tr.recvCh <- reconnectToastRecv{frame: ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
417+ tr.recvCh <- reconnectToastRecv{frame: wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
417418 Epoch: 1,
418419 Base: 0,
419420 New: 0,
@@ -425,7 +426,7 @@ func TestProbingToastReconcilesDaemonOutputAndDismissal(t *testing.T) {
425426 }, time.Second, time.Millisecond)
426427
427428 afterStateless := out.String()
428- tr.recvCh <- reconnectToastRecv{frame: ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
429+ tr.recvCh <- reconnectToastRecv{frame: wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
429430 Epoch: 1,
430431 Base: 2,
431432 New: 3,
@@ -436,7 +437,7 @@ func TestProbingToastReconcilesDaemonOutputAndDismissal(t *testing.T) {
436437 require.Contains(t, out.String(), "intervening incremental")
437438 require.Contains(t, afterStateless[len(beforeAwaitingReset):], "stateless side effect")
438439
439- tr.recvCh <- reconnectToastRecv{frame: ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
440+ tr.recvCh <- reconnectToastRecv{frame: wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
440441 Epoch: 2,
441442 Base: 0,
442443 New: 4,
@@ -449,7 +450,7 @@ func TestProbingToastReconcilesDaemonOutputAndDismissal(t *testing.T) {
449450 require.NotContains(t, out.String()[len(beforeAwaitingReset):], strings.Repeat(" ", reconnectToastBoundsFor(term.size, "probing UDP path").Width))
450451
451452 beforeIncrementFlushes := flushes.Load()
452- tr.recvCh <- reconnectToastRecv{frame: ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
453+ tr.recvCh <- reconnectToastRecv{frame: wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
453454 Epoch: 2,
454455 Base: 4,
455456 New: 5,
@@ -496,30 +497,30 @@ func TestActiveReconnectToastStageTransitionReconcilesBeforeRedraw(t *testing.T)
496497 {Epoch: 1, Base: 1, New: 2, Size: domain.Size{Cols: 1, Rows: 1}, Data: []byte("first skipped increment")},
497498 {Epoch: 1, Base: 2, New: 3, Size: domain.Size{Cols: 1, Rows: 1}, Data: []byte("second skipped increment")},
498499 } {
499- tr.recvCh <- reconnectToastRecv{frame: ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(output)}}
500+ tr.recvCh <- reconnectToastRecv{frame: wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(output)}}
500501 require.Equal(t, beforeReset, out.String())
501502 }
502- requireSentFrame(t, tr.sends, "output reset request", func(frame ports.Frame) bool {
503- return frame.Type == ports.MsgOutputResetRequest
503+ requireSentFrame(t, tr.sends, "output reset request", func(frame wire.Frame) bool {
504+ return frame.Type == wire.MsgOutputResetRequest
504505 })
505- _, sentAck := tr.sends.find(func(frame ports.Frame) bool { return frame.Type == ports.MsgAck })
506+ _, sentAck := tr.sends.find(func(frame wire.Frame) bool { return frame.Type == wire.MsgAck })
506507 require.False(t, sentAck, "discarded output must not be ACKed")
507508
508509 // Handoff cleanup is an ordered terminal side effect, not a replay state.
509510 // It must cross the outstanding reset gate, flush before the control handoff,
510511 // and must not manufacture an independent ACK.
511- tr.recvCh <- reconnectToastRecv{frame: ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
512+ tr.recvCh <- reconnectToastRecv{frame: wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
512513 Epoch: 2,
513514 Size: domain.Size{Cols: 1, Rows: 1},
514515 Data: []byte("handoff graphics cleanup"),
515516 })}}
516517 cleanup := requireReconnectToastOutput(t, out.completed)
517518 require.Contains(t, cleanup, "handoff graphics cleanup")
518- _, sentAck = tr.sends.find(func(frame ports.Frame) bool { return frame.Type == ports.MsgAck })
519+ _, sentAck = tr.sends.find(func(frame wire.Frame) bool { return frame.Type == wire.MsgAck })
519520 require.False(t, sentAck, "state-independent cleanup must not be ACKed")
520521
521522 beforeResetFlushes := flushes.Load()
522- tr.recvCh <- reconnectToastRecv{frame: ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
523+ tr.recvCh <- reconnectToastRecv{frame: wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
523524 Epoch: 2,
524525 Base: 0,
525526 New: 4,
@@ -534,7 +535,7 @@ func TestActiveReconnectToastStageTransitionReconcilesBeforeRedraw(t *testing.T)
534535 requireAckedState(t, tr.sends, 2, 4)
535536
536537 beforeIncrementFlushes := flushes.Load()
537- tr.recvCh <- reconnectToastRecv{frame: ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
538+ tr.recvCh <- reconnectToastRecv{frame: wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
538539 Epoch: 2,
539540 Base: 4,
540541 New: 5,
@@ -563,7 +564,7 @@ func requireReconnectToastOutput(t *testing.T, completed <-chan string) string {
563564 }
564565 }
565566
566-func requireSentFrame(t *testing.T, sends *reconnectToastSentFrames, description string, match func(ports.Frame) bool) ports.Frame {
567+func requireSentFrame(t *testing.T, sends *reconnectToastSentFrames, description string, match func(wire.Frame) bool) wire.Frame {
567568 t.Helper()
568569 timer := time.NewTimer(time.Second)
569570 defer timer.Stop()
@@ -575,31 +576,31 @@ func requireSentFrame(t *testing.T, sends *reconnectToastSentFrames, description
575576 case <-sends.changed:
576577 case <-timer.C:
577578 t.Fatalf("timed out waiting for %s", description)
578- return ports.Frame{}
579+ return wire.Frame{}
579580 }
580581 }
581582 }
582583
583584 func requireResize(t *testing.T, sends *reconnectToastSentFrames) protocol.Resize {
584585 t.Helper()
585- frame := requireSentFrame(t, sends, "Resize frame", func(frame ports.Frame) bool {
586- return frame.Type == ports.MsgResize
586+ frame := requireSentFrame(t, sends, "Resize frame", func(frame wire.Frame) bool {
587+ return frame.Type == wire.MsgResize
587588 })
588- resize, err := ports.UnmarshalResize(frame.Payload)
589+ resize, err := wire.UnmarshalResize(frame.Payload)
589590 require.NoError(t, err)
590591 return resize
591592 }
592593
593594 func requireAckedState(t *testing.T, sends *reconnectToastSentFrames, epoch, state uint64) {
594595 t.Helper()
595- frame := requireSentFrame(t, sends, fmt.Sprintf("ACK for epoch %d state %d", epoch, state), func(frame ports.Frame) bool {
596- if frame.Type != ports.MsgAck {
596+ frame := requireSentFrame(t, sends, fmt.Sprintf("ACK for epoch %d state %d", epoch, state), func(frame wire.Frame) bool {
597+ if frame.Type != wire.MsgAck {
597598 return false
598599 }
599- ack, err := ports.UnmarshalAck(frame.Payload)
600+ ack, err := wire.UnmarshalAck(frame.Payload)
600601 return err == nil && ack.Epoch == epoch && ack.State >= state
601602 })
602- ack, err := ports.UnmarshalAck(frame.Payload)
603+ ack, err := wire.UnmarshalAck(frame.Payload)
603604 require.NoError(t, err)
604605 require.Equal(t, epoch, ack.Epoch)
605606 require.GreaterOrEqual(t, ack.State, state)
@@ -707,7 +708,7 @@ func TestLocalReconnectStatusFlushesDaemonOutputBeforeObservationAndAck(t *testi
707708 require.Eventually(t, func() bool { return flushes.Load() > beforeStatus }, time.Second, time.Millisecond)
708709 beforeOutput := flushes.Load()
709710
710- tr.recvCh <- reconnectToastRecv{frame: ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
711+ tr.recvCh <- reconnectToastRecv{frame: wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
711712 Epoch: 1,
712713 Base: 0,
713714 New: 2,
@@ -759,7 +760,7 @@ func TestLocalReconnectStatusTransitionsDoNotRequestAuthoritativeReset(t *testin
759760 }
760761
761762 const output = "local status output remains visible"
762- tr.recvCh <- reconnectToastRecv{frame: ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
763+ tr.recvCh <- reconnectToastRecv{frame: wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
763764 Epoch: 1,
764765 Base: 0,
765766 New: 2,
@@ -769,7 +770,7 @@ func TestLocalReconnectStatusTransitionsDoNotRequestAuthoritativeReset(t *testin
769770 })}}
770771 require.Eventually(t, func() bool { return strings.Contains(out.String(), output) }, time.Second, time.Millisecond)
771772 requireAckedState(t, tr.sends, 1, 2)
772- _, sentResize := tr.sends.find(func(frame ports.Frame) bool { return frame.Type == ports.MsgResize })
773+ _, sentResize := tr.sends.find(func(frame wire.Frame) bool { return frame.Type == wire.MsgResize })
773774 require.False(t, sentResize, "local reconnect status must not request a daemon reset")
774775
775776 tr.recvCh <- reconnectToastRecv{frame: reconnectToastDetach(protocol.ReasonDetach)}
@@ -827,7 +828,7 @@ func TestReconnectOverlayRedrawFailureDoesNotObserveOrAckOutput(t *testing.T) {
827828 tr.events <- ports.LinkEvent{State: ports.LinkStateProbing}
828829 requireReconnectToastOutput(t, out.completed)
829830 arm()
830- tr.recvCh <- reconnectToastRecv{frame: ports.Frame{Type: ports.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
831+ tr.recvCh <- reconnectToastRecv{frame: wire.Frame{Type: wire.MsgOutput, Payload: mustMarshalOutput(protocol.Output{
831832 Epoch: 1,
832833 Base: 0,
833834 New: 2,
@@ -932,8 +933,8 @@ func newReconnectResetBlockingTransport() *reconnectResetBlockingTransport {
932933 }
933934 }
934935
935-func (t *reconnectResetBlockingTransport) Send(frame ports.Frame) error {
936- if frame.Type != ports.MsgResize {
936+func (t *reconnectResetBlockingTransport) Send(frame wire.Frame) error {
937+ if frame.Type != wire.MsgResize {
937938 return nil
938939 }
939940 t.enterOnce.Do(func() { close(t.entered) })
@@ -941,11 +942,11 @@ func (t *reconnectResetBlockingTransport) Send(frame ports.Frame) error {
941942 return io.ErrClosedPipe
942943 }
943944
944-func (t *reconnectResetBlockingTransport) Recv() (ports.Frame, error) {
945+func (t *reconnectResetBlockingTransport) Recv() (wire.Frame, error) {
945946 t.mu.Lock()
946947 defer t.mu.Unlock()
947948 if len(t.recvs) == 0 {
948- return ports.Frame{}, io.EOF
949+ return wire.Frame{}, io.EOF
949950 }
950951 recv := t.recvs[0]
951952 t.recvs = t.recvs[1:]
@@ -1014,7 +1015,7 @@ func TestPreSenderReconnectResetIsBounded(t *testing.T) {
10141015 type reconnectToastBlockingSendTransport struct {
10151016 *reconnectToastLinkTransport
10161017 armed atomic.Bool
1017- blockType ports.MsgType
1018+ blockType wire.MsgType
10181019 sendErr error
10191020 entered chan struct{}
10201021 release chan struct{}
@@ -1025,13 +1026,13 @@ type reconnectToastBlockingSendTransport struct {
10251026 func newReconnectToastBlockingSendTransport() *reconnectToastBlockingSendTransport {
10261027 return &reconnectToastBlockingSendTransport{
10271028 reconnectToastLinkTransport: newReconnectToastLinkTransport(),
1028- blockType: ports.MsgClientNotice,
1029+ blockType: wire.MsgClientNotice,
10291030 entered: make(chan struct{}),
10301031 release: make(chan struct{}),
10311032 }
10321033 }
10331034
1034-func (t *reconnectToastBlockingSendTransport) Send(frame ports.Frame) error {
1035+func (t *reconnectToastBlockingSendTransport) Send(frame wire.Frame) error {
10351036 if err := t.reconnectToastLinkTransport.Send(frame); err != nil {
10361037 return err
10371038 }
@@ -1083,7 +1084,7 @@ func TestReconnectResetEnqueueCancellationExitsCleanly(t *testing.T) {
10831084
10841085 tr.events <- ports.LinkEvent{State: ports.LinkStateProbing}
10851086 requireTerminalSizeCalls(t, term, 2)
1086- tr.blockType = ports.MsgResize
1087+ tr.blockType = wire.MsgResize
10871088 tr.armed.Store(true)
10881089 term.resizeCh <- domain.Geometry{Size: term.size}
10891090 select {
@@ -1123,7 +1124,7 @@ func TestReconnectResetCancellationPreservesQueuedSenderError(t *testing.T) {
11231124
11241125 tr.events <- ports.LinkEvent{State: ports.LinkStateProbing}
11251126 requireTerminalSizeCalls(t, term, 2)
1126- tr.blockType = ports.MsgResize
1127+ tr.blockType = wire.MsgResize
11271128 tr.armed.Store(true)
11281129 term.resizeCh <- domain.Geometry{Size: term.size}
11291130 select {
M internal/usecase/client/same_peer_switch_test.go +5 -5
@@ -8,8 +8,8 @@ import (
88 "github.com/stretchr/testify/require"
99
1010 "github.com/bnema/vev/internal/domain"
11- "github.com/bnema/vev/internal/ports"
1211 "github.com/bnema/vev/internal/protocol"
12+ "github.com/bnema/vev/internal/protocol/wire"
1313 )
1414
1515 func TestCloseAndDialAttachTargetSkipsSamePeerSwitch(t *testing.T) {
@@ -39,8 +39,8 @@ func TestCloseAndDialAttachTargetSkipsSamePeerSwitch(t *testing.T) {
3939 Session: "stopped", Intent: protocol.IntentAttach, ExactTarget: &exact,
4040 EnvironmentPolicy: protocol.EnvironmentPolicyDaemonOwned,
4141 }
42- transport.recvCh <- reconnectToastRecv{frame: ports.Frame{
43- Type: ports.MsgAttachTarget, Payload: ports.MarshalAttachTarget(target),
42+ transport.recvCh <- reconnectToastRecv{frame: wire.Frame{
43+ Type: wire.MsgAttachTarget, Payload: wire.MarshalAttachTarget(target),
4444 }}
4545
4646 select {
@@ -50,8 +50,8 @@ func TestCloseAndDialAttachTargetSkipsSamePeerSwitch(t *testing.T) {
5050 case <-time.After(time.Second):
5151 t.Fatal("close-and-dial target did not return a handoff")
5252 }
53- _, sentSamePeer := transport.sends.find(func(frame ports.Frame) bool {
54- return frame.Type == ports.MsgSamePeerSwitchRequest
53+ _, sentSamePeer := transport.sends.find(func(frame wire.Frame) bool {
54+ return frame.Type == wire.MsgSamePeerSwitchRequest
5555 })
5656 require.False(t, sentSamePeer)
5757 }
M internal/usecase/client/terminal_input_pump_test.go +23 -22
@@ -13,6 +13,7 @@ import (
1313
1414 "github.com/bnema/vev/internal/domain"
1515 "github.com/bnema/vev/internal/ports"
16+ "github.com/bnema/vev/internal/protocol/wire"
1617 )
1718
1819 type lifecycleReader struct {
@@ -81,14 +82,14 @@ func requireSignal(t *testing.T, ch <-chan struct{}, message string) {
8182 }
8283 }
8384
84-func requireFrame(t *testing.T, ch <-chan ports.Frame, message string) ports.Frame {
85+func requireFrame(t *testing.T, ch <-chan wire.Frame, message string) wire.Frame {
8586 t.Helper()
8687 select {
8788 case frame := <-ch:
8889 return frame
8990 case <-time.After(time.Second):
9091 t.Fatal(message)
91- return ports.Frame{}
92+ return wire.Frame{}
9293 }
9394 }
9495
@@ -181,7 +182,7 @@ func TestTerminalInputPumpCancellationHandoffPreservesQueuedBytes(t *testing.T)
181182 go func() {
182183 (&stdinPump{
183184 ctx: cancelledCtx, cancel: cancelCancelled, input: input,
184- out: make(chan ports.Frame, 1), clock: newAttachPaletteClock(),
185+ out: make(chan wire.Frame, 1), clock: newAttachPaletteClock(),
185186 logger: slog.New(slog.DiscardHandler), paletteEvents: make(chan paletteGenerationEvent),
186187 activeGeneration: &activeGeneration,
187188 }).run()
@@ -191,7 +192,7 @@ func TestTerminalInputPumpCancellationHandoffPreservesQueuedBytes(t *testing.T)
191192
192193 replacementCtx, cancelReplacement := context.WithCancel(context.Background())
193194 defer cancelReplacement()
194- out := make(chan ports.Frame, len(want))
195+ out := make(chan wire.Frame, len(want))
195196 replacementDone := make(chan struct{})
196197 go func() {
197198 (&stdinPump{
@@ -206,7 +207,7 @@ func TestTerminalInputPumpCancellationHandoffPreservesQueuedBytes(t *testing.T)
206207 var got []byte
207208 for len(got) < len(want) {
208209 frame := <-out
209- input, err := ports.UnmarshalInput(frame.Payload)
210+ input, err := wire.UnmarshalInput(frame.Payload)
210211 require.NoError(t, err)
211212 got = append(got, input.Data...)
212213 }
@@ -229,7 +230,7 @@ func TestTerminalInputPumpCancellationAfterTakeRequeuesRawRead(t *testing.T) {
229230 go func() {
230231 (&stdinPump{
231232 ctx: ctx, cancel: cancel, input: input, consumer: consumer,
232- out: make(chan ports.Frame, 1), clock: newAttachPaletteClock(),
233+ out: make(chan wire.Frame, 1), clock: newAttachPaletteClock(),
233234 logger: slog.New(slog.DiscardHandler), paletteEvents: make(chan paletteGenerationEvent),
234235 afterInputTake: func() {
235236 close(taken)
@@ -263,7 +264,7 @@ func TestTerminalInputPumpCancellationPreservesStandaloneEscapeForReplacement(t
263264 oldDone := make(chan struct{})
264265 go func() {
265266 (&stdinPump{
266- ctx: oldCtx, cancel: cancelOld, input: input, out: make(chan ports.Frame, 1),
267+ ctx: oldCtx, cancel: cancelOld, input: input, out: make(chan wire.Frame, 1),
267268 clock: oldClock, logger: slog.New(slog.DiscardHandler), paletteEvents: make(chan paletteGenerationEvent),
268269 activeGeneration: &activeGeneration,
269270 }).run()
@@ -278,7 +279,7 @@ func TestTerminalInputPumpCancellationPreservesStandaloneEscapeForReplacement(t
278279 newCtx, cancelNew := context.WithCancel(context.Background())
279280 defer cancelNew()
280281 newClock := newAttachPaletteClock()
281- out := make(chan ports.Frame, 1)
282+ out := make(chan wire.Frame, 1)
282283 newDone := make(chan struct{})
283284 go func() {
284285 (&stdinPump{
@@ -291,7 +292,7 @@ func TestTerminalInputPumpCancellationPreservesStandaloneEscapeForReplacement(t
291292 (<-newClock.timers).fire() // marker ambiguity deadline
292293 (<-newClock.timers).fire() // paste coalescer's lone-ESC deadline
293294 frame := <-out
294- got, err := ports.UnmarshalInput(frame.Payload)
295+ got, err := wire.UnmarshalInput(frame.Payload)
295296 require.NoError(t, err)
296297 require.Equal(t, []byte("\x1b"), got.Data)
297298
@@ -306,7 +307,7 @@ func TestTerminalInputPumpCancellationAfterInputBeforeOSCPreservesOnlyUndelivere
306307 ctx, cancel := context.WithCancel(context.Background())
307308 delivered := make(chan struct{})
308309 done := make(chan struct{})
309- out := make(chan ports.Frame, 1)
310+ out := make(chan wire.Frame, 1)
310311 go func() {
311312 (&stdinPump{
312313 ctx: ctx, cancel: cancel, input: input, out: out, clock: newAttachPaletteClock(),
@@ -320,14 +321,14 @@ func TestTerminalInputPumpCancellationAfterInputBeforeOSCPreservesOnlyUndelivere
320321 close(done)
321322 }()
322323 frame := requireFrame(t, out, "timed out waiting for delivered input prefix")
323- got, err := ports.UnmarshalInput(frame.Payload)
324+ got, err := wire.UnmarshalInput(frame.Payload)
324325 require.NoError(t, err)
325326 require.Equal(t, []byte("x"), got.Data)
326327 requireSignal(t, delivered, "timed out waiting for cancellation after input delivery")
327328 requireSignal(t, done, "timed out waiting for cancelled stdin pump")
328329
329330 replacementCtx, cancelReplacement := context.WithCancel(context.Background())
330- replacementOut := make(chan ports.Frame, 1)
331+ replacementOut := make(chan wire.Frame, 1)
331332 replacementDone := make(chan struct{})
332333 go func() {
333334 (&stdinPump{
@@ -339,7 +340,7 @@ func TestTerminalInputPumpCancellationAfterInputBeforeOSCPreservesOnlyUndelivere
339340 close(replacementDone)
340341 }()
341342 frame = requireFrame(t, replacementOut, "timed out waiting for undelivered input suffix")
342- got, err = ports.UnmarshalInput(frame.Payload)
343+ got, err = wire.UnmarshalInput(frame.Payload)
343344 require.NoError(t, err)
344345 require.Equal(t, []byte("y"), got.Data, "replacement must receive only the undelivered ordinary suffix")
345346
@@ -354,7 +355,7 @@ func TestTerminalInputPumpCancellationPreservesCoalescerHeldSuffix(t *testing.T)
354355 input.enqueue(terminalReadResult{data: raw}, 1)
355356 var activeGeneration atomic.Uint64
356357 ctx, cancel := context.WithCancel(context.Background())
357- out := make(chan ports.Frame, 1)
358+ out := make(chan wire.Frame, 1)
358359 done := make(chan struct{})
359360 go func() {
360361 (&stdinPump{
@@ -366,14 +367,14 @@ func TestTerminalInputPumpCancellationPreservesCoalescerHeldSuffix(t *testing.T)
366367 close(done)
367368 }()
368369 frame := requireFrame(t, out, "timed out waiting for delivered input prefix")
369- got, err := ports.UnmarshalInput(frame.Payload)
370+ got, err := wire.UnmarshalInput(frame.Payload)
370371 require.NoError(t, err)
371372 require.Equal(t, []byte("x"), got.Data)
372373 requireSignal(t, done, "timed out waiting for cancelled stdin pump")
373374
374375 replacementCtx, cancelReplacement := context.WithCancel(context.Background())
375376 replacementClock := newAttachPaletteClock()
376- replacementOut := make(chan ports.Frame, 1)
377+ replacementOut := make(chan wire.Frame, 1)
377378 replacementDone := make(chan struct{})
378379 go func() {
379380 (&stdinPump{
@@ -386,7 +387,7 @@ func TestTerminalInputPumpCancellationPreservesCoalescerHeldSuffix(t *testing.T)
386387 }()
387388 requireTimer(t, replacementClock.timers, "timed out waiting for paste-prefix ambiguity timer").fire()
388389 frame = requireFrame(t, replacementOut, "timed out waiting for coalescer-held suffix")
389- got, err = ports.UnmarshalInput(frame.Payload)
390+ got, err = wire.UnmarshalInput(frame.Payload)
390391 require.NoError(t, err)
391392 require.Equal(t, held, got.Data, "replacement must receive the coalescer-held suffix exactly once")
392393
@@ -424,7 +425,7 @@ func TestTerminalInputPumpCancellationAfterDeliveredCallbackDoesNotReplay(t *tes
424425 ctx, cancel := context.WithCancel(context.Background())
425426 delivered := make(chan struct{})
426427 done := make(chan struct{})
427- out := make(chan ports.Frame, 1)
428+ out := make(chan wire.Frame, 1)
428429 go func() {
429430 (&stdinPump{
430431 ctx: ctx, cancel: cancel, input: input, out: out, clock: newAttachPaletteClock(),
@@ -438,7 +439,7 @@ func TestTerminalInputPumpCancellationAfterDeliveredCallbackDoesNotReplay(t *tes
438439 close(done)
439440 }()
440441 frame := <-out
441- got, err := ports.UnmarshalInput(frame.Payload)
442+ got, err := wire.UnmarshalInput(frame.Payload)
442443 require.NoError(t, err)
443444 require.Equal(t, []byte("x"), got.Data)
444445 <-delivered
@@ -463,7 +464,7 @@ func TestTerminalInputPumpReusesOneReaderAcrossCancelledAttempts(t *testing.T) {
463464 go func() {
464465 (&stdinPump{
465466 ctx: firstCtx, cancel: cancelFirst, input: input,
466- out: make(chan ports.Frame), clock: newAttachPaletteClock(),
467+ out: make(chan wire.Frame), clock: newAttachPaletteClock(),
467468 logger: slog.New(slog.DiscardHandler), paletteEvents: make(chan paletteGenerationEvent),
468469 activeGeneration: &activeGeneration,
469470 }).run()
@@ -474,7 +475,7 @@ func TestTerminalInputPumpReusesOneReaderAcrossCancelledAttempts(t *testing.T) {
474475
475476 secondCtx, cancelSecond := context.WithCancel(context.Background())
476477 defer cancelSecond()
477- out := make(chan ports.Frame, 1)
478+ out := make(chan wire.Frame, 1)
478479 secondDone := make(chan struct{})
479480 go func() {
480481 (&stdinPump{
@@ -488,7 +489,7 @@ func TestTerminalInputPumpReusesOneReaderAcrossCancelledAttempts(t *testing.T) {
488489
489490 reader.chunks <- []byte("x")
490491 frame := <-out
491- got, err := ports.UnmarshalInput(frame.Payload)
492+ got, err := wire.UnmarshalInput(frame.Payload)
492493 require.NoError(t, err)
493494 require.Equal(t, []byte("x"), got.Data)
494495 require.Equal(t, 1, reader.maxActive(), "cancelled attempts must not leave competing reads")
M internal/usecase/client/transport_observability_test.go +12 -11
@@ -15,6 +15,7 @@ import (
1515 "github.com/bnema/vev/internal/ports"
1616 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1717 "github.com/bnema/vev/internal/protocol"
18+ "github.com/bnema/vev/internal/protocol/wire"
1819 "github.com/bnema/vev/internal/usecase/client"
1920 )
2021
@@ -46,12 +47,12 @@ func TestBlockingRuntimeObserverDoesNotDelayTerminalFlushOrACK(t *testing.T) {
4647 defer reporter.Close()
4748 acked := make(chan struct{})
4849 transport := portsmocks.NewMockTransport(t)
49- transport.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
50- transport.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
51- transport.EXPECT().Send(isType(ports.MsgAck)).Run(func(ports.Frame) { close(acked) }).Return(nil).Once()
50+ transport.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
51+ transport.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
52+ transport.EXPECT().Send(isType(wire.MsgAck)).Run(func(wire.Frame) { close(acked) }).Return(nil).Once()
5253 unblock := scriptRecv(transport,
53- recvItem{f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
54- recvItem{f: frameOf(ports.MsgOutput, mustMarshalOutput(protocol.Output{Epoch: 1, Size: domain.Size{Cols: 1, Rows: 1}, Full: true, Data: []byte("flush-before-observe"), New: 3}))},
54+ recvItem{f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
55+ recvItem{f: frameOf(wire.MsgOutput, mustMarshalOutput(protocol.Output{Epoch: 1, Size: domain.Size{Cols: 1, Rows: 1}, Full: true, Data: []byte("flush-before-observe"), New: 3}))},
5556 )
5657 defer unblock()
5758 transport.EXPECT().Close().Return(nil).Once()
@@ -87,15 +88,15 @@ func TestTerminalFlushBoundaryTransportObservability(t *testing.T) {
8788 reporter := ports.NewSerializedRuntimeObserver(observer, 64)
8889 defer reporter.Close()
8990 transport := portsmocks.NewMockTransport(t)
90- transport.EXPECT().Send(isType(ports.MsgHello)).Return(nil).Once()
91- transport.EXPECT().Send(isType(ports.MsgTheme)).Return(nil).Maybe()
91+ transport.EXPECT().Send(isType(wire.MsgHello)).Return(nil).Once()
92+ transport.EXPECT().Send(isType(wire.MsgTheme)).Return(nil).Maybe()
9293 unblock := scriptRecv(transport,
93- recvItem{f: frameOf(ports.MsgWelcome, ports.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
94- recvItem{f: frameOf(ports.MsgOutput, mustMarshalOutput(protocol.Output{Epoch: 1, Size: domain.Size{Cols: 1, Rows: 1}, Full: true, Data: []byte("unchanged-by-observer"), New: 3}))},
95- recvItem{f: frameOf(ports.MsgDetached, ports.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
94+ recvItem{f: frameOf(wire.MsgWelcome, wire.MarshalWelcome(protocol.Welcome{SessionID: "s1"}))},
95+ recvItem{f: frameOf(wire.MsgOutput, mustMarshalOutput(protocol.Output{Epoch: 1, Size: domain.Size{Cols: 1, Rows: 1}, Full: true, Data: []byte("unchanged-by-observer"), New: 3}))},
96+ recvItem{f: frameOf(wire.MsgDetached, wire.MarshalDetached(protocol.Detached{Reason: protocol.ReasonDetach}))},
9697 )
9798 defer unblock()
98- transport.EXPECT().Send(isType(ports.MsgAck)).Return(nil).Maybe()
99+ transport.EXPECT().Send(isType(wire.MsgAck)).Return(nil).Maybe()
99100 transport.EXPECT().Close().Return(nil).Once()
100101
101102 err := runTestClient(context.Background(), testDependencies(transportDialer{transport: transport}, term, realClock{}, nil, reporter), client.AttachRequest{Intent: protocol.IntentEphemeral})
M internal/usecase/client/wire_external_test_helpers_test.go +2 -2
@@ -1,12 +1,12 @@
11 package client_test
22
33 import (
4- "github.com/bnema/vev/internal/ports"
54 "github.com/bnema/vev/internal/protocol"
5+ "github.com/bnema/vev/internal/protocol/wire"
66 )
77
88 func mustMarshalOutput(m protocol.Output) []byte {
9- payload, err := ports.MarshalOutput(m)
9+ payload, err := wire.MarshalOutput(m)
1010 if err != nil {
1111 panic(err)
1212 }
M internal/usecase/client/wire_test_helpers_test.go +2 -2
@@ -1,12 +1,12 @@
11 package client
22
33 import (
4- "github.com/bnema/vev/internal/ports"
54 "github.com/bnema/vev/internal/protocol"
5+ "github.com/bnema/vev/internal/protocol/wire"
66 )
77
88 func mustMarshalOutput(m protocol.Output) []byte {
9- payload, err := ports.MarshalOutput(m)
9+ payload, err := wire.MarshalOutput(m)
1010 if err != nil {
1111 panic(err)
1212 }
M internal/usecase/daemon/attachment_effect_gate.go +2 -1
@@ -6,6 +6,7 @@ import (
66 "sync/atomic"
77
88 "github.com/bnema/vev/internal/ports"
9+ "github.com/bnema/vev/internal/protocol/wire"
910 )
1011
1112 type attachmentEffectPhase uint8
@@ -188,7 +189,7 @@ func (t *attachmentEffect) endTransportSend() {
188189 g.mu.Unlock()
189190 }
190191
191-func (t *attachmentEffect) sendControl(frame ports.Frame) error {
192+func (t *attachmentEffect) sendControl(frame wire.Frame) error {
192193 if !t.current() || t.ac == nil || t.transport.transport == nil {
193194 return errAttachmentTransition
194195 }
M internal/usecase/daemon/attachment_effect_gate_test.go +17 -17
@@ -11,8 +11,8 @@ import (
1111
1212 renderer "github.com/bnema/vev-vt"
1313 "github.com/bnema/vev/internal/domain"
14- "github.com/bnema/vev/internal/ports"
1514 "github.com/bnema/vev/internal/protocol"
15+ "github.com/bnema/vev/internal/protocol/wire"
1616 "github.com/bnema/vev/internal/usecase/keys"
1717 )
1818
@@ -44,14 +44,14 @@ func newBlockedRenderReplacementTransport(d *Daemon, sess *session) *blockedRend
4444 }
4545 }
4646
47-func (t *blockedRenderReplacementTransport) Send(ports.Frame) error {
47+func (t *blockedRenderReplacementTransport) Send(wire.Frame) error {
4848 t.sendOnce.Do(func() { close(t.sendEntered) })
4949 <-t.closed
5050 return errors.New("transport closed")
5151 }
5252
53-func (*blockedRenderReplacementTransport) Recv() (ports.Frame, error) {
54- return ports.Frame{}, io.EOF
53+func (*blockedRenderReplacementTransport) Recv() (wire.Frame, error) {
54+ return wire.Frame{}, io.EOF
5555 }
5656
5757 func (t *blockedRenderReplacementTransport) Close() error {
@@ -97,13 +97,13 @@ func newTeardownBlockingTransport() *teardownBlockingTransport {
9797 return &teardownBlockingTransport{sendEntered: make(chan struct{}), closed: make(chan struct{})}
9898 }
9999
100-func (t *teardownBlockingTransport) Send(ports.Frame) error {
100+func (t *teardownBlockingTransport) Send(wire.Frame) error {
101101 t.sendOnce.Do(func() { close(t.sendEntered) })
102102 <-t.closed
103103 return errors.New("transport closed")
104104 }
105105
106-func (*teardownBlockingTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
106+func (*teardownBlockingTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
107107
108108 func (t *teardownBlockingTransport) Close() error {
109109 t.closeOnce.Do(func() { close(t.closed) })
@@ -568,7 +568,7 @@ func TestAttachmentEffectGateAdmittedActiveEffectsFinishBeforeReplacement(t *tes
568568 tests := []struct {
569569 name string
570570 setup func(*Daemon, *session, *attachedClient, *transactionalResizePTY)
571- frame ports.Frame
571+ frame wire.Frame
572572 assert func(*testing.T, *attachedClient, *transactionalResizePTY)
573573 }{
574574 {
@@ -612,7 +612,7 @@ func TestAttachmentEffectGateAdmittedActiveEffectsFinishBeforeReplacement(t *tes
612612 },
613613 {
614614 name: "theme",
615- frame: ports.Frame{Type: ports.MsgTheme, Payload: ports.MarshalTheme(protocol.Theme{
615+ frame: wire.Frame{Type: wire.MsgTheme, Payload: wire.MarshalTheme(protocol.Theme{
616616 HasForeground: true, Foreground: renderer.RGB{R: 1, G: 2, B: 3},
617617 HasBackground: true, Background: renderer.RGB{R: 4, G: 5, B: 6},
618618 })},
@@ -625,7 +625,7 @@ func TestAttachmentEffectGateAdmittedActiveEffectsFinishBeforeReplacement(t *tes
625625 setup: func(_ *Daemon, _ *session, ac *attachedClient, _ *transactionalResizePTY) {
626626 ac.output.next = 3
627627 },
628- frame: ports.Frame{Type: ports.MsgAck, Payload: mustMarshalAck(protocol.Ack{Epoch: 1, State: 1})},
628+ frame: wire.Frame{Type: wire.MsgAck, Payload: mustMarshalAck(protocol.Ack{Epoch: 1, State: 1})},
629629 assert: func(t *testing.T, ac *attachedClient, _ *transactionalResizePTY) {
630630 ac.sendMu.Lock()
631631 defer ac.sendMu.Unlock()
@@ -697,10 +697,10 @@ func TestAttachedRouteSnapshotIsAcceptedAsAnAttachmentValue(t *testing.T) {
697697 Home: activeRef,
698698 Entries: []protocol.RecentRouteEntry{testRouteEntry(7, 3, "previous", 7, protocol.RouteKindLocal)},
699699 }
700- payload, err := ports.MarshalRecentRouteSnapshot(snapshot)
700+ payload, err := wire.MarshalRecentRouteSnapshot(snapshot)
701701 require.NoError(t, err)
702702
703- require.False(t, d.handleAttachmentClientFrame(token, ports.Frame{Type: ports.MsgRecentRouteSnapshot, Payload: payload}))
703+ require.False(t, d.handleAttachmentClientFrame(token, wire.Frame{Type: wire.MsgRecentRouteSnapshot, Payload: payload}))
704704 require.Equal(t, snapshot, ac.routeSnapshotCopy())
705705 }
706706
@@ -726,31 +726,31 @@ func TestAttachedNavigationCommandSendsResultAfterLocalTransition(t *testing.T)
726726 token := source.captureAttachmentCapability(ac, transport)
727727 token.lease = rc.attachmentLease(ac)
728728 ac.installTestAttachmentCapability(token)
729- payload, err := ports.MarshalCommandRequest(protocol.CommandRequest{
729+ payload, err := wire.MarshalCommandRequest(protocol.CommandRequest{
730730 Version: protocol.Version, RequestID: 1, Slug: "back-session", Attached: true,
731731 })
732732 require.NoError(t, err)
733733
734- require.False(t, d.handleAttachmentClientFrame(token, ports.Frame{Type: ports.MsgCommand, Payload: payload}))
734+ require.False(t, d.handleAttachmentClientFrame(token, wire.Frame{Type: wire.MsgCommand, Payload: payload}))
735735 require.Same(t, source, ac.currentAttachmentSession())
736736 frames := transport.Sends()
737737 require.NotEmpty(t, frames)
738738 var result protocol.CommandResult
739739 for _, frame := range frames {
740- if frame.Type != ports.MsgCommandResult {
740+ if frame.Type != wire.MsgCommandResult {
741741 continue
742742 }
743- result, err = ports.UnmarshalCommandResult(frame.Payload)
743+ result, err = wire.UnmarshalCommandResult(frame.Payload)
744744 require.NoError(t, err)
745745 break
746746 }
747747 require.True(t, result.OK, result.Text)
748748 var action protocol.RouteNavigationAction
749749 for _, frame := range frames {
750- if frame.Type != ports.MsgNavigateRecentRoute {
750+ if frame.Type != wire.MsgNavigateRecentRoute {
751751 continue
752752 }
753- action, err = ports.UnmarshalRouteNavigationAction(frame.Payload)
753+ action, err = wire.UnmarshalRouteNavigationAction(frame.Payload)
754754 require.NoError(t, err)
755755 }
756756 require.Equal(t, protocol.RouteNavigationAction{SnapshotGeneration: 2, Key: 1, Generation: 1}, action)
M internal/usecase/daemon/attachment_output.go +10 -9
@@ -10,6 +10,7 @@ import (
1010 "github.com/bnema/vev/internal/domain"
1111 "github.com/bnema/vev/internal/ports"
1212 "github.com/bnema/vev/internal/protocol"
13+ "github.com/bnema/vev/internal/protocol/wire"
1314 )
1415
1516 // attachmentOutput owns the terminal-output dependency chain and emitted
@@ -153,13 +154,13 @@ func (o *attachmentOutput) prepareFrame(d *Daemon, state *capturedRenderState, f
153154 return &preparedAttachmentOutput{output: o, ansi: ansi, graphics: preparedGraphics, cursor: cursor, data: data}, nil
154155 }
155156
156-func (p *preparedAttachmentOutput) send(echoAck uint64, send func(ports.Frame) error) error {
157+func (p *preparedAttachmentOutput) send(echoAck uint64, send func(wire.Frame) error) error {
157158 if p == nil || p.ansi == nil {
158159 return nil
159160 }
160161 sendFrame := send
161162 if p.graphics != nil {
162- sendFrame = func(frame ports.Frame) error {
163+ sendFrame = func(frame wire.Frame) error {
163164 p.graphics.markSendAttempted()
164165 return send(frame)
165166 }
@@ -285,7 +286,7 @@ func (s *attachmentOutput) prepare(frame renderer.Frame, damage []renderer.Damag
285286 }, nil
286287 }
287288
288-func (p *preparedOutput) send(data []byte, echoAck uint64, send func(ports.Frame) error) error {
289+func (p *preparedOutput) send(data []byte, echoAck uint64, send func(wire.Frame) error) error {
289290 if p == nil || p.stream == nil || p.attempted {
290291 return nil
291292 }
@@ -379,21 +380,21 @@ func (p *preparedOutput) currentLocked() bool {
379380 (p.stream.attachment == nil || p.stream.attachment.lifecycle.generationValue() == p.connectionGeneration)
380381 }
381382
382-func marshalOutputState(data []byte, epoch, base, next, echoAck, viewRevision uint64, size domain.Size, full bool) (ports.Frame, error) {
383- payload, err := ports.MarshalOutput(protocol.Output{
383+func marshalOutputState(data []byte, epoch, base, next, echoAck, viewRevision uint64, size domain.Size, full bool) (wire.Frame, error) {
384+ payload, err := wire.MarshalOutput(protocol.Output{
384385 Epoch: epoch, Base: base, New: next, Echo: echoAck, ViewRevision: viewRevision,
385386 Size: size, Full: full, Data: data,
386387 })
387388 if err != nil {
388- return ports.Frame{}, err
389+ return wire.Frame{}, err
389390 }
390- return ports.Frame{Type: ports.MsgOutput, Payload: payload}, nil
391+ return wire.Frame{Type: wire.MsgOutput, Payload: payload}, nil
391392 }
392393
393394 // sideEffect builds an output frame without advancing the state stream. When
394395 // an attachment is present, callers must hold its sendMu; attachment-bound
395396 // sends use sideEffectLocked after validating their transport incarnation.
396-func (s *attachmentOutput) sideEffect(data []byte, echoAck uint64) (ports.Frame, error) {
397+func (s *attachmentOutput) sideEffect(data []byte, echoAck uint64) (wire.Frame, error) {
397398 s.lockView()
398399 defer s.unlockView()
399400 return s.sideEffectLocked(data, echoAck)
@@ -402,7 +403,7 @@ func (s *attachmentOutput) sideEffect(data []byte, echoAck uint64) (ports.Frame,
402403 // sideEffectLocked requires the attachment view lock for both frame creation
403404 // and transport emission. Keeping that lock across the send prevents a view
404405 // publication from overtaking a frame that was fenced just before it.
405-func (s *attachmentOutput) sideEffectLocked(data []byte, echoAck uint64) (ports.Frame, error) {
406+func (s *attachmentOutput) sideEffectLocked(data []byte, echoAck uint64) (wire.Frame, error) {
406407 _, epoch, viewRevision := s.fenceLocked()
407408 size := domain.Size{Cols: 1, Rows: 1}
408409 if s != nil && s.attachment != nil {
M internal/usecase/daemon/attachment_output_test.go +32 -31
@@ -11,6 +11,7 @@ import (
1111 "github.com/bnema/vev/internal/domain"
1212 "github.com/bnema/vev/internal/ports"
1313 "github.com/bnema/vev/internal/protocol"
14+ "github.com/bnema/vev/internal/protocol/wire"
1415 "github.com/bnema/vev/internal/usecase/layout"
1516 "github.com/stretchr/testify/require"
1617 )
@@ -22,7 +23,7 @@ func TestAttachmentOutputBuildsPipelinedDependencyChain(t *testing.T) {
2223 first, ok, err := drawOutputState(t, stream, firstFrame, nil, true, 7)
2324 require.NoError(t, err)
2425 require.True(t, ok)
25- firstOut, err := ports.UnmarshalOutput(first.Payload)
26+ firstOut, err := wire.UnmarshalOutput(first.Payload)
2627 require.NoError(t, err)
2728 require.Equal(t, uint64(0), firstOut.Base)
2829 require.Equal(t, uint64(1), firstOut.New)
@@ -33,7 +34,7 @@ func TestAttachmentOutputBuildsPipelinedDependencyChain(t *testing.T) {
3334 second, ok, err := drawOutputState(t, stream, secondFrame, []renderer.Damage{{Kind: renderer.DamageText, Width: 1, Height: 1}}, false, 8)
3435 require.NoError(t, err)
3536 require.True(t, ok)
36- secondOut, err := ports.UnmarshalOutput(second.Payload)
37+ secondOut, err := wire.UnmarshalOutput(second.Payload)
3738 require.NoError(t, err)
3839 require.Equal(t, firstOut.New, secondOut.Base)
3940 require.Equal(t, uint64(2), secondOut.New)
@@ -91,7 +92,7 @@ func TestAttachmentOutputCapacityProbeDoesNotRaceWithSend(t *testing.T) {
9192 release := make(chan struct{})
9293 sendDone := make(chan error, 1)
9394 go func() {
94- sendDone <- prepared.send(prepared.data, 0, func(ports.Frame) error {
95+ sendDone <- prepared.send(prepared.data, 0, func(wire.Frame) error {
9596 close(entered)
9697 <-release
9798 return nil
@@ -132,12 +133,12 @@ func TestAttachmentOutputEpochsAreAttachmentLocalAndPreparedFramesAreFenced(t *t
132133 second := newAttachment()
133134 firstPrepared, err := first.prepare(frame, nil, true)
134135 require.NoError(t, err)
135- var firstFrame ports.Frame
136- require.NoError(t, firstPrepared.send(firstPrepared.data, 0, func(frame ports.Frame) error {
136+ var firstFrame wire.Frame
137+ require.NoError(t, firstPrepared.send(firstPrepared.data, 0, func(frame wire.Frame) error {
137138 firstFrame = frame
138139 return nil
139140 }))
140- firstOutput, err := ports.UnmarshalOutput(firstFrame.Payload)
141+ firstOutput, err := wire.UnmarshalOutput(firstFrame.Payload)
141142 require.NoError(t, err)
142143 require.Equal(t, uint64(1), firstOutput.Epoch)
143144 require.Equal(t, uint64(1), firstOutput.New)
@@ -146,7 +147,7 @@ func TestAttachmentOutputEpochsAreAttachmentLocalAndPreparedFramesAreFenced(t *t
146147
147148 secondPrepared, err := second.prepare(frame, nil, true)
148149 require.NoError(t, err)
149- require.NoError(t, secondPrepared.send(secondPrepared.data, 0, func(ports.Frame) error { return nil }))
150+ require.NoError(t, secondPrepared.send(secondPrepared.data, 0, func(wire.Frame) error { return nil }))
150151 require.Equal(t, uint64(1), second.epoch)
151152 require.Equal(t, uint64(1), second.next)
152153 require.Equal(t, uint64(1), first.next)
@@ -160,7 +161,7 @@ func TestAttachmentOutputEpochsAreAttachmentLocalAndPreparedFramesAreFenced(t *t
160161 require.Zero(t, first.next)
161162 require.Zero(t, first.acked)
162163 var staleSent bool
163- require.NoError(t, pending.send(pending.data, 0, func(ports.Frame) error {
164+ require.NoError(t, pending.send(pending.data, 0, func(wire.Frame) error {
164165 staleSent = true
165166 return nil
166167 }))
@@ -171,12 +172,12 @@ func TestAttachmentOutputEpochsAreAttachmentLocalAndPreparedFramesAreFenced(t *t
171172
172173 reset, err := first.prepare(changed, []renderer.Damage{{Kind: renderer.DamageText, Width: 1, Height: 1}}, false)
173174 require.NoError(t, err)
174- var resetFrame ports.Frame
175- require.NoError(t, reset.send(reset.data, 0, func(frame ports.Frame) error {
175+ var resetFrame wire.Frame
176+ require.NoError(t, reset.send(reset.data, 0, func(frame wire.Frame) error {
176177 resetFrame = frame
177178 return nil
178179 }))
179- resetOutput, err := ports.UnmarshalOutput(resetFrame.Payload)
180+ resetOutput, err := wire.UnmarshalOutput(resetFrame.Payload)
180181 require.NoError(t, err)
181182 require.Equal(t, uint64(2), resetOutput.Epoch)
182183 require.Zero(t, resetOutput.Base)
@@ -204,14 +205,14 @@ func TestPreparedOutputDropsReplacedConnectionAndView(t *testing.T) {
204205 fillOutputStateRows(frame, []string{"abc"})
205206 initial, err := stream.prepare(frame, nil, true)
206207 require.NoError(t, err)
207- require.NoError(t, initial.send(initial.data, 0, func(ports.Frame) error { return nil }))
208+ require.NoError(t, initial.send(initial.data, 0, func(wire.Frame) error { return nil }))
208209 changed := frame.Clone()
209210 changed.Set(0, 0, renderer.Cell{Rune: 'x', Style: renderer.DefaultStyle()})
210211 pending, err := stream.prepare(changed, []renderer.Damage{{Kind: renderer.DamageText, Width: 1, Height: 1}}, false)
211212 require.NoError(t, err)
212213 tt.change(ac)
213214 var sent bool
214- require.NoError(t, pending.send(pending.data, 0, func(ports.Frame) error {
215+ require.NoError(t, pending.send(pending.data, 0, func(wire.Frame) error {
215216 sent = true
216217 return nil
217218 }))
@@ -220,7 +221,7 @@ func TestPreparedOutputDropsReplacedConnectionAndView(t *testing.T) {
220221
221222 retry, err := stream.prepare(changed, []renderer.Damage{{Kind: renderer.DamageText, Width: 1, Height: 1}}, false)
222223 require.NoError(t, err)
223- require.NoError(t, retry.send(retry.data, 0, func(ports.Frame) error { return nil }))
224+ require.NoError(t, retry.send(retry.data, 0, func(wire.Frame) error { return nil }))
224225 require.Equal(t, uint64(2), stream.next)
225226 })
226227 }
@@ -243,7 +244,7 @@ func TestOutputStateSideEffectsDoNotAdvanceEpochStateOrACK(t *testing.T) {
243244 beforeEpoch, beforeNext, beforeAck := stream.epoch, stream.next, stream.acked
244245 frame, err := stream.sideEffect([]byte("pty"), 9)
245246 require.NoError(t, err)
246- out, err := ports.UnmarshalOutput(frame.Payload)
247+ out, err := wire.UnmarshalOutput(frame.Payload)
247248 require.NoError(t, err)
248249 require.Equal(t, beforeEpoch, out.Epoch)
249250 require.Zero(t, out.Base)
@@ -260,7 +261,7 @@ func TestAttachmentOutputFailedSendRetriesSnapshotWithoutAdvancing(t *testing.T)
260261 fillOutputStateRows(initial, []string{"abc"})
261262 first, err := stream.prepare(initial, nil, true)
262263 require.NoError(t, err)
263- require.NoError(t, first.send(first.data, 0, func(ports.Frame) error { return nil }))
264+ require.NoError(t, first.send(first.data, 0, func(wire.Frame) error { return nil }))
264265 require.Equal(t, uint64(1), stream.next)
265266
266267 changed := initial.Clone()
@@ -272,7 +273,7 @@ func TestAttachmentOutputFailedSendRetriesSnapshotWithoutAdvancing(t *testing.T)
272273 require.Empty(t, probe.Bytes(), "preparation must not advance the renderer shadow")
273274
274275 sendErr := errors.New("send failed")
275- require.ErrorIs(t, pending.send(pending.data, 0, func(ports.Frame) error { return sendErr }), sendErr)
276+ require.ErrorIs(t, pending.send(pending.data, 0, func(wire.Frame) error { return sendErr }), sendErr)
276277 require.Equal(t, uint64(1), stream.next, "failed send must not advance the state chain")
277278 pending.commitNoSend()
278279 probe, err = stream.renderer.Prepare(initial, nil, false)
@@ -281,17 +282,17 @@ func TestAttachmentOutputFailedSendRetriesSnapshotWithoutAdvancing(t *testing.T)
281282
282283 retry, err := stream.prepare(changed, []renderer.Damage{{Kind: renderer.DamageText, Width: 1, Height: 1}}, false)
283284 require.NoError(t, err)
284- var sent ports.Frame
285- require.NoError(t, retry.send(retry.data, 0, func(frame ports.Frame) error {
285+ var sent wire.Frame
286+ require.NoError(t, retry.send(retry.data, 0, func(frame wire.Frame) error {
286287 sent = frame
287288 return nil
288289 }))
289- out, err := ports.UnmarshalOutput(sent.Payload)
290+ out, err := wire.UnmarshalOutput(sent.Payload)
290291 require.NoError(t, err)
291292 require.Zero(t, out.Base, "retry after ambiguous send failure must be dependency-free")
292293 require.Equal(t, uint64(2), out.New, "state numbers remain monotonic across rebases")
293294 require.Equal(t, uint64(2), stream.next, "successful retry advances the chain exactly once")
294- require.NoError(t, retry.send(retry.data, 0, func(ports.Frame) error {
295+ require.NoError(t, retry.send(retry.data, 0, func(wire.Frame) error {
295296 t.Fatal("completed output sent twice")
296297 return nil
297298 }))
@@ -307,7 +308,7 @@ func TestAttachmentOutputNoByteCommitAdvancesShadowWithoutState(t *testing.T) {
307308 fillOutputStateRows(frame, []string{"abc"})
308309 initial, err := stream.prepare(frame, nil, true)
309310 require.NoError(t, err)
310- require.NoError(t, initial.send(initial.data, 0, func(ports.Frame) error { return nil }))
311+ require.NoError(t, initial.send(initial.data, 0, func(wire.Frame) error { return nil }))
311312
312313 noOp, err := stream.prepare(frame, nil, false)
313314 require.NoError(t, err)
@@ -371,7 +372,7 @@ func (s *attachmentOutput) render(frame renderer.Frame, damage []renderer.Damage
371372 return prepared.data, nil
372373 }
373374
374-func outputStateFrame(stream *attachmentOutput, data []byte, reset bool, echoAck uint64) ports.Frame {
375+func outputStateFrame(stream *attachmentOutput, data []byte, reset bool, echoAck uint64) wire.Frame {
375376 stream.next++
376377 base := stream.next - 1
377378 if reset {
@@ -384,22 +385,22 @@ func outputStateFrame(stream *attachmentOutput, data []byte, reset bool, echoAck
384385 return frame
385386 }
386387
387-func drawOutputState(t *testing.T, stream *attachmentOutput, frame renderer.Frame, damage []renderer.Damage, reset bool, echoAck uint64) (ports.Frame, bool, error) {
388+func drawOutputState(t *testing.T, stream *attachmentOutput, frame renderer.Frame, damage []renderer.Damage, reset bool, echoAck uint64) (wire.Frame, bool, error) {
388389 t.Helper()
389390 prepared, err := stream.prepare(frame, damage, reset)
390391 if err != nil || len(prepared.data) == 0 {
391392 if prepared != nil {
392393 prepared.commitNoSend()
393394 }
394- return ports.Frame{}, false, err
395+ return wire.Frame{}, false, err
395396 }
396- var output ports.Frame
397- err = prepared.send(prepared.data, echoAck, func(frame ports.Frame) error {
397+ var output wire.Frame
398+ err = prepared.send(prepared.data, echoAck, func(frame wire.Frame) error {
398399 output = frame
399400 return nil
400401 })
401402 if err != nil {
402- return ports.Frame{}, false, err
403+ return wire.Frame{}, false, err
403404 }
404405 return output, true, nil
405406 }
@@ -416,7 +417,7 @@ func TestAttachmentOutputResizeFrameThenNoopAndDamageAreDifferential(t *testing.
416417 resized, ok, err := drawOutputState(t, stream, screen.Frame, screen.Damage(), true, 0)
417418 require.NoError(t, err)
418419 require.True(t, ok)
419- resizeOutput, err := ports.UnmarshalOutput(resized.Payload)
420+ resizeOutput, err := wire.UnmarshalOutput(resized.Payload)
420421 require.NoError(t, err)
421422 require.Zero(t, resizeOutput.Base, "resize must emit the one reset frame")
422423 screen.ClearDamage()
@@ -429,7 +430,7 @@ func TestAttachmentOutputResizeFrameThenNoopAndDamageAreDifferential(t *testing.
429430 damaged, ok, err := drawOutputState(t, stream, screen.Frame, []renderer.Damage{{Kind: renderer.DamageText, Width: 1, Height: 1}}, false, 0)
430431 require.NoError(t, err)
431432 require.True(t, ok)
432- damageOutput, err := ports.UnmarshalOutput(damaged.Payload)
433+ damageOutput, err := wire.UnmarshalOutput(damaged.Payload)
433434 require.NoError(t, err)
434435 require.Equal(t, resizeOutput.New, damageOutput.Base, "later damage must remain incremental")
435436 }
@@ -455,7 +456,7 @@ func TestAttachmentOutputFailedSendKeepsTextCursorAndGraphicsSpeculative(t *test
455456 require.NoError(t, err)
456457
457458 sendErr := errors.New("send failed")
458- require.ErrorIs(t, prepared.send(0, func(ports.Frame) error { return sendErr }), sendErr)
459+ require.ErrorIs(t, prepared.send(0, func(wire.Frame) error { return sendErr }), sendErr)
459460
460461 require.Equal(t, cursorOut{valid: true, row: 1, col: 1}, output.lastCursor)
461462 require.Empty(t, graphicsState.assets)
M internal/usecase/daemon/attachment_parking_test.go +5 -5
@@ -6,15 +6,15 @@ import (
66
77 "github.com/stretchr/testify/require"
88
9- "github.com/bnema/vev/internal/ports"
109 "github.com/bnema/vev/internal/protocol"
10+ "github.com/bnema/vev/internal/protocol/wire"
1111 )
1212
1313 type resumeWelcomeFailureTransport struct {
1414 closeTrackingTransport
1515 }
1616
17-func (*resumeWelcomeFailureTransport) Send(ports.Frame) error { return errWelcomeSendFailed }
17+func (*resumeWelcomeFailureTransport) Send(wire.Frame) error { return errWelcomeSendFailed }
1818
1919 var errWelcomeSendFailed = errors.New("welcome send failed")
2020
@@ -142,7 +142,7 @@ func TestSuccessfulResumeRejectsOldCredentialAfterWelcome(t *testing.T) {
142142 d.clientGone(sess, ac, oldTransport, false)
143143
144144 resumedTransport := &closeTrackingTransport{}
145- d.handleHello(resumedTransport, ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(helloResumeCapable(protocol.IntentResume, "work", oldToken))})
145+ d.handleHello(resumedTransport, wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(helloResumeCapable(protocol.IntentResume, "work", oldToken))})
146146 newToken := ac.resumeToken
147147 require.NotEqual(t, oldToken, newToken)
148148 d.mu.Lock()
@@ -152,7 +152,7 @@ func TestSuccessfulResumeRejectsOldCredentialAfterWelcome(t *testing.T) {
152152 require.False(t, oldRetained)
153153 require.NotNil(t, newParked)
154154 require.NotEmpty(t, resumedTransport.Sends())
155- require.Equal(t, ports.MsgWelcome, resumedTransport.Sends()[0].Type, "Welcome must remain the first server frame after Hello")
155+ require.Equal(t, wire.MsgWelcome, resumedTransport.Sends()[0].Type, "Welcome must remain the first server frame after Hello")
156156 }
157157
158158 func TestFailedResumeHandshakeKeepsParkedCredential(t *testing.T) {
@@ -166,7 +166,7 @@ func TestFailedResumeHandshakeKeepsParkedCredential(t *testing.T) {
166166 d.clientGone(sess, ac, oldTransport, false)
167167
168168 failed := &resumeWelcomeFailureTransport{}
169- d.handleHello(failed, ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(helloResumeCapable(protocol.IntentResume, "work", token))})
169+ d.handleHello(failed, wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(helloResumeCapable(protocol.IntentResume, "work", token))})
170170 require.Nil(t, ac.transport())
171171 d.mu.Lock()
172172 parked := d.parked[token]
M internal/usecase/daemon/attachment_registry_test.go +3 -3
@@ -5,7 +5,7 @@ import (
55 "testing"
66
77 "github.com/bnema/vev/internal/domain"
8- "github.com/bnema/vev/internal/ports"
8+ "github.com/bnema/vev/internal/protocol/wire"
99 "github.com/stretchr/testify/require"
1010 )
1111
@@ -99,7 +99,7 @@ func TestTabForAttachmentRepairRebasesOutputBeforePublishingRevision(t *testing.
9999 require.Greater(t, ac.output.currentEpoch(), beforeEpoch)
100100 frame, err := ac.output.sideEffect([]byte("x"), 0)
101101 require.NoError(t, err)
102- output, err := ports.UnmarshalOutput(frame.Payload)
102+ output, err := wire.UnmarshalOutput(frame.Payload)
103103 require.NoError(t, err)
104104 require.Equal(t, after.revision, output.ViewRevision)
105105 require.Equal(t, ac.output.currentEpoch(), output.Epoch)
@@ -129,7 +129,7 @@ func TestPrepareRemovedTabViewRebasesOutputBeforeSideEffect(t *testing.T) {
129129
130130 frame, err := ac.output.sideEffect([]byte("replacement"), 0)
131131 require.NoError(t, err)
132- output, err := ports.UnmarshalOutput(frame.Payload)
132+ output, err := wire.UnmarshalOutput(frame.Payload)
133133 require.NoError(t, err)
134134 require.Equal(t, after.revision, output.ViewRevision)
135135 require.NotEqual(t, beforeEpoch, output.Epoch)
M internal/usecase/daemon/attachment_render_test.go +22 -21
@@ -10,27 +10,28 @@ import (
1010 "github.com/bnema/vev/internal/domain"
1111 "github.com/bnema/vev/internal/ports"
1212 "github.com/bnema/vev/internal/protocol"
13+ "github.com/bnema/vev/internal/protocol/wire"
1314 "github.com/stretchr/testify/require"
1415 )
1516
1617 type blockedAttachmentTransport struct {
1718 entered chan struct{}
1819 release chan struct{}
19- sends chan ports.Frame
20+ sends chan wire.Frame
2021 blocked atomic.Bool
2122 once sync.Once
2223 }
2324
24-func (t *blockedAttachmentTransport) Send(frame ports.Frame) error {
25- if frame.Type == ports.MsgOutput && t.blocked.Load() {
25+func (t *blockedAttachmentTransport) Send(frame wire.Frame) error {
26+ if frame.Type == wire.MsgOutput && t.blocked.Load() {
2627 t.once.Do(func() { close(t.entered) })
2728 <-t.release
2829 }
2930 t.sends <- frame
3031 return nil
3132 }
32-func (*blockedAttachmentTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
33-func (*blockedAttachmentTransport) Close() error { return nil }
33+func (*blockedAttachmentTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
34+func (*blockedAttachmentTransport) Close() error { return nil }
3435
3536 func TestStaleCapacityReadinessRequeuesWithoutAnotherInvalidation(t *testing.T) {
3637 d, sess, ac, sends := newManualSessionWithPTYs(t, nil)
@@ -85,8 +86,8 @@ func TestStaleCapacityReadinessRequeuesWithoutAnotherInvalidation(t *testing.T)
8586 ac.output.syncCapacityLocked()
8687 ac.sendMu.Unlock()
8788 close(releaseRetry)
88- frame := awaitFrame(t, sends, ports.MsgOutput)
89- output, err := ports.UnmarshalOutput(frame.Payload)
89+ frame := awaitFrame(t, sends, wire.MsgOutput)
90+ output, err := wire.UnmarshalOutput(frame.Payload)
9091 require.NoError(t, err)
9192 require.Contains(t, string(output.Data), "capacity race")
9293 awaitTestCompletion(t, fireDone, "initial fire did not finish")
@@ -146,10 +147,10 @@ func TestAttachmentFirstPaintDoesNotWaitForBlockedPeer(t *testing.T) {
146147 defer releasePTY()
147148 clock := &signalClock{timers: make(chan *signalTimer, 16)}
148149 d := newTestDaemon(t, newFactory(t, pty), clock)
149- oldTransport := &blockedAttachmentTransport{entered: make(chan struct{}), release: make(chan struct{}), sends: make(chan ports.Frame, 8)}
150+ oldTransport := &blockedAttachmentTransport{entered: make(chan struct{}), release: make(chan struct{}), sends: make(chan wire.Frame, 8)}
150151 sess, old, err := d.route(protocol.Hello{Version: protocol.Version, Intent: protocol.IntentNew, Name: "work", Size: domain.Size{Cols: 80, Rows: 24}, ClientID: [16]byte{1}}, oldTransport)
151152 require.NoError(t, err)
152- newTransport := &blockedAttachmentTransport{entered: make(chan struct{}), release: make(chan struct{}), sends: make(chan ports.Frame, 8)}
153+ newTransport := &blockedAttachmentTransport{entered: make(chan struct{}), release: make(chan struct{}), sends: make(chan wire.Frame, 8)}
153154 _, fresh, err := d.route(protocol.Hello{Version: protocol.Version, Intent: protocol.IntentAttach, Name: "work", Size: domain.Size{Cols: 80, Rows: 24}, ClientID: [16]byte{2}}, newTransport)
154155 require.NoError(t, err)
155156
@@ -172,7 +173,7 @@ func TestAttachmentFirstPaintDoesNotWaitForBlockedPeer(t *testing.T) {
172173 painted := make(chan bool, 1)
173174 go func() { painted <- d.firstPaintForTransition(token) }()
174175 frame := awaitTestValue(t, newTransport.sends, "healthy attachment first paint was gated by slow peer")
175- require.Equal(t, ports.MsgOutput, frame.Type)
176+ require.Equal(t, wire.MsgOutput, frame.Type)
176177 require.True(t, <-painted)
177178 close(oldTransport.release)
178179 }
@@ -182,7 +183,7 @@ func TestMultiAttachmentHandshakeFirstPaintNotGatedByBlockedPeer(t *testing.T) {
182183 defer releasePTY()
183184 clock := &signalClock{timers: make(chan *signalTimer, 16)}
184185 d := newTestDaemon(t, newFactory(t, pty), clock)
185- oldTransport := &blockedAttachmentTransport{entered: make(chan struct{}), release: make(chan struct{}), sends: make(chan ports.Frame, 8)}
186+ oldTransport := &blockedAttachmentTransport{entered: make(chan struct{}), release: make(chan struct{}), sends: make(chan wire.Frame, 8)}
186187 sess, old, err := d.route(protocol.Hello{Version: protocol.Version, Intent: protocol.IntentNew, Name: "work", Size: domain.Size{Cols: 80, Rows: 24}, ClientID: [16]byte{1}}, oldTransport)
187188 require.NoError(t, err)
188189 rc := sess.renderCoordinator()
@@ -202,16 +203,16 @@ func TestMultiAttachmentHandshakeFirstPaintNotGatedByBlockedPeer(t *testing.T) {
202203 awaitTestCompletion(t, oldTransport.entered, "slow attachment did not begin its blocked output")
203204
204205 hello := protocol.Hello{Version: protocol.Version, Intent: protocol.IntentAttach, Name: "work", Size: domain.Size{Cols: 80, Rows: 24}, ClientID: [16]byte{2}}
205- tr, sends, releaseConn := newConn(t, ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(hello)})
206+ tr, sends, releaseConn := newConn(t, wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(hello)})
206207 defer releaseConn()
207208 handshakeDone := make(chan struct{})
208209 go func() {
209210 d.handleConn(tr)
210211 close(handshakeDone)
211212 }()
212- awaitFrame(t, sends, ports.MsgWelcome)
213- firstPaint := awaitFrame(t, sends, ports.MsgOutput)
214- require.Equal(t, ports.MsgOutput, firstPaint.Type)
213+ awaitFrame(t, sends, wire.MsgWelcome)
214+ firstPaint := awaitFrame(t, sends, wire.MsgOutput)
215+ require.Equal(t, wire.MsgOutput, firstPaint.Type)
215216 releaseConn()
216217 awaitTestCompletion(t, handshakeDone, "multi-attachment handshake did not complete after first paint")
217218 close(oldTransport.release)
@@ -222,10 +223,10 @@ func TestAttachmentPaintFanoutDoesNotWaitForBlockedPeer(t *testing.T) {
222223 pty, releasePTY := newBlockingPTY(t)
223224 defer releasePTY()
224225 d := newTestDaemon(t, newFactory(t, pty), stubClock{})
225- firstTransport := &blockedAttachmentTransport{entered: make(chan struct{}), release: make(chan struct{}), sends: make(chan ports.Frame, 8)}
226+ firstTransport := &blockedAttachmentTransport{entered: make(chan struct{}), release: make(chan struct{}), sends: make(chan wire.Frame, 8)}
226227 sess, _, err := d.route(protocol.Hello{Version: protocol.Version, Intent: protocol.IntentNew, Name: "work", Size: domain.Size{Cols: 80, Rows: 24}, ClientID: [16]byte{1}}, firstTransport)
227228 require.NoError(t, err)
228- secondTransport := &blockedAttachmentTransport{entered: make(chan struct{}), release: make(chan struct{}), sends: make(chan ports.Frame, 8)}
229+ secondTransport := &blockedAttachmentTransport{entered: make(chan struct{}), release: make(chan struct{}), sends: make(chan wire.Frame, 8)}
229230 _, _, err = d.route(protocol.Hello{Version: protocol.Version, Intent: protocol.IntentAttach, Name: "work", Size: domain.Size{Cols: 80, Rows: 24}, ClientID: [16]byte{2}}, secondTransport)
230231 require.NoError(t, err)
231232 rc := sess.renderCoordinator()
@@ -244,8 +245,8 @@ func TestAttachmentPaintFanoutDoesNotWaitForBlockedPeer(t *testing.T) {
244245 }
245246 select {
246247 case frame := <-secondTransport.sends:
247- require.Equal(t, ports.MsgOutput, frame.Type)
248- out, err := ports.UnmarshalOutput(frame.Payload)
248+ require.Equal(t, wire.MsgOutput, frame.Type)
249+ out, err := wire.UnmarshalOutput(frame.Payload)
249250 require.NoError(t, err)
250251 require.True(t, out.Full, "fan-out peers must send a fresh first frame even after another attachment acknowledges shared damage")
251252 case <-time.After(time.Second):
@@ -284,8 +285,8 @@ func TestAttachmentResizeUsesLatestClaimedSessionGeometry(t *testing.T) {
284285 require.True(t, rc.markAttachmentReady(secondLease))
285286 resize := func(ac *attachedClient, tr ports.Transport, size domain.Size) {
286287 token := sess.captureAttachmentCapability(ac, tr)
287- d.handleAttachmentClientFrame(token, ports.Frame{
288- Type: ports.MsgResize,
288+ d.handleAttachmentClientFrame(token, wire.Frame{
289+ Type: wire.MsgResize,
289290 Payload: mustMarshalResize(protocol.Resize{Size: size}),
290291 })
291292 }
M internal/usecase/daemon/attachment_route_identity_test.go +5 -5
@@ -5,8 +5,8 @@ import (
55 "testing"
66
77 "github.com/bnema/vev/internal/domain"
8- "github.com/bnema/vev/internal/ports"
98 "github.com/bnema/vev/internal/protocol"
9+ "github.com/bnema/vev/internal/protocol/wire"
1010 "github.com/stretchr/testify/require"
1111 )
1212
@@ -14,8 +14,8 @@ type committedIdentityErrorTransport struct {
1414 closeTrackingTransport
1515 }
1616
17-func (t *committedIdentityErrorTransport) Send(frame ports.Frame) error {
18- if frame.Type == ports.MsgCommittedRouteIdentity {
17+func (t *committedIdentityErrorTransport) Send(frame wire.Frame) error {
18+ if frame.Type == wire.MsgCommittedRouteIdentity {
1919 return errors.New("committed identity send failed")
2020 }
2121 return t.closeTrackingTransport.Send(frame)
@@ -58,11 +58,11 @@ func TestAttachmentTransitionPublishesCommittedRouteIdentity(t *testing.T) {
5858 var identity protocol.CommittedRouteIdentity
5959 found := false
6060 for _, frame := range transport.Sends() {
61- if frame.Type != ports.MsgCommittedRouteIdentity {
61+ if frame.Type != wire.MsgCommittedRouteIdentity {
6262 continue
6363 }
6464 var decodeErr error
65- identity, decodeErr = ports.UnmarshalCommittedRouteIdentity(frame.Payload)
65+ identity, decodeErr = wire.UnmarshalCommittedRouteIdentity(frame.Payload)
6666 require.NoError(t, decodeErr)
6767 found = true
6868 }
M internal/usecase/daemon/attention_test.go +2 -1
@@ -14,6 +14,7 @@ import (
1414 "github.com/bnema/vev/internal/domain"
1515 "github.com/bnema/vev/internal/ports"
1616 portsmocks "github.com/bnema/vev/internal/ports/mocks"
17+ "github.com/bnema/vev/internal/protocol/wire"
1718 "github.com/bnema/vev/internal/usecase/keys"
1819 "github.com/bnema/vev/internal/usecase/layout"
1920 "github.com/bnema/vev/internal/usecase/ui"
@@ -166,7 +167,7 @@ func TestNoteAttentionDoesNotBlockOnWedgedOtherClient(t *testing.T) {
166167 trW := portsmocks.NewMockTransport(t)
167168 block := make(chan struct{})
168169 t.Cleanup(func() { close(block) })
169- trW.EXPECT().Send(mock.Anything).RunAndReturn(func(ports.Frame) error {
170+ trW.EXPECT().Send(mock.Anything).RunAndReturn(func(wire.Frame) error {
170171 <-block
171172 return nil
172173 }).Maybe()
M internal/usecase/daemon/client.go +5 -4
@@ -14,6 +14,7 @@ import (
1414 "github.com/bnema/vev/internal/domain"
1515 "github.com/bnema/vev/internal/ports"
1616 "github.com/bnema/vev/internal/protocol"
17+ "github.com/bnema/vev/internal/protocol/wire"
1718 "github.com/bnema/vev/internal/usecase/keys"
1819 "github.com/bnema/vev/internal/usecase/mouse"
1920 themeui "github.com/bnema/vev/internal/usecase/theme"
@@ -409,7 +410,7 @@ var errTransportReplaced = errors.New("client transport was replaced")
409410
410411 // sendExpectedTransport writes only when expected is still the attachment's
411412 // current transport incarnation. It preserves sendMu -> linkMu lock ordering.
412-func (ac *attachedClient) sendExpectedTransport(expected transportSnapshot, f ports.Frame) error {
413+func (ac *attachedClient) sendExpectedTransport(expected transportSnapshot, f wire.Frame) error {
413414 ac.sendMu.Lock()
414415 defer ac.sendMu.Unlock()
415416 if !ac.transportSnapshotCurrent(expected) {
@@ -435,7 +436,7 @@ func (ac *attachedClient) beginExpectedTransportSendLocked(expected transportSna
435436 return nil
436437 }
437438
438-func (ac *attachedClient) sendExpectedTransportForAttachment(expected transportSnapshot, f ports.Frame, ticket *attachmentEffect) error {
439+func (ac *attachedClient) sendExpectedTransportForAttachment(expected transportSnapshot, f wire.Frame, ticket *attachmentEffect) error {
439440 ac.sendMu.Lock()
440441 defer ac.sendMu.Unlock()
441442 // A attachment-bound send requires a live ticket, and reports every rejection,
@@ -459,11 +460,11 @@ func (ac *attachedClient) sendExpectedTransportForAttachment(expected transportS
459460 // detachNotifyTimeout, the transport is force-closed, failing the in-flight
460461 // write. Detach/kill/shutdown paths use this so they are never gated on a
461462 // client that has stopped draining its socket.
462-func (d *Daemon) boundedSend(ac *attachedClient, f ports.Frame) {
463+func (d *Daemon) boundedSend(ac *attachedClient, f wire.Frame) {
463464 _ = d.boundedSendErr(ac, f)
464465 }
465466
466-func (d *Daemon) boundedSendErr(ac *attachedClient, f ports.Frame) error {
467+func (d *Daemon) boundedSendErr(ac *attachedClient, f wire.Frame) error {
467468 expected := ac.transportSnapshot()
468469 if expected.transport == nil {
469470 return errors.New("client transport is nil")
M internal/usecase/daemon/client_frame_routing.go +30 -29
@@ -7,6 +7,7 @@ import (
77 "github.com/bnema/vev/internal/domain"
88 "github.com/bnema/vev/internal/ports"
99 "github.com/bnema/vev/internal/protocol"
10+ "github.com/bnema/vev/internal/protocol/wire"
1011 "github.com/bnema/vev/internal/usecase/command"
1112 )
1213
@@ -75,7 +76,7 @@ func (d *Daemon) runConnLoop(ac *attachedClient) {
7576 }
7677
7778 // handleAttachmentClientFrame owns the attached-client protocol.
78-func (d *Daemon) handleAttachmentClientFrame(capability attachmentCapability, f ports.Frame) bool {
79+func (d *Daemon) handleAttachmentClientFrame(capability attachmentCapability, f wire.Frame) bool {
7980 if !capability.current() {
8081 return false
8182 }
@@ -91,57 +92,57 @@ func (d *Daemon) handleAttachmentClientFrame(capability attachmentCapability, f
9192 d.afterAttachmentEffectAdmitted(effect.capability())
9293 }
9394 switch f.Type {
94- case ports.MsgInput:
95- if in, derr := ports.UnmarshalInput(f.Payload); derr == nil {
95+ case wire.MsgInput:
96+ if in, derr := wire.UnmarshalInput(f.Payload); derr == nil {
9697 d.handleSequencedInputForAttachment(effect, in.InputSeq, in.Data)
9798 }
98- case ports.MsgResize:
99- if rz, derr := ports.UnmarshalResize(f.Payload); derr == nil && effect.current() {
99+ case wire.MsgResize:
100+ if rz, derr := wire.UnmarshalResize(f.Payload); derr == nil && effect.current() {
100101 d.resizeAttachmentGeometryForLease(effect, rz.Geometry())
101102 }
102- case ports.MsgTheme:
103- if th, derr := ports.UnmarshalTheme(f.Payload); derr == nil {
103+ case wire.MsgTheme:
104+ if th, derr := wire.UnmarshalTheme(f.Payload); derr == nil {
104105 d.applyThemeForAttachment(effect, th)
105106 }
106- case ports.MsgImagePush:
107- if ip, derr := ports.UnmarshalImagePush(f.Payload); derr == nil {
107+ case wire.MsgImagePush:
108+ if ip, derr := wire.UnmarshalImagePush(f.Payload); derr == nil {
108109 d.handleSequencedImagePushForAttachment(effect, ip.InputSeq, ip)
109110 }
110- case ports.MsgClientNotice:
111- if notice, derr := ports.UnmarshalClientNotice(f.Payload); derr == nil {
111+ case wire.MsgClientNotice:
112+ if notice, derr := wire.UnmarshalClientNotice(f.Payload); derr == nil {
112113 d.handleClientNoticeForAttachment(effect, notice)
113114 } else {
114115 d.log.Warn("malformed client notice", "err", derr)
115116 }
116- case ports.MsgDetach:
117+ case wire.MsgDetach:
117118 if effect.current() {
118119 d.clientGoneForAttachment(effect, true)
119120 return true
120121 }
121- case ports.MsgAck:
122- if ack, derr := ports.UnmarshalAck(f.Payload); derr == nil {
122+ case wire.MsgAck:
123+ if ack, derr := wire.UnmarshalAck(f.Payload); derr == nil {
123124 d.ackOutput(effect, ack.Epoch, ack.State)
124125 }
125- case ports.MsgOutputResetRequest:
126- if _, derr := ports.UnmarshalOutputResetRequest(f.Payload); derr == nil {
126+ case wire.MsgOutputResetRequest:
127+ if _, derr := wire.UnmarshalOutputResetRequest(f.Payload); derr == nil {
127128 d.resetOutput(effect)
128129 }
129- case ports.MsgSamePeerSwitchRequest:
130- request, derr := ports.UnmarshalSamePeerSwitchRequest(f.Payload)
130+ case wire.MsgSamePeerSwitchRequest:
131+ request, derr := wire.UnmarshalSamePeerSwitchRequest(f.Payload)
131132 if derr != nil {
132133 d.log.Warn("malformed same-peer switch request", "err", derr)
133134 break
134135 }
135136 d.switchSamePeerForAttachment(effect, request)
136- case ports.MsgParkedRouteRequest:
137- request, derr := ports.UnmarshalParkedRouteRequest(f.Payload)
137+ case wire.MsgParkedRouteRequest:
138+ request, derr := wire.UnmarshalParkedRouteRequest(f.Payload)
138139 if derr != nil {
139140 d.log.Warn("malformed parked-route request", "err", derr)
140141 break
141142 }
142143 d.handleParkedRouteRequest(effect, request)
143- case ports.MsgRecentRouteSnapshot:
144- snapshot, derr := ports.UnmarshalRecentRouteSnapshot(f.Payload)
144+ case wire.MsgRecentRouteSnapshot:
145+ snapshot, derr := wire.UnmarshalRecentRouteSnapshot(f.Payload)
145146 if derr != nil {
146147 d.log.Warn("malformed recent route snapshot", "err", derr)
147148 break
@@ -154,16 +155,16 @@ func (d *Daemon) handleAttachmentClientFrame(capability attachmentCapability, f
154155 if err := d.sendCommittedRouteIdentityForAttachment(effect); err != nil {
155156 d.detachOnAttachmentSendError(effect.capability(), effect.transport.transport)
156157 }
157- case ports.MsgRouteAttentionSubscription:
158- subscription, derr := ports.UnmarshalRouteAttentionSubscription(f.Payload)
158+ case wire.MsgRouteAttentionSubscription:
159+ subscription, derr := wire.UnmarshalRouteAttentionSubscription(f.Payload)
159160 if derr != nil {
160161 d.log.Warn("malformed route attention subscription", "err", derr)
161162 break
162163 }
163164 effect.ac.setRouteAttentionSubscription(subscription)
164165 d.invalidateRender(effect.sess, effect.ac, false, "client_frame_routing.go:route-attention")
165- case ports.MsgRouteNavigationFailure:
166- failure, derr := ports.UnmarshalRouteNavigationFailure(f.Payload)
166+ case wire.MsgRouteNavigationFailure:
167+ failure, derr := wire.UnmarshalRouteNavigationFailure(f.Payload)
167168 if derr != nil {
168169 d.log.Warn("malformed route navigation failure", "err", derr)
169170 break
@@ -182,8 +183,8 @@ func (d *Daemon) handleAttachmentClientFrame(capability attachmentCapability, f
182183 message = "that recent route no longer exists"
183184 }
184185 d.notify(effect.sess, domain.NoticeWarn, domain.NoticeSessionUnavailable, message, nil)
185- case ports.MsgCommand:
186- request, derr := ports.UnmarshalCommandRequest(f.Payload)
186+ case wire.MsgCommand:
187+ request, derr := wire.UnmarshalCommandRequest(f.Payload)
187188 if derr != nil {
188189 return false
189190 }
@@ -200,7 +201,7 @@ func (d *Daemon) handleAttachmentClientFrame(capability attachmentCapability, f
200201 return true
201202 }
202203 resultEffect.End()
203- case ports.MsgPing:
204+ case wire.MsgPing:
204205 if err := effect.sendControl(framePong()); err != nil {
205206 effect.End()
206207 d.detachOnAttachmentSendError(effect.capability(), effect.transport.transport)
M internal/usecase/daemon/clipboard_test.go +14 -13
@@ -19,6 +19,7 @@ import (
1919 "github.com/bnema/vev/internal/ports"
2020 portsmocks "github.com/bnema/vev/internal/ports/mocks"
2121 "github.com/bnema/vev/internal/protocol"
22+ "github.com/bnema/vev/internal/protocol/wire"
2223 scopy "github.com/bnema/vev/internal/usecase/copy"
2324 )
2425
@@ -243,8 +244,8 @@ func TestPTYReaderForwardsOSC52ClipboardToAttachedClient(t *testing.T) {
243244
244245 var clipboardOutput string
245246 for range 3 {
246- f := awaitFrame(t, sends, ports.MsgOutput)
247- out, err := ports.UnmarshalOutput(f.Payload)
247+ f := awaitFrame(t, sends, wire.MsgOutput)
248+ out, err := wire.UnmarshalOutput(f.Payload)
248249 require.NoError(t, err)
249250 clipboardOutput = string(out.Data)
250251 if strings.Contains(clipboardOutput, "\x1b]52;c;aGVsbG8=\x07") {
@@ -276,7 +277,7 @@ func TestPTYReaderDropsOversizedClipboardPayload(t *testing.T) {
276277
277278 select {
278279 case f := <-sends:
279- require.NotEqual(t, ports.MsgOutput, f.Type)
280+ require.NotEqual(t, wire.MsgOutput, f.Type)
280281 default:
281282 }
282283 }
@@ -301,7 +302,7 @@ func TestPTYReaderDropsInvalidBase64Clipboard(t *testing.T) {
301302
302303 select {
303304 case f := <-sends:
304- require.NotEqual(t, ports.MsgOutput, f.Type)
305+ require.NotEqual(t, wire.MsgOutput, f.Type)
305306 default:
306307 }
307308 }
@@ -328,15 +329,15 @@ type staleClipboardErrorTransport struct {
328329 sends int
329330 }
330331
331-func (t *staleClipboardErrorTransport) Send(ports.Frame) error {
332+func (t *staleClipboardErrorTransport) Send(wire.Frame) error {
332333 t.mu.Lock()
333334 t.sends++
334335 t.mu.Unlock()
335336 return errors.New("stale clipboard send")
336337 }
337338
338-func (*staleClipboardErrorTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
339-func (*staleClipboardErrorTransport) Close() error { return nil }
339+func (*staleClipboardErrorTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
340+func (*staleClipboardErrorTransport) Close() error { return nil }
340341
341342 func (t *staleClipboardErrorTransport) sendCount() int {
342343 t.mu.Lock()
@@ -383,13 +384,13 @@ func newMovingClipboardErrorTransport() *movingClipboardErrorTransport {
383384 }
384385 }
385386
386-func (t *movingClipboardErrorTransport) Send(ports.Frame) error {
387+func (t *movingClipboardErrorTransport) Send(wire.Frame) error {
387388 t.startedOnce.Do(func() { close(t.started) })
388389 <-t.release
389390 return errors.New("clipboard send failed after pane move")
390391 }
391392
392-func (*movingClipboardErrorTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
393+func (*movingClipboardErrorTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
393394
394395 func (t *movingClipboardErrorTransport) Close() error {
395396 t.closeOnce.Do(func() { close(t.closed) })
@@ -509,8 +510,8 @@ func newBlockingClipboardTransport() *blockingClipboardTransport {
509510 }
510511 }
511512
512-func (tr *blockingClipboardTransport) Send(f ports.Frame) error {
513- out, err := ports.UnmarshalOutput(f.Payload)
513+func (tr *blockingClipboardTransport) Send(f wire.Frame) error {
514+ out, err := wire.UnmarshalOutput(f.Payload)
514515 if err != nil {
515516 return err
516517 }
@@ -532,5 +533,5 @@ func (tr *blockingClipboardTransport) Send(f ports.Frame) error {
532533 return nil
533534 }
534535
535-func (tr *blockingClipboardTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
536-func (tr *blockingClipboardTransport) Close() error { return nil }
536+func (tr *blockingClipboardTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
537+func (tr *blockingClipboardTransport) Close() error { return nil }
M internal/usecase/daemon/config_test.go +5 -5
@@ -8,8 +8,8 @@ import (
88
99 renderer "github.com/bnema/vev-vt"
1010 "github.com/bnema/vev/internal/domain"
11- "github.com/bnema/vev/internal/ports"
1211 "github.com/bnema/vev/internal/protocol"
12+ "github.com/bnema/vev/internal/protocol/wire"
1313 "github.com/bnema/vev/internal/usecase/keys"
1414 themeui "github.com/bnema/vev/internal/usecase/theme"
1515 )
@@ -318,11 +318,11 @@ func TestApplyConfigRepaintsActivePaletteWithoutReplacingModel(t *testing.T) {
318318 defer release()
319319 d, sess, ac, sends := newManualSessionWithPTYs(t, p)
320320 d.enterPalette(sess, ac)
321- awaitFrame(t, sends, ports.MsgOutput)
321+ awaitFrame(t, sends, wire.MsgOutput)
322322 d.handlePaletteInput(ac, []byte("new"))
323- awaitFrame(t, sends, ports.MsgOutput)
323+ awaitFrame(t, sends, wire.MsgOutput)
324324 d.handlePaletteInput(ac, []byte{0x0e})
325- awaitFrame(t, sends, ports.MsgOutput)
325+ awaitFrame(t, sends, wire.MsgOutput)
326326
327327 ac.overlays.paletteMu.Lock()
328328 model := ac.overlays.palette
@@ -334,7 +334,7 @@ func TestApplyConfigRepaintsActivePaletteWithoutReplacingModel(t *testing.T) {
334334 cfg := domain.Defaults()
335335 cfg.Palette = domain.PaletteConfig{Anchor: domain.AnchorTopLeft, AnchorSet: true}
336336 d.ApplyConfig(cfg)
337- awaitFrame(t, sends, ports.MsgOutput)
337+ awaitFrame(t, sends, wire.MsgOutput)
338338
339339 ac.overlays.paletteMu.Lock()
340340 defer ac.overlays.paletteMu.Unlock()
M internal/usecase/daemon/control.go +7 -6
@@ -13,19 +13,20 @@ import (
1313 "github.com/bnema/vev/internal/domain"
1414 "github.com/bnema/vev/internal/ports"
1515 "github.com/bnema/vev/internal/protocol"
16+ "github.com/bnema/vev/internal/protocol/wire"
1617 "github.com/bnema/vev/internal/usecase/command"
1718 "github.com/bnema/vev/internal/usecase/layout"
1819 )
1920
2021 // handleCommand serves one one-shot control request. The leading version is
2122 // checked before decoding the versioned body so newer layouts fail cleanly.
22-func (d *Daemon) handleCommand(tr ports.Transport, f ports.Frame) error {
23+func (d *Daemon) handleCommand(tr ports.Transport, f wire.Frame) error {
2324 defer func() { _ = tr.Close() }()
2425
25- if version, ok := ports.PeekCommandVersion(f.Payload); !ok || version != protocol.Version {
26+ if version, ok := wire.PeekCommandVersion(f.Payload); !ok || version != protocol.Version {
2627 return d.sendCommandResult(tr, protocol.CommandResult{Code: protocol.ErrVersionMismatch, Text: "protocol version mismatch"})
2728 }
28- request, err := ports.UnmarshalCommandRequest(f.Payload)
29+ request, err := wire.UnmarshalCommandRequest(f.Payload)
2930 if err != nil {
3031 return d.sendCommandResult(tr, protocol.CommandResult{Code: protocol.ErrInternal, Text: "malformed command request"})
3132 }
@@ -79,7 +80,7 @@ func (d *Daemon) sendCommandResult(tr ports.Transport, result protocol.CommandRe
7980 // boundedControlSend keeps one-shot control handlers from waiting forever on a
8081 // client that stopped reading. A timeout closes the exact transport so a
8182 // blocked Send can unwind before the handler returns.
82-func (d *Daemon) boundedControlSend(tr ports.Transport, frame ports.Frame) error {
83+func (d *Daemon) boundedControlSend(tr ports.Transport, frame wire.Frame) error {
8384 _, err := d.boundedSendWith(tr, func() error { return tr.Send(frame) })
8485 if errors.Is(err, errSendTimedOut) {
8586 _ = tr.Close()
@@ -87,8 +88,8 @@ func (d *Daemon) boundedControlSend(tr ports.Transport, frame ports.Frame) error
8788 return err
8889 }
8990
90-func frameCommandResult(result protocol.CommandResult) ports.Frame {
91- return ports.Frame{Type: ports.MsgCommandResult, Payload: ports.MarshalCommandResult(result)}
91+func frameCommandResult(result protocol.CommandResult) wire.Frame {
92+ return wire.Frame{Type: wire.MsgCommandResult, Payload: wire.MarshalCommandResult(result)}
9293 }
9394
9495 func (d *Daemon) dispatchCommand(ctx context.Context, request protocol.CommandRequest) protocol.CommandResult {
M internal/usecase/daemon/control_test.go +16 -15
@@ -17,6 +17,7 @@ import (
1717 "github.com/bnema/vev/internal/ports"
1818 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1919 "github.com/bnema/vev/internal/protocol"
20+ "github.com/bnema/vev/internal/protocol/wire"
2021 "github.com/bnema/vev/internal/usecase/command"
2122 "github.com/bnema/vev/internal/usecase/layout"
2223 "github.com/stretchr/testify/require"
@@ -196,7 +197,7 @@ func TestHandleCommandAttachedRejectionReturnsSendFailure(t *testing.T) {
196197 func TestHandleCommandRejectsVersionBeforeDecodeOrDispatch(t *testing.T) {
197198 // The version prefix is valid and mismatched, while the remainder is not a
198199 // decodable request. The daemon must still return ErrVersionMismatch.
199- frame := ports.Frame{Type: ports.MsgCommand, Payload: []byte{0, byte(protocol.Version + 1)}}
200+ frame := wire.Frame{Type: wire.MsgCommand, Payload: []byte{0, byte(protocol.Version + 1)}}
200201 tr, sends, _ := newConn(t, frame)
201202
202203 d := newTestDaemon(t, nil, stubClock{})
@@ -210,9 +211,9 @@ func TestHandleCommandRejectsVersionBeforeDecodeOrDispatch(t *testing.T) {
210211 func TestHandleConnRoutesCommand(t *testing.T) {
211212 d := newTestDaemon(t, nil, stubClock{})
212213 request := protocol.CommandRequest{Version: protocol.Version, Slug: "list-sessions"}
213- payload, err := ports.MarshalCommandRequest(request)
214+ payload, err := wire.MarshalCommandRequest(request)
214215 require.NoError(t, err)
215- frame := ports.Frame{Type: ports.MsgCommand, Payload: payload}
216+ frame := wire.Frame{Type: wire.MsgCommand, Payload: payload}
216217 tr, sends, _ := newConn(t, frame)
217218
218219 d.handleConn(tr)
@@ -1299,7 +1300,7 @@ func TestHandleCommandOppositeMoveCommandsDoNotDeadlock(t *testing.T) {
12991300 done <- struct{}{}
13001301 }()
13011302 close(start)
1302- for _, sends := range []chan ports.Frame{leftSends, rightSends} {
1303+ for _, sends := range []chan wire.Frame{leftSends, rightSends} {
13031304 select {
13041305 case <-done:
13051306 result := awaitCommandResult(t, sends)
@@ -1324,14 +1325,14 @@ func addNamedMoveDestination(d *Daemon, name, tabID, paneID string) *session {
13241325 return sess
13251326 }
13261327
1327-func commandFrame(t *testing.T, request protocol.CommandRequest) ports.Frame {
1328+func commandFrame(t *testing.T, request protocol.CommandRequest) wire.Frame {
13281329 t.Helper()
13291330 if request.Version == 0 {
13301331 request.Version = protocol.Version
13311332 }
1332- payload, err := ports.MarshalCommandRequest(request)
1333+ payload, err := wire.MarshalCommandRequest(request)
13331334 require.NoError(t, err)
1334- return ports.Frame{Type: ports.MsgCommand, Payload: payload}
1335+ return wire.Frame{Type: wire.MsgCommand, Payload: payload}
13351336 }
13361337
13371338 func sendCommand(t *testing.T, d *Daemon, request protocol.CommandRequest) protocol.CommandResult {
@@ -1342,10 +1343,10 @@ func sendCommand(t *testing.T, d *Daemon, request protocol.CommandRequest) proto
13421343 return awaitCommandResult(t, sends)
13431344 }
13441345
1345-func awaitCommandResult(t *testing.T, sends chan ports.Frame) protocol.CommandResult {
1346+func awaitCommandResult(t *testing.T, sends chan wire.Frame) protocol.CommandResult {
13461347 t.Helper()
1347- reply := awaitFrame(t, sends, ports.MsgCommandResult)
1348- result, err := ports.UnmarshalCommandResult(reply.Payload)
1348+ reply := awaitFrame(t, sends, wire.MsgCommandResult)
1349+ result, err := wire.UnmarshalCommandResult(reply.Payload)
13491350 require.NoError(t, err)
13501351 return result
13511352 }
@@ -1397,13 +1398,13 @@ func newBlockingControlSendTransport() *blockingControlSendTransport {
13971398 return &blockingControlSendTransport{started: make(chan struct{}), closed: make(chan struct{})}
13981399 }
13991400
1400-func (tr *blockingControlSendTransport) Send(ports.Frame) error {
1401+func (tr *blockingControlSendTransport) Send(wire.Frame) error {
14011402 tr.once.Do(func() { close(tr.started) })
14021403 <-tr.closed
14031404 return io.ErrClosedPipe
14041405 }
14051406
1406-func (*blockingControlSendTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
1407+func (*blockingControlSendTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
14071408 func (tr *blockingControlSendTransport) Close() error {
14081409 tr.once.Do(func() { close(tr.started) })
14091410 select {
@@ -1419,9 +1420,9 @@ type commandSendErrorTransport struct {
14191420 closed bool
14201421 }
14211422
1422-func (tr *commandSendErrorTransport) Send(ports.Frame) error { return tr.err }
1423-func (*commandSendErrorTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
1424-func (tr *commandSendErrorTransport) Close() error { tr.closed = true; return nil }
1423+func (tr *commandSendErrorTransport) Send(wire.Frame) error { return tr.err }
1424+func (*commandSendErrorTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
1425+func (tr *commandSendErrorTransport) Close() error { tr.closed = true; return nil }
14251426
14261427 func addControlSession(d *Daemon, name, tabID, paneID string) *session {
14271428 tb := newTabWithStableID(tabID, paneID, newQuietPTY(), domain.Size{Cols: 80, Rows: 22})
M internal/usecase/daemon/copymode_test.go +72 -71
@@ -19,6 +19,7 @@ import (
1919 "github.com/bnema/vev/internal/ports"
2020 portsmocks "github.com/bnema/vev/internal/ports/mocks"
2121 "github.com/bnema/vev/internal/protocol"
22+ "github.com/bnema/vev/internal/protocol/wire"
2223 scopy "github.com/bnema/vev/internal/usecase/copy"
2324 "github.com/bnema/vev/internal/usecase/layout"
2425 "github.com/bnema/vev/internal/usecase/ui"
@@ -47,7 +48,7 @@ func TestOwnedSynchronousSendReturnsCapturedTransportAcrossReplacement(t *testin
4748 replacement := &closeTrackingTransport{}
4849 sendErr := errors.New("owned send failed")
4950 ac := &attachedClient{output: newOutputStateStream()}
50- failed := &ownedSwapErrorTransport{ac: ac, replacement: replacement, err: sendErr, sent: make(chan ports.Frame, 1)}
51+ failed := &ownedSwapErrorTransport{ac: ac, replacement: replacement, err: sendErr, sent: make(chan wire.Frame, 1)}
5152 ac.replaceTransport(failed)
5253 sess := &session{sessionCore: sessionCore{name: "work", attachments: map[*attachedClient]struct{}{ac: {}}}}
5354 ac.setSession(sess)
@@ -57,7 +58,7 @@ func TestOwnedSynchronousSendReturnsCapturedTransportAcrossReplacement(t *testin
5758 require.ErrorIs(t, err, sendErr)
5859 require.Same(t, failed, used)
5960 require.Same(t, replacement, ac.transport())
60- out, decodeErr := ports.UnmarshalOutput((<-failed.sent).Payload)
61+ out, decodeErr := wire.UnmarshalOutput((<-failed.sent).Payload)
6162 require.NoError(t, decodeErr)
6263 require.Equal(t, []byte("copy"), out.Data)
6364 d.detachOnSendError(sess, ac, used)
@@ -71,29 +72,29 @@ type swapErrorTransport struct {
7172 err error
7273 }
7374
74-func (t *swapErrorTransport) Send(ports.Frame) error {
75+func (t *swapErrorTransport) Send(wire.Frame) error {
7576 t.ac.replaceTransport(t.replacement)
7677 return t.err
7778 }
7879
79-func (t *swapErrorTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
80-func (t *swapErrorTransport) Close() error { return nil }
80+func (t *swapErrorTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
81+func (t *swapErrorTransport) Close() error { return nil }
8182
8283 type ownedSwapErrorTransport struct {
8384 ac *attachedClient
8485 replacement ports.Transport
8586 err error
86- sent chan ports.Frame
87+ sent chan wire.Frame
8788 }
8889
89-func (t *ownedSwapErrorTransport) Send(f ports.Frame) error { return t.SendSynchronous(f) }
90-func (t *ownedSwapErrorTransport) SendSynchronous(f ports.Frame) error {
90+func (t *ownedSwapErrorTransport) Send(f wire.Frame) error { return t.SendSynchronous(f) }
91+func (t *ownedSwapErrorTransport) SendSynchronous(f wire.Frame) error {
9192 t.sent <- f
9293 t.ac.replaceTransport(t.replacement)
9394 return t.err
9495 }
95-func (t *ownedSwapErrorTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
96-func (t *ownedSwapErrorTransport) Close() error { return nil }
96+func (t *ownedSwapErrorTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
97+func (t *ownedSwapErrorTransport) Close() error { return nil }
9798
9899 // stubClock returns timers whose channel never fires, so a scheduler under it
99100 // blocks in its debounce loop until the session context is cancelled. Used by
@@ -117,7 +118,7 @@ func TestCopyModeDocumentCarriesPaneRowIDs(t *testing.T) {
117118 require.NotEqual(t, historyID, liveID)
118119
119120 d.enterCopyMode(sess, ac)
120- awaitFrame(t, sends, ports.MsgOutput)
121+ awaitFrame(t, sends, wire.MsgOutput)
121122 doc := ac.overlays.copyDocument
122123
123124 require.Equal(t, 0, doc.FindRowID(historyID))
@@ -197,7 +198,7 @@ func TestCopyModePaletteCommandEntersAndDoesNotForward(t *testing.T) {
197198 sess.tabs[0].focusedPane().screen.Write([]byte("live"))
198199
199200 d.handleInput(sess, ac, []byte("\x1b "))
200- awaitFrame(t, sends, ports.MsgOutput)
201+ awaitFrame(t, sends, wire.MsgOutput)
201202 d.handleInput(sess, ac, []byte("VIS\r"))
202203
203204 if ac.overlays.copyMode == nil {
@@ -208,16 +209,16 @@ func TestCopyModePaletteCommandEntersAndDoesNotForward(t *testing.T) {
208209 t.Fatalf("scrollback binding forwarded to PTY: %q", got)
209210 default:
210211 }
211- out := awaitFrame(t, sends, ports.MsgOutput)
212- msg, err := ports.UnmarshalOutput(out.Payload)
212+ out := awaitFrame(t, sends, wire.MsgOutput)
213+ msg, err := wire.UnmarshalOutput(out.Payload)
213214 require.NoError(t, err)
214215 if got := string(msg.Data); !strings.Contains(got, "[SCROLL]") || strings.Contains(got, "[SELECT]") || strings.Contains(got, "[COPY]") {
215216 t.Fatalf("passive scrollback paint = %q, want [SCROLL] without [SELECT]/[COPY]", got)
216217 }
217218
218219 d.handleInput(sess, ac, []byte(" "))
219- out = awaitFrame(t, sends, ports.MsgOutput)
220- msg, err = ports.UnmarshalOutput(out.Payload)
220+ out = awaitFrame(t, sends, wire.MsgOutput)
221+ msg, err = wire.UnmarshalOutput(out.Payload)
221222 require.NoError(t, err)
222223 if got := string(msg.Data); !strings.Contains(got, "[SELECT]") || strings.Contains(got, "[SCROLL]") {
223224 t.Fatalf("visual selection paint = %q, want [SELECT] without [SCROLL]", got)
@@ -233,9 +234,9 @@ func TestCopyModeSearchModalRoutesBatchedInputAfterSlash(t *testing.T) {
233234 copy(pane.screen.Frame.Row(1), testRow("beta alpha"))
234235
235236 d.enterCopyMode(sess, ac)
236- awaitFrame(t, sends, ports.MsgOutput)
237+ awaitFrame(t, sends, wire.MsgOutput)
237238 d.handleInput(sess, ac, []byte("/alpha\r"))
238- awaitFrame(t, sends, ports.MsgOutput)
239+ awaitFrame(t, sends, wire.MsgOutput)
239240
240241 require.Nil(t, ac.overlays.copySearch)
241242 require.NotNil(t, ac.overlays.copyMode)
@@ -257,32 +258,32 @@ func TestCopyModeSearchModalJumpsAndKeepsNavigation(t *testing.T) {
257258 copy(pane.screen.Frame.Row(2), testRow("gamma"))
258259
259260 d.enterCopyMode(sess, ac)
260- awaitFrame(t, sends, ports.MsgOutput)
261+ awaitFrame(t, sends, wire.MsgOutput)
261262 d.handleInput(sess, ac, []byte("/"))
262- out := awaitFrame(t, sends, ports.MsgOutput)
263- msg, err := ports.UnmarshalOutput(out.Payload)
263+ out := awaitFrame(t, sends, wire.MsgOutput)
264+ msg, err := wire.UnmarshalOutput(out.Payload)
264265 require.NoError(t, err)
265266 require.Contains(t, string(msg.Data), "Search")
266267 require.NotNil(t, ac.overlays.copySearch)
267268
268269 d.handleInput(sess, ac, []byte("alpha"))
269- out = awaitFrame(t, sends, ports.MsgOutput)
270- msg, err = ports.UnmarshalOutput(out.Payload)
270+ out = awaitFrame(t, sends, wire.MsgOutput)
271+ msg, err = wire.UnmarshalOutput(out.Payload)
271272 require.NoError(t, err)
272273 require.Contains(t, string(msg.Data), "/alpha")
273274 require.Contains(t, string(msg.Data), "1:1 alpha")
274275 require.Contains(t, string(msg.Data), "2:6 beta alpha")
275276
276277 d.handleInput(sess, ac, []byte("\r"))
277- awaitFrame(t, sends, ports.MsgOutput)
278+ awaitFrame(t, sends, wire.MsgOutput)
278279 require.Nil(t, ac.overlays.copySearch)
279280 require.Equal(t, 0, ac.overlays.copyMode.Cursor().Row)
280281
281282 d.handleInput(sess, ac, []byte("n"))
282- awaitFrame(t, sends, ports.MsgOutput)
283+ awaitFrame(t, sends, wire.MsgOutput)
283284 require.Equal(t, 1, ac.overlays.copyMode.Cursor().Row)
284285 d.handleInput(sess, ac, []byte("N"))
285- awaitFrame(t, sends, ports.MsgOutput)
286+ awaitFrame(t, sends, wire.MsgOutput)
286287 require.Equal(t, 0, ac.overlays.copyMode.Cursor().Row)
287288
288289 select {
@@ -301,17 +302,17 @@ func TestCopyModeSearchModalSelectionPreviewsBehindModal(t *testing.T) {
301302 copy(pane.screen.Frame.Row(2), testRow("gamma"))
302303
303304 d.enterCopyMode(sess, ac)
304- awaitFrame(t, sends, ports.MsgOutput)
305+ awaitFrame(t, sends, wire.MsgOutput)
305306
306307 d.handleInput(sess, ac, []byte("/"))
307- awaitFrame(t, sends, ports.MsgOutput)
308+ awaitFrame(t, sends, wire.MsgOutput)
308309 d.handleInput(sess, ac, []byte("alpha"))
309- awaitFrame(t, sends, ports.MsgOutput)
310+ awaitFrame(t, sends, wire.MsgOutput)
310311 require.NotNil(t, ac.overlays.copySearch)
311312 require.Equal(t, 0, ac.overlays.copyMode.Cursor().Row, "typing a query previews the selected first result behind the modal")
312313
313314 d.handleInput(sess, ac, []byte{0x0e})
314- awaitFrame(t, sends, ports.MsgOutput)
315+ awaitFrame(t, sends, wire.MsgOutput)
315316 require.NotNil(t, ac.overlays.copySearch)
316317 require.Equal(t, 1, ac.overlays.copyMode.Cursor().Row, "moving modal selection previews that result without Enter")
317318 }
@@ -325,9 +326,9 @@ func TestCopyModeSearchModalCapturesMouseAndClearsOnExit(t *testing.T) {
325326 copy(pane.screen.Frame.Row(0), testRow("live alpha"))
326327
327328 d.enterCopyMode(sess, ac)
328- awaitFrame(t, sends, ports.MsgOutput)
329+ awaitFrame(t, sends, wire.MsgOutput)
329330 d.handleInput(sess, ac, []byte("/alpha"))
330- awaitFrame(t, sends, ports.MsgOutput)
331+ awaitFrame(t, sends, wire.MsgOutput)
331332 require.NotNil(t, ac.overlays.copySearch)
332333 cursor := ac.overlays.copyMode.Cursor().Row
333334
@@ -336,11 +337,11 @@ func TestCopyModeSearchModalCapturesMouseAndClearsOnExit(t *testing.T) {
336337 require.Equal(t, cursor, ac.overlays.copyMode.Cursor().Row)
337338
338339 d.handleInput(sess, ac, []byte("\x1b"))
339- awaitFrame(t, sends, ports.MsgOutput)
340+ awaitFrame(t, sends, wire.MsgOutput)
340341 require.Nil(t, ac.overlays.copySearch)
341342 require.NotNil(t, ac.overlays.copyMode)
342343 d.handleInput(sess, ac, []byte("q"))
343- awaitFrame(t, sends, ports.MsgOutput)
344+ awaitFrame(t, sends, wire.MsgOutput)
344345 require.Nil(t, ac.overlays.copySearch)
345346 require.Nil(t, ac.overlays.copyMode)
346347 }
@@ -355,7 +356,7 @@ func TestCopyModeInputNotForwardedAndOSC52Copy(t *testing.T) {
355356 sess.tabs[0].focusedPane().screen.Write([]byte("live"))
356357
357358 d.enterCopyMode(sess, ac)
358- awaitFrame(t, sends, ports.MsgOutput)
359+ awaitFrame(t, sends, wire.MsgOutput)
359360 d.handleInput(sess, ac, []byte{'g', ' ', 'j', 'y'})
360361
361362 select {
@@ -363,8 +364,8 @@ func TestCopyModeInputNotForwardedAndOSC52Copy(t *testing.T) {
363364 t.Fatalf("copy-mode navigation forwarded to PTY: %q", got)
364365 default:
365366 }
366- out := awaitFrame(t, sends, ports.MsgOutput)
367- msg, err := ports.UnmarshalOutput(out.Payload)
367+ out := awaitFrame(t, sends, wire.MsgOutput)
368+ msg, err := wire.UnmarshalOutput(out.Payload)
368369 require.NoError(t, err)
369370 if got, want := string(msg.Data), "\x1b]52;c;b2xkMQpvbGQy\x07"; got != want {
370371 t.Fatalf("OSC52 = %q, want %q", got, want)
@@ -372,8 +373,8 @@ func TestCopyModeInputNotForwardedAndOSC52Copy(t *testing.T) {
372373 if ac.overlays.copyMode != nil {
373374 t.Fatal("copy mode still active after yank")
374375 }
375- live := awaitFrame(t, sends, ports.MsgOutput)
376- liveMsg, err := ports.UnmarshalOutput(live.Payload)
376+ live := awaitFrame(t, sends, wire.MsgOutput)
377+ liveMsg, err := wire.UnmarshalOutput(live.Payload)
377378 require.NoError(t, err)
378379 if strings.Contains(string(liveMsg.Data), "[COPY]") || strings.Contains(string(liveMsg.Data), "[SCROLL]") {
379380 t.Fatalf("live repaint still contains copy/scroll status: %q", string(liveMsg.Data))
@@ -386,8 +387,8 @@ func TestCopyModeInputNotForwardedAndOSC52Copy(t *testing.T) {
386387 }
387388
388389 d.paint(sess, ac, true, nil)
389- followup := awaitFrame(t, sends, ports.MsgOutput)
390- followupMsg, err := ports.UnmarshalOutput(followup.Payload)
390+ followup := awaitFrame(t, sends, wire.MsgOutput)
391+ followupMsg, err := wire.UnmarshalOutput(followup.Payload)
391392 require.NoError(t, err)
392393 if strings.Contains(string(followupMsg.Data), "copied 9 chars to clipboard") {
393394 t.Fatalf("copy feedback persisted after next repaint: %q", string(followupMsg.Data))
@@ -430,9 +431,9 @@ func TestScrollbackEvictionFeedsCopyModeYank(t *testing.T) {
430431 }
431432 var hg sync.WaitGroup
432433 hg.Go(func() { d.handleConn(tr) })
433- awaitFrame(t, sends, ports.MsgWelcome)
434+ awaitFrame(t, sends, wire.MsgWelcome)
434435 advanceRender() // initial coordinator invalidation
435- awaitFrame(t, sends, ports.MsgOutput)
436+ awaitFrame(t, sends, wire.MsgOutput)
436437
437438 for i := range 12 {
438439 reads <- fmt.Appendf(nil, "line-%02d\r\n", i)
@@ -469,8 +470,8 @@ func TestScrollbackEvictionFeedsCopyModeYank(t *testing.T) {
469470
470471 var payload string
471472 require.Eventually(t, func() bool {
472- out := awaitFrame(t, sends, ports.MsgOutput)
473- msg, err := ports.UnmarshalOutput(out.Payload)
473+ out := awaitFrame(t, sends, wire.MsgOutput)
474+ msg, err := wire.UnmarshalOutput(out.Payload)
474475 require.NoError(t, err)
475476 payload = string(msg.Data)
476477 return strings.HasPrefix(payload, "\x1b]52;c;")
@@ -495,14 +496,14 @@ func TestCopyModeEscapeRestoresLiveFullRepaint(t *testing.T) {
495496 sess.tabs[0].focusedPane().screen.Write([]byte("live"))
496497
497498 d.enterCopyMode(sess, ac)
498- awaitFrame(t, sends, ports.MsgOutput)
499+ awaitFrame(t, sends, wire.MsgOutput)
499500 d.handleInput(sess, ac, []byte("q"))
500501
501502 if ac.overlays.copyMode != nil {
502503 t.Fatal("copy mode still active after q")
503504 }
504- out := awaitFrame(t, sends, ports.MsgOutput)
505- msg, err := ports.UnmarshalOutput(out.Payload)
505+ out := awaitFrame(t, sends, wire.MsgOutput)
506+ msg, err := wire.UnmarshalOutput(out.Payload)
506507 require.NoError(t, err)
507508 if strings.Contains(string(msg.Data), "[COPY]") || !strings.Contains(string(msg.Data), "live") {
508509 t.Fatalf("exit repaint = %q, want live full repaint without copy status", string(msg.Data))
@@ -531,7 +532,7 @@ func TestCopyModeSplitArrowDoesNotExit(t *testing.T) {
531532 sess.tabs[0].focusedPane().screen.Write([]byte("live"))
532533
533534 d.enterCopyMode(sess, ac)
534- awaitFrame(t, sends, ports.MsgOutput)
535+ awaitFrame(t, sends, wire.MsgOutput)
535536 for _, input := range tc.input {
536537 d.handleInput(sess, ac, input)
537538 }
@@ -551,11 +552,11 @@ func TestCopyModeOversizedYankShowsTooLargeFeedback(t *testing.T) {
551552 copy(sess.tabs[0].focusedPane().screen.Frame.Row(0), testRow(longLine))
552553
553554 d.enterCopyMode(sess, ac)
554- awaitFrame(t, sends, ports.MsgOutput)
555+ awaitFrame(t, sends, wire.MsgOutput)
555556 d.handleInput(sess, ac, []byte{' ', 'y'})
556557
557- out := awaitFrame(t, sends, ports.MsgOutput)
558- msg, err := ports.UnmarshalOutput(out.Payload)
558+ out := awaitFrame(t, sends, wire.MsgOutput)
559+ msg, err := wire.UnmarshalOutput(out.Payload)
559560 require.NoError(t, err)
560561 if strings.Contains(string(msg.Data), "\x1b]52;") {
561562 t.Fatalf("oversized yank emitted OSC52: %q", string(msg.Data))
@@ -574,7 +575,7 @@ func TestCopyModeLoneEscapeExitsAfterDelay(t *testing.T) {
574575 sess.tabs[0].focusedPane().screen.Write([]byte("live"))
575576
576577 d.enterCopyMode(sess, ac)
577- awaitFrame(t, sends, ports.MsgOutput)
578+ awaitFrame(t, sends, wire.MsgOutput)
578579 d.handleInput(sess, ac, []byte("\x1b"))
579580 timer := <-clk.timers
580581 ac.overlays.copyMu.Lock()
@@ -598,11 +599,11 @@ func TestCopyModePendingEscapeDoesNotCloseNewMode(t *testing.T) {
598599 sess.tabs[0].focusedPane().screen.Write([]byte("live"))
599600
600601 d.enterCopyMode(sess, ac)
601- awaitFrame(t, sends, ports.MsgOutput)
602+ awaitFrame(t, sends, wire.MsgOutput)
602603 d.handleInput(sess, ac, []byte("\x1b"))
603604 timer := <-clk.timers
604605 d.enterCopyMode(sess, ac)
605- awaitFrame(t, sends, ports.MsgOutput)
606+ awaitFrame(t, sends, wire.MsgOutput)
606607
607608 ac.overlays.copyMu.Lock()
608609 require.Nil(t, ac.overlays.copyESC.timer)
@@ -641,14 +642,14 @@ func TestCopyModeEmptyYankDoesNotClearClipboard(t *testing.T) {
641642 sess.tabs[0].focusedPane().screen.Write([]byte("live"))
642643
643644 d.enterCopyMode(sess, ac)
644- awaitFrame(t, sends, ports.MsgOutput)
645+ awaitFrame(t, sends, wire.MsgOutput)
645646 d.handleInput(sess, ac, tc.input)
646647
647648 if ac.overlays.copyMode != nil {
648649 t.Fatal("copy mode still active after empty yank")
649650 }
650- out := awaitFrame(t, sends, ports.MsgOutput)
651- msg, err := ports.UnmarshalOutput(out.Payload)
651+ out := awaitFrame(t, sends, wire.MsgOutput)
652+ msg, err := wire.UnmarshalOutput(out.Payload)
652653 require.NoError(t, err)
653654 if strings.Contains(string(msg.Data), "\x1b]52;") {
654655 t.Fatalf("empty yank emitted OSC52 clipboard clear: %q", string(msg.Data))
@@ -667,7 +668,7 @@ func TestHandleCopyInputUsesImmutableSnapshotWithoutPaneLock(t *testing.T) {
667668 pane := testAttachmentTab(sess).focusedPane()
668669 pane.screen.Write([]byte("live"))
669670 d.enterCopyMode(sess, ac)
670- awaitFrame(t, sends, ports.MsgOutput)
671+ awaitFrame(t, sends, wire.MsgOutput)
671672
672673 pane.mu.Lock()
673674 defer pane.mu.Unlock()
@@ -769,7 +770,7 @@ func TestCopyModeCapturesSourceAndRetainsItAcrossFocusMove(t *testing.T) {
769770 }
770771
771772 d.enterCopyMode(sess, ac)
772- awaitFrame(t, sends, ports.MsgOutput)
773+ awaitFrame(t, sends, wire.MsgOutput)
773774 ac.overlays.copyMu.Lock()
774775 captured := ac.overlays.copyPane
775776 ac.overlays.copyMu.Unlock()
@@ -780,8 +781,8 @@ func TestCopyModeCapturesSourceAndRetainsItAcrossFocusMove(t *testing.T) {
780781 tb.mu.Unlock()
781782
782783 d.handleInput(sess, ac, []byte{'g', ' ', 'j', 'y'})
783- out := awaitFrame(t, sends, ports.MsgOutput)
784- msg, err := ports.UnmarshalOutput(out.Payload)
784+ out := awaitFrame(t, sends, wire.MsgOutput)
785+ msg, err := wire.UnmarshalOutput(out.Payload)
785786 require.NoError(t, err)
786787 wantOSC := "\x1b]52;c;" + base64.StdEncoding.EncodeToString([]byte(tc.wantText)) + "\x07"
787788 require.Equal(t, wantOSC, string(msg.Data))
@@ -809,15 +810,15 @@ func TestFloatingCopyModeWheelUsesCapturedSnapshot(t *testing.T) {
809810 fp.mu.Unlock()
810811
811812 d.enterCopyMode(sess, ac)
812- awaitFrame(t, sends, ports.MsgOutput)
813+ awaitFrame(t, sends, wire.MsgOutput)
813814 require.Equal(t, total-1, ac.overlays.copyMode.Cursor().Row)
814815
815816 d.copyWheel(sess, ac, -3)
816- awaitFrame(t, sends, ports.MsgOutput)
817+ awaitFrame(t, sends, wire.MsgOutput)
817818 require.Equal(t, total-4, ac.overlays.copyMode.Cursor().Row)
818819
819820 d.copyWheel(sess, ac, 3)
820- awaitFrame(t, sends, ports.MsgOutput)
821+ awaitFrame(t, sends, wire.MsgOutput)
821822 require.Nil(t, ac.overlays.copyMode, "wheel down reaching the captured bottom exits copy mode")
822823 }
823824
@@ -832,7 +833,7 @@ func TestFloatingCopyModeMouseSelectsFloatingRows(t *testing.T) {
832833 fp.screen.Write([]byte("live"))
833834
834835 d.enterCopyMode(sess, ac)
835- awaitFrame(t, sends, ports.MsgOutput)
836+ awaitFrame(t, sends, wire.MsgOutput)
836837 ac.overlays.copyMu.Lock()
837838 viewportTop := ac.overlays.copyMode.ViewportTop
838839 ac.overlays.copyMu.Unlock()
@@ -842,12 +843,12 @@ func TestFloatingCopyModeMouseSelectsFloatingRows(t *testing.T) {
842843 // offset); clicking a row must select exactly that row.
843844 press := fmt.Sprintf("\x1b[<0;%d;%dM", inner.X+3, inner.Y+3)
844845 d.handleInput(sess, ac, []byte(press))
845- awaitFrame(t, sends, ports.MsgOutput)
846+ awaitFrame(t, sends, wire.MsgOutput)
846847 require.Equal(t, viewportTop+1, ac.overlays.copyMode.Cursor().Row)
847848
848849 motion := fmt.Sprintf("\x1b[<32;%d;%dM", inner.X+3, inner.Y+4)
849850 d.handleInput(sess, ac, []byte(motion))
850- awaitFrame(t, sends, ports.MsgOutput)
851+ awaitFrame(t, sends, wire.MsgOutput)
851852 selection := ac.overlays.copyMode.Selection()
852853 require.True(t, selection.Enabled)
853854 require.Equal(t, viewportTop+1, selection.Anchor.Row)
@@ -863,7 +864,7 @@ func TestFloatingExitClearsCopyModeBeforeRepaint(t *testing.T) {
863864 fp.screen.Write([]byte("flt-live"))
864865
865866 d.enterCopyMode(sess, ac)
866- awaitFrame(t, sends, ports.MsgOutput)
867+ awaitFrame(t, sends, wire.MsgOutput)
867868 tb.mu.Lock()
868869 generation := tb.floating.generation
869870 publishPaneOwner(fp, sess, tb, generation)
@@ -896,8 +897,8 @@ func TestMouseDragCopyEntryCapturesSourceForYank(t *testing.T) {
896897 require.Same(t, pane, captured, "drag entry must capture the copy source pane")
897898
898899 d.handleInput(sess, ac, []byte("y"))
899- out := awaitFrame(t, sends, ports.MsgOutput)
900- msg, err := ports.UnmarshalOutput(out.Payload)
900+ out := awaitFrame(t, sends, wire.MsgOutput)
901+ msg, err := wire.UnmarshalOutput(out.Payload)
901902 require.NoError(t, err)
902903 want := "\x1b]52;c;" + base64.StdEncoding.EncodeToString([]byte("alpha\nb")) + "\x07"
903904 require.Equal(t, want, string(msg.Data))
M internal/usecase/daemon/daemon.go +14 -13
@@ -18,6 +18,7 @@ import (
1818 "github.com/bnema/vev/internal/domain"
1919 "github.com/bnema/vev/internal/ports"
2020 "github.com/bnema/vev/internal/protocol"
21+ "github.com/bnema/vev/internal/protocol/wire"
2122 "github.com/bnema/vev/internal/usecase/keys"
2223 recoveryusecase "github.com/bnema/vev/internal/usecase/recovery"
2324 )
@@ -1020,7 +1021,7 @@ func (d *Daemon) handleConn(tr ports.Transport) {
10201021 defer finishHandshake()
10211022 defer stopTransport()
10221023
1023- var first ports.Frame
1024+ var first wire.Frame
10241025 err := boundedHandshakeOperation(handshakeCtx, tr, func() error {
10251026 var recvErr error
10261027 first, recvErr = tr.Recv()
@@ -1033,27 +1034,27 @@ func (d *Daemon) handleConn(tr ports.Transport) {
10331034 return
10341035 }
10351036 switch first.Type {
1036- case ports.MsgList:
1037+ case wire.MsgList:
10371038 stopTransport()
10381039 finishHandshake()
10391040 d.handleList(tr)
1040- case ports.MsgCommand:
1041+ case wire.MsgCommand:
10411042 stopTransport()
10421043 finishHandshake()
10431044 if err := d.handleCommand(tr, first); err != nil {
10441045 d.log.Warn("command handler failed", "err", err)
10451046 }
1046- case ports.MsgRemotePreviewRequest:
1047+ case wire.MsgRemotePreviewRequest:
10471048 stopTransport()
10481049 finishHandshake()
10491050 if err := d.handleRemotePreview(tr, first); err != nil {
10501051 d.log.Warn("remote preview handler failed", "err", err)
10511052 }
1052- case ports.MsgKill:
1053+ case wire.MsgKill:
10531054 stopTransport()
10541055 finishHandshake()
10551056 d.handleKill(tr, first)
1056- case ports.MsgHello:
1057+ case wire.MsgHello:
10571058 d.handleHelloWithContext(handshakeCtx, timedOut, stopTransport, finishHandshake, tr, first)
10581059 default:
10591060 d.log.Warn("hello rejected", "err", "expected hello", "type", first.Type)
@@ -1110,10 +1111,10 @@ func (d *Daemon) handleList(tr ports.Transport) {
11101111 // handleKill terminates the requested live session or stopped named session,
11111112 // or all sessions, and closes the control connection; the resulting EOF is the
11121113 // client's success signal.
1113-func (d *Daemon) handleKill(tr ports.Transport, f ports.Frame) {
1114+func (d *Daemon) handleKill(tr ports.Transport, f wire.Frame) {
11141115 defer func() { _ = tr.Close() }()
11151116
1116- k, err := ports.UnmarshalKill(f.Payload)
1117+ k, err := wire.UnmarshalKill(f.Payload)
11171118 if err != nil {
11181119 _ = d.boundedControlSend(tr, frameError(protocol.ErrInternal, "malformed kill request"))
11191120 return
@@ -1151,7 +1152,7 @@ func (d *Daemon) handleKill(tr ports.Transport, f ports.Frame) {
11511152 // handleHello runs the attach handshake for direct package callers. Accepted
11521153 // connections use handleHelloWithContext so the deadline also covers the first
11531154 // frame read in handleConn.
1154-func (d *Daemon) handleHello(tr ports.Transport, f ports.Frame) {
1155+func (d *Daemon) handleHello(tr ports.Transport, f wire.Frame) {
11551156 handshakeCtx, timedOut, finishHandshake := d.newHandshakeContext(context.Background())
11561157 stopTransport := watchHandshakeTransport(handshakeCtx, tr)
11571158 defer finishHandshake()
@@ -1161,21 +1162,21 @@ func (d *Daemon) handleHello(tr ports.Transport, f ports.Frame) {
11611162
11621163 // handleHelloWithContext runs the attach handshake: version check, intent
11631164 // routing, Welcome, guaranteed first paint, then the per-connection input loop.
1164-func (d *Daemon) handleHelloWithContext(handshakeCtx context.Context, timedOut <-chan struct{}, stopHandshakeTransport, finishHandshake func(), tr ports.Transport, f ports.Frame) {
1165+func (d *Daemon) handleHelloWithContext(handshakeCtx context.Context, timedOut <-chan struct{}, stopHandshakeTransport, finishHandshake func(), tr ports.Transport, f wire.Frame) {
11651166 if err := handshakeContextError(context.Background(), timedOut, handshakeCtx.Err()); err != nil {
11661167 _ = tr.Close()
11671168 return
11681169 }
1169- sendHandshake := func(frame ports.Frame) error {
1170+ sendHandshake := func(frame wire.Frame) error {
11701171 err := boundedHandshakeOperation(handshakeCtx, tr, func() error { return tr.Send(frame) })
11711172 if err != nil {
11721173 return handshakeContextError(context.Background(), timedOut, err)
11731174 }
11741175 return nil
11751176 }
1176- h, err := ports.UnmarshalHello(f.Payload)
1177+ h, err := wire.UnmarshalHello(f.Payload)
11771178 if err != nil {
1178- if version, ok := ports.PeekHelloVersion(f.Payload); ok && version != protocol.Version {
1179+ if version, ok := wire.PeekHelloVersion(f.Payload); ok && version != protocol.Version {
11791180 d.log.Warn("hello rejected", "err", "protocol version mismatch", "version", version, "expected", protocol.Version)
11801181 _ = sendHandshake(frameError(protocol.ErrVersionMismatch, "protocol version mismatch"))
11811182 } else {
M internal/usecase/daemon/daemon_test.go +36 -35
@@ -17,6 +17,7 @@ import (
1717 "github.com/bnema/vev/internal/ports"
1818 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1919 "github.com/bnema/vev/internal/protocol"
20+ "github.com/bnema/vev/internal/protocol/wire"
2021 "github.com/bnema/vev/internal/usecase/keys"
2122 )
2223
@@ -236,11 +237,11 @@ func newFactorySeq(t *testing.T, ptys ...ports.PTY) *portsmocks.MockPTYFactory {
236237 // newConn scripts a MockTransport: Recv yields first then more (in order),
237238 // then blocks until the connection is released or Closed (returning io.EOF).
238239 // Every Send is captured on the returned channel.
239-func newConn(t *testing.T, first ports.Frame, more ...ports.Frame) (*portsmocks.MockTransport, chan ports.Frame, func()) {
240+func newConn(t *testing.T, first wire.Frame, more ...wire.Frame) (*portsmocks.MockTransport, chan wire.Frame, func()) {
240241 t.Helper()
241242 tr := portsmocks.NewMockTransport(t)
242- sends := make(chan ports.Frame, 64)
243- recvCh := make(chan ports.Frame, 1+len(more))
243+ sends := make(chan wire.Frame, 64)
244+ recvCh := make(chan wire.Frame, 1+len(more))
244245 recvCh <- first
245246 for _, f := range more {
246247 recvCh <- f
@@ -249,15 +250,15 @@ func newConn(t *testing.T, first ports.Frame, more ...ports.Frame) (*portsmocks.
249250 var once sync.Once
250251 closeDone := func() { once.Do(func() { close(done) }) }
251252
252- tr.EXPECT().Recv().RunAndReturn(func() (ports.Frame, error) {
253+ tr.EXPECT().Recv().RunAndReturn(func() (wire.Frame, error) {
253254 select {
254255 case f := <-recvCh:
255256 return f, nil
256257 case <-done:
257- return ports.Frame{}, io.EOF
258+ return wire.Frame{}, io.EOF
258259 }
259260 }).Maybe()
260- tr.EXPECT().Send(mock.Anything).RunAndReturn(func(f ports.Frame) error {
261+ tr.EXPECT().Send(mock.Anything).RunAndReturn(func(f wire.Frame) error {
261262 sends <- f
262263 return nil
263264 }).Maybe()
@@ -265,14 +266,14 @@ func newConn(t *testing.T, first ports.Frame, more ...ports.Frame) (*portsmocks.
265266 return tr, sends, closeDone
266267 }
267268
268-func mustHello(intent uint8, name string, sz domain.Size) ports.Frame {
269- return ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(protocol.Hello{
269+func mustHello(intent uint8, name string, sz domain.Size) wire.Frame {
270+ return wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(protocol.Hello{
270271 Version: protocol.Version, Intent: intent, Name: name, Size: sz, TermEnv: "xterm-256color",
271272 })}
272273 }
273274
274-func frameInput(data []byte) ports.Frame {
275- return ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: data})}
275+func frameInput(data []byte) wire.Frame {
276+ return wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: data})}
276277 }
277278
278279 func screenLineText(s *vt.Screen, y int) string {
@@ -284,7 +285,7 @@ func screenLineText(s *vt.Screen, y int) string {
284285 }
285286
286287 // awaitFrame waits for the next frame of type typ on ch, failing on timeout.
287-func awaitFrame(t *testing.T, ch chan ports.Frame, typ ports.MsgType) ports.Frame {
288+func awaitFrame(t *testing.T, ch chan wire.Frame, typ wire.MsgType) wire.Frame {
288289 t.Helper()
289290 deadline := time.After(2 * time.Second)
290291 for {
@@ -318,10 +319,10 @@ func sessionCount(d *Daemon) int {
318319
319320 func listSessions(t *testing.T, d *Daemon) protocol.Sessions {
320321 t.Helper()
321- tr, sends, _ := newConn(t, ports.Frame{Type: ports.MsgList, Payload: ports.MarshalList(protocol.List{})})
322+ tr, sends, _ := newConn(t, wire.Frame{Type: wire.MsgList, Payload: wire.MarshalList(protocol.List{})})
322323 d.handleList(tr)
323- f := awaitFrame(t, sends, ports.MsgSessions)
324- sessions, err := ports.UnmarshalSessions(f.Payload)
324+ f := awaitFrame(t, sends, wire.MsgSessions)
325+ sessions, err := wire.UnmarshalSessions(f.Payload)
325326 require.NoError(t, err)
326327 return sessions
327328 }
@@ -331,7 +332,7 @@ func listSessions(t *testing.T, d *Daemon) protocol.Sessions {
331332 // transport mock while channels make both sides of the wire deterministic.
332333 type welcomeBlockingTransport struct {
333334 tr *portsmocks.MockTransport
334- sends chan ports.Frame
335+ sends chan wire.Frame
335336 welcomeEntered chan struct{}
336337 releaseWelcome chan struct{}
337338 recvDone chan struct{}
@@ -342,23 +343,23 @@ type welcomeBlockingTransport struct {
342343 func newWelcomeBlockingTransport(t *testing.T) *welcomeBlockingTransport {
343344 t.Helper()
344345 b := &welcomeBlockingTransport{
345- sends: make(chan ports.Frame, 8),
346+ sends: make(chan wire.Frame, 8),
346347 welcomeEntered: make(chan struct{}),
347348 releaseWelcome: make(chan struct{}),
348349 recvDone: make(chan struct{}),
349350 }
350351 b.tr = portsmocks.NewMockTransport(t)
351- b.tr.EXPECT().Send(mock.Anything).RunAndReturn(func(f ports.Frame) error {
352+ b.tr.EXPECT().Send(mock.Anything).RunAndReturn(func(f wire.Frame) error {
352353 b.sends <- f
353- if f.Type == ports.MsgWelcome {
354+ if f.Type == wire.MsgWelcome {
354355 close(b.welcomeEntered)
355356 <-b.releaseWelcome
356357 }
357358 return nil
358359 }).Maybe()
359- b.tr.EXPECT().Recv().RunAndReturn(func() (ports.Frame, error) {
360+ b.tr.EXPECT().Recv().RunAndReturn(func() (wire.Frame, error) {
360361 <-b.recvDone
361- return ports.Frame{}, io.EOF
362+ return wire.Frame{}, io.EOF
362363 }).Maybe()
363364 b.tr.EXPECT().Close().Return(nil).Maybe()
364365 t.Cleanup(b.finish)
@@ -374,11 +375,11 @@ func (b *welcomeBlockingTransport) finish() {
374375 b.closeOnce.Do(func() { close(b.recvDone) })
375376 }
376377
377-func newCapturingTransport(t testing.TB) (*portsmocks.MockTransport, chan ports.Frame) {
378+func newCapturingTransport(t testing.TB) (*portsmocks.MockTransport, chan wire.Frame) {
378379 t.Helper()
379380 tr := portsmocks.NewMockTransport(t)
380- sends := make(chan ports.Frame, 64)
381- tr.EXPECT().Send(mock.Anything).RunAndReturn(func(f ports.Frame) error {
381+ sends := make(chan wire.Frame, 64)
382+ tr.EXPECT().Send(mock.Anything).RunAndReturn(func(f wire.Frame) error {
382383 sends <- f
383384 return nil
384385 }).Maybe()
@@ -386,22 +387,22 @@ func newCapturingTransport(t testing.TB) (*portsmocks.MockTransport, chan ports.
386387 return tr, sends
387388 }
388389
389-func newManualSessionWithPTYs(t testing.TB, ptys ...ports.PTY) (*Daemon, *session, *attachedClient, chan ports.Frame) {
390+func newManualSessionWithPTYs(t testing.TB, ptys ...ports.PTY) (*Daemon, *session, *attachedClient, chan wire.Frame) {
390391 t.Helper()
391392 return newManualSessionWithPTYsClock(t, stubClock{}, ptys...)
392393 }
393394
394-func newManualSessionWithPTYsClock(t testing.TB, clock ports.Clock, ptys ...ports.PTY) (*Daemon, *session, *attachedClient, chan ports.Frame) {
395+func newManualSessionWithPTYsClock(t testing.TB, clock ports.Clock, ptys ...ports.PTY) (*Daemon, *session, *attachedClient, chan wire.Frame) {
395396 t.Helper()
396397 return newManualSessionWithPTYsClockCleanup(t, clock, true, ptys...)
397398 }
398399
399-func newManualSessionWithPTYsCleanup(t testing.TB, registerCleanup bool, ptys ...ports.PTY) (*Daemon, *session, *attachedClient, chan ports.Frame) {
400+func newManualSessionWithPTYsCleanup(t testing.TB, registerCleanup bool, ptys ...ports.PTY) (*Daemon, *session, *attachedClient, chan wire.Frame) {
400401 t.Helper()
401402 return newManualSessionWithPTYsClockCleanup(t, stubClock{}, registerCleanup, ptys...)
402403 }
403404
404-func newManualSessionWithPTYsClockCleanup(t testing.TB, clock ports.Clock, registerCleanup bool, ptys ...ports.PTY) (*Daemon, *session, *attachedClient, chan ports.Frame) {
405+func newManualSessionWithPTYsClockCleanup(t testing.TB, clock ports.Clock, registerCleanup bool, ptys ...ports.PTY) (*Daemon, *session, *attachedClient, chan wire.Frame) {
405406 t.Helper()
406407 d := newTestDaemonWithCleanup(t, nil, clock, registerCleanup)
407408 tr, sends := newCapturingTransport(t)
@@ -432,7 +433,7 @@ func newManualSessionWithPTYsClockCleanup(t testing.TB, clock ports.Clock, regis
432433 return d, sess, ac, sends
433434 }
434435
435-func newManualTabSession(t *testing.T, n int) (*Daemon, *session, *attachedClient, chan ports.Frame, []func()) {
436+func newManualTabSession(t *testing.T, n int) (*Daemon, *session, *attachedClient, chan wire.Frame, []func()) {
436437 t.Helper()
437438 ptys := make([]ports.PTY, 0, n)
438439 releases := make([]func(), 0, n)
@@ -671,8 +672,8 @@ func TestHandleHelloDefersFreshOutputUntilWelcome(t *testing.T) {
671672
672673 awaitTestCompletion(t, tr.welcomeEntered, "timed out waiting for Welcome send")
673674 handshakeTimer := awaitHandshakeTimer(t, clock)
674- welcome := awaitFrame(t, tr.sends, ports.MsgWelcome)
675- require.Equal(t, ports.MsgWelcome, welcome.Type)
675+ welcome := awaitFrame(t, tr.sends, wire.MsgWelcome)
676+ require.Equal(t, wire.MsgWelcome, welcome.Type)
676677 sess := firstSession(d)
677678 sess.mu.Lock()
678679 ac := sess.snapshotAttachmentsLocked()[0]
@@ -695,8 +696,8 @@ func TestHandleHelloDefersFreshOutputUntilWelcome(t *testing.T) {
695696 requireNoCoordinatorOutputFrame(t, tr.sends)
696697
697698 tr.release()
698- output := awaitFrame(t, tr.sends, ports.MsgOutput)
699- first, err := ports.UnmarshalOutput(output.Payload)
699+ output := awaitFrame(t, tr.sends, wire.MsgOutput)
700+ first, err := wire.UnmarshalOutput(output.Payload)
700701 require.NoError(t, err)
701702 require.Zero(t, first.Base, "the first post-Welcome frame is full")
702703 tr.finish()
@@ -710,10 +711,10 @@ func TestHandleHelloDefersFreshOutputUntilWelcome(t *testing.T) {
710711 // MsgDetached send until the transport is closed (mirroring a full kernel
711712 // send buffer: only Close fails the in-flight write). Serve must still tear
712713
713-func mustOutputData(t *testing.T, sends chan ports.Frame) []byte {
714+func mustOutputData(t *testing.T, sends chan wire.Frame) []byte {
714715 t.Helper()
715- f := awaitFrame(t, sends, ports.MsgOutput)
716- out, err := ports.UnmarshalOutput(f.Payload)
716+ f := awaitFrame(t, sends, wire.MsgOutput)
717+ out, err := wire.UnmarshalOutput(f.Payload)
717718 require.NoError(t, err)
718719 return out.Data
719720 }
M internal/usecase/daemon/final_acceptance_test.go +4 -4
@@ -5,8 +5,8 @@ import (
55
66 vt "github.com/bnema/vev-vt"
77 "github.com/bnema/vev/internal/domain"
8- "github.com/bnema/vev/internal/ports"
98 "github.com/bnema/vev/internal/protocol"
9+ "github.com/bnema/vev/internal/protocol/wire"
1010 "github.com/bnema/vev/internal/usecase/layout"
1111 "github.com/stretchr/testify/require"
1212 )
@@ -149,9 +149,9 @@ func TestAcceptanceAttachmentStateIsolationAcrossResetResizeAndDetach(t *testing
149149 firstEpoch := first.output.currentEpoch()
150150 secondEpoch := second.output.currentEpoch()
151151
152- d.handleAttachmentClientFrame(secondToken, ports.Frame{
153- Type: ports.MsgOutputResetRequest,
154- Payload: ports.MarshalOutputResetRequest(protocol.OutputResetRequest{}),
152+ d.handleAttachmentClientFrame(secondToken, wire.Frame{
153+ Type: wire.MsgOutputResetRequest,
154+ Payload: wire.MarshalOutputResetRequest(protocol.OutputResetRequest{}),
155155 })
156156 require.Greater(t, second.output.currentEpoch(), secondEpoch)
157157 require.Equal(t, firstEpoch, first.output.currentEpoch(), "peer output reset crossed attachment boundary")
M internal/usecase/daemon/floating_test.go +6 -5
@@ -18,6 +18,7 @@ import (
1818 "github.com/bnema/vev/internal/ports"
1919 portsmocks "github.com/bnema/vev/internal/ports/mocks"
2020 "github.com/bnema/vev/internal/protocol"
21+ "github.com/bnema/vev/internal/protocol/wire"
2122 scopy "github.com/bnema/vev/internal/usecase/copy"
2223 "github.com/bnema/vev/internal/usecase/layout"
2324 themeui "github.com/bnema/vev/internal/usecase/theme"
@@ -482,7 +483,7 @@ func TestFloatingTeardownClearsCapturedCopyMode(t *testing.T) {
482483 }
483484
484485 func TestFloatingEOFRepaintsVisibleSlotOnly(t *testing.T) {
485- newCase := func(t *testing.T, state floatingState) (*Daemon, *session, *attachedClient, chan ports.Frame, *tab, *pane, func()) {
486+ newCase := func(t *testing.T, state floatingState) (*Daemon, *session, *attachedClient, chan wire.Frame, *tab, *pane, func()) {
486487 t.Helper()
487488 normalPTY, releaseNormal := newBlockingPTY(t)
488489 d, sess, ac, sends := newManualSessionWithPTYs(t, normalPTY)
@@ -513,8 +514,8 @@ func TestFloatingEOFRepaintsVisibleSlotOnly(t *testing.T) {
513514 // Establish a renderer shadow first. The EOF repaint must reset it and
514515 // redraw the underlying cell rather than depending on popup damage.
515516 d.paint(sess, ac, true, nil)
516- baseline := awaitFrame(t, sends, ports.MsgOutput)
517- baselineOutput, err := ports.UnmarshalOutput(baseline.Payload)
517+ baseline := awaitFrame(t, sends, wire.MsgOutput)
518+ baselineOutput, err := wire.UnmarshalOutput(baseline.Payload)
518519 require.NoError(t, err)
519520 require.Contains(t, string(baselineOutput.Data), "underlying-cell")
520521
@@ -541,8 +542,8 @@ func TestFloatingEOFRepaintsVisibleSlotOnly(t *testing.T) {
541542 require.Equal(t, floatingUninitialized, tb.floating.state)
542543 require.Nil(t, tb.floating.pane)
543544 tb.mu.Unlock()
544- repaint := awaitFrame(t, sends, ports.MsgOutput)
545- output, err := ports.UnmarshalOutput(repaint.Payload)
545+ repaint := awaitFrame(t, sends, wire.MsgOutput)
546+ output, err := wire.UnmarshalOutput(repaint.Payload)
546547 require.NoError(t, err)
547548 require.Zero(t, output.Base, "visible EOF must force a dependency-free full repaint")
548549 require.Contains(t, string(output.Data), "underlying-cell", "repaint must restore cells previously covered by the popup")
M internal/usecase/daemon/graphics_output_test.go +12 -11
@@ -15,6 +15,7 @@ import (
1515 "github.com/bnema/vev/internal/domain"
1616 "github.com/bnema/vev/internal/ports"
1717 "github.com/bnema/vev/internal/protocol"
18+ "github.com/bnema/vev/internal/protocol/wire"
1819 "github.com/bnema/vev/internal/usecase/layout"
1920 "github.com/stretchr/testify/require"
2021 )
@@ -53,7 +54,7 @@ type lateGraphicsCleanupTransport struct {
5354 started chan struct{}
5455 release chan struct{}
5556 finished chan struct{}
56- sends chan ports.Frame
57+ sends chan wire.Frame
5758 startedOnce sync.Once
5859 finishOnce sync.Once
5960 }
@@ -63,11 +64,11 @@ func newLateGraphicsCleanupTransport() *lateGraphicsCleanupTransport {
6364 started: make(chan struct{}),
6465 release: make(chan struct{}),
6566 finished: make(chan struct{}),
66- sends: make(chan ports.Frame, 1),
67+ sends: make(chan wire.Frame, 1),
6768 }
6869 }
6970
70-func (t *lateGraphicsCleanupTransport) Send(frame ports.Frame) error {
71+func (t *lateGraphicsCleanupTransport) Send(frame wire.Frame) error {
7172 t.startedOnce.Do(func() { close(t.started) })
7273 <-t.release
7374 t.sends <- frame
@@ -75,8 +76,8 @@ func (t *lateGraphicsCleanupTransport) Send(frame ports.Frame) error {
7576 return nil
7677 }
7778
78-func (*lateGraphicsCleanupTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
79-func (*lateGraphicsCleanupTransport) Close() error { return nil }
79+func (*lateGraphicsCleanupTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
80+func (*lateGraphicsCleanupTransport) Close() error { return nil }
8081
8182 func TestGraphicsOutputReplaysStaticKittenIcatFixture(t *testing.T) {
8283 state := newGraphicsOutputState()
@@ -127,8 +128,8 @@ func TestKittyAttachmentPaintCarriesGraphicsInTheOutputStateRecord(t *testing.T)
127128 pane.screen.Write(fixture)
128129
129130 d.paint(sess, ac, true, nil)
130- frame := awaitFrame(t, sends, ports.MsgOutput)
131- output, err := ports.UnmarshalOutput(frame.Payload)
131+ frame := awaitFrame(t, sends, wire.MsgOutput)
132+ output, err := wire.UnmarshalOutput(frame.Payload)
132133 require.NoError(t, err)
133134 require.Contains(t, string(output.Data), "\x1b_Ga=t,i=")
134135 require.Contains(t, string(output.Data), ",f=100")
@@ -157,8 +158,8 @@ func TestFreshCapabilityDowngradePreservesForeignTerminalGraphics(t *testing.T)
157158 require.False(t, ac.output.graphicsUnsupportedWarned.Load(), "replacement terminal must receive its own unsupported-graphics warning")
158159
159160 d.paint(sess, ac, true, nil)
160- frame := awaitFrame(t, sends, ports.MsgOutput)
161- output, err := ports.UnmarshalOutput(frame.Payload)
161+ frame := awaitFrame(t, sends, wire.MsgOutput)
162+ output, err := wire.UnmarshalOutput(frame.Payload)
162163 require.NoError(t, err)
163164 require.Positive(t, output.New)
164165 require.NotContains(t, string(output.Data), "\x1b_Ga=d,d=A", "vev must never globally delete terminal images")
@@ -449,7 +450,7 @@ func TestGraphicsNamespaceStaysQuarantinedAfterSocketSendSuccessWithoutTerminalF
449450 require.Nil(t, ac.output.graphicsOutput)
450451 sends := transport.Sends()
451452 require.Len(t, sends, 1)
452- cleanup, err := ports.UnmarshalOutput(sends[0].Payload)
453+ cleanup, err := wire.UnmarshalOutput(sends[0].Payload)
453454 require.NoError(t, err)
454455 require.Zero(t, cleanup.New, "graphics cleanup is an unacknowledged side-effect frame")
455456
@@ -572,7 +573,7 @@ func TestGraphicsNamespaceQuarantineFencesTimedOutLateDelete(t *testing.T) {
572573 close(transport.release)
573574 staleFrame := awaitTestValue(t, transport.sends, "late stale cleanup did not execute")
574575 awaitTestCompletion(t, transport.finished, "late stale cleanup did not return")
575- staleOutput, err := ports.UnmarshalOutput(staleFrame.Payload)
576+ staleOutput, err := wire.UnmarshalOutput(staleFrame.Payload)
576577 require.NoError(t, err)
577578 require.Contains(t, string(staleOutput.Data), "i="+strconv.FormatUint(oldID, 10))
578579 require.NotContains(t, string(staleOutput.Data), "i="+strconv.FormatUint(newID, 10), "late cleanup must never target the fresh attachment")
M internal/usecase/daemon/handshake_test.go +6 -6
@@ -10,8 +10,8 @@ import (
1010 "github.com/stretchr/testify/require"
1111
1212 "github.com/bnema/vev/internal/domain"
13- "github.com/bnema/vev/internal/ports"
1413 "github.com/bnema/vev/internal/protocol"
14+ "github.com/bnema/vev/internal/protocol/wire"
1515 )
1616
1717 type handshakeBlockingTransport struct {
@@ -29,8 +29,8 @@ func newHandshakeBlockingTransport(blockWelcome bool) *handshakeBlockingTranspor
2929 }
3030 }
3131
32-func (t *handshakeBlockingTransport) Send(frame ports.Frame) error {
33- if t.blockWelcome && frame.Type == ports.MsgWelcome {
32+func (t *handshakeBlockingTransport) Send(frame wire.Frame) error {
33+ if t.blockWelcome && frame.Type == wire.MsgWelcome {
3434 close(t.welcome)
3535 <-t.closed
3636 return io.ErrClosedPipe
@@ -38,9 +38,9 @@ func (t *handshakeBlockingTransport) Send(frame ports.Frame) error {
3838 return nil
3939 }
4040
41-func (t *handshakeBlockingTransport) Recv() (ports.Frame, error) {
41+func (t *handshakeBlockingTransport) Recv() (wire.Frame, error) {
4242 <-t.closed
43- return ports.Frame{}, io.ErrClosedPipe
43+ return wire.Frame{}, io.ErrClosedPipe
4444 }
4545
4646 func (t *handshakeBlockingTransport) Close() error {
@@ -183,7 +183,7 @@ func TestFailedResumeHandshakeRestoresParkedCredential(t *testing.T) {
183183 resumeTransport := newHandshakeBlockingTransport(true)
184184 done := make(chan struct{})
185185 go func() {
186- d.handleHello(resumeTransport, ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(helloResumeCapable(protocol.IntentResume, sess.name, token))})
186+ d.handleHello(resumeTransport, wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(helloResumeCapable(protocol.IntentResume, sess.name, token))})
187187 close(done)
188188 }()
189189
M internal/usecase/daemon/input_test.go +29 -29
@@ -15,9 +15,9 @@ import (
1515
1616 vt "github.com/bnema/vev-vt"
1717 "github.com/bnema/vev/internal/domain"
18- "github.com/bnema/vev/internal/ports"
1918 portsmocks "github.com/bnema/vev/internal/ports/mocks"
2019 "github.com/bnema/vev/internal/protocol"
20+ "github.com/bnema/vev/internal/protocol/wire"
2121 scopy "github.com/bnema/vev/internal/usecase/copy"
2222 "github.com/bnema/vev/internal/usecase/keys"
2323 "github.com/bnema/vev/internal/usecase/layout"
@@ -196,8 +196,8 @@ func TestSwitchTabFirstFrameDoesNotReuseSamePaneIDCapture(t *testing.T) {
196196 source.screen.Write([]byte("source"))
197197 source.mu.Unlock()
198198 d.paint(sess, ac, true, nil)
199- first := awaitFrame(t, sends, ports.MsgOutput)
200- firstOutput, err := ports.UnmarshalOutput(first.Payload)
199+ first := awaitFrame(t, sends, wire.MsgOutput)
200+ firstOutput, err := wire.UnmarshalOutput(first.Payload)
201201 require.NoError(t, err)
202202 terminal := vt.NewScreen(ac.size.Cols, ac.size.Rows)
203203 terminal.Write(firstOutput.Data)
@@ -212,8 +212,8 @@ func TestSwitchTabFirstFrameDoesNotReuseSamePaneIDCapture(t *testing.T) {
212212 daemonKeyHandler{d: d, ac: ac}.Action(keys.ActionSwitchTab2, nil)
213213 require.Equal(t, 1, testAttachmentTabIndex(sess))
214214
215- second := awaitFrame(t, sends, ports.MsgOutput)
216- secondOutput, err := ports.UnmarshalOutput(second.Payload)
215+ second := awaitFrame(t, sends, wire.MsgOutput)
216+ secondOutput, err := wire.UnmarshalOutput(second.Payload)
217217 require.NoError(t, err)
218218 require.Zero(t, secondOutput.Base, "tab switch must emit the complete target frame first")
219219 terminal.Write(secondOutput.Data)
@@ -309,11 +309,11 @@ func TestAltFToggleRetainedFloatingPaneRepaintsImmediately(t *testing.T) {
309309
310310 // Establish the client shadow while the retained popup is hidden.
311311 d.paint(sess, ac, true, nil)
312- mustApplyOutput(t, client, awaitFrame(t, sends, ports.MsgOutput))
312+ mustApplyOutput(t, client, awaitFrame(t, sends, wire.MsgOutput))
313313
314314 // Route the real Alt+F binding. Showing a retained pane must paint once.
315315 d.handleInput(sess, ac, []byte("\x1bf"))
316- mustApplyOutput(t, client, awaitFrame(t, sends, ports.MsgOutput))
316+ mustApplyOutput(t, client, awaitFrame(t, sends, wire.MsgOutput))
317317 require.Contains(t, strings.Join(frameRows(client.Frame), "\n"), "popup-content")
318318 require.Contains(t, strings.Join(frameRows(client.Frame), "\n"), "┌")
319319 select {
@@ -325,7 +325,7 @@ func TestAltFToggleRetainedFloatingPaneRepaintsImmediately(t *testing.T) {
325325 // The second real key must repaint immediately, rather than waiting for
326326 // unrelated output, while retaining the existing PTY and context.
327327 d.handleInput(sess, ac, []byte("\x1bf"))
328- mustApplyOutput(t, client, awaitFrame(t, sends, ports.MsgOutput))
328+ mustApplyOutput(t, client, awaitFrame(t, sends, wire.MsgOutput))
329329 require.NotContains(t, strings.Join(frameRows(client.Frame), "\n"), "popup-content")
330330 tb := testAttachmentTab(sess)
331331 tb.mu.Lock()
@@ -343,7 +343,7 @@ func TestAltFToggleRetainedFloatingPaneRepaintsImmediately(t *testing.T) {
343343 // A third show uses the installed pane; it must not launch or emit a second
344344 // resize/start frame.
345345 d.handleInput(sess, ac, []byte("\x1bf"))
346- mustApplyOutput(t, client, awaitFrame(t, sends, ports.MsgOutput))
346+ mustApplyOutput(t, client, awaitFrame(t, sends, wire.MsgOutput))
347347 require.Contains(t, strings.Join(frameRows(client.Frame), "\n"), "popup-content")
348348 tb.mu.Lock()
349349 require.Equal(t, floatingVisible, tb.floating.state)
@@ -630,13 +630,13 @@ func TestBracketedMultilinePasteForwardsDelimitersAndNewlines(t *testing.T) {
630630 func TestOverlayInputPrecedence(t *testing.T) {
631631 cases := []struct {
632632 name string
633- setup func(t *testing.T, d *Daemon, sess *session, ac *attachedClient, sends chan ports.Frame)
633+ setup func(t *testing.T, d *Daemon, sess *session, ac *attachedClient, sends chan wire.Frame)
634634 input []byte
635635 check func(t *testing.T, ac *attachedClient, writes chan []byte)
636636 }{
637637 {
638638 name: "prompt before palette picker copy and normal",
639- setup: func(t *testing.T, d *Daemon, sess *session, ac *attachedClient, sends chan ports.Frame) {
639+ setup: func(t *testing.T, d *Daemon, sess *session, ac *attachedClient, sends chan wire.Frame) {
640640 t.Helper()
641641 d.enterCopyMode(sess, ac)
642642 d.enterPicker(sess, ac)
@@ -655,7 +655,7 @@ func TestOverlayInputPrecedence(t *testing.T) {
655655 },
656656 {
657657 name: "palette before picker copy and normal",
658- setup: func(t *testing.T, d *Daemon, sess *session, ac *attachedClient, sends chan ports.Frame) {
658+ setup: func(t *testing.T, d *Daemon, sess *session, ac *attachedClient, sends chan wire.Frame) {
659659 t.Helper()
660660 d.enterCopyMode(sess, ac)
661661 d.enterPicker(sess, ac)
@@ -672,7 +672,7 @@ func TestOverlayInputPrecedence(t *testing.T) {
672672 },
673673 {
674674 name: "picker before copy and normal",
675- setup: func(t *testing.T, d *Daemon, sess *session, ac *attachedClient, sends chan ports.Frame) {
675+ setup: func(t *testing.T, d *Daemon, sess *session, ac *attachedClient, sends chan wire.Frame) {
676676 t.Helper()
677677 d.enterCopyMode(sess, ac)
678678 d.enterPicker(sess, ac)
@@ -689,7 +689,7 @@ func TestOverlayInputPrecedence(t *testing.T) {
689689 },
690690 {
691691 name: "copy before normal",
692- setup: func(t *testing.T, d *Daemon, sess *session, ac *attachedClient, sends chan ports.Frame) {
692+ setup: func(t *testing.T, d *Daemon, sess *session, ac *attachedClient, sends chan wire.Frame) {
693693 t.Helper()
694694 d.enterCopyMode(sess, ac)
695695 },
@@ -798,8 +798,8 @@ func TestPaletteBackSessionSendsClientPreviousRouteAction(t *testing.T) {
798798 })
799799
800800 require.NoError(t, d.backSessionForAttachment(effect))
801- frame := awaitFrame(t, sends, ports.MsgNavigateRecentRoute)
802- action, err := ports.UnmarshalRouteNavigationAction(frame.Payload)
801+ frame := awaitFrame(t, sends, wire.MsgNavigateRecentRoute)
802+ action, err := wire.UnmarshalRouteNavigationAction(frame.Payload)
803803 require.NoError(t, err)
804804 require.Equal(t, protocol.RouteNavigationAction{SnapshotGeneration: 3, Key: 2, Generation: 2}, action)
805805 }
@@ -825,7 +825,7 @@ func TestSwitchSourceDoesNotOwnPreviousRoute(t *testing.T) {
825825 require.Equal(t, uint64(0), ac.routeSnapshotCopy().Generation, "route ownership starts in the client")
826826 }
827827
828-func newRecentNavigationTestSessions(t *testing.T) (*Daemon, *session, *attachedClient, chan ports.Frame, []func()) {
828+func newRecentNavigationTestSessions(t *testing.T) (*Daemon, *session, *attachedClient, chan wire.Frame, []func()) {
829829 t.Helper()
830830 p1, release1 := newBlockingPTY(t)
831831 p2, release2 := newBlockingPTY(t)
@@ -881,8 +881,8 @@ func TestAltDDetachesCurrentClient(t *testing.T) {
881881 d.handleInput(sess, ac, []byte("DET\r"))
882882
883883 require.Empty(t, sess.snapshotAttachments())
884- f := awaitFrame(t, sends, ports.MsgDetached)
885- det, err := ports.UnmarshalDetached(f.Payload)
884+ f := awaitFrame(t, sends, wire.MsgDetached)
885+ det, err := wire.UnmarshalDetached(f.Payload)
886886 require.NoError(t, err)
887887 require.Equal(t, protocol.ReasonDetach, det.Reason)
888888 }
@@ -894,13 +894,13 @@ func TestRNSOpensPromptAndEnterPromotesEphemeralSession(t *testing.T) {
894894 sess.name = "0"
895895
896896 d.handleInput(sess, ac, []byte("\x1b "))
897- awaitFrame(t, sends, ports.MsgOutput)
897+ awaitFrame(t, sends, wire.MsgOutput)
898898 d.handleInput(sess, ac, []byte("RNS\r"))
899- awaitFrame(t, sends, ports.MsgOutput)
899+ awaitFrame(t, sends, wire.MsgOutput)
900900 require.True(t, ac.overlays.promptActive())
901901
902902 d.handleInput(sess, ac, []byte("\r"))
903- awaitFrame(t, sends, ports.MsgOutput)
903+ awaitFrame(t, sends, wire.MsgOutput)
904904
905905 require.False(t, ac.overlays.promptActive())
906906 require.False(t, sess.ephemeral)
@@ -917,16 +917,16 @@ func TestRNTOpensPromptAndRenamesActiveTab(t *testing.T) {
917917 selectTestAttachmentTab(sess, 1)
918918
919919 d.handleInput(sess, ac, []byte("\x1b "))
920- awaitFrame(t, sends, ports.MsgOutput)
920+ awaitFrame(t, sends, wire.MsgOutput)
921921 d.handleInput(sess, ac, []byte("RNT\r"))
922- out := awaitFrame(t, sends, ports.MsgOutput)
922+ out := awaitFrame(t, sends, wire.MsgOutput)
923923 require.True(t, ac.overlays.promptActive())
924- msg, err := ports.UnmarshalOutput(out.Payload)
924+ msg, err := wire.UnmarshalOutput(out.Payload)
925925 require.NoError(t, err)
926926 require.Contains(t, string(msg.Data), "> 2")
927927
928928 d.handleInput(sess, ac, []byte("\x7flogs\r"))
929- awaitFrame(t, sends, ports.MsgOutput)
929+ awaitFrame(t, sends, wire.MsgOutput)
930930
931931 require.False(t, ac.overlays.promptActive())
932932 require.Equal(t, "logs", sess.tabs[1].name)
@@ -1091,9 +1091,9 @@ func TestCopyModeMouseHorizontalReverseDragUsesExactOSC52(t *testing.T) {
10911091 d.handleInput(sess, ac, []byte("y"))
10921092 var msg protocol.Output
10931093 require.Eventually(t, func() bool {
1094- frame := awaitFrame(t, sends, ports.MsgOutput)
1094+ frame := awaitFrame(t, sends, wire.MsgOutput)
10951095 var err error
1096- msg, err = ports.UnmarshalOutput(frame.Payload)
1096+ msg, err = wire.UnmarshalOutput(frame.Payload)
10971097 return err == nil && string(msg.Data) == string(scopy.OSC52("lph")[0])
10981098 }, 2*time.Second, 5*time.Millisecond)
10991099 require.Equal(t, scopy.OSC52("lph")[0], msg.Data)
@@ -1573,7 +1573,7 @@ func TestMouseGatedWhileNoticesOverlayActive(t *testing.T) {
15731573
15741574 d.notices.record(domain.Notification{Code: domain.NoticePaneSpawn, Message: "m", Time: time.Unix(1, 0)})
15751575 d.enterNotices(sess, ac)
1576- awaitFrame(t, sends, ports.MsgOutput)
1576+ awaitFrame(t, sends, wire.MsgOutput)
15771577 require.True(t, ac.overlays.noticesActive())
15781578
15791579 // Same coordinates that TestMouseHitTestFocusesPaneAndTranslatesSGRColumns
M internal/usecase/daemon/notice_render_test.go +7 -7
@@ -9,24 +9,24 @@ import (
99
1010 vt "github.com/bnema/vev-vt"
1111 "github.com/bnema/vev/internal/domain"
12- "github.com/bnema/vev/internal/ports"
12+ "github.com/bnema/vev/internal/protocol/wire"
1313 )
1414
1515 // replayOutputFrames decodes and writes each MsgOutput frame's terminal bytes
1616 // into screen, growing its shadow the same way a real client would.
17-func replayOutputFrames(t *testing.T, screen *vt.Screen, frames []ports.Frame) {
17+func replayOutputFrames(t *testing.T, screen *vt.Screen, frames []wire.Frame) {
1818 t.Helper()
1919 for _, f := range frames {
20- require.Equal(t, ports.MsgOutput, f.Type)
21- out, err := ports.UnmarshalOutput(f.Payload)
20+ require.Equal(t, wire.MsgOutput, f.Type)
21+ out, err := wire.UnmarshalOutput(f.Payload)
2222 require.NoError(t, err)
2323 screen.Write(out.Data)
2424 }
2525 }
2626
2727 // drainAllFrames collects every frame already queued on sends without blocking.
28-func drainAllFrames(sends chan ports.Frame) []ports.Frame {
29- var out []ports.Frame
28+func drainAllFrames(sends chan wire.Frame) []wire.Frame {
29+ var out []wire.Frame
3030 for {
3131 select {
3232 case f := <-sends:
@@ -96,7 +96,7 @@ func TestPaintComposesNoticeToastTopRightAndExpiresOnTTL(t *testing.T) {
9696 awaitToastCount(t, ac, 0)
9797 select {
9898 case f := <-sends:
99- replayOutputFrames(t, screen, append([]ports.Frame{f}, drainAllFrames(sends)...))
99+ replayOutputFrames(t, screen, append([]wire.Frame{f}, drainAllFrames(sends)...))
100100 case <-time.After(2 * time.Second):
101101 t.Fatal("expiry did not invalidate the render")
102102 }
M internal/usecase/daemon/notices_overlay_test.go +31 -31
@@ -11,7 +11,7 @@ import (
1111
1212 renderer "github.com/bnema/vev-vt"
1313 "github.com/bnema/vev/internal/domain"
14- "github.com/bnema/vev/internal/ports"
14+ "github.com/bnema/vev/internal/protocol/wire"
1515 "github.com/bnema/vev/internal/usecase/notices"
1616 themeui "github.com/bnema/vev/internal/usecase/theme"
1717 )
@@ -47,13 +47,13 @@ func TestNoticesOpenViaPaletteCommandShowsModalWithHistory(t *testing.T) {
4747 d.notices.record(domain.Notification{Code: domain.NoticePaneSpawn, Severity: domain.NoticeError, Message: "couldn't open pane", Time: time.Unix(1, 0)})
4848
4949 d.enterPalette(sess, ac)
50- awaitFrame(t, sends, ports.MsgOutput)
50+ awaitFrame(t, sends, wire.MsgOutput)
5151 d.handlePaletteInput(ac, []byte("NTC\r"))
52- out := awaitFrame(t, sends, ports.MsgOutput)
52+ out := awaitFrame(t, sends, wire.MsgOutput)
5353
5454 require.False(t, ac.overlays.paletteActive())
5555 require.NotNil(t, ac.overlays.noticesOverlay)
56- msg, err := ports.UnmarshalOutput(out.Payload)
56+ msg, err := wire.UnmarshalOutput(out.Payload)
5757 require.NoError(t, err)
5858 require.Contains(t, string(msg.Data), "Notifications")
5959 require.Contains(t, string(msg.Data), "pane-spawn")
@@ -69,32 +69,32 @@ func TestNoticesJKNavigatesSelection(t *testing.T) {
6969 d.notices.record(domain.Notification{Code: domain.NoticeInternal, Message: "third", Time: time.Unix(3, 0)})
7070
7171 d.enterNotices(sess, ac)
72- awaitFrame(t, sends, ports.MsgOutput)
72+ awaitFrame(t, sends, wire.MsgOutput)
7373 selected, ok := ac.overlays.noticesOverlay.Selected()
7474 require.True(t, ok)
7575 require.Equal(t, "third", selected.Message)
7676
7777 d.handleInput(sess, ac, []byte("j"))
78- awaitFrame(t, sends, ports.MsgOutput)
78+ awaitFrame(t, sends, wire.MsgOutput)
7979 selected, ok = ac.overlays.noticesOverlay.Selected()
8080 require.True(t, ok)
8181 require.Equal(t, "second", selected.Message)
8282
8383 d.handleInput(sess, ac, []byte("j"))
84- awaitFrame(t, sends, ports.MsgOutput)
84+ awaitFrame(t, sends, wire.MsgOutput)
8585 selected, ok = ac.overlays.noticesOverlay.Selected()
8686 require.True(t, ok)
8787 require.Equal(t, "first", selected.Message)
8888
8989 // One more 'j' at the bottom must clamp rather than wrap.
9090 d.handleInput(sess, ac, []byte("j"))
91- awaitFrame(t, sends, ports.MsgOutput)
91+ awaitFrame(t, sends, wire.MsgOutput)
9292 selected, ok = ac.overlays.noticesOverlay.Selected()
9393 require.True(t, ok)
9494 require.Equal(t, "first", selected.Message)
9595
9696 d.handleInput(sess, ac, []byte("k"))
97- awaitFrame(t, sends, ports.MsgOutput)
97+ awaitFrame(t, sends, wire.MsgOutput)
9898 selected, ok = ac.overlays.noticesOverlay.Selected()
9999 require.True(t, ok)
100100 require.Equal(t, "second", selected.Message)
@@ -109,11 +109,11 @@ func TestNoticesQAndCtrlCCloseImmediatelyAndClearState(t *testing.T) {
109109 d.notices.record(domain.Notification{Code: domain.NoticePaneSpawn, Message: "m", Time: time.Unix(1, 0)})
110110
111111 d.enterNotices(sess, ac)
112- awaitFrame(t, sends, ports.MsgOutput)
112+ awaitFrame(t, sends, wire.MsgOutput)
113113 require.True(t, ac.overlays.noticesActive())
114114
115115 d.handleInput(sess, ac, []byte{key})
116- awaitFrame(t, sends, ports.MsgOutput)
116+ awaitFrame(t, sends, wire.MsgOutput)
117117
118118 require.False(t, ac.overlays.noticesActive())
119119 require.Nil(t, ac.overlays.noticesOverlay)
@@ -128,7 +128,7 @@ func TestNoticesLoneEscapeClosesAfterDelay(t *testing.T) {
128128 d.notices.record(domain.Notification{Code: domain.NoticePaneSpawn, Message: "m", Time: time.Unix(1, 0)})
129129
130130 d.enterNotices(sess, ac)
131- awaitFrame(t, sends, ports.MsgOutput)
131+ awaitFrame(t, sends, wire.MsgOutput)
132132
133133 clk := &signalClock{timers: make(chan *signalTimer, 1)}
134134 d.clock = clk
@@ -138,7 +138,7 @@ func TestNoticesLoneEscapeClosesAfterDelay(t *testing.T) {
138138
139139 timer.ch <- time.Now()
140140 require.Eventually(t, func() bool { return !ac.overlays.noticesActive() }, time.Second, 5*time.Millisecond)
141- awaitFrame(t, sends, ports.MsgOutput)
141+ awaitFrame(t, sends, wire.MsgOutput)
142142 }
143143
144144 func TestNoticesSplitArrowNavigatesWithoutClosing(t *testing.T) {
@@ -161,7 +161,7 @@ func TestNoticesSplitArrowNavigatesWithoutClosing(t *testing.T) {
161161 d.notices.record(domain.Notification{Code: domain.NoticeTabSpawn, Message: "second", Time: time.Unix(2, 0)})
162162
163163 d.enterNotices(sess, ac)
164- awaitFrame(t, sends, ports.MsgOutput)
164+ awaitFrame(t, sends, wire.MsgOutput)
165165 for _, input := range tc.input {
166166 d.handleInput(sess, ac, input)
167167 }
@@ -343,10 +343,10 @@ func TestNoticesOpenWithEmptyHistoryShowsPlaceholderWithoutPanic(t *testing.T) {
343343 defer release()
344344
345345 require.NotPanics(t, func() { d.enterNotices(sess, ac) })
346- out := awaitFrame(t, sends, ports.MsgOutput)
346+ out := awaitFrame(t, sends, wire.MsgOutput)
347347
348348 require.NotNil(t, ac.overlays.noticesOverlay)
349- msg, err := ports.UnmarshalOutput(out.Payload)
349+ msg, err := wire.UnmarshalOutput(out.Payload)
350350 require.NoError(t, err)
351351 require.Contains(t, string(msg.Data), "Notifications")
352352 require.Contains(t, string(msg.Data), "No notifications")
@@ -391,11 +391,11 @@ func TestYankLastNotificationCommandCopiesOSC52AndShowsFeedback(t *testing.T) {
391391 want := noticeYankPayload(n)
392392
393393 d.enterPalette(sess, ac)
394- awaitFrame(t, sends, ports.MsgOutput)
394+ awaitFrame(t, sends, wire.MsgOutput)
395395 d.handlePaletteInput(ac, []byte("YLN\r"))
396396
397- out := awaitFrame(t, sends, ports.MsgOutput)
398- msg, err := ports.UnmarshalOutput(out.Payload)
397+ out := awaitFrame(t, sends, wire.MsgOutput)
398+ msg, err := wire.UnmarshalOutput(out.Payload)
399399 require.NoError(t, err)
400400 payload := string(msg.Data)
401401 require.True(t, strings.HasPrefix(payload, "\x1b]52;c;"), "OSC52 payload = %q", payload)
@@ -404,8 +404,8 @@ func TestYankLastNotificationCommandCopiesOSC52AndShowsFeedback(t *testing.T) {
404404 require.NoError(t, err)
405405 require.Equal(t, want, string(decoded))
406406
407- live := awaitFrame(t, sends, ports.MsgOutput)
408- liveMsg, err := ports.UnmarshalOutput(live.Payload)
407+ live := awaitFrame(t, sends, wire.MsgOutput)
408+ liveMsg, err := wire.UnmarshalOutput(live.Payload)
409409 require.NoError(t, err)
410410 require.Contains(t, string(liveMsg.Data), "copied notification details")
411411 }
@@ -416,11 +416,11 @@ func TestYankLastNotificationWithEmptyHistoryShowsWarnToast(t *testing.T) {
416416 defer release()
417417
418418 d.enterPalette(sess, ac)
419- awaitFrame(t, sends, ports.MsgOutput)
419+ awaitFrame(t, sends, wire.MsgOutput)
420420 d.handlePaletteInput(ac, []byte("YLN\r"))
421421
422- out := awaitFrame(t, sends, ports.MsgOutput)
423- msg, err := ports.UnmarshalOutput(out.Payload)
422+ out := awaitFrame(t, sends, wire.MsgOutput)
423+ msg, err := wire.UnmarshalOutput(out.Payload)
424424 require.NoError(t, err)
425425 require.Contains(t, string(msg.Data), "no notifications yet")
426426
@@ -439,7 +439,7 @@ func TestNoticesYKeyYanksSelectedNotification(t *testing.T) {
439439 d.notices.record(domain.Notification{Code: domain.NoticeTabSpawn, Severity: domain.NoticeError, Message: "second", Time: time.Unix(2, 0)})
440440
441441 d.enterNotices(sess, ac)
442- awaitFrame(t, sends, ports.MsgOutput)
442+ awaitFrame(t, sends, wire.MsgOutput)
443443 selected, ok := ac.overlays.noticesOverlay.Selected()
444444 require.True(t, ok)
445445 require.Equal(t, "second", selected.Message)
@@ -447,8 +447,8 @@ func TestNoticesYKeyYanksSelectedNotification(t *testing.T) {
447447
448448 d.handleInput(sess, ac, []byte("y"))
449449
450- out := awaitFrame(t, sends, ports.MsgOutput)
451- msg, err := ports.UnmarshalOutput(out.Payload)
450+ out := awaitFrame(t, sends, wire.MsgOutput)
451+ msg, err := wire.UnmarshalOutput(out.Payload)
452452 require.NoError(t, err)
453453 payload := string(msg.Data)
454454 require.True(t, strings.HasPrefix(payload, "\x1b]52;c;"), "OSC52 payload = %q", payload)
@@ -481,11 +481,11 @@ func TestNoticesBatchedYankCapturesSelectionAtY(t *testing.T) {
481481 d.notices.record(domain.Notification{Code: domain.NoticeInternal, Message: "third", Time: time.Unix(3, 0)})
482482
483483 d.enterNotices(sess, ac)
484- awaitFrame(t, sends, ports.MsgOutput)
484+ awaitFrame(t, sends, wire.MsgOutput)
485485 d.handleInput(sess, ac, tt.input)
486486
487- out := awaitFrame(t, sends, ports.MsgOutput)
488- msg, err := ports.UnmarshalOutput(out.Payload)
487+ out := awaitFrame(t, sends, wire.MsgOutput)
488+ msg, err := wire.UnmarshalOutput(out.Payload)
489489 require.NoError(t, err)
490490 payload := strings.TrimSuffix(strings.TrimPrefix(string(msg.Data), "\x1b]52;c;"), "\a")
491491 decoded, err := base64.StdEncoding.DecodeString(payload)
@@ -505,7 +505,7 @@ func TestNoticesYKeyWithNoSelectionIsNoOp(t *testing.T) {
505505 defer release()
506506
507507 require.NotPanics(t, func() { d.enterNotices(sess, ac) })
508- awaitFrame(t, sends, ports.MsgOutput)
508+ awaitFrame(t, sends, wire.MsgOutput)
509509
510510 require.NotPanics(t, func() { d.handleInput(sess, ac, []byte("y")) })
511511 require.True(t, ac.overlays.noticesActive())
M internal/usecase/daemon/notify_test.go +10 -9
@@ -17,6 +17,7 @@ import (
1717 "github.com/bnema/vev/internal/ports"
1818 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1919 "github.com/bnema/vev/internal/protocol"
20+ "github.com/bnema/vev/internal/protocol/wire"
2021 )
2122
2223 func TestReportAttachmentErrorPreservesLocalOrNilScope(t *testing.T) {
@@ -56,11 +57,11 @@ func TestMalformedClientNoticeIsIgnored(t *testing.T) {
5657 d.attachCoordinator(sess, nil, ac, true)
5758 tr, ok := ac.tr.(*portsmocks.MockTransport)
5859 require.True(t, ok, "attached client transport must be a MockTransport")
59- frames := []ports.Frame{
60- {Type: ports.MsgClientNotice, Payload: []byte{0xff}},
61- {Type: ports.MsgDetach, Payload: ports.MarshalDetach(protocol.Detach{})},
60+ frames := []wire.Frame{
61+ {Type: wire.MsgClientNotice, Payload: []byte{0xff}},
62+ {Type: wire.MsgDetach, Payload: wire.MarshalDetach(protocol.Detach{})},
6263 }
63- tr.EXPECT().Recv().RunAndReturn(func() (ports.Frame, error) {
64+ tr.EXPECT().Recv().RunAndReturn(func() (wire.Frame, error) {
6465 frame := frames[0]
6566 frames = frames[1:]
6667 return frame, nil
@@ -268,7 +269,7 @@ func (c *noticeClock) durations() []time.Duration {
268269 // caller-supplied clock so toast TTLs are deterministic. The session has no
269270 // render coordinator, so invalidateRender paints directly and every repaint is
270271 // observable as a frame on the returned channel.
271-func newNoticeFixture(t *testing.T, clk ports.Clock) (*Daemon, *session, *attachedClient, chan ports.Frame) {
272+func newNoticeFixture(t *testing.T, clk ports.Clock) (*Daemon, *session, *attachedClient, chan wire.Frame) {
272273 t.Helper()
273274 p, _ := newBlockingPTY(t)
274275 d := newTestDaemon(t, nil, clk)
@@ -864,8 +865,8 @@ func TestToastExpiresOnFakeClock(t *testing.T) {
864865
865866 d.notify(sess, domain.NoticeError, domain.NoticePaneSpawn, "could not open pane", nil)
866867 awaitToastCount(t, ac, 1)
867- shown := awaitFrame(t, sends, ports.MsgOutput)
868- shownOutput, err := ports.UnmarshalOutput(shown.Payload)
868+ shown := awaitFrame(t, sends, wire.MsgOutput)
869+ shownOutput, err := wire.UnmarshalOutput(shown.Payload)
869870 require.NoError(t, err)
870871 terminal := vt.NewScreen(80, 25)
871872 terminal.Write(shownOutput.Data)
@@ -880,8 +881,8 @@ func TestToastExpiresOnFakeClock(t *testing.T) {
880881 _, overflow := visibleToasts(ac)
881882 require.Zero(t, overflow, "overflow resets once no toast is visible")
882883
883- expired := awaitFrame(t, sends, ports.MsgOutput)
884- expiredOutput, err := ports.UnmarshalOutput(expired.Payload)
884+ expired := awaitFrame(t, sends, wire.MsgOutput)
885+ expiredOutput, err := wire.UnmarshalOutput(expired.Payload)
885886 require.NoError(t, err)
886887 terminal.Write(expiredOutput.Data)
887888 ac.sendMu.Lock()
M internal/usecase/daemon/output_state_sync_test.go +37 -36
@@ -13,36 +13,37 @@ import (
1313 "github.com/bnema/vev/internal/domain"
1414 "github.com/bnema/vev/internal/ports"
1515 "github.com/bnema/vev/internal/protocol"
16+ "github.com/bnema/vev/internal/protocol/wire"
1617 "github.com/stretchr/testify/require"
1718 )
1819
1920 type datagramTestTransport struct {
20- sends chan ports.Frame
21- recv chan ports.Frame
21+ sends chan wire.Frame
22+ recv chan wire.Frame
2223 once sync.Once
2324 }
2425
2526 type failingOutputTransport struct{}
2627
27-func (failingOutputTransport) Send(ports.Frame) error { return errors.New("send failed") }
28-func (failingOutputTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
29-func (failingOutputTransport) Close() error { return nil }
28+func (failingOutputTransport) Send(wire.Frame) error { return errors.New("send failed") }
29+func (failingOutputTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
30+func (failingOutputTransport) Close() error { return nil }
3031
3132 type asyncPaintTransport struct {
32- syncSends chan ports.Frame
33- asyncSends chan ports.Frame
33+ syncSends chan wire.Frame
34+ asyncSends chan wire.Frame
3435 }
3536
36-func (t *asyncPaintTransport) Send(f ports.Frame) error { t.syncSends <- f; return nil }
37-func (t *asyncPaintTransport) SendAsync(f ports.Frame) error { t.asyncSends <- f; return nil }
38-func (t *asyncPaintTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
39-func (t *asyncPaintTransport) Close() error { return nil }
37+func (t *asyncPaintTransport) Send(f wire.Frame) error { t.syncSends <- f; return nil }
38+func (t *asyncPaintTransport) SendAsync(f wire.Frame) error { t.asyncSends <- f; return nil }
39+func (t *asyncPaintTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
40+func (t *asyncPaintTransport) Close() error { return nil }
4041
4142 type timedSideEffectTransport struct {
4243 closeTrackingTransport
4344 }
4445
45-func (t *timedSideEffectTransport) SendSynchronous(f ports.Frame) error {
46+func (t *timedSideEffectTransport) SendSynchronous(f wire.Frame) error {
4647 return t.Send(f)
4748 }
4849
@@ -54,14 +55,14 @@ func (noWatchdogClock) NewTimer(time.Duration) ports.Timer {
5455 }
5556
5657 func newDatagramTestTransport() *datagramTestTransport {
57- return &datagramTestTransport{sends: make(chan ports.Frame, 64), recv: make(chan ports.Frame, 64)}
58+ return &datagramTestTransport{sends: make(chan wire.Frame, 64), recv: make(chan wire.Frame, 64)}
5859 }
5960
60-func (t *datagramTestTransport) Send(f ports.Frame) error { t.sends <- f; return nil }
61-func (t *datagramTestTransport) Recv() (ports.Frame, error) {
61+func (t *datagramTestTransport) Send(f wire.Frame) error { t.sends <- f; return nil }
62+func (t *datagramTestTransport) Recv() (wire.Frame, error) {
6263 f, ok := <-t.recv
6364 if !ok {
64- return ports.Frame{}, io.EOF
65+ return wire.Frame{}, io.EOF
6566 }
6667 return f, nil
6768 }
@@ -90,20 +91,20 @@ func TestDatagramAttachPipelinesRendererBeforeAck(t *testing.T) {
9091
9192 sess.tabs[0].focusedPane().screen.Write([]byte("A"))
9293 d.paint(sess, ac, false, nil)
93- first := awaitFrame(t, tr.sends, ports.MsgOutput)
94- out, err := ports.UnmarshalOutput(first.Payload)
94+ first := awaitFrame(t, tr.sends, wire.MsgOutput)
95+ out, err := wire.UnmarshalOutput(first.Payload)
9596 require.NoError(t, err)
9697 require.Equal(t, uint64(1), out.New)
9798
9899 sess.tabs[0].focusedPane().screen.Write([]byte("\rB"))
99100 d.paint(sess, ac, false, nil)
100- updated := awaitFrame(t, tr.sends, ports.MsgOutput)
101- out, err = ports.UnmarshalOutput(updated.Payload)
101+ updated := awaitFrame(t, tr.sends, wire.MsgOutput)
102+ out, err = wire.UnmarshalOutput(updated.Payload)
102103 require.NoError(t, err)
103104 require.Equal(t, uint64(2), out.New)
104105 // Both states were emitted before the MsgAck; the ordered dependency chain
105106 // must therefore remain valid without waiting for renderer acknowledgement.
106- tr.recv <- ports.Frame{Type: ports.MsgAck, Payload: mustMarshalAck(protocol.Ack{Epoch: out.Epoch, State: out.New})}
107+ tr.recv <- wire.Frame{Type: wire.MsgAck, Payload: mustMarshalAck(protocol.Ack{Epoch: out.Epoch, State: out.New})}
107108 require.NoError(t, tr.Close())
108109 d.runConnLoop(ac)
109110
@@ -117,13 +118,13 @@ func TestPaintExplicitlyUsesAsyncTransportCapability(t *testing.T) {
117118 p, releasePTY := newBlockingPTY(t)
118119 defer releasePTY()
119120 d, sess, ac, _ := newManualSessionWithPTYs(t, p)
120- tr := &asyncPaintTransport{syncSends: make(chan ports.Frame, 1), asyncSends: make(chan ports.Frame, 1)}
121+ tr := &asyncPaintTransport{syncSends: make(chan wire.Frame, 1), asyncSends: make(chan wire.Frame, 1)}
121122 ac.replaceTransport(tr)
122123 sess.tabs[0].focusedPane().screen.Write([]byte("A"))
123124
124125 d.paint(sess, ac, false, nil)
125126
126- awaitFrame(t, tr.asyncSends, ports.MsgOutput)
127+ awaitFrame(t, tr.asyncSends, wire.MsgOutput)
127128 select {
128129 case frame := <-tr.syncSends:
129130 t.Fatalf("paint used synchronous Send: %+v", frame)
@@ -155,7 +156,7 @@ func TestDatagramMultipleUnackedScrollPaintsMatchLatestFrame(t *testing.T) {
155156 pane.screen.ClearDamage()
156157 client := vt.NewScreen(80, 25)
157158 d.paint(sess, ac, true, nil)
158- first := mustApplyOutput(t, client, awaitFrame(t, tr.sends, ports.MsgOutput))
159+ first := mustApplyOutput(t, client, awaitFrame(t, tr.sends, wire.MsgOutput))
159160 ac.ackOutputState(first.Epoch, first.New)
160161
161162 // Preserve the frame after one scroll while inducing a real VT scroll
@@ -175,7 +176,7 @@ func TestDatagramMultipleUnackedScrollPaintsMatchLatestFrame(t *testing.T) {
175176 pane.screen.Write([]byte("\nq"))
176177 pane.screen.Frame = desired.Clone()
177178 d.paint(sess, ac, false, nil)
178- mustApplyOutput(t, client, awaitFrame(t, tr.sends, ports.MsgOutput))
179+ mustApplyOutput(t, client, awaitFrame(t, tr.sends, wire.MsgOutput))
179180 }
180181
181182 require.Equal(t, 'B', client.Frame.At(0, 1).Rune,
@@ -198,9 +199,9 @@ func outputStateRow(row []renderer.Cell) string {
198199 return string(runes)
199200 }
200201
201-func mustApplyOutput(t *testing.T, screen *vt.Screen, frame ports.Frame) protocol.Output {
202+func mustApplyOutput(t *testing.T, screen *vt.Screen, frame wire.Frame) protocol.Output {
202203 t.Helper()
203- out, err := ports.UnmarshalOutput(frame.Payload)
204+ out, err := wire.UnmarshalOutput(frame.Payload)
204205 require.NoError(t, err)
205206 screen.Write(out.Data)
206207 return out
@@ -213,7 +214,7 @@ func TestLocalAttachStillAdvancesRendererOnSend(t *testing.T) {
213214
214215 sess.tabs[0].focusedPane().screen.Write([]byte("A"))
215216 d.paint(sess, ac, false, nil)
216- awaitFrame(t, sends, ports.MsgOutput)
217+ awaitFrame(t, sends, wire.MsgOutput)
217218
218219 // The first paint updates the renderer shadow without waiting for MsgAck.
219220 d.paint(sess, ac, false, nil)
@@ -228,13 +229,13 @@ func TestLocalOutputAckDoesNotMoveRendererShadowBackward(t *testing.T) {
228229
229230 pane.screen.Write([]byte("A"))
230231 d.paint(sess, ac, false, nil)
231- first := awaitFrame(t, sends, ports.MsgOutput)
232- out, err := ports.UnmarshalOutput(first.Payload)
232+ first := awaitFrame(t, sends, wire.MsgOutput)
233+ out, err := wire.UnmarshalOutput(first.Payload)
233234 require.NoError(t, err)
234235
235236 pane.screen.Write([]byte("\rB"))
236237 d.paint(sess, ac, false, nil)
237- awaitFrame(t, sends, ports.MsgOutput)
238+ awaitFrame(t, sends, wire.MsgOutput)
238239
239240 ac.ackOutputState(out.Epoch, out.New)
240241 d.paint(sess, ac, false, nil)
@@ -252,7 +253,7 @@ func TestRawTerminalSideEffectDoesNotEnterFullOutputWindow(t *testing.T) {
252253 require.Equal(t, uint64(maxUnackedOutputStates), ac.output.outstanding())
253254 sends := tr.Sends()
254255 require.Len(t, sends, 1)
255- out, err := ports.UnmarshalOutput(sends[0].Payload)
256+ out, err := wire.UnmarshalOutput(sends[0].Payload)
256257 require.NoError(t, err)
257258 require.Zero(t, out.Base)
258259 require.Zero(t, out.New)
@@ -304,7 +305,7 @@ func TestResizeGrowthFirstFrameIncludesConcurrentPTYRedraw(t *testing.T) {
304305 pane.screen.Write([]byte(strings.Repeat("A", 79)))
305306 d.paint(sess, ac, true, nil)
306307 client := vt.NewScreen(80, 24)
307- initial := mustApplyOutput(t, client, awaitFrame(t, sends, ports.MsgOutput))
308+ initial := mustApplyOutput(t, client, awaitFrame(t, sends, wire.MsgOutput))
308309 ac.ackOutputState(initial.Epoch, initial.New)
309310
310311 d.sessWg.Add(1)
@@ -325,7 +326,7 @@ func TestResizeGrowthFirstFrameIncludesConcurrentPTYRedraw(t *testing.T) {
325326 require.Equal(t, 'B', pane.screen.Frame.At(100, 0).Rune, "replay must parse the redraw before commit")
326327
327328 client.Resize(120, 24)
328- mustApplyOutput(t, client, awaitFrame(t, sends, ports.MsgOutput))
329+ mustApplyOutput(t, client, awaitFrame(t, sends, wire.MsgOutput))
329330 require.Equal(t, 'B', client.Frame.At(100, 1).Rune,
330331 "first grown frame exposed stale pre-SIGWINCH pane content")
331332 select {
@@ -356,7 +357,7 @@ func TestResizeWithoutPTYOutputFlushesOneFullFrameAtDeadline(t *testing.T) {
356357 done := captureResizeCallbackDone(t, sess.renderCoordinator())
357358 timer.ch <- time.Now()
358359 awaitTestCompletion(t, done, "resize callback did not complete")
359- frame := awaitFrame(t, sends, ports.MsgOutput)
360+ frame := awaitFrame(t, sends, wire.MsgOutput)
360361 client := vt.NewScreen(120, 24)
361362 out := mustApplyOutput(t, client, frame)
362363 require.Zero(t, out.Base)
@@ -395,7 +396,7 @@ func TestResizeBurstFlushesOnlyLatestGeometry(t *testing.T) {
395396 requireNoOutputFrame(t, sends)
396397 latest.ch <- time.Now()
397398 awaitTestCompletion(t, latestDone, "latest resize callback did not complete")
398- awaitFrame(t, sends, ports.MsgOutput)
399+ awaitFrame(t, sends, wire.MsgOutput)
399400 require.Equal(t, domain.Size{Cols: 120, Rows: 24}, ac.size)
400401 require.Equal(t, 120, testAttachmentTab(sess).focusedPane().screen.Frame.Width)
401402 requireNoOutputFrame(t, sends)
M internal/usecase/daemon/paint_gate_test.go +3 -3
@@ -4,16 +4,16 @@ import (
44 "testing"
55 "time"
66
7- "github.com/bnema/vev/internal/ports"
7+ "github.com/bnema/vev/internal/protocol/wire"
88 )
99
10-func requireNoOutputFrame(t *testing.T, sends chan ports.Frame) {
10+func requireNoOutputFrame(t *testing.T, sends chan wire.Frame) {
1111 t.Helper()
1212 deadline := time.After(50 * time.Millisecond)
1313 for {
1414 select {
1515 case f := <-sends:
16- if f.Type == ports.MsgOutput {
16+ if f.Type == wire.MsgOutput {
1717 t.Fatalf("unexpected output frame: %+v", f)
1818 }
1919 case <-deadline:
M internal/usecase/daemon/palette_test.go +80 -79
@@ -14,6 +14,7 @@ import (
1414 "github.com/bnema/vev/internal/ports"
1515 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1616 "github.com/bnema/vev/internal/protocol"
17+ "github.com/bnema/vev/internal/protocol/wire"
1718 "github.com/bnema/vev/internal/usecase/command"
1819 "github.com/bnema/vev/internal/usecase/palette"
1920 "github.com/bnema/vev/internal/usecase/picker"
@@ -130,8 +131,8 @@ func TestPaletteOpenTypeEnterRunAndEscClose(t *testing.T) {
130131
131132 d.handleInput(sess, ac, []byte("\x1b "))
132133 require.True(t, ac.overlays.paletteActive())
133- out := awaitFrame(t, sends, ports.MsgOutput)
134- msg, err := ports.UnmarshalOutput(out.Payload)
134+ out := awaitFrame(t, sends, wire.MsgOutput)
135+ msg, err := wire.UnmarshalOutput(out.Payload)
135136 require.NoError(t, err)
136137 require.Contains(t, string(msg.Data), "Commands")
137138
@@ -139,13 +140,13 @@ func TestPaletteOpenTypeEnterRunAndEscClose(t *testing.T) {
139140 require.False(t, ac.overlays.paletteActive())
140141 require.Equal(t, 1, testAttachmentTabIndex(sess))
141142 requireFloatingInitialized(t, testAttachmentTab(sess))
142- awaitFrame(t, sends, ports.MsgOutput)
143+ awaitFrame(t, sends, wire.MsgOutput)
143144
144145 d.handleInput(sess, ac, []byte("\x1b "))
145- awaitFrame(t, sends, ports.MsgOutput)
146+ awaitFrame(t, sends, wire.MsgOutput)
146147 d.handleInput(sess, ac, []byte("\x1b"))
147148 require.False(t, ac.overlays.paletteActive())
148- awaitFrame(t, sends, ports.MsgOutput)
149+ awaitFrame(t, sends, wire.MsgOutput)
149150 }
150151
151152 // TestPaletteCommandFailureSurfacesAsNotice drives a palette command whose Run
@@ -162,9 +163,9 @@ func TestPaletteCommandFailureSurfacesAsNotice(t *testing.T) {
162163 d.ptys = ptys
163164
164165 d.handleInput(sess, ac, []byte("\x1b "))
165- awaitFrame(t, sends, ports.MsgOutput)
166+ awaitFrame(t, sends, wire.MsgOutput)
166167 d.handleInput(sess, ac, []byte("CNT\r"))
167- awaitFrame(t, sends, ports.MsgOutput)
168+ awaitFrame(t, sends, wire.MsgOutput)
168169
169170 history := d.notices.history()
170171 require.NotEmpty(t, history, "failed palette command must record a notice")
@@ -291,13 +292,13 @@ func TestPaletteEnterFreezesActiveSessionSelectionAgainstTrailingInput(t *testin
291292 target.createdAt = 42
292293
293294 d.handleInput(current, ac, []byte("\x1b "))
294- awaitFrame(t, sends, ports.MsgOutput)
295+ awaitFrame(t, sends, wire.MsgOutput)
295296 d.handleInput(current, ac, []byte("recent\rx"))
296297
297298 require.Same(t, target, ac.currentSession(), "Enter must retain the selected session despite trailing frame bytes")
298299 require.False(t, ac.overlays.paletteActive(), "trailing input must not prevent the captured selection from closing the palette")
299- awaitFrame(t, sends, ports.MsgOutput)
300- awaitFrame(t, sends, ports.MsgOutput)
300+ awaitFrame(t, sends, wire.MsgOutput)
301+ awaitFrame(t, sends, wire.MsgOutput)
301302 }
302303
303304 func TestPaletteSelectedActiveSessionSwitchesWithoutRecordingCommandRecency(t *testing.T) {
@@ -307,15 +308,15 @@ func TestPaletteSelectedActiveSessionSwitchesWithoutRecordingCommandRecency(t *t
307308 target.createdAt = 42
308309
309310 d.handleInput(current, ac, []byte("\x1b "))
310- awaitFrame(t, sends, ports.MsgOutput)
311+ awaitFrame(t, sends, wire.MsgOutput)
311312 d.handleInput(current, ac, []byte("recent\r"))
312313
313314 require.Same(t, target, ac.currentSession())
314315 require.Contains(t, target.snapshotAttachments(), ac, "canonical handoff reuses the attached client")
315316 require.False(t, ac.overlays.paletteActive())
316317 require.Empty(t, d.paletteRecent, "session selections are not command recency")
317- awaitFrame(t, sends, ports.MsgOutput)
318- awaitFrame(t, sends, ports.MsgOutput)
318+ awaitFrame(t, sends, wire.MsgOutput)
319+ awaitFrame(t, sends, wire.MsgOutput)
319320 }
320321
321322 func TestPaletteStoppedSessionResumeFailureKeepsPaletteAndSourceAttachment(t *testing.T) {
@@ -329,7 +330,7 @@ func TestPaletteStoppedSessionResumeFailureKeepsPaletteAndSourceAttachment(t *te
329330 d.ptys = ptys
330331
331332 d.handleInput(current, ac, []byte("\x1b "))
332- awaitFrame(t, sends, ports.MsgOutput)
333+ awaitFrame(t, sends, wire.MsgOutput)
333334 d.handleInput(current, ac, []byte("stopped\r"))
334335
335336 require.Same(t, current, ac.currentSession(), "failed resume must retain the source attachment")
@@ -341,7 +342,7 @@ func TestPaletteStoppedSessionResumeFailureKeepsPaletteAndSourceAttachment(t *te
341342 require.Equal(t, "requested session is unavailable", ac.overlays.paletteFeedback)
342343 ac.overlays.paletteMu.Unlock()
343344 require.Equal(t, stopped, d.inactive[stopped.name], "failed resume must retain stopped lifecycle metadata")
344- awaitFrame(t, sends, ports.MsgOutput)
345+ awaitFrame(t, sends, wire.MsgOutput)
345346 }
346347
347348 func TestPaletteSelectedStoppedSessionResumesAndSwitches(t *testing.T) {
@@ -354,7 +355,7 @@ func TestPaletteSelectedStoppedSessionResumesAndSwitches(t *testing.T) {
354355 d.inactive["stopped"] = inactiveSession{name: "stopped", cwd: "/tmp", createdAt: 42, state: protocol.SessionDown}
355356
356357 d.handleInput(current, ac, []byte("\x1b "))
357- awaitFrame(t, sends, ports.MsgOutput)
358+ awaitFrame(t, sends, wire.MsgOutput)
358359 generation := ac.lifecycle.generationValue()
359360 d.handleInput(current, ac, []byte("stopped\r"))
360361
@@ -364,11 +365,11 @@ func TestPaletteSelectedStoppedSessionResumesAndSwitches(t *testing.T) {
364365 require.Equal(t, true, resumed.attachmentRegistered(ac))
365366 require.Greater(t, ac.lifecycle.generationValue(), generation, "stopped-session handoff must publish through the attachment transition")
366367 require.False(t, ac.overlays.paletteActive())
367- firstPaint := awaitFrame(t, sends, ports.MsgOutput)
368- firstOutput, err := ports.UnmarshalOutput(firstPaint.Payload)
368+ firstPaint := awaitFrame(t, sends, wire.MsgOutput)
369+ firstOutput, err := wire.UnmarshalOutput(firstPaint.Payload)
369370 require.NoError(t, err)
370371 require.Zero(t, firstOutput.Base, "stopped-session first paint must reset moving output state")
371- awaitFrame(t, sends, ports.MsgOutput)
372+ awaitFrame(t, sends, wire.MsgOutput)
372373 }
373374
374375 func TestPaletteJRSActivatesOnlyExactContextualHint(t *testing.T) {
@@ -378,9 +379,9 @@ func TestPaletteJRSActivatesOnlyExactContextualHint(t *testing.T) {
378379 ac.setRouteSnapshot(protocol.RecentRouteSnapshot{Generation: 1, Active: protocol.RouteRef{Key: 1, Generation: 1}})
379380
380381 d.handleInput(sess, ac, []byte("\x1b "))
381- awaitFrame(t, sends, ports.MsgOutput)
382+ awaitFrame(t, sends, wire.MsgOutput)
382383 d.handleInput(sess, ac, []byte("JRS"))
383- awaitFrame(t, sends, ports.MsgOutput)
384+ awaitFrame(t, sends, wire.MsgOutput)
384385
385386 ac.overlays.paletteMu.Lock()
386387 require.Equal(t, command.ContextHintRecentSessions, ac.overlays.paletteHints.Kind)
@@ -388,7 +389,7 @@ func TestPaletteJRSActivatesOnlyExactContextualHint(t *testing.T) {
388389 ac.overlays.paletteMu.Unlock()
389390
390391 d.handleInput(sess, ac, []byte("\b\b\bRNS"))
391- awaitFrame(t, sends, ports.MsgOutput)
392+ awaitFrame(t, sends, wire.MsgOutput)
392393 ac.overlays.paletteMu.Lock()
393394 require.Equal(t, command.ContextHintNone, ac.overlays.paletteHints.Kind)
394395 ac.overlays.paletteMu.Unlock()
@@ -402,25 +403,25 @@ func TestPaletteJRSUsesEffectiveOverrideOnly(t *testing.T) {
402403 d.ApplyConfig(domain.Config{Codes: map[string]string{"jump-recent-session": "RJS"}})
403404
404405 d.handleInputForAttachment(token, []byte("\x1b "))
405- awaitFrame(t, sends, ports.MsgOutput)
406+ awaitFrame(t, sends, wire.MsgOutput)
406407 d.handleInputForAttachment(token, []byte("RJS"))
407- awaitFrame(t, sends, ports.MsgOutput)
408+ awaitFrame(t, sends, wire.MsgOutput)
408409 ac.overlays.paletteMu.Lock()
409410 require.Equal(t, command.ContextHintRecentSessions, ac.overlays.paletteHints.Kind)
410411 ac.overlays.paletteMu.Unlock()
411412 d.handleInputForAttachment(token, []byte(" 1\r"))
412413
413- actionFrame := awaitFrame(t, sends, ports.MsgNavigateRecentRoute)
414- action, err := ports.UnmarshalRouteNavigationAction(actionFrame.Payload)
414+ actionFrame := awaitFrame(t, sends, wire.MsgNavigateRecentRoute)
415+ action, err := wire.UnmarshalRouteNavigationAction(actionFrame.Payload)
415416 require.NoError(t, err)
416417 require.Equal(t, protocol.RouteNavigationAction{SnapshotGeneration: 1, Key: 2, Generation: 1}, action)
417418 require.Same(t, current, ac.currentSession())
418419 require.False(t, ac.overlays.paletteActive())
419420
420421 d.handleInputForAttachment(token, []byte("\x1b "))
421- awaitFrame(t, sends, ports.MsgOutput)
422+ awaitFrame(t, sends, wire.MsgOutput)
422423 d.handleInputForAttachment(token, []byte("JRS"))
423- awaitFrame(t, sends, ports.MsgOutput)
424+ awaitFrame(t, sends, wire.MsgOutput)
424425 ac.overlays.paletteMu.Lock()
425426 require.Equal(t, command.ContextHintNone, ac.overlays.paletteHints.Kind)
426427 ac.overlays.paletteMu.Unlock()
@@ -588,11 +589,11 @@ func TestPaletteRemoteCatalogSelectionSendsExactAttachTarget(t *testing.T) {
588589 token := beginRecentRoutePaletteEffect(t, d, current, ac)
589590
590591 d.handleInputForAttachment(token, []byte("\x1b "))
591- awaitFrame(t, sends, ports.MsgOutput)
592+ awaitFrame(t, sends, wire.MsgOutput)
592593 d.handleInputForAttachment(token, []byte("work@arch\r"))
593594
594- frame := awaitFrame(t, sends, ports.MsgAttachTarget)
595- target, err := ports.UnmarshalAttachTarget(frame.Payload)
595+ frame := awaitFrame(t, sends, wire.MsgAttachTarget)
596+ target, err := wire.UnmarshalAttachTarget(frame.Payload)
596597 require.NoError(t, err)
597598 remoteTarget := domain.RemoteSessionTarget{
598599 Endpoint: "user@arch", DisplayOrigin: "arch", LifecycleID: remoteLifecycle,
@@ -627,7 +628,7 @@ func TestPaletteCachedRemoteSelectionFailsClosed(t *testing.T) {
627628 token := beginRecentRoutePaletteEffect(t, d, current, ac)
628629
629630 d.handleInputForAttachment(token, []byte("\x1b "))
630- awaitFrame(t, sends, ports.MsgOutput)
631+ awaitFrame(t, sends, wire.MsgOutput)
631632 d.handleInputForAttachment(token, []byte("cached@arch\r"))
632633
633634 require.Same(t, current, ac.currentAttachmentSession())
@@ -639,7 +640,7 @@ func TestPaletteCachedRemoteSelectionFailsClosed(t *testing.T) {
639640 for {
640641 select {
641642 case frame := <-sends:
642- require.NotEqual(t, ports.MsgAttachTarget, frame.Type)
643+ require.NotEqual(t, wire.MsgAttachTarget, frame.Type)
643644 default:
644645 return
645646 }
@@ -772,7 +773,7 @@ func TestPaletteLifecycleTargetRejectsSameNameReplacement(t *testing.T) {
772773 for {
773774 select {
774775 case frame := <-sends:
775- require.NotEqual(t, ports.MsgAttachTarget, frame.Type)
776+ require.NotEqual(t, wire.MsgAttachTarget, frame.Type)
776777 default:
777778 return
778779 }
@@ -793,11 +794,11 @@ func TestPaletteFuzzyRemoteRecentRouteSendsExactNavigationAction(t *testing.T) {
793794 token := beginRecentRoutePaletteEffect(t, d, current, ac)
794795
795796 d.handleInputForAttachment(token, []byte("\x1b "))
796- awaitFrame(t, sends, ports.MsgOutput)
797+ awaitFrame(t, sends, wire.MsgOutput)
797798 d.handleInputForAttachment(token, []byte("logs@edge\r"))
798799
799- actionFrame := awaitFrame(t, sends, ports.MsgNavigateRecentRoute)
800- action, err := ports.UnmarshalRouteNavigationAction(actionFrame.Payload)
800+ actionFrame := awaitFrame(t, sends, wire.MsgNavigateRecentRoute)
801+ action, err := wire.UnmarshalRouteNavigationAction(actionFrame.Payload)
801802 require.NoError(t, err)
802803 require.Equal(t, protocol.RouteNavigationAction{SnapshotGeneration: 9, Key: 8, Generation: 4}, action)
803804 require.False(t, ac.overlays.paletteActive())
@@ -809,12 +810,12 @@ func TestPaletteFuzzySelectedStaticCommandExecutes(t *testing.T) {
809810 defer releases[1]()
810811
811812 d.handleInput(sess, ac, []byte("\x1b "))
812- awaitFrame(t, sends, ports.MsgOutput)
813+ awaitFrame(t, sends, wire.MsgOutput)
813814 d.handleInput(sess, ac, []byte("next\r"))
814815
815816 require.False(t, ac.overlays.paletteActive())
816817 require.Equal(t, 1, testAttachmentTabIndex(sess))
817- awaitFrame(t, sends, ports.MsgOutput)
818+ awaitFrame(t, sends, wire.MsgOutput)
818819 }
819820
820821 func TestPaletteJRSUsesCapturedRankAfterMRUChanges(t *testing.T) {
@@ -823,13 +824,13 @@ func TestPaletteJRSUsesCapturedRankAfterMRUChanges(t *testing.T) {
823824 ac.setRouteSnapshot(testRecentRouteSnapshot())
824825 token := beginRecentRoutePaletteEffect(t, d, current, ac)
825826 d.handleInputForAttachment(token, []byte("\x1b "))
826- awaitFrame(t, sends, ports.MsgOutput)
827+ awaitFrame(t, sends, wire.MsgOutput)
827828 // Reordering live MRU after opening must not shift rank 1 from its capture.
828829 d.sessions[domain.SessionID("older")].core().mruAt.Store(100)
829830 d.handleInputForAttachment(token, []byte("JRS 1\r"))
830831
831- actionFrame := awaitFrame(t, sends, ports.MsgNavigateRecentRoute)
832- action, err := ports.UnmarshalRouteNavigationAction(actionFrame.Payload)
832+ actionFrame := awaitFrame(t, sends, wire.MsgNavigateRecentRoute)
833+ action, err := wire.UnmarshalRouteNavigationAction(actionFrame.Payload)
833834 require.NoError(t, err)
834835 require.Equal(t, protocol.RouteNavigationAction{SnapshotGeneration: 1, Key: 2, Generation: 1}, action)
835836 require.Same(t, current, ac.currentSession())
@@ -877,7 +878,7 @@ func TestPaletteJRSDoesNotRevalidateTargetInDaemon(t *testing.T) {
877878 }
878879 token := beginRecentRoutePaletteEffect(t, d, sess, ac)
879880 d.handleInputForAttachment(token, []byte("\x1b "))
880- awaitFrame(t, sends, ports.MsgOutput)
881+ awaitFrame(t, sends, wire.MsgOutput)
881882 inputHandled := make(chan struct{})
882883 go func() {
883884 d.handleInputForAttachment(token, []byte("JRS 1\r"))
@@ -889,8 +890,8 @@ func TestPaletteJRSDoesNotRevalidateTargetInDaemon(t *testing.T) {
889890 close(releaseHandoff)
890891 awaitTestCompletion(t, inputHandled, "JRS route-action input did not complete")
891892
892- actionFrame := awaitFrame(t, sends, ports.MsgNavigateRecentRoute)
893- action, err := ports.UnmarshalRouteNavigationAction(actionFrame.Payload)
893+ actionFrame := awaitFrame(t, sends, wire.MsgNavigateRecentRoute)
894+ action, err := wire.UnmarshalRouteNavigationAction(actionFrame.Payload)
894895 require.NoError(t, err)
895896 require.Equal(t, protocol.RouteNavigationAction{SnapshotGeneration: 7, Key: 9, Generation: 4}, action)
896897 require.False(t, ac.overlays.paletteActive())
@@ -903,7 +904,7 @@ func TestPaletteJRSOutOfRangeKeepsPaletteOpenWithoutClamping(t *testing.T) {
903904 token := beginRecentRoutePaletteEffect(t, d, current, ac)
904905
905906 d.handleInputForAttachment(token, []byte("\x1b "))
906- awaitFrame(t, sends, ports.MsgOutput)
907+ awaitFrame(t, sends, wire.MsgOutput)
907908 d.handleInputForAttachment(token, []byte("JRS 3\r"))
908909
909910 require.Same(t, current, ac.currentSession())
@@ -912,7 +913,7 @@ func TestPaletteJRSOutOfRangeKeepsPaletteOpenWithoutClamping(t *testing.T) {
912913 require.Equal(t, "JRS 3", ac.overlays.palette.Query())
913914 require.Equal(t, "requested recent session is unavailable", ac.overlays.paletteFeedback)
914915 ac.overlays.paletteMu.Unlock()
915- awaitFrame(t, sends, ports.MsgOutput)
916+ awaitFrame(t, sends, wire.MsgOutput)
916917 }
917918
918919 func TestPaletteJRSMalformedRankFeedback(t *testing.T) {
@@ -920,7 +921,7 @@ func TestPaletteJRSMalformedRankFeedback(t *testing.T) {
920921 defer releaseAll(releases)
921922
922923 d.handleInput(current, ac, []byte("\x1b "))
923- awaitFrame(t, sends, ports.MsgOutput)
924+ awaitFrame(t, sends, wire.MsgOutput)
924925 d.handleInput(current, ac, []byte("JRS 0\r"))
925926
926927 require.Same(t, current, ac.currentSession())
@@ -929,7 +930,7 @@ func TestPaletteJRSMalformedRankFeedback(t *testing.T) {
929930 require.Equal(t, "JRS 0", ac.overlays.palette.Query())
930931 require.Equal(t, "rank must be one positive decimal", ac.overlays.paletteFeedback)
931932 ac.overlays.paletteMu.Unlock()
932- awaitFrame(t, sends, ports.MsgOutput)
933+ awaitFrame(t, sends, wire.MsgOutput)
933934 }
934935
935936 func TestPaletteFailureDoesNotOverwriteChangedQueryInSameGeneration(t *testing.T) {
@@ -964,8 +965,8 @@ func TestPaletteFailureDoesNotOverwriteNewerInteraction(t *testing.T) {
964965 ac.overlays.paletteMu.Lock()
965966 require.Empty(t, ac.overlays.paletteFeedback)
966967 ac.overlays.paletteMu.Unlock()
967- awaitFrame(t, sends, ports.MsgOutput)
968- awaitFrame(t, sends, ports.MsgOutput)
968+ awaitFrame(t, sends, wire.MsgOutput)
969+ awaitFrame(t, sends, wire.MsgOutput)
969970 }
970971
971972 func TestPaletteTFPExecutesFloatingToggle(t *testing.T) {
@@ -976,14 +977,14 @@ func TestPaletteTFPExecutesFloatingToggle(t *testing.T) {
976977 installTestFloating(tb, newPane("floating", nil, domain.Size{Cols: 20, Rows: 5}), false)
977978
978979 d.handleInput(sess, ac, []byte("\x1b "))
979- awaitFrame(t, sends, ports.MsgOutput)
980+ awaitFrame(t, sends, wire.MsgOutput)
980981 d.handleInput(sess, ac, []byte("TFP\r"))
981982
982983 require.False(t, ac.overlays.paletteActive())
983984 tb.mu.Lock()
984985 require.Equal(t, floatingVisible, tb.floating.state)
985986 tb.mu.Unlock()
986- awaitFrame(t, sends, ports.MsgOutput)
987+ awaitFrame(t, sends, wire.MsgOutput)
987988 }
988989
989990 func TestPaletteCNSPromptsForSessionNameThenCreatesAndSwitches(t *testing.T) {
@@ -995,10 +996,10 @@ func TestPaletteCNSPromptsForSessionNameThenCreatesAndSwitches(t *testing.T) {
995996 d.ptys = newFactorySeq(t, p2)
996997
997998 d.handleInput(sess, ac, []byte("\x1b "))
998- awaitFrame(t, sends, ports.MsgOutput)
999+ awaitFrame(t, sends, wire.MsgOutput)
9991000 d.handleInput(sess, ac, []byte("CNS\r"))
1000- promptFrame := awaitFrame(t, sends, ports.MsgOutput)
1001- promptOutput, err := ports.UnmarshalOutput(promptFrame.Payload)
1001+ promptFrame := awaitFrame(t, sends, wire.MsgOutput)
1002+ promptOutput, err := wire.UnmarshalOutput(promptFrame.Payload)
10021003 require.NoError(t, err)
10031004 require.False(t, ac.overlays.paletteActive())
10041005 require.True(t, ac.overlays.promptActive())
@@ -1009,12 +1010,12 @@ func TestPaletteCNSPromptsForSessionNameThenCreatesAndSwitches(t *testing.T) {
10091010 // The submit first paints the newly attached session while the prompt is
10101011 // still open, then handlePromptInput closes the prompt and repaints the
10111012 // client's current session. The final frame must be for the new session.
1012- firstPaint := awaitFrame(t, sends, ports.MsgOutput)
1013- firstOutput, err := ports.UnmarshalOutput(firstPaint.Payload)
1013+ firstPaint := awaitFrame(t, sends, wire.MsgOutput)
1014+ firstOutput, err := wire.UnmarshalOutput(firstPaint.Payload)
10141015 require.NoError(t, err)
10151016 require.Zero(t, firstOutput.Base, "new-session first paint must reset moving output state")
1016- finalRepaint := awaitFrame(t, sends, ports.MsgOutput)
1017- finalOutput, err := ports.UnmarshalOutput(finalRepaint.Payload)
1017+ finalRepaint := awaitFrame(t, sends, wire.MsgOutput)
1018+ finalOutput, err := wire.UnmarshalOutput(finalRepaint.Payload)
10181019 require.NoError(t, err)
10191020 require.False(t, ac.overlays.promptActive())
10201021 require.Equal(t, 2, sessionCount(d))
@@ -1049,9 +1050,9 @@ func TestPaletteDirectSessionCreation(t *testing.T) {
10491050 d.ptys = newFactorySeq(t, p2)
10501051
10511052 d.handleInput(sess, ac, []byte("\x1b "))
1052- awaitFrame(t, sends, ports.MsgOutput)
1053+ awaitFrame(t, sends, wire.MsgOutput)
10531054 d.handleInput(sess, ac, []byte(tc.input))
1054- awaitFrame(t, sends, ports.MsgOutput)
1055+ awaitFrame(t, sends, wire.MsgOutput)
10551056
10561057 require.False(t, ac.overlays.paletteActive())
10571058 require.False(t, ac.overlays.promptActive())
@@ -1072,13 +1073,13 @@ func TestPaletteReopensWithSuccessfulCommandFirst(t *testing.T) {
10721073 defer releases[1]()
10731074
10741075 d.handleInput(sess, ac, []byte("\x1b "))
1075- awaitFrame(t, sends, ports.MsgOutput)
1076+ awaitFrame(t, sends, wire.MsgOutput)
10761077 d.handleInput(sess, ac, []byte("NXT\r"))
10771078 require.False(t, ac.overlays.paletteActive())
1078- awaitFrame(t, sends, ports.MsgOutput)
1079+ awaitFrame(t, sends, wire.MsgOutput)
10791080
10801081 d.handleInput(sess, ac, []byte("\x1b "))
1081- awaitFrame(t, sends, ports.MsgOutput)
1082+ awaitFrame(t, sends, wire.MsgOutput)
10821083 result, ok := ac.overlays.palette.Selected()
10831084 require.True(t, ok)
10841085 cmd, ok := result.Command()
@@ -1163,15 +1164,15 @@ func TestPaletteCommandNoopRepaintsAfterClose(t *testing.T) {
11631164 defer releases[0]()
11641165
11651166 d.handleInput(sess, ac, []byte("\x1b "))
1166- paletteFrame := awaitFrame(t, sends, ports.MsgOutput)
1167- paletteOutput, err := ports.UnmarshalOutput(paletteFrame.Payload)
1167+ paletteFrame := awaitFrame(t, sends, wire.MsgOutput)
1168+ paletteOutput, err := wire.UnmarshalOutput(paletteFrame.Payload)
11681169 require.NoError(t, err)
11691170 require.Contains(t, string(paletteOutput.Data), "Commands")
11701171
11711172 d.handleInput(sess, ac, []byte("NXT\r"))
11721173 require.False(t, ac.overlays.paletteActive())
1173- repaint := awaitFrame(t, sends, ports.MsgOutput)
1174- repaintOutput, err := ports.UnmarshalOutput(repaint.Payload)
1174+ repaint := awaitFrame(t, sends, wire.MsgOutput)
1175+ repaintOutput, err := wire.UnmarshalOutput(repaint.Payload)
11751176 require.NoError(t, err)
11761177 require.NotContains(t, string(repaintOutput.Data), "Commands")
11771178 }
@@ -1184,11 +1185,11 @@ func TestPaletteCreateTabErrorRepaintsAfterClose(t *testing.T) {
11841185 d.ptys = ptys
11851186
11861187 d.handleInput(sess, ac, []byte("\x1b "))
1187- awaitFrame(t, sends, ports.MsgOutput)
1188+ awaitFrame(t, sends, wire.MsgOutput)
11881189 d.handleInput(sess, ac, []byte("CNT\r"))
11891190 require.False(t, ac.overlays.paletteActive())
1190- repaint := awaitFrame(t, sends, ports.MsgOutput)
1191- repaintOutput, err := ports.UnmarshalOutput(repaint.Payload)
1191+ repaint := awaitFrame(t, sends, wire.MsgOutput)
1192+ repaintOutput, err := wire.UnmarshalOutput(repaint.Payload)
11921193 require.NoError(t, err)
11931194 require.NotContains(t, string(repaintOutput.Data), "Commands")
11941195 require.Len(t, sess.tabs, 1)
@@ -1199,10 +1200,10 @@ func TestPaletteEnterNoMatchKeepsOpenAndEscapeSplit(t *testing.T) {
11991200 defer releases[0]()
12001201
12011202 d.handleInput(sess, ac, []byte("\x1b "))
1202- awaitFrame(t, sends, ports.MsgOutput)
1203+ awaitFrame(t, sends, wire.MsgOutput)
12031204 d.handleInput(sess, ac, []byte("zzzz\r"))
12041205 require.True(t, ac.overlays.paletteActive())
1205- awaitFrame(t, sends, ports.MsgOutput)
1206+ awaitFrame(t, sends, wire.MsgOutput)
12061207
12071208 d.handlePaletteInput(ac, []byte{0x1b, '['})
12081209 require.True(t, ac.overlays.paletteActive())
@@ -1210,7 +1211,7 @@ func TestPaletteEnterNoMatchKeepsOpenAndEscapeSplit(t *testing.T) {
12101211 d.handlePaletteInput(ac, []byte{'A'})
12111212 require.True(t, ac.overlays.paletteActive())
12121213 require.Empty(t, ac.overlays.palettePending)
1213- awaitFrame(t, sends, ports.MsgOutput)
1214+ awaitFrame(t, sends, wire.MsgOutput)
12141215 }
12151216
12161217 func TestPaletteCtrlNAndCtrlPNavigate(t *testing.T) {
@@ -1218,10 +1219,10 @@ func TestPaletteCtrlNAndCtrlPNavigate(t *testing.T) {
12181219 defer releases[0]()
12191220
12201221 d.handleInput(sess, ac, []byte("\x1b "))
1221- awaitFrame(t, sends, ports.MsgOutput)
1222+ awaitFrame(t, sends, wire.MsgOutput)
12221223
12231224 d.handlePaletteInput(ac, []byte{0x0e})
1224- awaitFrame(t, sends, ports.MsgOutput)
1225+ awaitFrame(t, sends, wire.MsgOutput)
12251226 result, ok := ac.overlays.palette.Selected()
12261227 require.True(t, ok)
12271228 cmd, ok := result.Command()
@@ -1229,7 +1230,7 @@ func TestPaletteCtrlNAndCtrlPNavigate(t *testing.T) {
12291230 require.Equal(t, "CNS", cmd.Code)
12301231
12311232 d.handlePaletteInput(ac, []byte{0x10})
1232- awaitFrame(t, sends, ports.MsgOutput)
1233+ awaitFrame(t, sends, wire.MsgOutput)
12331234 result, ok = ac.overlays.palette.Selected()
12341235 require.True(t, ok)
12351236 cmd, ok = result.Command()
@@ -1337,7 +1338,7 @@ func TestPaletteUTF8PendingCompletesFilter(t *testing.T) {
13371338 defer releases[0]()
13381339
13391340 d.handleInput(sess, ac, []byte("\x1b "))
1340- awaitFrame(t, sends, ports.MsgOutput)
1341+ awaitFrame(t, sends, wire.MsgOutput)
13411342 d.handlePaletteInput(ac, []byte{0xc3})
13421343 require.Equal(t, []byte{0xc3}, ac.overlays.palettePending)
13431344 d.handlePaletteInput(ac, []byte{0xa9})
M internal/usecase/daemon/pane_actions_test.go +3 -2
@@ -16,6 +16,7 @@ import (
1616 "github.com/bnema/vev/internal/domain"
1717 "github.com/bnema/vev/internal/ports"
1818 portsmocks "github.com/bnema/vev/internal/ports/mocks"
19+ "github.com/bnema/vev/internal/protocol/wire"
1920 scopy "github.com/bnema/vev/internal/usecase/copy"
2021 "github.com/bnema/vev/internal/usecase/layout"
2122 )
@@ -695,7 +696,7 @@ func TestClosePaneRepaintFanoutRespectsAttachmentScope(t *testing.T) {
695696 closingPTY.EXPECT().Close().Return(nil).Once()
696697
697698 clients := make([]*attachedClient, 2)
698- sends := make([]chan ports.Frame, 2)
699+ sends := make([]chan wire.Frame, 2)
699700 for i := range clients {
700701 tr, sent := newCapturingTransport(t)
701702 clients[i] = &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 41, Rows: 12}}
@@ -715,7 +716,7 @@ func TestClosePaneRepaintFanoutRespectsAttachmentScope(t *testing.T) {
715716 select {
716717 case frame := <-sends[i]:
717718 require.True(t, want, "attachment %d unexpectedly repainted: %#v", i, frame)
718- require.Equal(t, ports.MsgOutput, frame.Type)
719+ require.Equal(t, wire.MsgOutput, frame.Type)
719720 default:
720721 require.False(t, want, "attachment %d was not repainted", i)
721722 }
M internal/usecase/daemon/parked_route.go +5 -4
@@ -9,6 +9,7 @@ import (
99 "github.com/bnema/vev/internal/domain"
1010 "github.com/bnema/vev/internal/ports"
1111 "github.com/bnema/vev/internal/protocol"
12+ "github.com/bnema/vev/internal/protocol/wire"
1213 )
1314
1415 const parkedRouteLeaseTTL = 15 * time.Minute
@@ -237,12 +238,12 @@ func (ac *attachedClient) consumePublishedParkedRoute(previous, published attach
237238 return 0
238239 }
239240
240-func parkedRouteResponseFrame(response protocol.ParkedRouteResponse) (ports.Frame, error) {
241- payload := ports.MarshalParkedRouteResponse(response)
241+func parkedRouteResponseFrame(response protocol.ParkedRouteResponse) (wire.Frame, error) {
242+ payload := wire.MarshalParkedRouteResponse(response)
242243 if payload == nil {
243- return ports.Frame{}, errAttachmentTransition
244+ return wire.Frame{}, errAttachmentTransition
244245 }
245- return ports.Frame{Type: ports.MsgParkedRouteResponse, Payload: payload}, nil
246+ return wire.Frame{Type: wire.MsgParkedRouteResponse, Payload: payload}, nil
246247 }
247248
248249 func (d *Daemon) sendParkedRouteResponse(effect *attachmentEffect, response protocol.ParkedRouteResponse) error {
M internal/usecase/daemon/parked_route_test.go +37 -36
@@ -14,28 +14,29 @@ import (
1414 "github.com/bnema/vev/internal/ports"
1515 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1616 "github.com/bnema/vev/internal/protocol"
17+ "github.com/bnema/vev/internal/protocol/wire"
1718 )
1819
1920 type parkedRouteFailTransport struct{}
2021
21-func (parkedRouteFailTransport) Send(ports.Frame) error { return errors.New("send failed") }
22-func (parkedRouteFailTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
23-func (parkedRouteFailTransport) Close() error { return nil }
22+func (parkedRouteFailTransport) Send(wire.Frame) error { return errors.New("send failed") }
23+func (parkedRouteFailTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
24+func (parkedRouteFailTransport) Close() error { return nil }
2425
2526 type parkedRouteExpiryTransport struct {
26- sends chan ports.Frame
27+ sends chan wire.Frame
2728 closed chan struct{}
2829 once sync.Once
2930 }
3031
31-func (t *parkedRouteExpiryTransport) Send(frame ports.Frame) error {
32+func (t *parkedRouteExpiryTransport) Send(frame wire.Frame) error {
3233 t.sends <- frame
3334 return nil
3435 }
3536
36-func (t *parkedRouteExpiryTransport) Recv() (ports.Frame, error) {
37+func (t *parkedRouteExpiryTransport) Recv() (wire.Frame, error) {
3738 <-t.closed
38- return ports.Frame{}, io.EOF
39+ return wire.Frame{}, io.EOF
3940 }
4041
4142 func (t *parkedRouteExpiryTransport) Close() error {
@@ -62,7 +63,7 @@ func (c *parkedRouteClock) setNow(now time.Time) {
6263 c.mu.Unlock()
6364 }
6465
65-func armAndPrepareParkedRoute(t *testing.T, d *Daemon, source *session, ac *attachedClient, sends chan ports.Frame) protocol.ParkedRouteLeaseID {
66+func armAndPrepareParkedRoute(t *testing.T, d *Daemon, source *session, ac *attachedClient, sends chan wire.Frame) protocol.ParkedRouteLeaseID {
6667 t.Helper()
6768 ac.navigationCapabilities |= protocol.NavigationCapabilityHomePicker
6869 token := source.captureAttachmentCapability(ac, ac.transport())
@@ -70,14 +71,14 @@ func armAndPrepareParkedRoute(t *testing.T, d *Daemon, source *session, ac *atta
7071 require.True(t, admitted)
7172 require.NoError(t, d.sendNavigationActionForAttachment(effect, protocol.NavigationOpenHomePicker))
7273 effect.End()
73- directiveFrame := awaitFrame(t, sends, ports.MsgNavigationAction)
74- directive, err := ports.UnmarshalNavigationDirective(directiveFrame.Payload)
74+ directiveFrame := awaitFrame(t, sends, wire.MsgNavigationAction)
75+ directive, err := wire.UnmarshalNavigationDirective(directiveFrame.Payload)
7576 require.NoError(t, err)
7677
7778 prepare := protocol.ParkedRouteRequest{RequestID: 1, LeaseID: directive.LeaseID, Action: protocol.ParkedRoutePrepare}
78- require.False(t, d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), ports.Frame{Type: ports.MsgParkedRouteRequest, Payload: ports.MarshalParkedRouteRequest(prepare)}))
79- readyFrame := awaitFrame(t, sends, ports.MsgParkedRouteResponse)
80- ready, err := ports.UnmarshalParkedRouteResponse(readyFrame.Payload)
79+ require.False(t, d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), wire.Frame{Type: wire.MsgParkedRouteRequest, Payload: wire.MarshalParkedRouteRequest(prepare)}))
80+ readyFrame := awaitFrame(t, sends, wire.MsgParkedRouteResponse)
81+ ready, err := wire.UnmarshalParkedRouteResponse(readyFrame.Payload)
8182 require.NoError(t, err)
8283 require.Equal(t, protocol.ParkedRouteResponse{RequestID: 1, Status: protocol.ParkedRouteReady}, ready)
8384 return directive.LeaseID
@@ -108,12 +109,12 @@ func TestParkedRouteSwitchesExactLiveTargetOnRetainedTransport(t *testing.T) {
108109 SessionName: "target", LiveTabID: domain.TabStableID(target.tabs[0].stableID),
109110 }
110111 switchRequest := protocol.ParkedRouteRequest{RequestID: 2, LeaseID: leaseID, Action: protocol.ParkedRouteSwitch, Target: &targetRequest}
111- require.False(t, d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), ports.Frame{Type: ports.MsgParkedRouteRequest, Payload: ports.MarshalParkedRouteRequest(switchRequest)}))
112+ require.False(t, d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), wire.Frame{Type: wire.MsgParkedRouteRequest, Payload: wire.MarshalParkedRouteRequest(switchRequest)}))
112113
113114 require.Same(t, target, ac.currentAttachmentSession())
114115 require.False(t, ac.parkedRouteOutput.Load())
115- responseFrame := awaitFrame(t, sends, ports.MsgParkedRouteResponse)
116- response, err := ports.UnmarshalParkedRouteResponse(responseFrame.Payload)
116+ responseFrame := awaitFrame(t, sends, wire.MsgParkedRouteResponse)
117+ response, err := wire.UnmarshalParkedRouteResponse(responseFrame.Payload)
117118 require.NoError(t, err)
118119 require.Equal(t, protocol.ParkedRouteResponse{RequestID: 2, Status: protocol.ParkedRouteSwitched}, response)
119120 }
@@ -147,11 +148,11 @@ func TestParkedRouteSwitchAcceptedBeforeLeaseExpiryCommitsAtomically(t *testing.
147148 SessionName: "target", LiveTabID: domain.TabStableID(target.tabs[0].stableID),
148149 }
149150 switchRequest := protocol.ParkedRouteRequest{RequestID: 2, LeaseID: leaseID, Action: protocol.ParkedRouteSwitch, Target: &targetRequest}
150- d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), ports.Frame{Type: ports.MsgParkedRouteRequest, Payload: ports.MarshalParkedRouteRequest(switchRequest)})
151+ d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), wire.Frame{Type: wire.MsgParkedRouteRequest, Payload: wire.MarshalParkedRouteRequest(switchRequest)})
151152
152153 require.Same(t, target, ac.currentAttachmentSession())
153- responseFrame := awaitFrame(t, sends, ports.MsgParkedRouteResponse)
154- response, err := ports.UnmarshalParkedRouteResponse(responseFrame.Payload)
154+ responseFrame := awaitFrame(t, sends, wire.MsgParkedRouteResponse)
155+ response, err := wire.UnmarshalParkedRouteResponse(responseFrame.Payload)
155156 require.NoError(t, err)
156157 require.Equal(t, protocol.ParkedRouteSwitched, response.Status)
157158 }
@@ -185,7 +186,7 @@ func TestParkedRouteRestoresExactStoppedTargetWithDaemonEnvironment(t *testing.T
185186 SessionName: "stopped", Stopped: true, StoppedTab: domain.NewStableTabSelector("tab-b"),
186187 }
187188 switchRequest := protocol.ParkedRouteRequest{RequestID: 2, LeaseID: leaseID, Action: protocol.ParkedRouteSwitch, Target: &target}
188- d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), ports.Frame{Type: ports.MsgParkedRouteRequest, Payload: ports.MarshalParkedRouteRequest(switchRequest)})
189+ d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), wire.Frame{Type: wire.MsgParkedRouteRequest, Payload: wire.MarshalParkedRouteRequest(switchRequest)})
189190
190191 restored := ac.currentAttachmentSession()
191192 require.NotNil(t, restored)
@@ -210,23 +211,23 @@ func TestParkedRouteStaleTargetLeavesSourceResumable(t *testing.T) {
210211 SessionName: "missing", LiveTabID: "tab-1",
211212 }
212213 switchRequest := protocol.ParkedRouteRequest{RequestID: 2, LeaseID: leaseID, Action: protocol.ParkedRouteSwitch, Target: &missing}
213- d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), ports.Frame{Type: ports.MsgParkedRouteRequest, Payload: ports.MarshalParkedRouteRequest(switchRequest)})
214- staleFrame := awaitFrame(t, sends, ports.MsgParkedRouteResponse)
215- stale, err := ports.UnmarshalParkedRouteResponse(staleFrame.Payload)
214+ d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), wire.Frame{Type: wire.MsgParkedRouteRequest, Payload: wire.MarshalParkedRouteRequest(switchRequest)})
215+ staleFrame := awaitFrame(t, sends, wire.MsgParkedRouteResponse)
216+ stale, err := wire.UnmarshalParkedRouteResponse(staleFrame.Payload)
216217 require.NoError(t, err)
217218 require.Equal(t, protocol.ParkedRouteStaleTarget, stale.Status)
218219 require.Same(t, source, ac.currentAttachmentSession())
219220 require.True(t, ac.parkedRouteOutput.Load())
220221
221222 resume := protocol.ParkedRouteRequest{RequestID: 3, LeaseID: leaseID, Action: protocol.ParkedRouteResume}
222- d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), ports.Frame{Type: ports.MsgParkedRouteRequest, Payload: ports.MarshalParkedRouteRequest(resume)})
223- resumedFrame := awaitFrame(t, sends, ports.MsgParkedRouteResponse)
224- resumed, err := ports.UnmarshalParkedRouteResponse(resumedFrame.Payload)
223+ d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), wire.Frame{Type: wire.MsgParkedRouteRequest, Payload: wire.MarshalParkedRouteRequest(resume)})
224+ resumedFrame := awaitFrame(t, sends, wire.MsgParkedRouteResponse)
225+ resumed, err := wire.UnmarshalParkedRouteResponse(resumedFrame.Payload)
225226 require.NoError(t, err)
226227 require.Equal(t, protocol.ParkedRouteResumed, resumed.Status)
227228 require.False(t, ac.parkedRouteOutput.Load())
228- outputFrame := awaitFrame(t, sends, ports.MsgOutput)
229- output, err := ports.UnmarshalOutput(outputFrame.Payload)
229+ outputFrame := awaitFrame(t, sends, wire.MsgOutput)
230+ output, err := wire.UnmarshalOutput(outputFrame.Payload)
230231 require.NoError(t, err)
231232 require.True(t, output.Full)
232233 }
@@ -245,10 +246,10 @@ func TestParkedRouteDoesNotReplayOneShotEffectsAfterResume(t *testing.T) {
245246 }
246247
247248 resume := protocol.ParkedRouteRequest{RequestID: 2, LeaseID: leaseID, Action: protocol.ParkedRouteResume}
248- d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), ports.Frame{Type: ports.MsgParkedRouteRequest, Payload: ports.MarshalParkedRouteRequest(resume)})
249- _ = awaitFrame(t, sends, ports.MsgParkedRouteResponse)
250- outputFrame := awaitFrame(t, sends, ports.MsgOutput)
251- output, err := ports.UnmarshalOutput(outputFrame.Payload)
249+ d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), wire.Frame{Type: wire.MsgParkedRouteRequest, Payload: wire.MarshalParkedRouteRequest(resume)})
250+ _ = awaitFrame(t, sends, wire.MsgParkedRouteResponse)
251+ outputFrame := awaitFrame(t, sends, wire.MsgOutput)
252+ output, err := wire.UnmarshalOutput(outputFrame.Payload)
252253 require.NoError(t, err)
253254 require.True(t, output.Full)
254255 require.NotContains(t, string(output.Data), "one-shot effect")
@@ -270,9 +271,9 @@ func TestParkedRouteExpiredLeaseFailsClosed(t *testing.T) {
270271 ac.parkedRoute.expiresAt = d.clock.Now()
271272 ac.parkedRouteMu.Unlock()
272273 resume := protocol.ParkedRouteRequest{RequestID: 2, LeaseID: leaseID, Action: protocol.ParkedRouteResume}
273- d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), ports.Frame{Type: ports.MsgParkedRouteRequest, Payload: ports.MarshalParkedRouteRequest(resume)})
274- responseFrame := awaitFrame(t, sends, ports.MsgParkedRouteResponse)
275- response, err := ports.UnmarshalParkedRouteResponse(responseFrame.Payload)
274+ d.handleAttachmentClientFrame(source.captureAttachmentCapability(ac, ac.transport()), wire.Frame{Type: wire.MsgParkedRouteRequest, Payload: wire.MarshalParkedRouteRequest(resume)})
275+ responseFrame := awaitFrame(t, sends, wire.MsgParkedRouteResponse)
276+ response, err := wire.UnmarshalParkedRouteResponse(responseFrame.Payload)
276277 require.NoError(t, err)
277278 require.Equal(t, protocol.ParkedRouteExpired, response.Status)
278279 require.Same(t, source, ac.currentAttachmentSession())
@@ -308,7 +309,7 @@ func TestParkedRouteLeaseExpiryClosesExactRetainedTransport(t *testing.T) {
308309 clock.EXPECT().Now().Return(time.Time{}).Maybe()
309310 clock.EXPECT().NewTimer(mock.Anything).Return(timer).Once()
310311 d.clock = clock
311- transport := &parkedRouteExpiryTransport{sends: make(chan ports.Frame, 8), closed: make(chan struct{})}
312+ transport := &parkedRouteExpiryTransport{sends: make(chan wire.Frame, 8), closed: make(chan struct{})}
312313 ac.replaceTransport(transport)
313314
314315 _ = armAndPrepareParkedRoute(t, d, source, ac, transport.sends)
M internal/usecase/daemon/performance_benchmark_test.go +9 -8
@@ -16,6 +16,7 @@ import (
1616 "github.com/bnema/vev/internal/domain"
1717 "github.com/bnema/vev/internal/ports"
1818 "github.com/bnema/vev/internal/protocol"
19+ "github.com/bnema/vev/internal/protocol/wire"
1920 "github.com/bnema/vev/internal/usecase/layout"
2021 snapcodec "github.com/bnema/vev/internal/usecase/snapshot"
2122 )
@@ -184,12 +185,12 @@ func TestPerformanceFixtureExplicitCloseReleasesIterationState(t *testing.T) {
184185 func TestCountingOutputTransportCountsOpaquePayloadAndRejectsShortPayload(t *testing.T) {
185186 transport := &countingOutputTransport{}
186187
187- err := transport.Send(ports.Frame{Type: ports.MsgOutput, Payload: make([]byte, 23)})
188+ err := transport.Send(wire.Frame{Type: wire.MsgOutput, Payload: make([]byte, 23)})
188189 require.Error(t, err)
189190 require.Equal(t, countingOutputMetrics{}, transport.metrics())
190191
191192 payload := append(make([]byte, 24), 0xff, 0x00, 0xfe)
192- require.NoError(t, transport.Send(ports.Frame{Type: ports.MsgOutput, Payload: payload}))
193+ require.NoError(t, transport.Send(wire.Frame{Type: wire.MsgOutput, Payload: payload}))
193194 require.Equal(t, countingOutputMetrics{frames: 1, bytes: 3, payloadBytes: uint64(len(payload))}, transport.metrics())
194195 require.Equal(t, payload, transport.lastPayload())
195196
@@ -230,7 +231,7 @@ func TestPerformanceFixtureCounters(t *testing.T) {
230231 func TestRenderStageHooksCountProductionBoundariesOnFailedSend(t *testing.T) {
231232 d, sess, ac, sends := newManualSessionWithPTYs(t, nil)
232233 d.paint(sess, ac, true, nil)
233- _ = awaitFrame(t, sends, ports.MsgOutput)
234+ _ = awaitFrame(t, sends, wire.MsgOutput)
234235 var captures, compositions, emissions int
235236 ac.renderStages = renderStageHooks{
236237 capture: func() { captures++ },
@@ -446,7 +447,7 @@ func benchmarkDaemonLargeHistory(b *testing.B, workload string, run func(*perfor
446447 }
447448 metrics := fixture.metrics()
448449 if payload := fixture.output.lastPayload(); payload != nil {
449- if _, err := ports.UnmarshalOutput(payload); err != nil {
450+ if _, err := wire.UnmarshalOutput(payload); err != nil {
450451 b.Fatalf("decode last output: %v", err)
451452 }
452453 }
@@ -1347,8 +1348,8 @@ type countingOutputTransport struct {
13471348 last []byte
13481349 }
13491350
1350-func (t *countingOutputTransport) Send(frame ports.Frame) error {
1351- if frame.Type != ports.MsgOutput {
1351+func (t *countingOutputTransport) Send(frame wire.Frame) error {
1352+ if frame.Type != wire.MsgOutput {
13521353 return nil
13531354 }
13541355 if len(frame.Payload) < outputPayloadHeaderBytes {
@@ -1362,8 +1363,8 @@ func (t *countingOutputTransport) Send(frame ports.Frame) error {
13621363 t.mu.Unlock()
13631364 return nil
13641365 }
1365-func (*countingOutputTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
1366-func (*countingOutputTransport) Close() error { return nil }
1366+func (*countingOutputTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
1367+func (*countingOutputTransport) Close() error { return nil }
13671368 func (t *countingOutputTransport) reset() {
13681369 t.mu.Lock()
13691370 t.countingOutputMetrics = countingOutputMetrics{}
M internal/usecase/daemon/picker.go +5 -4
@@ -11,6 +11,7 @@ import (
1111 "github.com/bnema/vev/internal/domain"
1212 "github.com/bnema/vev/internal/ports"
1313 "github.com/bnema/vev/internal/protocol"
14+ "github.com/bnema/vev/internal/protocol/wire"
1415 "github.com/bnema/vev/internal/usecase/layout"
1516 "github.com/bnema/vev/internal/usecase/picker"
1617 "github.com/bnema/vev/internal/usecase/ui"
@@ -923,7 +924,7 @@ func (d *Daemon) sendLocalAttachTargetForAttachment(effect *attachmentEffect, ta
923924 // Explicit tab rows already take the direct transition path above, while
924925 // stopped sessions have no active target session and retain the fallback.
925926 samePeerEligible := guard.allowSamePeer && targetSess != nil && target.TabIndex <= 0
926- payload := ports.MarshalAttachTarget(protocol.AttachTarget{
927+ payload := wire.MarshalAttachTarget(protocol.AttachTarget{
927928 Session: sessionName,
928929 Intent: protocol.IntentAttach,
929930 ExactTarget: exactTarget,
@@ -943,7 +944,7 @@ func (d *Daemon) sendLocalAttachTargetForAttachment(effect *attachmentEffect, ta
943944 } else {
944945 effect.ac.offerSamePeerTarget(*exactTarget)
945946 }
946- if err := effect.sendControl(ports.Frame{Type: ports.MsgAttachTarget, Payload: payload}); err != nil {
947+ if err := effect.sendControl(wire.Frame{Type: wire.MsgAttachTarget, Payload: payload}); err != nil {
947948 if samePeerEligible {
948949 effect.ac.clearSamePeerOffer()
949950 }
@@ -989,11 +990,11 @@ func (d *Daemon) sendRemoteAttachTargetForAttachment(effect *attachmentEffect, t
989990 RemoteTarget: &remoteTarget,
990991 EnvironmentPolicy: protocol.EnvironmentPolicyDaemonOwned,
991992 }
992- payload := ports.MarshalAttachTarget(handoff)
993+ payload := wire.MarshalAttachTarget(handoff)
993994 if payload == nil {
994995 return failUnavailable()
995996 }
996- if err := effect.sendControl(ports.Frame{Type: ports.MsgAttachTarget, Payload: payload}); err != nil {
997+ if err := effect.sendControl(wire.Frame{Type: wire.MsgAttachTarget, Payload: payload}); err != nil {
997998 return domain.UserErr(domain.NoticeSessionUnavailable, "couldn't attach to remote session", err)
998999 }
9991000 if guard.closePicker {
M internal/usecase/daemon/picker_test.go +30 -29
@@ -17,6 +17,7 @@ import (
1717 "github.com/bnema/vev/internal/ports"
1818 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1919 "github.com/bnema/vev/internal/protocol"
20+ "github.com/bnema/vev/internal/protocol/wire"
2021 "github.com/bnema/vev/internal/usecase/layout"
2122 "github.com/bnema/vev/internal/usecase/picker"
2223 recoveryusecase "github.com/bnema/vev/internal/usecase/recovery"
@@ -34,9 +35,9 @@ type remotePickerSendErrorTransport struct {
3435 err error
3536 }
3637
37-func (t *remotePickerSendErrorTransport) Send(ports.Frame) error { return t.err }
38-func (*remotePickerSendErrorTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
39-func (*remotePickerSendErrorTransport) Close() error { return nil }
38+func (t *remotePickerSendErrorTransport) Send(wire.Frame) error { return t.err }
39+func (*remotePickerSendErrorTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
40+func (*remotePickerSendErrorTransport) Close() error { return nil }
4041
4142 type refusingSnapshotDeleteRepository struct {
4243 noOpSnapshotRepository
@@ -222,11 +223,11 @@ func TestPickerSortToggleFlipsModeAndKeepsSelection(t *testing.T) {
222223 d.mu.Unlock()
223224
224225 d.enterPicker(sess, ac)
225- awaitFrame(t, sends, ports.MsgOutput)
226+ awaitFrame(t, sends, wire.MsgOutput)
226227 // Move off the attached session's active tab: a navigate rebuild snaps
227228 // back to it unless the toggle preserves the selection.
228229 d.handleInput(sess, ac, tc.navigate)
229- awaitFrame(t, sends, ports.MsgOutput)
230+ awaitFrame(t, sends, wire.MsgOutput)
230231 ac.overlays.pickerMu.Lock()
231232 before, ok := ac.overlays.picker.Selected()
232233 ac.overlays.pickerMu.Unlock()
@@ -239,7 +240,7 @@ func TestPickerSortToggleFlipsModeAndKeepsSelection(t *testing.T) {
239240 }
240241
241242 d.handleInput(sess, ac, []byte("s"))
242- awaitFrame(t, sends, ports.MsgOutput)
243+ awaitFrame(t, sends, wire.MsgOutput)
243244
244245 require.Equal(t, uint32(pickerSortGrouped), d.pickerSort.Load())
245246 require.True(t, ac.overlays.pickerActive())
@@ -252,7 +253,7 @@ func TestPickerSortToggleFlipsModeAndKeepsSelection(t *testing.T) {
252253 require.Equal(t, " Sessions · grouped ", title)
253254
254255 d.handleInput(sess, ac, []byte("s"))
255- awaitFrame(t, sends, ports.MsgOutput)
256+ awaitFrame(t, sends, wire.MsgOutput)
256257
257258 require.Equal(t, uint32(pickerSortRecent), d.pickerSort.Load())
258259 require.True(t, ac.overlays.pickerActive())
@@ -812,7 +813,7 @@ func TestPickerResumesStoppedSessionWithPersistedTabNames(t *testing.T) {
812813 d.inactive["work"] = inactiveSession{name: "work", cwd: "/tmp/work", createdAt: 7, tabNames: []string{"shell", "logs"}, record: domain.CatalogueRecord{Name: "work"}, state: protocol.SessionDown}
813814
814815 d.resumeStoppedAndSwitch(from, ac, picker.Target{Name: "work", Stopped: true})
815- awaitFrame(t, sends, ports.MsgOutput)
816+ awaitFrame(t, sends, wire.MsgOutput)
816817
817818 target := ac.currentSession()
818819 require.NotNil(t, target)
@@ -835,16 +836,16 @@ func TestPickerSameSessionNavigationSwitchAndEscClose(t *testing.T) {
835836 }()
836837
837838 d.enterPicker(sess, ac)
838- awaitFrame(t, sends, ports.MsgOutput)
839+ awaitFrame(t, sends, wire.MsgOutput)
839840 d.handleInput(sess, ac, []byte("j"))
840- awaitFrame(t, sends, ports.MsgOutput)
841+ awaitFrame(t, sends, wire.MsgOutput)
841842 d.handleInput(sess, ac, []byte("\r"))
842843
843844 require.Equal(t, 1, testAttachmentTabIndex(sess))
844845 requireFloatingInitialized(t, testAttachmentTab(sess))
845- awaitFrame(t, sends, ports.MsgOutput)
846+ awaitFrame(t, sends, wire.MsgOutput)
846847 d.enterPicker(sess, ac)
847- awaitFrame(t, sends, ports.MsgOutput)
848+ awaitFrame(t, sends, wire.MsgOutput)
848849 clk := &signalClock{timers: make(chan *signalTimer, 1)}
849850 d.clock = clk
850851 d.handleInput(sess, ac, []byte("\x1b"))
@@ -852,7 +853,7 @@ func TestPickerSameSessionNavigationSwitchAndEscClose(t *testing.T) {
852853 require.True(t, ac.overlays.pickerActive())
853854 timer.ch <- time.Now()
854855 require.Eventually(t, func() bool { return !ac.overlays.pickerActive() }, time.Second, 5*time.Millisecond)
855- awaitFrame(t, sends, ports.MsgOutput)
856+ awaitFrame(t, sends, wire.MsgOutput)
856857 }
857858
858859 func TestPickerSplitArrowNavigatesWithoutExiting(t *testing.T) {
@@ -876,7 +877,7 @@ func TestPickerSplitArrowNavigatesWithoutExiting(t *testing.T) {
876877 }()
877878
878879 d.enterPicker(sess, ac)
879- awaitFrame(t, sends, ports.MsgOutput)
880+ awaitFrame(t, sends, wire.MsgOutput)
880881 for _, input := range tc.input {
881882 d.handleInput(sess, ac, input)
882883 }
@@ -950,7 +951,7 @@ func TestPickerLoneEscapeExitsAfterDelay(t *testing.T) {
950951 defer releases[0]()
951952
952953 d.enterPicker(sess, ac)
953- awaitFrame(t, sends, ports.MsgOutput)
954+ awaitFrame(t, sends, wire.MsgOutput)
954955 clk := &signalClock{timers: make(chan *signalTimer, 1)}
955956 d.clock = clk
956957 d.handleInput(sess, ac, []byte("\x1b"))
@@ -984,10 +985,10 @@ func TestPickerStalePaintAfterSessionSwitchSendsNoFrame(t *testing.T) {
984985 d.sessions[sess2.id] = sess2
985986
986987 d.firstPaint(sess1, ac1)
987- awaitFrame(t, sends1, ports.MsgOutput)
988+ awaitFrame(t, sends1, wire.MsgOutput)
988989 d.stealClientForTarget(sess1, ac1, sess2, picker.Target{Session: sess2.id})
989990 d.firstPaint(sess2, ac1)
990- awaitFrame(t, sends1, ports.MsgOutput)
991+ awaitFrame(t, sends1, wire.MsgOutput)
991992 require.Same(t, sess2, ac1.currentSession())
992993 for len(sends1) > 0 {
993994 <-sends1
@@ -1011,7 +1012,7 @@ func TestPickerSessionSwitchPublishesBeforeInFlightPaintSendCompletes(t *testing
10111012 enteredSend := make(chan struct{})
10121013 releaseSend := make(chan struct{})
10131014 tr := portsmocks.NewMockTransport(t)
1014- tr.EXPECT().Send(mock.Anything).RunAndReturn(func(ports.Frame) error {
1015+ tr.EXPECT().Send(mock.Anything).RunAndReturn(func(wire.Frame) error {
10151016 close(enteredSend)
10161017 <-releaseSend
10171018 return nil
@@ -1370,14 +1371,14 @@ func TestPickerResizeRecomposesModal(t *testing.T) {
13701371 defer releasePTY()
13711372
13721373 d.enterPicker(sess, ac)
1373- awaitFrame(t, sends, ports.MsgOutput)
1374+ awaitFrame(t, sends, wire.MsgOutput)
13741375 d.resize(sess, ac, domain.Size{Cols: 100, Rows: 30})
13751376 // Picker recomposition is the relevant event here; do not depend on a real
13761377 // resize-idle timer in this synchronous rendering test.
13771378 d.paint(sess, ac, false, nil)
13781379
1379- out := awaitFrame(t, sends, ports.MsgOutput)
1380- msg, err := ports.UnmarshalOutput(out.Payload)
1380+ out := awaitFrame(t, sends, wire.MsgOutput)
1381+ msg, err := wire.UnmarshalOutput(out.Payload)
13811382 require.NoError(t, err)
13821383 require.Contains(t, string(msg.Data), "┌")
13831384 require.Contains(t, string(msg.Data), "Sessions")
@@ -1455,11 +1456,11 @@ func TestPickerEnterOnStoppedSessionRestoreFailureSurfacesNoticeAndStaysPut(t *t
14551456 d.ptys = ptys
14561457
14571458 d.enterPicker(from, ac)
1458- awaitFrame(t, sends, ports.MsgOutput)
1459+ awaitFrame(t, sends, wire.MsgOutput)
14591460 d.handleInput(from, ac, []byte("j"))
1460- awaitFrame(t, sends, ports.MsgOutput)
1461+ awaitFrame(t, sends, wire.MsgOutput)
14611462 d.handleInput(from, ac, []byte("\r"))
1462- awaitFrame(t, sends, ports.MsgOutput)
1463+ awaitFrame(t, sends, wire.MsgOutput)
14631464
14641465 history := d.notices.history()
14651466 require.Len(t, history, 1, "failed stopped-session restore must record exactly one notice")
@@ -1615,10 +1616,10 @@ func TestPickerKillActiveSessionSnapshotDeleteRefusalReportsOnceAndKeepsPicker(t
16151616 require.NoError(t, d.catalogue.Create(record))
16161617
16171618 d.enterPicker(from, ac)
1618- awaitFrame(t, sends, ports.MsgOutput)
1619+ awaitFrame(t, sends, wire.MsgOutput)
16191620 // MRU-desc order is from(current), recent, older; "j" moves onto "recent".
16201621 d.handleInput(from, ac, []byte("j"))
1621- awaitFrame(t, sends, ports.MsgOutput)
1622+ awaitFrame(t, sends, wire.MsgOutput)
16221623 d.handleInput(from, ac, []byte("x"))
16231624
16241625 history := d.notices.history()
@@ -1662,11 +1663,11 @@ func TestPickerKillStoppedSessionPersistDeleteFailureSurfacesNoticeAndKeepsEntry
16621663 d.inactive["stopped"] = inactiveSession{name: "stopped", cwd: "/tmp/stopped", createdAt: 7, incarnation: record.IncarnationID, record: record}
16631664
16641665 d.enterPicker(from, ac)
1665- awaitFrame(t, sends, ports.MsgOutput)
1666+ awaitFrame(t, sends, wire.MsgOutput)
16661667 d.handleInput(from, ac, []byte("j"))
1667- awaitFrame(t, sends, ports.MsgOutput)
1668+ awaitFrame(t, sends, wire.MsgOutput)
16681669 d.handleInput(from, ac, []byte("x"))
1669- awaitFrame(t, sends, ports.MsgOutput)
1670+ awaitFrame(t, sends, wire.MsgOutput)
16701671
16711672 history := d.notices.history()
16721673 require.NotEmpty(t, history, "failed persisted-record delete must record a notice")
M internal/usecase/daemon/prompt_test.go +20 -19
@@ -9,6 +9,7 @@ import (
99
1010 "github.com/bnema/vev/internal/domain"
1111 "github.com/bnema/vev/internal/ports"
12+ "github.com/bnema/vev/internal/protocol/wire"
1213 )
1314
1415 type failingPTYFactory struct{ err error }
@@ -29,8 +30,8 @@ func TestEnterPromptRendersTitleAndPrefill(t *testing.T) {
2930 defer release()
3031
3132 d.enterPrompt(sess, ac, " Rename session ", "0", func(string) error { return nil })
32- out := awaitFrame(t, sends, ports.MsgOutput)
33- msg, err := ports.UnmarshalOutput(out.Payload)
33+ out := awaitFrame(t, sends, wire.MsgOutput)
34+ msg, err := wire.UnmarshalOutput(out.Payload)
3435 require.NoError(t, err)
3536 require.Contains(t, string(msg.Data), "Rename session")
3637 require.Contains(t, string(msg.Data), "> 0")
@@ -45,14 +46,14 @@ func TestPromptSubmitRenamesAndPromotesSession(t *testing.T) {
4546 sess.ephemeral = true
4647
4748 d.enterPrompt(sess, ac, " Rename session ", sess.name, func(name string) error { return d.renameSession(sess, name) })
48- awaitFrame(t, sends, ports.MsgOutput)
49+ awaitFrame(t, sends, wire.MsgOutput)
4950 d.handlePromptInput(ac, []byte("work\r"))
50- repaint := awaitFrame(t, sends, ports.MsgOutput)
51+ repaint := awaitFrame(t, sends, wire.MsgOutput)
5152
5253 require.False(t, ac.overlays.promptActive())
5354 require.Equal(t, "0work", sess.name)
5455 require.False(t, sess.ephemeral)
55- out, err := ports.UnmarshalOutput(repaint.Payload)
56+ out, err := wire.UnmarshalOutput(repaint.Payload)
5657 require.NoError(t, err)
5758 require.Contains(t, string(out.Data), "0work")
5859 require.NotContains(t, string(out.Data), "0work*")
@@ -69,13 +70,13 @@ func TestPromptSubmitErrorKeepsPromptOpen(t *testing.T) {
6970 d.sessions["other"] = &session{sessionCore: sessionCore{id: "other", name: "taken"}, ctx: ctx, cancel: cancel, tabs: []*tab{newTestTabWithContext(p2, ctx, cancel)}}
7071
7172 d.enterPrompt(sess, ac, " Rename session ", "", func(name string) error { return d.renameSession(sess, name) })
72- awaitFrame(t, sends, ports.MsgOutput)
73+ awaitFrame(t, sends, wire.MsgOutput)
7374 d.handlePromptInput(ac, []byte("taken\r"))
74- repaint := awaitFrame(t, sends, ports.MsgOutput)
75+ repaint := awaitFrame(t, sends, wire.MsgOutput)
7576
7677 require.True(t, ac.overlays.promptActive())
7778 require.NotEqual(t, "taken", sess.name)
78- out, err := ports.UnmarshalOutput(repaint.Payload)
79+ out, err := wire.UnmarshalOutput(repaint.Payload)
7980 require.NoError(t, err)
8081 require.Contains(t, string(out.Data), "name already in use")
8182 }
@@ -92,10 +93,10 @@ func TestPromptSubmitSessionSpawnFailureReportsOneSafeNotice(t *testing.T) {
9293 d.enterPrompt(sess, ac, " Create session ", "", func(name string) error {
9394 return d.createSessionAndSwitch(sess, ac, name)
9495 })
95- awaitFrame(t, sends, ports.MsgOutput)
96+ awaitFrame(t, sends, wire.MsgOutput)
9697 d.handlePromptInput(ac, []byte("new\r"))
97- repaint := awaitFrame(t, sends, ports.MsgOutput)
98- output, err := ports.UnmarshalOutput(repaint.Payload)
98+ repaint := awaitFrame(t, sends, wire.MsgOutput)
99+ output, err := wire.UnmarshalOutput(repaint.Payload)
99100 require.NoError(t, err)
100101 require.Contains(t, string(output.Data), "couldn't create session: shell failed to start")
101102
@@ -140,10 +141,10 @@ func TestPromptSubmitValidationErrorStaysInlineOnly(t *testing.T) {
140141 d.enterPrompt(sess, ac, " Create session ", "", func(name string) error {
141142 return d.createSessionAndSwitch(sess, ac, name)
142143 })
143- awaitFrame(t, sends, ports.MsgOutput)
144+ awaitFrame(t, sends, wire.MsgOutput)
144145 d.handlePromptInput(ac, tt.input)
145- repaint := awaitFrame(t, sends, ports.MsgOutput)
146- output, err := ports.UnmarshalOutput(repaint.Payload)
146+ repaint := awaitFrame(t, sends, wire.MsgOutput)
147+ output, err := wire.UnmarshalOutput(repaint.Payload)
147148 require.NoError(t, err)
148149 require.Contains(t, string(output.Data), tt.wantErr.Error())
149150
@@ -160,9 +161,9 @@ func TestPromptEscapeCancelsWithoutRename(t *testing.T) {
160161 oldName := sess.name
161162
162163 d.enterPrompt(sess, ac, " Rename session ", oldName, func(name string) error { return d.renameSession(sess, name) })
163- awaitFrame(t, sends, ports.MsgOutput)
164+ awaitFrame(t, sends, wire.MsgOutput)
164165 d.handlePromptInput(ac, []byte("new\x1b"))
165- awaitFrame(t, sends, ports.MsgOutput)
166+ awaitFrame(t, sends, wire.MsgOutput)
166167
167168 require.False(t, ac.overlays.promptActive())
168169 require.Equal(t, oldName, sess.name)
@@ -176,13 +177,13 @@ func TestPaletteRNSOpensRenamePrompt(t *testing.T) {
176177 sess.ephemeral = true
177178
178179 d.enterPalette(sess, ac)
179- awaitFrame(t, sends, ports.MsgOutput)
180+ awaitFrame(t, sends, wire.MsgOutput)
180181 d.handlePaletteInput(ac, []byte("RNS\r"))
181- out := awaitFrame(t, sends, ports.MsgOutput)
182+ out := awaitFrame(t, sends, wire.MsgOutput)
182183
183184 require.False(t, ac.overlays.paletteActive())
184185 require.True(t, ac.overlays.promptActive())
185- msg, err := ports.UnmarshalOutput(out.Payload)
186+ msg, err := wire.UnmarshalOutput(out.Payload)
186187 require.NoError(t, err)
187188 require.Contains(t, string(msg.Data), "Rename session")
188189 require.Contains(t, string(msg.Data), "> 0")
M internal/usecase/daemon/recent_route_snapshot_test.go +6 -6
@@ -4,8 +4,8 @@ import (
44 "testing"
55
66 "github.com/bnema/vev/internal/domain"
7- "github.com/bnema/vev/internal/ports"
87 "github.com/bnema/vev/internal/protocol"
8+ "github.com/bnema/vev/internal/protocol/wire"
99 "github.com/stretchr/testify/require"
1010 )
1111
@@ -69,11 +69,11 @@ func TestRecentRouteSnapshotRepaintsWithoutDeferredIdentity(t *testing.T) {
6969 ac.installTestAttachmentCapability(token)
7070 ac.setRouteSnapshot(protocol.RecentRouteSnapshot{Generation: 1})
7171
72- payload, err := ports.MarshalRecentRouteSnapshot(protocol.RecentRouteSnapshot{Generation: 2})
72+ payload, err := wire.MarshalRecentRouteSnapshot(protocol.RecentRouteSnapshot{Generation: 2})
7373 require.NoError(t, err)
74- require.False(t, d.handleAttachmentClientFrame(token, ports.Frame{Type: ports.MsgRecentRouteSnapshot, Payload: payload}))
74+ require.False(t, d.handleAttachmentClientFrame(token, wire.Frame{Type: wire.MsgRecentRouteSnapshot, Payload: payload}))
7575
76- awaitFrame(t, sends, ports.MsgOutput)
76+ awaitFrame(t, sends, wire.MsgOutput)
7777 }
7878
7979 func TestAttachmentRouteSnapshotCopiesPublishedEntries(t *testing.T) {
@@ -115,11 +115,11 @@ func TestPaletteRecentRouteSelectionSendsTypedClientAction(t *testing.T) {
115115 var action protocol.RouteNavigationAction
116116 found := false
117117 for _, frame := range frames {
118- if frame.Type != ports.MsgNavigateRecentRoute {
118+ if frame.Type != wire.MsgNavigateRecentRoute {
119119 continue
120120 }
121121 var err error
122- action, err = ports.UnmarshalRouteNavigationAction(frame.Payload)
122+ action, err = wire.UnmarshalRouteNavigationAction(frame.Payload)
123123 require.NoError(t, err)
124124 found = true
125125 }
M internal/usecase/daemon/remote_attach.go +7 -6
@@ -7,6 +7,7 @@ import (
77 "github.com/bnema/vev/internal/domain"
88 "github.com/bnema/vev/internal/ports"
99 "github.com/bnema/vev/internal/protocol"
10+ "github.com/bnema/vev/internal/protocol/wire"
1011 )
1112
1213 // routeRemoteTargetWithContext is the exact-target branch for picker handoffs.
@@ -119,12 +120,12 @@ func (d *Daemon) sendNavigationActionForAttachment(effect *attachmentEffect, act
119120 effect.ac.clearParkedRoute()
120121 }
121122 }
122- payload := ports.MarshalNavigationDirective(directive)
123+ payload := wire.MarshalNavigationDirective(directive)
123124 if payload == nil {
124125 rollback()
125126 return errAttachmentTransition
126127 }
127- if err := effect.sendControl(ports.Frame{Type: ports.MsgNavigationAction, Payload: payload}); err != nil {
128+ if err := effect.sendControl(wire.Frame{Type: wire.MsgNavigationAction, Payload: payload}); err != nil {
128129 rollback()
129130 return err
130131 }
@@ -132,11 +133,11 @@ func (d *Daemon) sendNavigationActionForAttachment(effect *attachmentEffect, act
132133 }
133134
134135 func (d *Daemon) sendRecentRouteNavigationActionForAttachment(effect *attachmentEffect, action protocol.RouteNavigationAction) error {
135- payload, err := ports.MarshalRouteNavigationAction(action)
136+ payload, err := wire.MarshalRouteNavigationAction(action)
136137 if err != nil {
137138 return errAttachmentTransition
138139 }
139- return effect.sendControl(ports.Frame{Type: ports.MsgNavigateRecentRoute, Payload: payload})
140+ return effect.sendControl(wire.Frame{Type: wire.MsgNavigateRecentRoute, Payload: payload})
140141 }
141142
142143 func (d *Daemon) sendCommittedRouteIdentityForAttachment(effect *attachmentEffect) error {
@@ -149,11 +150,11 @@ func (d *Daemon) sendCommittedRouteIdentityForAttachment(effect *attachmentEffec
149150 Ephemeral: effect.sess.ephemeral,
150151 }
151152 effect.sess.mu.Unlock()
152- payload, err := ports.MarshalCommittedRouteIdentity(identity)
153+ payload, err := wire.MarshalCommittedRouteIdentity(identity)
153154 if err != nil {
154155 return errAttachmentTransition
155156 }
156- return effect.sendControl(ports.Frame{Type: ports.MsgCommittedRouteIdentity, Payload: payload})
157+ return effect.sendControl(wire.Frame{Type: wire.MsgCommittedRouteIdentity, Payload: payload})
157158 }
158159
159160 func (d *Daemon) finishRouteAttach(sess *session, tr ports.Transport, sz domain.Size, h protocol.Hello, routeCreated, purge bool) (*attachedClient, error) {
M internal/usecase/daemon/remote_attach_test.go +8 -7
@@ -9,6 +9,7 @@ import (
99 "github.com/bnema/vev/internal/domain"
1010 "github.com/bnema/vev/internal/ports"
1111 "github.com/bnema/vev/internal/protocol"
12+ "github.com/bnema/vev/internal/protocol/wire"
1213 "github.com/bnema/vev/internal/usecase/picker"
1314 )
1415
@@ -30,7 +31,7 @@ func TestLocalPickerOfferCarriesExactLifecycle(t *testing.T) {
3031
3132 require.NoError(t, d.switchToTargetForAttachment(effect, picker.Target{Session: target.id}, sessionHandoffGuard{allowSamePeer: true}, "picker-select"))
3233 frame := receiveRemotePicker(t, sends, "local attach target")
33- got, err := ports.UnmarshalAttachTarget(frame.Payload)
34+ got, err := wire.UnmarshalAttachTarget(frame.Payload)
3435 require.NoError(t, err)
3536 require.Empty(t, got.Endpoint)
3637 require.Equal(t, protocol.IntentAttach, got.Intent)
@@ -61,10 +62,10 @@ func TestStoppedLocalPickerHandoffWaitsForClientClose(t *testing.T) {
6162 }, sessionHandoffGuard{closePicker: true, allowSamePeer: true}, "picker-select"))
6263
6364 cleanup := receiveRemotePicker(t, sends, "graphics cleanup")
64- require.Equal(t, ports.MsgOutput, cleanup.Type)
65+ require.Equal(t, wire.MsgOutput, cleanup.Type)
6566 handoff := receiveRemotePicker(t, sends, "stopped local attach target")
66- require.Equal(t, ports.MsgAttachTarget, handoff.Type)
67- target, err := ports.UnmarshalAttachTarget(handoff.Payload)
67+ require.Equal(t, wire.MsgAttachTarget, handoff.Type)
68+ target, err := wire.UnmarshalAttachTarget(handoff.Payload)
6869 require.NoError(t, err)
6970 require.False(t, target.SamePeer)
7071 require.Equal(t, &protocol.ExactSessionTarget{LifecycleID: lifecycle, SessionName: "stopped"}, target.ExactTarget)
@@ -93,7 +94,7 @@ func TestRemotePickerRichHandoffCarriesLifecycleTabAndPolicy(t *testing.T) {
9394 target := picker.Target{Session: key.ID(), RemoteKey: &key, RemoteTarget: &remoteTarget, TabID: "tab-1"}
9495 require.NoError(t, d.sendRemoteAttachTargetForAttachment(effect, target, sessionHandoffGuard{closePicker: true}, "picker-select"))
9596 frame := receiveRemotePicker(t, sends, "rich attach target")
96- got, err := ports.UnmarshalAttachTarget(frame.Payload)
97+ got, err := wire.UnmarshalAttachTarget(frame.Payload)
9798 require.NoError(t, err)
9899 require.NotNil(t, got.RemoteTarget)
99100 require.Equal(t, remoteTarget, *got.RemoteTarget)
@@ -140,7 +141,7 @@ func TestRemotePickerRichHandoffRejectsMismatchedRouteKey(t *testing.T) {
140141 for {
141142 select {
142143 case frame := <-sends:
143- require.NotEqual(t, ports.MsgAttachTarget, frame.Type)
144+ require.NotEqual(t, wire.MsgAttachTarget, frame.Type)
144145 default:
145146 return
146147 }
@@ -174,7 +175,7 @@ func TestRemotePickerRichHandoffRejectsReplacedLifecycle(t *testing.T) {
174175 for {
175176 select {
176177 case frame := <-sends:
177- require.NotEqual(t, ports.MsgAttachTarget, frame.Type, "rejected handoff must not send an attach target")
178+ require.NotEqual(t, wire.MsgAttachTarget, frame.Type, "rejected handoff must not send an attach target")
178179 default:
179180 return
180181 }
M internal/usecase/daemon/remote_picker_test.go +9 -8
@@ -18,6 +18,7 @@ import (
1818 "github.com/bnema/vev/internal/ports"
1919 portsmocks "github.com/bnema/vev/internal/ports/mocks"
2020 "github.com/bnema/vev/internal/protocol"
21+ "github.com/bnema/vev/internal/protocol/wire"
2122 "github.com/bnema/vev/internal/usecase/picker"
2223 )
2324
@@ -696,8 +697,8 @@ func TestRemotePickerHandoffSendsTargetAndLeavesNoShadowSession(t *testing.T) {
696697 require.NoError(t, d.sendRemoteAttachTargetForAttachment(effect, target, sessionHandoffGuard{closePicker: true}, "picker-select"))
697698
698699 frame := receiveRemotePicker(t, sends, "attach target")
699- require.Equal(t, ports.MsgAttachTarget, frame.Type)
700- got, err := ports.UnmarshalAttachTarget(frame.Payload)
700+ require.Equal(t, wire.MsgAttachTarget, frame.Type)
701+ got, err := wire.UnmarshalAttachTarget(frame.Payload)
701702 require.NoError(t, err)
702703 require.Equal(t, protocol.AttachTarget{Endpoint: "arch", Session: "work", Intent: protocol.IntentAttach, RemoteTarget: &remoteTarget, EnvironmentPolicy: protocol.EnvironmentPolicyDaemonOwned}, got)
703704 require.Nil(t, ac.currentAttachmentSession())
@@ -748,7 +749,7 @@ func TestRemotePickerSelectsStoppedRemoteTabAndRestoresIt(t *testing.T) {
748749 require.NoError(t, local.sendRemoteAttachTargetForAttachment(effect, selected, sessionHandoffGuard{closePicker: true}, "picker-select"))
749750
750751 frame := receiveRemotePicker(t, sends, "stopped remote target")
751- handoff, err := ports.UnmarshalAttachTarget(frame.Payload)
752+ handoff, err := wire.UnmarshalAttachTarget(frame.Payload)
752753 require.NoError(t, err)
753754 require.NotNil(t, handoff.RemoteTarget)
754755 require.Equal(t, selected.RemoteTarget, handoff.RemoteTarget)
@@ -819,7 +820,7 @@ func TestRemotePickerResurrectsStoppedRemoteSessionWithoutTabMetadata(t *testing
819820 require.NoError(t, local.sendRemoteAttachTargetForAttachment(effect, selected, sessionHandoffGuard{closePicker: true}, "picker-select"))
820821
821822 frame := receiveRemotePicker(t, sends, "stopped remote target without tab metadata")
822- handoff, err := ports.UnmarshalAttachTarget(frame.Payload)
823+ handoff, err := wire.UnmarshalAttachTarget(frame.Payload)
823824 require.NoError(t, err)
824825 require.Equal(t, selected.RemoteTarget, handoff.RemoteTarget)
825826
@@ -862,8 +863,8 @@ func TestNavigationActionHandoffSendsBoundedAction(t *testing.T) {
862863 defer effect.End()
863864 require.NoError(t, d.sendNavigationActionForAttachment(effect, tt.action))
864865 frame := receiveRemotePicker(t, sends, "navigation action")
865- require.Equal(t, ports.MsgNavigationAction, frame.Type)
866- directive, err := ports.UnmarshalNavigationDirective(frame.Payload)
866+ require.Equal(t, wire.MsgNavigationAction, frame.Type)
867+ directive, err := wire.UnmarshalNavigationDirective(frame.Payload)
867868 require.NoError(t, err)
868869 require.Equal(t, tt.action, directive.Action)
869870 if tt.action == protocol.NavigationOpenHomePicker {
@@ -920,10 +921,10 @@ func TestRemotePickerHandoffSendFailureKeepsPickerOpen(t *testing.T) {
920921 require.Same(t, sess, ac.currentAttachmentSession())
921922 }
922923
923-func addRemoteRefreshPickerOwner(t *testing.T, d *Daemon, id domain.SessionID, transports ...ports.Transport) (*session, *attachedClient, chan ports.Frame) {
924+func addRemoteRefreshPickerOwner(t *testing.T, d *Daemon, id domain.SessionID, transports ...ports.Transport) (*session, *attachedClient, chan wire.Frame) {
924925 t.Helper()
925926 var tr ports.Transport
926- var sends chan ports.Frame
927+ var sends chan wire.Frame
927928 if len(transports) != 0 {
928929 tr = transports[0]
929930 } else {
M internal/usecase/daemon/remote_preview.go +6 -5
@@ -7,11 +7,12 @@ import (
77 "github.com/bnema/vev/internal/domain"
88 "github.com/bnema/vev/internal/ports"
99 "github.com/bnema/vev/internal/protocol"
10+ "github.com/bnema/vev/internal/protocol/wire"
1011 )
1112
12-func (d *Daemon) handleRemotePreview(tr ports.Transport, frame ports.Frame) error {
13+func (d *Daemon) handleRemotePreview(tr ports.Transport, frame wire.Frame) error {
1314 defer func() { _ = tr.Close() }()
14- request, err := ports.UnmarshalRemotePreviewRequest(frame.Payload)
15+ request, err := wire.UnmarshalRemotePreviewRequest(frame.Payload)
1516 if err != nil {
1617 return d.sendRemotePreview(tr, protocol.RemotePreview{Version: protocol.RemotePreviewSchemaVersion, Status: protocol.RemotePreviewMalformed})
1718 }
@@ -27,11 +28,11 @@ func (d *Daemon) handleRemotePreview(tr ports.Transport, frame ports.Frame) erro
2728 }
2829
2930 func (d *Daemon) sendRemotePreview(tr ports.Transport, preview protocol.RemotePreview) error {
30- payload := ports.MarshalRemotePreview(preview)
31+ payload := wire.MarshalRemotePreview(preview)
3132 if payload == nil {
32- payload = ports.MarshalRemotePreview(protocol.RemotePreview{Version: protocol.RemotePreviewSchemaVersion, Status: protocol.RemotePreviewMalformed})
33+ payload = wire.MarshalRemotePreview(protocol.RemotePreview{Version: protocol.RemotePreviewSchemaVersion, Status: protocol.RemotePreviewMalformed})
3334 }
34- return d.boundedControlSend(tr, ports.Frame{Type: ports.MsgRemotePreviewResponse, Payload: payload})
35+ return d.boundedControlSend(tr, wire.Frame{Type: wire.MsgRemotePreviewResponse, Payload: payload})
3536 }
3637
3738 var errRemotePreviewNoSuchTarget = errors.New("daemon: remote preview target does not exist")
M internal/usecase/daemon/render_coordinator_handshake_test.go +7 -7
@@ -9,13 +9,13 @@ import (
99 "github.com/stretchr/testify/require"
1010
1111 "github.com/bnema/vev/internal/domain"
12- "github.com/bnema/vev/internal/ports"
12+ "github.com/bnema/vev/internal/protocol/wire"
1313 )
1414
1515 // fanoutBlockingTransport blocks only the first output send. The paired
1616 // healthy attachment must still receive its frame while this peer is stuck.
1717 type fanoutBlockingTransport struct {
18- sent chan ports.Frame
18+ sent chan wire.Frame
1919 entered chan struct{}
2020 release chan struct{}
2121 done chan struct{}
@@ -26,16 +26,16 @@ type fanoutBlockingTransport struct {
2626
2727 func newFanoutBlockingTransport() *fanoutBlockingTransport {
2828 return &fanoutBlockingTransport{
29- sent: make(chan ports.Frame, 8),
29+ sent: make(chan wire.Frame, 8),
3030 entered: make(chan struct{}),
3131 release: make(chan struct{}),
3232 done: make(chan struct{}),
3333 }
3434 }
3535
36-func (t *fanoutBlockingTransport) Send(frame ports.Frame) error {
36+func (t *fanoutBlockingTransport) Send(frame wire.Frame) error {
3737 t.sent <- frame
38- if frame.Type == ports.MsgOutput {
38+ if frame.Type == wire.MsgOutput {
3939 t.enteredOne.Do(func() { close(t.entered) })
4040 <-t.release
4141 }
@@ -43,7 +43,7 @@ func (t *fanoutBlockingTransport) Send(frame ports.Frame) error {
4343 return nil
4444 }
4545
46-func (t *fanoutBlockingTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
46+func (t *fanoutBlockingTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
4747
4848 func (t *fanoutBlockingTransport) Close() error {
4949 t.releaseOne.Do(func() { close(t.release) })
@@ -102,7 +102,7 @@ func TestRenderCoordinatorFanoutDoesNotWaitForSlowTransport(t *testing.T) {
102102
103103 select {
104104 case frame := <-healthySends:
105- require.Equal(t, ports.MsgOutput, frame.Type, "healthy attachment did not receive output")
105+ require.Equal(t, wire.MsgOutput, frame.Type, "healthy attachment did not receive output")
106106 case <-time.After(2 * time.Second):
107107 t.Fatal("healthy attachment remained blocked behind the slow transport")
108108 }
M internal/usecase/daemon/render_coordinator_test.go +4 -3
@@ -15,6 +15,7 @@ import (
1515 "github.com/bnema/vev/internal/ports"
1616 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1717 "github.com/bnema/vev/internal/protocol"
18+ "github.com/bnema/vev/internal/protocol/wire"
1819 "github.com/bnema/vev/internal/usecase/keys"
1920 "github.com/bnema/vev/internal/usecase/layout"
2021 )
@@ -267,12 +268,12 @@ func awaitLatestCoordinatorTimer(t *testing.T, clk *coordinatorMockClock) *coord
267268 }
268269 }
269270
270-func requireNoCoordinatorOutputFrame(t *testing.T, sends chan ports.Frame) {
271+func requireNoCoordinatorOutputFrame(t *testing.T, sends chan wire.Frame) {
271272 t.Helper()
272273 for {
273274 select {
274275 case frame := <-sends:
275- if frame.Type == ports.MsgRoutePosition {
276+ if frame.Type == wire.MsgRoutePosition {
276277 continue
277278 }
278279 t.Fatalf("unexpected output frame: %+v", frame)
@@ -1509,7 +1510,7 @@ func TestRenderCoordinatorSyncBatchSurvivesAttachmentLifecycle(t *testing.T) {
15091510 }
15101511
15111512 func TestRenderCoordinatorResizeEpochDispatch(t *testing.T) {
1512- newResizeFixture := func(t *testing.T) (*Daemon, *session, *attachedClient, chan ports.Frame, *coordinatorMockClock, chan renderInvalidation) {
1513+ newResizeFixture := func(t *testing.T) (*Daemon, *session, *attachedClient, chan wire.Frame, *coordinatorMockClock, chan renderInvalidation) {
15131514 t.Helper()
15141515 p, releasePTY := newBlockingPTY(t)
15151516 t.Cleanup(releasePTY)
M internal/usecase/daemon/render_damage_receipt_test.go +3 -2
@@ -6,6 +6,7 @@ import (
66 renderer "github.com/bnema/vev-vt"
77 "github.com/bnema/vev/internal/domain"
88 "github.com/bnema/vev/internal/ports"
9+ "github.com/bnema/vev/internal/protocol/wire"
910 "github.com/bnema/vev/internal/usecase/layout"
1011 "github.com/bnema/vev/internal/usecase/picker"
1112 "github.com/stretchr/testify/require"
@@ -156,7 +157,7 @@ func TestRenderDamageReceiptRetainsRealVTDamageAcrossFailedEmission(t *testing.T
156157 state, composed = captureComposeForReceiptTest(t, sess, ac)
157158 require.True(t, d.emitFrame(sess, ac, state, composed))
158159 frame := <-sends
159- out, err := ports.UnmarshalOutput(frame.Payload)
160+ out, err := wire.UnmarshalOutput(frame.Payload)
160161 require.NoError(t, err)
161162 require.Contains(t, string(out.Data), "changed", "retry must emit the retained terminal bytes")
162163 p.mu.Lock()
@@ -223,7 +224,7 @@ func TestPrepareFailureFallbackOnlySuppressesRecursiveNoticePaint(t *testing.T)
223224 require.True(t, d.emitFrame(sess, ac, state, composed))
224225 require.Len(t, d.notices.history(), want)
225226 frame := <-sends
226- require.Equal(t, ports.MsgOutput, frame.Type, "outer failure %d must repaint its notice", want)
227+ require.Equal(t, wire.MsgOutput, frame.Type, "outer failure %d must repaint its notice", want)
227228 require.False(t, ac.prepareFailureFallback.Load())
228229 }
229230 }
M internal/usecase/daemon/render_pipeline.go +3 -2
@@ -7,6 +7,7 @@ import (
77 "github.com/bnema/vev/internal/domain"
88 "github.com/bnema/vev/internal/ports"
99 "github.com/bnema/vev/internal/protocol"
10+ "github.com/bnema/vev/internal/protocol/wire"
1011 "github.com/bnema/vev/internal/usecase/layout"
1112 "github.com/bnema/vev/internal/usecase/notices"
1213 "github.com/bnema/vev/internal/usecase/palette"
@@ -539,7 +540,7 @@ func (d *Daemon) emitFrame(entry *session, ac *attachedClient, state *capturedRe
539540 }
540541
541542 if position.ActiveTabID != "" && position != ac.output.lastRoutePosition {
542- payload, positionMarshalErr := ports.MarshalRoutePosition(position)
543+ payload, positionMarshalErr := wire.MarshalRoutePosition(position)
543544 if positionMarshalErr != nil {
544545 d.log.Error("marshal route position", "err", positionMarshalErr)
545546 } else {
@@ -560,7 +561,7 @@ func (d *Daemon) emitFrame(entry *session, ac *attachedClient, state *capturedRe
560561 if async, ok := sendTr.(ports.AsyncTransport); ok {
561562 send = async.SendAsync
562563 }
563- sendErr = send(ports.Frame{Type: ports.MsgRoutePosition, Payload: payload})
564+ sendErr = send(wire.Frame{Type: wire.MsgRoutePosition, Payload: payload})
564565 }
565566 }
566567 if marks.attachmentEffect != nil && interruptible {
M internal/usecase/daemon/render_pipeline_cache_test.go +5 -5
@@ -8,7 +8,7 @@ import (
88
99 renderer "github.com/bnema/vev-vt"
1010 "github.com/bnema/vev/internal/domain"
11- "github.com/bnema/vev/internal/ports"
11+ "github.com/bnema/vev/internal/protocol/wire"
1212 "github.com/bnema/vev/internal/usecase/layout"
1313 themeui "github.com/bnema/vev/internal/usecase/theme"
1414 "github.com/bnema/vev/internal/usecase/ui"
@@ -17,9 +17,9 @@ import (
1717
1818 type cacheFailTransport struct{}
1919
20-func (cacheFailTransport) Send(ports.Frame) error { return errors.New("send failed") }
21-func (cacheFailTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
22-func (cacheFailTransport) Close() error { return nil }
20+func (cacheFailTransport) Send(wire.Frame) error { return errors.New("send failed") }
21+func (cacheFailTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
22+func (cacheFailTransport) Close() error { return nil }
2323
2424 func TestPipelineCachePublishesOnlyAfterEmission(t *testing.T) {
2525 for _, failure := range []struct {
@@ -69,7 +69,7 @@ func TestPipelineCachePublishesOnlyAfterEmission(t *testing.T) {
6969 require.True(t, d.emitFrame(sess, ac, &state, pending))
7070 require.Equal(t, pending.cache, ac.pipelineCache)
7171 frame := <-sends
72- output, err := ports.UnmarshalOutput(frame.Payload)
72+ output, err := wire.UnmarshalOutput(frame.Payload)
7373 require.NoError(t, err)
7474 require.Contains(t, string(output.Data), "next", "the retry must emit state retained after the failed emission")
7575 })
M internal/usecase/daemon/render_pipeline_test.go +10 -10
@@ -14,7 +14,7 @@ import (
1414 vt "github.com/bnema/vev-vt"
1515 renderer "github.com/bnema/vev-vt/ansi"
1616 "github.com/bnema/vev/internal/domain"
17- "github.com/bnema/vev/internal/ports"
17+ "github.com/bnema/vev/internal/protocol/wire"
1818 "github.com/bnema/vev/internal/testutil/replaytest"
1919 scopy "github.com/bnema/vev/internal/usecase/copy"
2020 "github.com/bnema/vev/internal/usecase/layout"
@@ -32,11 +32,11 @@ import (
3232 // assertions must not depend on any adapter's framing implementation.
3333 type scriptedReplayTransport struct {
3434 t *testing.T
35- frames []ports.Frame
35+ frames []wire.Frame
3636 next int
3737 }
3838
39-func (s *scriptedReplayTransport) Send(got ports.Frame) error {
39+func (s *scriptedReplayTransport) Send(got wire.Frame) error {
4040 s.t.Helper()
4141 if s.next >= len(s.frames) {
4242 s.t.Errorf("unexpected frame %#v", got)
@@ -48,8 +48,8 @@ func (s *scriptedReplayTransport) Send(got ports.Frame) error {
4848 require.Equal(s.t, want.Payload, got.Payload)
4949 return nil
5050 }
51-func (*scriptedReplayTransport) Recv() (ports.Frame, error) { return ports.Frame{}, io.EOF }
52-func (*scriptedReplayTransport) Close() error { return nil }
51+func (*scriptedReplayTransport) Recv() (wire.Frame, error) { return wire.Frame{}, io.EOF }
52+func (*scriptedReplayTransport) Close() error { return nil }
5353
5454 func TestTransportReplayFinalShadowAndTerminalBytes(t *testing.T) {
5555 frames := replaytest.Transcript()
@@ -57,7 +57,7 @@ func TestTransportReplayFinalShadowAndTerminalBytes(t *testing.T) {
5757 terminal := vt.NewScreen(8, 3)
5858 for _, frame := range frames {
5959 require.NoError(t, transport.Send(frame))
60- output, err := ports.UnmarshalOutput(frame.Payload)
60+ output, err := wire.UnmarshalOutput(frame.Payload)
6161 require.NoError(t, err)
6262 require.Equal(t, frame.Payload, mustMarshalOutput(output), "output payload must remain byte exact")
6363 terminal.Write(output.Data)
@@ -253,7 +253,7 @@ func TestEmitFrameFailedSendDoesNotPublishCursorOrOutputState(t *testing.T) {
253253 require.True(t, d.emitFrame(sess, ac, &state, composed))
254254 require.Equal(t, cursorOut{valid: true, row: 3, col: 4, style: 2, hasStyle: true}, ac.output.lastCursor)
255255 require.Equal(t, uint64(1), ac.output.next)
256- out, err := ports.UnmarshalOutput((<-sends).Payload)
256+ out, err := wire.UnmarshalOutput((<-sends).Payload)
257257 require.NoError(t, err)
258258 require.Zero(t, out.Base)
259259 require.Equal(t, uint64(1), out.New)
@@ -332,12 +332,12 @@ func TestComposeEmitExactReplayTiledFloatingBarsOverlayAndCursor(t *testing.T) {
332332 stream := newOutputStateStream()
333333 prepared, err := stream.prepareFrame(nil, &state, composed.frame, composed.damage, composed.reset, composed.cursor)
334334 require.NoError(t, err)
335- var outputFrame ports.Frame
336- require.NoError(t, prepared.send(0, func(frame ports.Frame) error {
335+ var outputFrame wire.Frame
336+ require.NoError(t, prepared.send(0, func(frame wire.Frame) error {
337337 outputFrame = frame
338338 return nil
339339 }))
340- output, err := ports.UnmarshalOutput(outputFrame.Payload)
340+ output, err := wire.UnmarshalOutput(outputFrame.Payload)
341341 require.NoError(t, err)
342342 terminalBytes := output.Data
343343 require.Equal(t, "\x1b[1;1H\x1b[0;7m tab \x1b[0m R\x1b[2;1HAAAAAAAAAAAA\x1b[3;1HBBB┌─fl─┐BBB\x1b[4;1H───Prompt───\x1b[5;1HPROMPT\x1b[K\x1b[B\x1b[2K\x1b[7;1H\x1b[0;7m sess \x1b[0m B\x1b[0m\x1b[?25l", string(terminalBytes))
M internal/usecase/daemon/render_test.go +26 -26
@@ -19,9 +19,9 @@ import (
1919 vt "github.com/bnema/vev-vt"
2020 renderer "github.com/bnema/vev-vt/ansi"
2121 "github.com/bnema/vev/internal/domain"
22- "github.com/bnema/vev/internal/ports"
2322 portsmocks "github.com/bnema/vev/internal/ports/mocks"
2423 "github.com/bnema/vev/internal/protocol"
24+ "github.com/bnema/vev/internal/protocol/wire"
2525 scopy "github.com/bnema/vev/internal/usecase/copy"
2626 "github.com/bnema/vev/internal/usecase/layout"
2727 themeui "github.com/bnema/vev/internal/usecase/theme"
@@ -38,14 +38,14 @@ func TestPaletteBackdropDimsSimultaneousCopyMode(t *testing.T) {
3838 pane.screen.Write([]byte("\x1b[38;2;180;90;30mX"))
3939
4040 d.enterCopyMode(sess, ac)
41- mustApplyOutput(t, client, awaitFrame(t, sends, ports.MsgOutput))
41+ mustApplyOutput(t, client, awaitFrame(t, sends, wire.MsgOutput))
4242 undimmed := client.Frame.At(0, 1)
4343 require.Equal(t, 'X', undimmed.Rune, "fixture must address copy-mode pane content")
4444 copyBar := append([]renderer.Cell(nil), client.Frame.Row(client.Frame.Height-1)...)
4545 require.Contains(t, rowText(copyBar), "[SCROLL]", "fixture must capture the copy status bar")
4646
4747 d.enterPalette(sess, ac)
48- mustApplyOutput(t, client, awaitFrame(t, sends, ports.MsgOutput))
48+ mustApplyOutput(t, client, awaitFrame(t, sends, wire.MsgOutput))
4949 dimmed := client.Frame.At(0, 1)
5050 require.Equal(t, undimmed.Rune, dimmed.Rune, "palette backdrop must preserve copy content")
5151 require.Equal(t, themeui.NewDimmer(theme).Dim(undimmed.Style), dimmed.Style, "palette backdrop must dim the composed copy frame")
@@ -84,8 +84,8 @@ func TestFirstPaintRetainedFloatingPaneEmitsOneReset(t *testing.T) {
8484
8585 d.firstPaint(sess, ac)
8686
87- frame := awaitFrame(t, sends, ports.MsgOutput)
88- output, err := ports.UnmarshalOutput(frame.Payload)
87+ frame := awaitFrame(t, sends, wire.MsgOutput)
88+ output, err := wire.UnmarshalOutput(frame.Payload)
8989 require.NoError(t, err)
9090 require.Zero(t, output.Base, "first paint must be a mandatory reset")
9191 select {
@@ -107,13 +107,13 @@ func TestPaletteBackdropDimsSimultaneousPicker(t *testing.T) {
107107 pane.screen.Write([]byte("X"))
108108
109109 d.enterPicker(sess, ac)
110- mustApplyOutput(t, client, awaitFrame(t, sends, ports.MsgOutput))
110+ mustApplyOutput(t, client, awaitFrame(t, sends, wire.MsgOutput))
111111 pickerTitle := client.Frame.At(31, 2)
112112 require.Equal(t, 'S', pickerTitle.Rune, "fixture must address the picker title")
113113 undimmedPane := client.Frame.At(0, 1)
114114
115115 d.enterPalette(sess, ac)
116- mustApplyOutput(t, client, awaitFrame(t, sends, ports.MsgOutput))
116+ mustApplyOutput(t, client, awaitFrame(t, sends, wire.MsgOutput))
117117 dimmedPickerTitle := pickerTitle
118118 dimmedPickerTitle.Style = themeui.NewDimmer(backdropTheme()).Dim(pickerTitle.Style)
119119 require.Equal(t, dimmedPickerTitle, client.Frame.At(31, 2), "the lower-priority picker must be part of the palette backdrop")
@@ -129,13 +129,13 @@ func TestPaletteBackdropProductionRenderAndDismissal(t *testing.T) {
129129 pane := sess.tabs[0].focusedPane()
130130 pane.screen.Write([]byte("X"))
131131 d.paint(sess, ac, true, nil)
132- mustApplyOutput(t, client, awaitFrame(t, sends, ports.MsgOutput))
132+ mustApplyOutput(t, client, awaitFrame(t, sends, wire.MsgOutput))
133133 undimmed := client.Frame.At(0, 1)
134134 topBar := client.Frame.At(0, 0)
135135 bottomBar := client.Frame.At(0, 24)
136136
137137 d.handleInput(sess, ac, []byte("\x1b "))
138- mustApplyOutput(t, client, awaitFrame(t, sends, ports.MsgOutput))
138+ mustApplyOutput(t, client, awaitFrame(t, sends, wire.MsgOutput))
139139 dimmed := client.Frame.At(0, 1)
140140 require.Equal(t, 'X', dimmed.Rune)
141141 require.Equal(t, themeui.NewDimmer(backdropTheme()).Dim(undimmed.Style), dimmed.Style, "open palette must use the theme dim style")
@@ -147,7 +147,7 @@ func TestPaletteBackdropProductionRenderAndDismissal(t *testing.T) {
147147 require.Equal(t, dimmedBottomBar, client.Frame.At(0, 24), "bottom chrome is part of the complete backdrop")
148148
149149 d.handleInput(sess, ac, []byte("\x1b"))
150- mustApplyOutput(t, client, awaitFrame(t, sends, ports.MsgOutput))
150+ mustApplyOutput(t, client, awaitFrame(t, sends, wire.MsgOutput))
151151 require.Equal(t, undimmed, client.Frame.At(0, 1), "full redraw restores pane rune and style")
152152 require.Equal(t, topBar, client.Frame.At(0, 0))
153153 require.Equal(t, bottomBar, client.Frame.At(0, 24))
@@ -189,10 +189,10 @@ func awaitPTYReadProcessed(t *testing.T, processed <-chan struct{}) {
189189 <-processed
190190 }
191191
192-func awaitOutputFrameWithoutSleep(t *testing.T, sends <-chan ports.Frame) ports.Frame {
192+func awaitOutputFrameWithoutSleep(t *testing.T, sends <-chan wire.Frame) wire.Frame {
193193 t.Helper()
194194 frame := <-sends
195- require.Equal(t, ports.MsgOutput, frame.Type)
195+ require.Equal(t, wire.MsgOutput, frame.Type)
196196 return frame
197197 }
198198
@@ -332,7 +332,7 @@ func TestPTYReaderSyncVisibilityTransitions(t *testing.T) {
332332 awaitPTYReadProcessed(t, inactiveProcessed)
333333 fireCoordinatorTimer(t, rc, drainCoordinatorTimers(clock), urgentRenderDeadline)
334334 frame := awaitOutputFrameWithoutSleep(t, sends)
335- output, err := ports.UnmarshalOutput(frame.Payload)
335+ output, err := wire.UnmarshalOutput(frame.Payload)
336336 require.NoError(t, err)
337337 require.Contains(t, string(output.Data), "partial complete")
338338 requireNoCoordinatorOutputFrame(t, sends)
@@ -365,7 +365,7 @@ func TestPTYReaderSyncVisibilityTransitions(t *testing.T) {
365365 awaitPTYReadProcessed(t, newProcessed)
366366 fireCoordinatorTimer(t, rc, drainCoordinatorTimers(clock), minOutputRenderDeadline)
367367 frame := awaitOutputFrameWithoutSleep(t, sends)
368- output, err := ports.UnmarshalOutput(frame.Payload)
368+ output, err := wire.UnmarshalOutput(frame.Payload)
369369 require.NoError(t, err)
370370 require.Contains(t, string(output.Data), "newly active")
371371 require.NotContains(t, string(output.Data), "old partial")
@@ -515,7 +515,7 @@ func TestPaneRenderableActiveAttachmentDoesNotScanPickerPreviews(t *testing.T) {
515515 }
516516
517517 func TestNonRenderablePaneDamageRemainsPendingForCapture(t *testing.T) {
518- newFixture := func(t *testing.T) (*Daemon, *session, *tab, *pane, chan ports.Frame) {
518+ newFixture := func(t *testing.T) (*Daemon, *session, *tab, *pane, chan wire.Frame) {
519519 t.Helper()
520520 p, release := newBlockingPTY(t)
521521 t.Cleanup(release)
@@ -589,8 +589,8 @@ func TestAltXClosesFinalTabAndDetaches(t *testing.T) {
589589 d.handleInput(sess, ac, []byte("CLT\r"))
590590
591591 require.Equal(t, 0, sessionCount(d))
592- f := awaitFrame(t, sends, ports.MsgDetached)
593- det, err := ports.UnmarshalDetached(f.Payload)
592+ f := awaitFrame(t, sends, wire.MsgDetached)
593+ det, err := wire.UnmarshalDetached(f.Payload)
594594 require.NoError(t, err)
595595 require.Equal(t, protocol.ReasonSessionKilled, det.Reason)
596596 }
@@ -610,8 +610,8 @@ func TestPTYEOFClosesActiveNonFinalTabAndRepaintsRemaining(t *testing.T) {
610610 require.Eventually(t, func() bool { return tabCount(sess) == 1 }, 2*time.Second, 5*time.Millisecond)
611611 require.Equal(t, 1, sessionCount(d))
612612 require.Equal(t, 0, testAttachmentTabIndex(sess))
613- f := awaitFrame(t, sends, ports.MsgOutput)
614- out, err := ports.UnmarshalOutput(f.Payload)
613+ f := awaitFrame(t, sends, wire.MsgOutput)
614+ out, err := wire.UnmarshalOutput(f.Payload)
615615 require.NoError(t, err)
616616 data := string(out.Data)
617617 require.Contains(t, data, "remaining")
@@ -636,8 +636,8 @@ func TestPTYEOFClosesInactiveNonFinalTabAndRepaintsStatus(t *testing.T) {
636636 require.Eventually(t, func() bool { return tabCount(sess) == 1 }, 2*time.Second, 5*time.Millisecond)
637637 require.Equal(t, 1, sessionCount(d))
638638 require.Equal(t, 0, testAttachmentTabIndex(sess))
639- f := awaitFrame(t, sends, ports.MsgOutput)
640- out, err := ports.UnmarshalOutput(f.Payload)
639+ f := awaitFrame(t, sends, wire.MsgOutput)
640+ out, err := wire.UnmarshalOutput(f.Payload)
641641 require.NoError(t, err)
642642 data := string(out.Data)
643643 require.Contains(t, data, "active")
@@ -655,8 +655,8 @@ func TestPTYEOFFinalTabKillsSessionAndDetaches(t *testing.T) {
655655 releases[0]()
656656
657657 require.Eventually(t, func() bool { return sessionCount(d) == 0 }, 2*time.Second, 5*time.Millisecond)
658- f := awaitFrame(t, sends, ports.MsgDetached)
659- det, err := ports.UnmarshalDetached(f.Payload)
658+ f := awaitFrame(t, sends, wire.MsgDetached)
659+ det, err := wire.UnmarshalDetached(f.Payload)
660660 require.NoError(t, err)
661661 require.Equal(t, protocol.ReasonSessionKilled, det.Reason)
662662
@@ -933,8 +933,8 @@ func TestResizeOrdersPTYBeforeScreen(t *testing.T) {
933933 var gotOutput atomic.Bool
934934 tr := portsmocks.NewMockTransport(t)
935935 tr.EXPECT().Close().Return(nil).Maybe()
936- tr.EXPECT().Send(mock.Anything).RunAndReturn(func(f ports.Frame) error {
937- if f.Type == ports.MsgOutput {
936+ tr.EXPECT().Send(mock.Anything).RunAndReturn(func(f wire.Frame) error {
937+ if f.Type == wire.MsgOutput {
938938 gotOutput.Store(true)
939939 }
940940 return nil
@@ -1053,7 +1053,7 @@ func TestPTYKittyIcatDetectionGetsResponsesWrittenBackToPTY(t *testing.T) {
10531053 }
10541054 select {
10551055 case f := <-sends:
1056- require.NotEqual(t, ports.MsgOutput, f.Type)
1056+ require.NotEqual(t, wire.MsgOutput, f.Type)
10571057 default:
10581058 }
10591059 }
M internal/usecase/daemon/resume_test.go +24 -23
@@ -16,23 +16,24 @@ import (
1616 "github.com/bnema/vev/internal/ports"
1717 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1818 "github.com/bnema/vev/internal/protocol"
19+ "github.com/bnema/vev/internal/protocol/wire"
1920 "github.com/bnema/vev/internal/usecase/layout"
2021 )
2122
2223 type closeTrackingTransport struct {
2324 mu sync.Mutex
2425 closed bool
25- sends []ports.Frame
26+ sends []wire.Frame
2627 }
2728
28-func (t *closeTrackingTransport) Send(f ports.Frame) error {
29+func (t *closeTrackingTransport) Send(f wire.Frame) error {
2930 t.mu.Lock()
3031 defer t.mu.Unlock()
3132 t.sends = append(t.sends, f)
3233 return nil
3334 }
34-func (t *closeTrackingTransport) Recv() (ports.Frame, error) {
35- return ports.Frame{}, errors.New("closed")
35+func (t *closeTrackingTransport) Recv() (wire.Frame, error) {
36+ return wire.Frame{}, errors.New("closed")
3637 }
3738 func (t *closeTrackingTransport) Close() error {
3839 t.mu.Lock()
@@ -46,10 +47,10 @@ func (t *closeTrackingTransport) Closed() bool {
4647 return t.closed
4748 }
4849
49-func (t *closeTrackingTransport) Sends() []ports.Frame {
50+func (t *closeTrackingTransport) Sends() []wire.Frame {
5051 t.mu.Lock()
5152 defer t.mu.Unlock()
52- return append([]ports.Frame(nil), t.sends...)
53+ return append([]wire.Frame(nil), t.sends...)
5354 }
5455
5556 type closeCountingBlockedTransport struct {
@@ -63,13 +64,13 @@ func newCloseCountingBlockedTransport() *closeCountingBlockedTransport {
6364 return &closeCountingBlockedTransport{closed: make(chan struct{})}
6465 }
6566
66-func (t *closeCountingBlockedTransport) Send(ports.Frame) error {
67+func (t *closeCountingBlockedTransport) Send(wire.Frame) error {
6768 <-t.closed
6869 return errors.New("closed")
6970 }
7071
71-func (t *closeCountingBlockedTransport) Recv() (ports.Frame, error) {
72- return ports.Frame{}, errors.New("closed")
72+func (t *closeCountingBlockedTransport) Recv() (wire.Frame, error) {
73+ return wire.Frame{}, errors.New("closed")
7374 }
7475
7576 func (t *closeCountingBlockedTransport) Close() error {
@@ -326,14 +327,14 @@ func TestHandleHelloResumeDefersFreshOutputUntilWelcome(t *testing.T) {
326327 done := make(chan struct{})
327328 resumeHello := helloResumeCapable(protocol.IntentResume, sess.name, token)
328329 go func() {
329- d.handleHello(tr.tr, ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(resumeHello)})
330+ d.handleHello(tr.tr, wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(resumeHello)})
330331 close(done)
331332 }()
332333
333334 <-tr.welcomeEntered
334335 handshakeTimer := awaitHandshakeTimer(t, clock)
335336 welcome := <-tr.sends
336- require.Equal(t, ports.MsgWelcome, welcome.Type)
337+ require.Equal(t, wire.MsgWelcome, welcome.Type)
337338 sess.mu.Lock()
338339 resumed := sess.snapshotAttachmentsLocked()[0]
339340 sess.mu.Unlock()
@@ -351,8 +352,8 @@ func TestHandleHelloResumeDefersFreshOutputUntilWelcome(t *testing.T) {
351352 requireNoCoordinatorOutputFrame(t, tr.sends)
352353
353354 tr.release()
354- output := awaitFrame(t, tr.sends, ports.MsgOutput)
355- first, err := ports.UnmarshalOutput(output.Payload)
355+ output := awaitFrame(t, tr.sends, wire.MsgOutput)
356+ first, err := wire.UnmarshalOutput(output.Payload)
356357 require.NoError(t, err)
357358 require.Zero(t, first.Base)
358359 tr.finish()
@@ -1094,7 +1095,7 @@ func TestOutputAckLagAloneDoesNotForceFullStateRepaint(t *testing.T) {
10941095
10951096 f := outputStateFrame(ac.output, []byte("incremental while reliable backlog drains"), reset, 0)
10961097 ac.sendMu.Unlock()
1097- out, err := ports.UnmarshalOutput(f.Payload)
1098+ out, err := wire.UnmarshalOutput(f.Payload)
10981099 require.NoError(t, err)
10991100 require.Equal(t, uint64(5), out.Base, "output should remain incremental unless an explicit reset is requested")
11001101 require.Equal(t, uint64(6), out.New)
@@ -1104,7 +1105,7 @@ func TestOutputAckLagAloneDoesNotForceFullStateRepaint(t *testing.T) {
11041105 require.True(t, reset, "explicit reset should still force full repaint")
11051106 full := outputStateFrame(ac.output, []byte("explicit full repaint"), reset, 0)
11061107 ac.sendMu.Unlock()
1107- fullOut, err := ports.UnmarshalOutput(full.Payload)
1108+ fullOut, err := wire.UnmarshalOutput(full.Payload)
11081109 require.NoError(t, err)
11091110 require.Equal(t, uint64(0), fullOut.Base)
11101111 require.Equal(t, uint64(7), fullOut.New)
@@ -1185,9 +1186,9 @@ func TestResumeRebasesFullOutputWindowBeforeFirstPaint(t *testing.T) {
11851186 require.Same(t, ac, resumedAC)
11861187 d.paint(resumedSess, resumedAC, true, nil)
11871188
1188- sends := testFramesOfType(newTr.Sends(), ports.MsgOutput)
1189+ sends := testFramesOfType(newTr.Sends(), wire.MsgOutput)
11891190 require.Len(t, sends, 1)
1190- first, err := ports.UnmarshalOutput(sends[0].Payload)
1191+ first, err := wire.UnmarshalOutput(sends[0].Payload)
11911192 require.NoError(t, err)
11921193 require.Zero(t, first.Base)
11931194 require.Equal(t, uint64(1), first.New)
@@ -1195,9 +1196,9 @@ func TestResumeRebasesFullOutputWindowBeforeFirstPaint(t *testing.T) {
11951196
11961197 resumedSess.tabs[0].focusedPane().screen.Write([]byte("A"))
11971198 d.paint(resumedSess, resumedAC, false, nil)
1198- sends = testFramesOfType(newTr.Sends(), ports.MsgOutput)
1199+ sends = testFramesOfType(newTr.Sends(), wire.MsgOutput)
11991200 require.Len(t, sends, 2)
1200- second, err := ports.UnmarshalOutput(sends[1].Payload)
1201+ second, err := wire.UnmarshalOutput(sends[1].Payload)
12011202 require.NoError(t, err)
12021203 require.Equal(t, first.New, second.Base)
12031204 }
@@ -1245,9 +1246,9 @@ func TestParkingReleasesPaneCapturesBeforeHeadlessCloseAndResume(t *testing.T) {
12451246 require.True(t, resumedSess.renderCoordinator().markAttachmentReady(resumedSess.renderCoordinator().attachmentLease(resumedAC)))
12461247 d.firstPaint(resumedSess, resumedAC)
12471248
1248- sends := testFramesOfType(newTransport.Sends(), ports.MsgOutput)
1249+ sends := testFramesOfType(newTransport.Sends(), wire.MsgOutput)
12491250 require.Len(t, sends, 1)
1250- output, err := ports.UnmarshalOutput(sends[0].Payload)
1251+ output, err := wire.UnmarshalOutput(sends[0].Payload)
12511252 require.NoError(t, err)
12521253 require.Zero(t, output.Base, "resume must start with a complete frame")
12531254 terminal := vt.NewScreen(resumedAC.size.Cols, resumedAC.size.Rows)
@@ -1543,9 +1544,9 @@ func TestRawTerminalSideEffectsAreOutputStateNeutral(t *testing.T) {
15431544
15441545 sends := tr.Sends()
15451546 require.Len(t, sends, 2)
1546- first, err := ports.UnmarshalOutput(sends[0].Payload)
1547+ first, err := wire.UnmarshalOutput(sends[0].Payload)
15471548 require.NoError(t, err)
1548- second, err := ports.UnmarshalOutput(sends[1].Payload)
1549+ second, err := wire.UnmarshalOutput(sends[1].Payload)
15491550 require.NoError(t, err)
15501551 require.Zero(t, first.Base)
15511552 require.Zero(t, first.New)
M internal/usecase/daemon/route_position_test.go +3 -3
@@ -7,8 +7,8 @@ import (
77 "github.com/stretchr/testify/require"
88
99 "github.com/bnema/vev/internal/domain"
10- "github.com/bnema/vev/internal/ports"
1110 "github.com/bnema/vev/internal/protocol"
11+ "github.com/bnema/vev/internal/protocol/wire"
1212 )
1313
1414 func TestRouteAppliesPreferredTabPerAttachment(t *testing.T) {
@@ -96,10 +96,10 @@ func TestPaintPublishesChangedAttachmentRoutePosition(t *testing.T) {
9696
9797 var positions []protocol.RoutePosition
9898 for _, frame := range tr.Sends() {
99- if frame.Type != ports.MsgRoutePosition {
99+ if frame.Type != wire.MsgRoutePosition {
100100 continue
101101 }
102- position, err := ports.UnmarshalRoutePosition(frame.Payload)
102+ position, err := wire.UnmarshalRoutePosition(frame.Payload)
103103 require.NoError(t, err)
104104 positions = append(positions, position)
105105 }
M internal/usecase/daemon/same_peer_switch.go +3 -3
@@ -2,8 +2,8 @@ package daemon
22
33 import (
44 "github.com/bnema/vev/internal/domain"
5- "github.com/bnema/vev/internal/ports"
65 "github.com/bnema/vev/internal/protocol"
6+ "github.com/bnema/vev/internal/protocol/wire"
77 )
88
99 func (ac *attachedClient) offerSamePeerTarget(target protocol.ExactSessionTarget) {
@@ -122,11 +122,11 @@ func (d *Daemon) sendSamePeerSwitchFailure(effect *attachmentEffect, requestID u
122122 }
123123 defer sender.End()
124124 }
125- payload, err := ports.MarshalSamePeerSwitchFailure(protocol.SamePeerSwitchFailure{RequestID: requestID, Code: code})
125+ payload, err := wire.MarshalSamePeerSwitchFailure(protocol.SamePeerSwitchFailure{RequestID: requestID, Code: code})
126126 if err != nil {
127127 return
128128 }
129- if err := sender.sendControl(ports.Frame{Type: ports.MsgSamePeerSwitchFailure, Payload: payload}); err != nil {
129+ if err := sender.sendControl(wire.Frame{Type: wire.MsgSamePeerSwitchFailure, Payload: payload}); err != nil {
130130 d.detachOnAttachmentSendError(sender.capability(), sender.transport.transport)
131131 }
132132 }
M internal/usecase/daemon/same_peer_switch_test.go +10 -10
@@ -6,8 +6,8 @@ import (
66 "github.com/stretchr/testify/require"
77
88 "github.com/bnema/vev/internal/domain"
9- "github.com/bnema/vev/internal/ports"
109 "github.com/bnema/vev/internal/protocol"
10+ "github.com/bnema/vev/internal/protocol/wire"
1111 )
1212
1313 func TestPaletteSessionSelectionCompletesSamePeerSwitch(t *testing.T) {
@@ -31,10 +31,10 @@ func TestPaletteSessionSelectionCompletesSamePeerSwitch(t *testing.T) {
3131
3232 token := beginRecentRoutePaletteEffect(t, d, source, ac)
3333 d.handleInputForAttachment(token, []byte("\x1b "))
34- awaitFrame(t, sends, ports.MsgOutput)
34+ awaitFrame(t, sends, wire.MsgOutput)
3535 d.handleInputForAttachment(token, []byte("target\r"))
36- targetFrame := awaitFrame(t, sends, ports.MsgAttachTarget)
37- attachTarget, err := ports.UnmarshalAttachTarget(targetFrame.Payload)
36+ targetFrame := awaitFrame(t, sends, wire.MsgAttachTarget)
37+ attachTarget, err := wire.UnmarshalAttachTarget(targetFrame.Payload)
3838 require.NoError(t, err)
3939 require.Equal(t, &protocol.ExactSessionTarget{LifecycleID: lifecycle, SessionName: "target"}, attachTarget.ExactTarget)
4040
@@ -43,8 +43,8 @@ func TestPaletteSessionSelectionCompletesSamePeerSwitch(t *testing.T) {
4343 })
4444
4545 require.Same(t, target, ac.currentAttachmentSession())
46- identityFrame := awaitFrame(t, sends, ports.MsgCommittedRouteIdentity)
47- identity, err := ports.UnmarshalCommittedRouteIdentity(identityFrame.Payload)
46+ identityFrame := awaitFrame(t, sends, wire.MsgCommittedRouteIdentity)
47+ identity, err := wire.UnmarshalCommittedRouteIdentity(identityFrame.Payload)
4848 require.NoError(t, err)
4949 require.Equal(t, attachTarget.ExactTarget, &identity.Target)
5050 }
@@ -86,8 +86,8 @@ func TestSamePeerSwitchTransitionsExactTargetAndPreferredTab(t *testing.T) {
8686
8787 require.Same(t, target, ac.currentAttachmentSession())
8888 require.Equal(t, domain.TabStableID(target.tabs[1].stableID), ac.viewSnapshot().tabID)
89- identityFrame := awaitFrame(t, sends, ports.MsgCommittedRouteIdentity)
90- identity, err := ports.UnmarshalCommittedRouteIdentity(identityFrame.Payload)
89+ identityFrame := awaitFrame(t, sends, wire.MsgCommittedRouteIdentity)
90+ identity, err := wire.UnmarshalCommittedRouteIdentity(identityFrame.Payload)
9191 require.NoError(t, err)
9292 require.Equal(t, protocol.ExactSessionTarget{LifecycleID: lifecycle, SessionName: "target"}, identity.Target)
9393 }
@@ -120,8 +120,8 @@ func TestSamePeerSwitchRejectsStaleTargetWithoutMutation(t *testing.T) {
120120 })
121121
122122 require.Same(t, source, ac.currentAttachmentSession())
123- failureFrame := awaitFrame(t, sends, ports.MsgSamePeerSwitchFailure)
124- failure, err := ports.UnmarshalSamePeerSwitchFailure(failureFrame.Payload)
123+ failureFrame := awaitFrame(t, sends, wire.MsgSamePeerSwitchFailure)
124+ failure, err := wire.UnmarshalSamePeerSwitchFailure(failureFrame.Payload)
125125 require.NoError(t, err)
126126 require.Equal(t, protocol.SamePeerSwitchFailure{RequestID: 1, Code: protocol.SamePeerSwitchStaleTarget}, failure)
127127 }
M internal/usecase/daemon/session_back_route_test.go +3 -3
@@ -3,8 +3,8 @@ package daemon
33 import (
44 "testing"
55
6- "github.com/bnema/vev/internal/ports"
76 "github.com/bnema/vev/internal/protocol"
7+ "github.com/bnema/vev/internal/protocol/wire"
88 "github.com/stretchr/testify/require"
99 )
1010
@@ -32,11 +32,11 @@ func TestBackSessionUsesClientPreviousRouteAfterSnapshotPublication(t *testing.T
3232 var action protocol.RouteNavigationAction
3333 found := false
3434 for _, frame := range frames {
35- if frame.Type != ports.MsgNavigateRecentRoute {
35+ if frame.Type != wire.MsgNavigateRecentRoute {
3636 continue
3737 }
3838 var err error
39- action, err = ports.UnmarshalRouteNavigationAction(frame.Payload)
39+ action, err = wire.UnmarshalRouteNavigationAction(frame.Payload)
4040 require.NoError(t, err)
4141 found = true
4242 }
M internal/usecase/daemon/session_rename_purge_test.go +7 -7
@@ -6,9 +6,9 @@ import (
66 "github.com/stretchr/testify/require"
77
88 "github.com/bnema/vev/internal/domain"
9- "github.com/bnema/vev/internal/ports"
109 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1110 "github.com/bnema/vev/internal/protocol"
11+ "github.com/bnema/vev/internal/protocol/wire"
1212 )
1313
1414 func TestRenamePublishesCommittedRouteIdentityToAttachedClients(t *testing.T) {
@@ -24,8 +24,8 @@ func TestRenamePublishesCommittedRouteIdentityToAttachedClients(t *testing.T) {
2424
2525 require.NoError(t, d.renameSession(sess, "vps-infra"))
2626
27- frame := awaitFrame(t, sends, ports.MsgCommittedRouteIdentity)
28- identity, err := ports.UnmarshalCommittedRouteIdentity(frame.Payload)
27+ frame := awaitFrame(t, sends, wire.MsgCommittedRouteIdentity)
28+ identity, err := wire.UnmarshalCommittedRouteIdentity(frame.Payload)
2929 require.NoError(t, err)
3030 require.Equal(t, protocol.ExactSessionTarget{
3131 LifecycleID: domain.IncarnationID{1},
@@ -54,12 +54,12 @@ func TestRenameDefersCommittedRouteIdentityUntilFirstRouteSnapshot(t *testing.T)
5454 ActiveEntry: protocol.RecentRouteEntry{Key: 1, Generation: 1, Target: testRouteTarget("0", 1), Name: "0", Kind: protocol.RouteKindLocal},
5555 Home: activeRef,
5656 }
57- payload, err := ports.MarshalRecentRouteSnapshot(snapshot)
57+ payload, err := wire.MarshalRecentRouteSnapshot(snapshot)
5858 require.NoError(t, err)
59- require.False(t, d.handleAttachmentClientFrame(token, ports.Frame{Type: ports.MsgRecentRouteSnapshot, Payload: payload}))
59+ require.False(t, d.handleAttachmentClientFrame(token, wire.Frame{Type: wire.MsgRecentRouteSnapshot, Payload: payload}))
6060
61- frame := awaitFrame(t, sends, ports.MsgCommittedRouteIdentity)
62- identity, err := ports.UnmarshalCommittedRouteIdentity(frame.Payload)
61+ frame := awaitFrame(t, sends, wire.MsgCommittedRouteIdentity)
62+ identity, err := wire.UnmarshalCommittedRouteIdentity(frame.Payload)
6363 require.NoError(t, err)
6464 require.Equal(t, protocol.ExactSessionTarget{
6565 LifecycleID: domain.IncarnationID{1},
M internal/usecase/daemon/session_test.go +46 -45
@@ -20,6 +20,7 @@ import (
2020 "github.com/bnema/vev/internal/ports"
2121 portsmocks "github.com/bnema/vev/internal/ports/mocks"
2222 "github.com/bnema/vev/internal/protocol"
23+ "github.com/bnema/vev/internal/protocol/wire"
2324 "github.com/bnema/vev/internal/usecase/picker"
2425 themeui "github.com/bnema/vev/internal/usecase/theme"
2526 )
@@ -42,12 +43,12 @@ func TestHandshakeEphemeralHappy(t *testing.T) {
4243 var hg sync.WaitGroup
4344 hg.Go(func() { d.handleConn(tr) })
4445
45- w := awaitFrame(t, sends, ports.MsgWelcome)
46- welcome, err := ports.UnmarshalWelcome(w.Payload)
46+ w := awaitFrame(t, sends, wire.MsgWelcome)
47+ welcome, err := wire.UnmarshalWelcome(w.Payload)
4748 require.NoError(t, err)
4849 require.Equal(t, "0", welcome.SessionName)
4950 require.True(t, welcome.Ephemeral)
50- awaitFrame(t, sends, ports.MsgOutput) // guaranteed first paint
51+ awaitFrame(t, sends, wire.MsgOutput) // guaranteed first paint
5152 require.Equal(t, 1, sessionCount(d))
5253
5354 releaseConn()
@@ -64,8 +65,8 @@ func TestHandshakeNewHappy(t *testing.T) {
6465 var hg sync.WaitGroup
6566 hg.Go(func() { d.handleConn(tr) })
6667
67- w := awaitFrame(t, sends, ports.MsgWelcome)
68- welcome, err := ports.UnmarshalWelcome(w.Payload)
68+ w := awaitFrame(t, sends, wire.MsgWelcome)
69+ welcome, err := wire.UnmarshalWelcome(w.Payload)
6970 require.NoError(t, err)
7071 require.Equal(t, "work", welcome.SessionName)
7172 require.False(t, welcome.Ephemeral)
@@ -84,15 +85,15 @@ func TestHandshakeNewHappy(t *testing.T) {
8485 func TestHandshakeVersionMismatch(t *testing.T) {
8586 // No Open expectation: the factory must never be asked to spawn a PTY.
8687 d := newTestDaemon(t, portsmocks.NewMockPTYFactory(t), stubClock{})
87- bad := ports.Frame{Type: ports.MsgHello, Payload: ports.MarshalHello(protocol.Hello{
88+ bad := wire.Frame{Type: wire.MsgHello, Payload: wire.MarshalHello(protocol.Hello{
8889 Version: protocol.Version + 99, Intent: protocol.IntentEphemeral, Size: domain.Size{Cols: 80, Rows: 24},
8990 })}
9091 tr, sends, _ := newConn(t, bad)
9192
9293 d.handleConn(tr) // returns after the rejection; no session, no goroutines
9394
94- e := awaitFrame(t, sends, ports.MsgError)
95- em, err := ports.UnmarshalErrorMsg(e.Payload)
95+ e := awaitFrame(t, sends, wire.MsgError)
96+ em, err := wire.UnmarshalErrorMsg(e.Payload)
9697 require.NoError(t, err)
9798 require.Equal(t, protocol.ErrVersionMismatch, em.Code)
9899 require.Equal(t, 0, sessionCount(d))
@@ -108,11 +109,11 @@ func TestHandshakeOldHelloLayoutReportsVersionMismatch(t *testing.T) {
108109 0x00, 0x18,
109110 0x00, 0x00,
110111 }
111- tr, sends, _ := newConn(t, ports.Frame{Type: ports.MsgHello, Payload: oldLayout})
112+ tr, sends, _ := newConn(t, wire.Frame{Type: wire.MsgHello, Payload: oldLayout})
112113 d.handleConn(tr)
113114
114- e := awaitFrame(t, sends, ports.MsgError)
115- em, err := ports.UnmarshalErrorMsg(e.Payload)
115+ e := awaitFrame(t, sends, wire.MsgError)
116+ em, err := wire.UnmarshalErrorMsg(e.Payload)
116117 require.NoError(t, err)
117118 require.Equal(t, protocol.ErrVersionMismatch, em.Code)
118119 require.Equal(t, 0, sessionCount(d))
@@ -126,8 +127,8 @@ func TestHandshakeNameTaken(t *testing.T) {
126127 tr, sends, _ := newConn(t, mustHello(protocol.IntentNew, "work", domain.Size{Cols: 80, Rows: 24}))
127128 d.handleConn(tr)
128129
129- e := awaitFrame(t, sends, ports.MsgError)
130- em, _ := ports.UnmarshalErrorMsg(e.Payload)
130+ e := awaitFrame(t, sends, wire.MsgError)
131+ em, _ := wire.UnmarshalErrorMsg(e.Payload)
131132 require.Equal(t, protocol.ErrNameTaken, em.Code)
132133 }
133134
@@ -136,14 +137,14 @@ func TestHandshakeNoSuchSession(t *testing.T) {
136137 tr, sends, _ := newConn(t, mustHello(protocol.IntentAttach, "ghost", domain.Size{Cols: 80, Rows: 24}))
137138 d.handleConn(tr)
138139
139- e := awaitFrame(t, sends, ports.MsgError)
140- em, _ := ports.UnmarshalErrorMsg(e.Payload)
140+ e := awaitFrame(t, sends, wire.MsgError)
141+ em, _ := wire.UnmarshalErrorMsg(e.Payload)
141142 require.Equal(t, protocol.ErrNoSuchSession, em.Code)
142143 }
143144
144145 func TestKillAllEmptyDaemonSignalsShutdown(t *testing.T) {
145146 d := newTestDaemon(t, portsmocks.NewMockPTYFactory(t), stubClock{})
146- tr, _, _ := newConn(t, ports.Frame{Type: ports.MsgKill, Payload: ports.MarshalKill(protocol.Kill{All: true})})
147+ tr, _, _ := newConn(t, wire.Frame{Type: wire.MsgKill, Payload: wire.MarshalKill(protocol.Kill{All: true})})
147148 d.handleConn(tr)
148149
149150 select {
@@ -232,8 +233,8 @@ func TestCreateTabClosesPTYIfSessionKilledDuringOpen(t *testing.T) {
232233 tr, sends, releaseConn := newConn(t, mustHello(protocol.IntentNew, "work", domain.Size{Cols: 80, Rows: 24}))
233234 var hg sync.WaitGroup
234235 hg.Go(func() { d.handleConn(tr) })
235- awaitFrame(t, sends, ports.MsgWelcome)
236- awaitFrame(t, sends, ports.MsgOutput)
236+ awaitFrame(t, sends, wire.MsgWelcome)
237+ awaitFrame(t, sends, wire.MsgOutput)
237238 sess := firstSession(d)
238239 require.NotNil(t, sess)
239240
@@ -289,13 +290,13 @@ func TestDetachKeepsEphemeralHeadless(t *testing.T) {
289290 d := newTestDaemon(t, newFactory(t, p), stubClock{})
290291 tr, sends, _ := newConn(t,
291292 mustHello(protocol.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}),
292- ports.Frame{Type: ports.MsgDetach, Payload: ports.MarshalDetach(protocol.Detach{})},
293+ wire.Frame{Type: wire.MsgDetach, Payload: wire.MarshalDetach(protocol.Detach{})},
293294 )
294295
295296 var hg sync.WaitGroup
296297 hg.Go(func() { d.handleConn(tr) })
297- awaitFrame(t, sends, ports.MsgWelcome)
298- awaitFrame(t, sends, ports.MsgDetached)
298+ awaitFrame(t, sends, wire.MsgWelcome)
299+ awaitFrame(t, sends, wire.MsgDetached)
299300 hg.Wait()
300301
301302 require.Equal(t, 1, sessionCount(d), "ephemeral session survives explicit detach")
@@ -338,14 +339,14 @@ func TestDetachKeepsNamed(t *testing.T) {
338339 d := newTestDaemon(t, newFactory(t, p), stubClock{})
339340 tr, sends, _ := newConn(t,
340341 mustHello(protocol.IntentNew, "keep", domain.Size{Cols: 80, Rows: 24}),
341- ports.Frame{Type: ports.MsgDetach, Payload: ports.MarshalDetach(protocol.Detach{})},
342+ wire.Frame{Type: wire.MsgDetach, Payload: wire.MarshalDetach(protocol.Detach{})},
342343 )
343344
344345 var hg sync.WaitGroup
345346 hg.Go(func() { d.handleConn(tr) })
346- awaitFrame(t, sends, ports.MsgWelcome)
347- awaitFrame(t, sends, ports.MsgDetached) // ack for explicit detach
348- hg.Wait() // handler returns after detach
347+ awaitFrame(t, sends, wire.MsgWelcome)
348+ awaitFrame(t, sends, wire.MsgDetached) // ack for explicit detach
349+ hg.Wait() // handler returns after detach
349350
350351 require.Equal(t, 1, sessionCount(d), "named session survives detach")
351352 sess := firstSession(d)
@@ -375,8 +376,8 @@ func TestReaderEOFRemovesSessionAndSignalsShutdown(t *testing.T) {
375376 hg.Go(func() { d.handleConn(tr) })
376377
377378 // The client is detached with ReasonSessionKilled when the child exits.
378- det := awaitFrame(t, sends, ports.MsgDetached)
379- dm, _ := ports.UnmarshalDetached(det.Payload)
379+ det := awaitFrame(t, sends, wire.MsgDetached)
380+ dm, _ := wire.UnmarshalDetached(det.Payload)
380381 require.Equal(t, protocol.ReasonSessionKilled, dm.Reason)
381382
382383 select {
@@ -418,8 +419,8 @@ func TestServeReturnsWhenLastSessionExits(t *testing.T) {
418419 served := make(chan error, 1)
419420 go func() { served <- d.Serve(context.Background(), l) }()
420421
421- awaitFrame(t, sends, ports.MsgWelcome)
422- awaitFrame(t, sends, ports.MsgOutput)
422+ awaitFrame(t, sends, wire.MsgWelcome)
423+ awaitFrame(t, sends, wire.MsgOutput)
423424
424425 // Child exits -> session removed -> registry empties -> Serve returns.
425426 releasePTY()
@@ -459,13 +460,13 @@ func TestServeGracefulShutdownOnContextCancel(t *testing.T) {
459460 served := make(chan error, 1)
460461 go func() { served <- d.Serve(ctx, l) }()
461462
462- awaitFrame(t, sends, ports.MsgWelcome)
463- awaitFrame(t, sends, ports.MsgOutput)
463+ awaitFrame(t, sends, wire.MsgWelcome)
464+ awaitFrame(t, sends, wire.MsgOutput)
464465
465466 cancel() // graceful shutdown
466467
467- det := awaitFrame(t, sends, ports.MsgDetached)
468- dm, _ := ports.UnmarshalDetached(det.Payload)
468+ det := awaitFrame(t, sends, wire.MsgDetached)
469+ dm, _ := wire.UnmarshalDetached(det.Payload)
469470 require.Equal(t, protocol.ReasonServerShutdown, dm.Reason)
470471
471472 select {
@@ -515,11 +516,11 @@ func TestHelloRacingShutdownIsRejected(t *testing.T) {
515516 tr1, sends1, release1 := newConn(t, mustHello(protocol.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}))
516517 var hg sync.WaitGroup
517518 hg.Go(func() { d.handleConn(tr1) })
518- awaitFrame(t, sends1, ports.MsgWelcome)
519+ awaitFrame(t, sends1, wire.MsgWelcome)
519520 // firstPaint starts the background prewarm asynchronously. It is valid for
520521 // this Open to occur before shutdown, so do not mistake it for the racing
521522 // Hello's forbidden launch.
522- awaitFrame(t, sends1, ports.MsgOutput)
523+ awaitFrame(t, sends1, wire.MsgOutput)
523524 select {
524525 case <-preShutdownFloatingOpen:
525526 case <-time.After(time.Second):
@@ -548,8 +549,8 @@ func TestHelloRacingShutdownIsRejected(t *testing.T) {
548549 } {
549550 tr2, sends2, _ := newConn(t, mustHello(intent.intent, intent.sess, domain.Size{Cols: 80, Rows: 24}))
550551 d.handleConn(tr2)
551- e := awaitFrame(t, sends2, ports.MsgError)
552- em, err := ports.UnmarshalErrorMsg(e.Payload)
552+ e := awaitFrame(t, sends2, wire.MsgError)
553+ em, err := wire.UnmarshalErrorMsg(e.Payload)
553554 require.NoError(t, err)
554555 require.Equal(t, protocol.ErrServerShutdown, em.Code, "%s hello racing shutdown must be rejected", intent.name)
555556 }
@@ -573,23 +574,23 @@ func TestServeReturnsDespiteWedgedClientOnShutdown(t *testing.T) {
573574 p, _ := newBlockingPTY(t) // Close unblocks the parked reader
574575
575576 tr := portsmocks.NewMockTransport(t)
576- sends := make(chan ports.Frame, 64)
577- recvCh := make(chan ports.Frame, 1)
577+ sends := make(chan wire.Frame, 64)
578+ recvCh := make(chan wire.Frame, 1)
578579 recvCh <- mustHello(protocol.IntentNew, "wedge", domain.Size{Cols: 80, Rows: 24})
579580 connDone := make(chan struct{})
580581 var connOnce sync.Once
581582 closeConn := func() { connOnce.Do(func() { close(connDone) }) }
582583
583- tr.EXPECT().Recv().RunAndReturn(func() (ports.Frame, error) {
584+ tr.EXPECT().Recv().RunAndReturn(func() (wire.Frame, error) {
584585 select {
585586 case f := <-recvCh:
586587 return f, nil
587588 case <-connDone:
588- return ports.Frame{}, io.EOF
589+ return wire.Frame{}, io.EOF
589590 }
590591 }).Maybe()
591- tr.EXPECT().Send(mock.Anything).RunAndReturn(func(f ports.Frame) error {
592- if f.Type == ports.MsgDetached {
592+ tr.EXPECT().Send(mock.Anything).RunAndReturn(func(f wire.Frame) error {
593+ if f.Type == wire.MsgDetached {
593594 // Wedged: blocks until the transport is force-closed.
594595 <-connDone
595596 return io.ErrClosedPipe
@@ -621,8 +622,8 @@ func TestServeReturnsDespiteWedgedClientOnShutdown(t *testing.T) {
621622 served := make(chan error, 1)
622623 go func() { served <- d.Serve(ctx, l) }()
623624
624- awaitFrame(t, sends, ports.MsgWelcome)
625- awaitFrame(t, sends, ports.MsgOutput)
625+ awaitFrame(t, sends, wire.MsgWelcome)
626+ awaitFrame(t, sends, wire.MsgOutput)
626627
627628 cancel() // shutdown with the wedged client still attached
628629
M internal/usecase/daemon/snapshot_scheduler_test.go +4 -3
@@ -15,6 +15,7 @@ import (
1515 "github.com/bnema/vev/internal/ports"
1616 portsmocks "github.com/bnema/vev/internal/ports/mocks"
1717 "github.com/bnema/vev/internal/protocol"
18+ "github.com/bnema/vev/internal/protocol/wire"
1819 )
1920
2021 func TestSnapshotCoordinatorQuarantineCancelsPublicationBeforeDelete(t *testing.T) {
@@ -311,7 +312,7 @@ func TestServeShutdownDeadlineStillJoinsPaneExitWriter(t *testing.T) {
311312 served := make(chan error, 1)
312313 go func() { served <- d.Serve(ctx, listener) }()
313314
314- awaitFrame(t, sends, ports.MsgWelcome)
315+ awaitFrame(t, sends, wire.MsgWelcome)
315316 sess := firstSession(d)
316317 require.NotNil(t, sess)
317318
@@ -476,7 +477,7 @@ func TestServeShutdownCheckpointsBeforeStoppingSnapshotWorker(t *testing.T) {
476477 served := make(chan error, 1)
477478 go func() { served <- d.Serve(ctx, l) }()
478479
479- awaitFrame(t, sends, ports.MsgWelcome)
480+ awaitFrame(t, sends, wire.MsgWelcome)
480481 markSnapshotDirty(firstSession(d))
481482 d.snapshotWake <- struct{}{}
482483 first := <-published
@@ -533,7 +534,7 @@ func TestServeShutdownDeadlineStillJoinsUncooperativeSnapshotRepository(t *testi
533534 served := make(chan error, 1)
534535 go func() { served <- d.Serve(ctx, l) }()
535536
536- awaitFrame(t, sends, ports.MsgWelcome)
537+ awaitFrame(t, sends, wire.MsgWelcome)
537538 markSnapshotDirty(firstSession(d))
538539 d.snapshotWake <- struct{}{}
539540 select {
M internal/usecase/daemon/status_test.go +6 -6
@@ -13,8 +13,8 @@ import (
1313
1414 renderer "github.com/bnema/vev-vt/ansi"
1515 "github.com/bnema/vev/internal/domain"
16- "github.com/bnema/vev/internal/ports"
1716 "github.com/bnema/vev/internal/protocol"
17+ "github.com/bnema/vev/internal/protocol/wire"
1818 "github.com/bnema/vev/internal/usecase/command"
1919 "github.com/bnema/vev/internal/usecase/layout"
2020 "github.com/bnema/vev/internal/usecase/palette"
@@ -1445,13 +1445,13 @@ func TestStatusCoalescesCreateSwitchAndResize(t *testing.T) {
14451445 frameInput([]byte("\x1b ")),
14461446 frameInput([]byte("CNT\r")),
14471447 frameInput([]byte("\x1b1")),
1448- ports.Frame{Type: ports.MsgResize, Payload: mustMarshalResize(protocol.Resize{Size: domain.Size{Cols: 22, Rows: 6}})},
1448+ wire.Frame{Type: wire.MsgResize, Payload: mustMarshalResize(protocol.Resize{Size: domain.Size{Cols: 22, Rows: 6}})},
14491449 )
14501450
14511451 var hg sync.WaitGroup
14521452 hg.Go(func() { d.handleConn(tr) })
1453- awaitFrame(t, sends, ports.MsgWelcome)
1454- first := awaitFrame(t, sends, ports.MsgOutput)
1453+ awaitFrame(t, sends, wire.MsgWelcome)
1454+ first := awaitFrame(t, sends, wire.MsgOutput)
14551455 require.Eventually(t, func() bool {
14561456 sessions := listSessions(t, d)
14571457 return len(sessions.Sessions) == 1 && sessions.Sessions[0].Tabs == 2
@@ -1466,8 +1466,8 @@ func TestStatusCoalescesCreateSwitchAndResize(t *testing.T) {
14661466 "timed out awaiting resized output frame",
14671467 )
14681468
1469- for _, f := range []ports.Frame{first, resized} {
1470- out, err := ports.UnmarshalOutput(f.Payload)
1469+ for _, f := range []wire.Frame{first, resized} {
1470+ out, err := wire.UnmarshalOutput(f.Payload)
14711471 require.NoError(t, err)
14721472 require.Contains(t, string(out.Data), "work")
14731473 require.Contains(t, string(out.Data), ";7m", "active status tab should be inverse-highlighted")
M internal/usecase/daemon/test_wait_helpers_test.go +8 -8
@@ -4,13 +4,13 @@ import (
44 "testing"
55 "time"
66
7- "github.com/bnema/vev/internal/ports"
7+ "github.com/bnema/vev/internal/protocol/wire"
88 )
99
1010 const testWaitTimeout = 2 * time.Second
1111
12-func testFramesOfType(frames []ports.Frame, typ ports.MsgType) []ports.Frame {
13- matched := make([]ports.Frame, 0, len(frames))
12+func testFramesOfType(frames []wire.Frame, typ wire.MsgType) []wire.Frame {
13+ matched := make([]wire.Frame, 0, len(frames))
1414 for _, frame := range frames {
1515 if frame.Type == typ {
1616 matched = append(matched, frame)
@@ -40,21 +40,21 @@ func awaitTestCompletion(t *testing.T, done <-chan struct{}, failure string) {
4040
4141 func awaitCoordinatorOutput(
4242 t *testing.T,
43- sends <-chan ports.Frame,
43+ sends <-chan wire.Frame,
4444 timers <-chan *coordinatorMockTimer,
4545 frameContext string,
4646 timeoutFailure string,
47-) ports.Frame {
47+) wire.Frame {
4848 t.Helper()
4949 deadline := time.NewTimer(testWaitTimeout)
5050 defer deadline.Stop()
5151 for {
5252 select {
5353 case frame := <-sends:
54- if frame.Type == ports.MsgRoutePosition {
54+ if frame.Type == wire.MsgRoutePosition {
5555 continue
5656 }
57- if frame.Type != ports.MsgOutput {
57+ if frame.Type != wire.MsgOutput {
5858 t.Fatalf("unexpected frame type %d %s", frame.Type, frameContext)
5959 }
6060 return frame
@@ -62,7 +62,7 @@ func awaitCoordinatorOutput(
6262 timer.ch <- time.Time{}
6363 case <-deadline.C:
6464 t.Fatal(timeoutFailure)
65- return ports.Frame{}
65+ return wire.Frame{}
6666 }
6767 }
6868 }
M internal/usecase/daemon/transactional_resize_test.go +5 -4
@@ -12,6 +12,7 @@ import (
1212
1313 "github.com/bnema/vev/internal/domain"
1414 "github.com/bnema/vev/internal/ports"
15+ "github.com/bnema/vev/internal/protocol/wire"
1516 "github.com/bnema/vev/internal/usecase/layout"
1617 "github.com/bnema/vev/internal/usecase/ui"
1718 "github.com/bnema/vev/internal/usecase/visualsearch"
@@ -189,8 +190,8 @@ func TestProcessPTYDataRetainsCallbacksDuringResizeReplay(t *testing.T) {
189190 // normal asynchronous client path, proving replay uses processPTYData.
190191 var clipboardOutput string
191192 for {
192- frame := awaitFrame(t, sends, ports.MsgOutput)
193- out, err := ports.UnmarshalOutput(frame.Payload)
193+ frame := awaitFrame(t, sends, wire.MsgOutput)
194+ out, err := wire.UnmarshalOutput(frame.Payload)
194195 require.NoError(t, err)
195196 clipboardOutput = string(out.Data)
196197 if strings.Contains(clipboardOutput, "\x1b]52;c;YQ==\a") {
@@ -839,8 +840,8 @@ func TestTransactionalResizeObsoleteTimerCallbacksCommitOnlyLatestEpoch(t *testi
839840 // The resize deadline is the only debounce: commit fires its sticky reset
840841 // through S2 immediately (subject to ACK/sync gates).
841842 frame := <-frames
842- require.Equal(t, ports.MsgOutput, frame.Type, "the committed epoch emits a full frame")
843- output, err := ports.UnmarshalOutput(frame.Payload)
843+ require.Equal(t, wire.MsgOutput, frame.Type, "the committed epoch emits a full frame")
844+ output, err := wire.UnmarshalOutput(frame.Payload)
844845 require.NoError(t, err)
845846 require.Zero(t, output.Base, "the accepted resize frame must reset the renderer state")
846847 select {
M internal/usecase/daemon/transport_observability_test.go +9 -8
@@ -9,6 +9,7 @@ import (
99
1010 "github.com/bnema/vev/internal/domain"
1111 "github.com/bnema/vev/internal/ports"
12+ "github.com/bnema/vev/internal/protocol/wire"
1213 )
1314
1415 // This acceptance boundary exercises the attached resize, render, and ACK
@@ -25,11 +26,11 @@ func TestTransportObservabilityDaemonBoundaries(t *testing.T) {
2526 if !d.resizeForFirstPaint(sess, ac, domain.Size{Cols: 90, Rows: 25}) {
2627 t.Fatal("first resize was not accepted")
2728 }
28- first := awaitFrame(t, sends, ports.MsgOutput)
29- if first.Type != ports.MsgOutput {
29+ first := awaitFrame(t, sends, wire.MsgOutput)
30+ if first.Type != wire.MsgOutput {
3031 t.Fatalf("first frame = %v, want output", first.Type)
3132 }
32- output, err := ports.UnmarshalOutput(first.Payload)
33+ output, err := wire.UnmarshalOutput(first.Payload)
3334 if err != nil {
3435 t.Fatalf("decode first output: %v", err)
3536 }
@@ -38,8 +39,8 @@ func TestTransportObservabilityDaemonBoundaries(t *testing.T) {
3839 }
3940 ac.ackOutputState(output.Epoch, output.New)
4041 rc.notifyAck()
41- second := awaitFrame(t, sends, ports.MsgOutput)
42- if second.Type != ports.MsgOutput {
42+ second := awaitFrame(t, sends, wire.MsgOutput)
43+ if second.Type != wire.MsgOutput {
4344 t.Fatalf("second frame = %v, want output", second.Type)
4445 }
4546
@@ -118,8 +119,8 @@ func TestTransportObservabilityBlockedRenderObserverReleasesArchitectureLocks(t
118119 close(paintDone)
119120 }()
120121 awaitDaemonObserver(t, observer.entered, "blocked render observer")
121- frame := awaitFrame(t, sends, ports.MsgOutput) // The frame was emitted before the observer began its I/O.
122- output, err := ports.UnmarshalOutput(frame.Payload)
122+ frame := awaitFrame(t, sends, wire.MsgOutput) // The frame was emitted before the observer began its I/O.
123+ output, err := wire.UnmarshalOutput(frame.Payload)
123124 if err != nil {
124125 t.Fatalf("decode emitted output: %v", err)
125126 }
@@ -153,7 +154,7 @@ func TestTransportObservabilityBlockedRenderObserverReleasesArchitectureLocks(t
153154 d.paint(sess, ac, false, nil)
154155 close(secondPaint)
155156 }()
156- _ = awaitFrame(t, sends, ports.MsgOutput) // The second frame is live even though serialized observer I/O waits.
157+ _ = awaitFrame(t, sends, wire.MsgOutput) // The second frame is live even though serialized observer I/O waits.
157158
158159 close(observer.release)
159160 awaitDaemonObserver(t, paintDone, "first render completion")
M internal/usecase/daemon/wire.go +17 -17
@@ -3,11 +3,11 @@ package daemon
33
44 import (
55 "github.com/bnema/vev/internal/domain"
6- "github.com/bnema/vev/internal/ports"
76 "github.com/bnema/vev/internal/protocol"
7+ "github.com/bnema/vev/internal/protocol/wire"
88 )
99
10-func frameWelcome(s *session, resumeToken uint64) (ports.Frame, error) {
10+func frameWelcome(s *session, resumeToken uint64) (wire.Frame, error) {
1111 s.mu.Lock()
1212 var identity *protocol.CommittedRouteIdentity
1313 if s.incarnation != (domain.SessionLifecycleID{}) {
@@ -25,36 +25,36 @@ func frameWelcome(s *session, resumeToken uint64) (ports.Frame, error) {
2525 }
2626 s.mu.Unlock()
2727 w.ResumeToken = resumeToken
28- payload := ports.MarshalWelcome(w)
28+ payload := wire.MarshalWelcome(w)
2929 if payload == nil {
30- return ports.Frame{}, protocol.ErrInvalidRouteWire
30+ return wire.Frame{}, protocol.ErrInvalidRouteWire
3131 }
32- return ports.Frame{Type: ports.MsgWelcome, Payload: payload}, nil
32+ return wire.Frame{Type: wire.MsgWelcome, Payload: payload}, nil
3333 }
3434
35-func frameError(code uint16, text string) ports.Frame {
36- return ports.Frame{Type: ports.MsgError, Payload: ports.MarshalErrorMsg(protocol.ErrorMsg{Code: code, Text: text})}
35+func frameError(code uint16, text string) wire.Frame {
36+ return wire.Frame{Type: wire.MsgError, Payload: wire.MarshalErrorMsg(protocol.ErrorMsg{Code: code, Text: text})}
3737 }
3838
39-func frameOutputState(b []byte, baseState uint64, state uint64, echoAck uint64) (ports.Frame, error) {
40- payload, err := ports.MarshalOutput(protocol.Output{
39+func frameOutputState(b []byte, baseState uint64, state uint64, echoAck uint64) (wire.Frame, error) {
40+ payload, err := wire.MarshalOutput(protocol.Output{
4141 Epoch: 1, Base: baseState, New: state, Echo: echoAck,
4242 Size: domain.Size{Cols: 1, Rows: 1}, Full: state != 0 && baseState == 0, Data: b,
4343 })
4444 if err != nil {
45- return ports.Frame{}, err
45+ return wire.Frame{}, err
4646 }
47- return ports.Frame{Type: ports.MsgOutput, Payload: payload}, nil
47+ return wire.Frame{Type: wire.MsgOutput, Payload: payload}, nil
4848 }
4949
50-func frameDetached(reason uint8) ports.Frame {
51- return ports.Frame{Type: ports.MsgDetached, Payload: ports.MarshalDetached(protocol.Detached{Reason: reason})}
50+func frameDetached(reason uint8) wire.Frame {
51+ return wire.Frame{Type: wire.MsgDetached, Payload: wire.MarshalDetached(protocol.Detached{Reason: reason})}
5252 }
5353
54-func framePong() ports.Frame {
55- return ports.Frame{Type: ports.MsgPong, Payload: ports.MarshalPong(protocol.Pong{})}
54+func framePong() wire.Frame {
55+ return wire.Frame{Type: wire.MsgPong, Payload: wire.MarshalPong(protocol.Pong{})}
5656 }
5757
58-func frameSessions(infos []protocol.SessionInfo) ports.Frame {
59- return ports.Frame{Type: ports.MsgSessions, Payload: ports.MarshalSessions(protocol.Sessions{Sessions: infos})}
58+func frameSessions(infos []protocol.SessionInfo) wire.Frame {
59+ return wire.Frame{Type: wire.MsgSessions, Payload: wire.MarshalSessions(protocol.Sessions{Sessions: infos})}
6060 }
M internal/usecase/daemon/wire_metadata_test.go +2 -2
@@ -5,7 +5,7 @@ import (
55 "sync"
66 "testing"
77
8- "github.com/bnema/vev/internal/ports"
8+ "github.com/bnema/vev/internal/protocol/wire"
99 )
1010
1111 func TestFrameWelcomeSnapshotsSessionMetadataUnderLock(t *testing.T) {
@@ -27,7 +27,7 @@ func TestFrameWelcomeSnapshotsSessionMetadataUnderLock(t *testing.T) {
2727 if frameErr != nil {
2828 t.Fatalf("frameWelcome() error = %v", frameErr)
2929 }
30- welcome, err := ports.UnmarshalWelcome(frame.Payload)
30+ welcome, err := wire.UnmarshalWelcome(frame.Payload)
3131 if err != nil {
3232 t.Fatalf("UnmarshalWelcome() error = %v", err)
3333 }
M internal/usecase/daemon/wire_test_helpers_test.go +4 -4
@@ -1,12 +1,12 @@
11 package daemon
22
33 import (
4- "github.com/bnema/vev/internal/ports"
54 "github.com/bnema/vev/internal/protocol"
5+ "github.com/bnema/vev/internal/protocol/wire"
66 )
77
88 func mustMarshalOutput(m protocol.Output) []byte {
9- payload, err := ports.MarshalOutput(m)
9+ payload, err := wire.MarshalOutput(m)
1010 if err != nil {
1111 panic(err)
1212 }
@@ -14,7 +14,7 @@ func mustMarshalOutput(m protocol.Output) []byte {
1414 }
1515
1616 func mustMarshalAck(m protocol.Ack) []byte {
17- payload, err := ports.MarshalAck(m)
17+ payload, err := wire.MarshalAck(m)
1818 if err != nil {
1919 panic(err)
2020 }
@@ -22,7 +22,7 @@ func mustMarshalAck(m protocol.Ack) []byte {
2222 }
2323
2424 func mustMarshalResize(m protocol.Resize) []byte {
25- payload, err := ports.MarshalResize(m)
25+ payload, err := wire.MarshalResize(m)
2626 if err != nil {
2727 panic(err)
2828 }
M scripts/remote-picker-harness/main.go +32 -31
@@ -21,6 +21,7 @@ import (
2121 "github.com/bnema/vev/internal/domain"
2222 "github.com/bnema/vev/internal/ports"
2323 "github.com/bnema/vev/internal/protocol"
24+ "github.com/bnema/vev/internal/protocol/wire"
2425 )
2526
2627 const (
@@ -458,7 +459,7 @@ func localEnvironment() []string {
458459 }
459460
460461 type harnessFrame struct {
461- frame ports.Frame
462+ frame wire.Frame
462463 err error
463464 }
464465
@@ -642,44 +643,44 @@ func attachAndCheck(ctx context.Context, tr *harnessTransport, hello protocol.He
642643 if hello.EnvironmentPolicy == 0 {
643644 hello.EnvironmentPolicy = protocol.EnvironmentPolicyClientOwned
644645 }
645- payload := ports.MarshalHello(hello)
646- if err := tr.Send(ports.Frame{Type: ports.MsgHello, Payload: payload}); err != nil {
646+ payload := wire.MarshalHello(hello)
647+ if err := tr.Send(wire.Frame{Type: wire.MsgHello, Payload: payload}); err != nil {
647648 return err
648649 }
649650 frame, err := receiveWithTimeout(ctx, tr)
650651 if err != nil {
651652 return err
652653 }
653- if frame.Type == ports.MsgError {
654- message, decodeErr := ports.UnmarshalErrorMsg(frame.Payload)
654+ if frame.Type == wire.MsgError {
655+ message, decodeErr := wire.UnmarshalErrorMsg(frame.Payload)
655656 if decodeErr != nil {
656657 return decodeErr
657658 }
658659 return fmt.Errorf("remote error code %d", message.Code)
659660 }
660- if frame.Type != ports.MsgWelcome {
661+ if frame.Type != wire.MsgWelcome {
661662 return fmt.Errorf("welcome frame type %d", frame.Type)
662663 }
663664 tr.probe.recordControl(probeEventWelcome)
664- if err := tr.Send(ports.Frame{Type: ports.MsgTheme, Payload: ports.MarshalTheme(protocol.Theme{TrueColor: hello.TrueColor, SchemeKnown: true})}); err != nil {
665+ if err := tr.Send(wire.Frame{Type: wire.MsgTheme, Payload: wire.MarshalTheme(protocol.Theme{TrueColor: hello.TrueColor, SchemeKnown: true})}); err != nil {
665666 return fmt.Errorf("send initial theme: %w", err)
666667 }
667668 return nil
668669 }
669670
670671 func sendRawInput(tr *harnessTransport, data string) error {
671- return tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte(data)})})
672+ return tr.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte(data)})})
672673 }
673674
674-func processOutputFrame(tr *harnessTransport, frame ports.Frame) error {
675- output, err := ports.UnmarshalOutput(frame.Payload)
675+func processOutputFrame(tr *harnessTransport, frame wire.Frame) error {
676+ output, err := wire.UnmarshalOutput(frame.Payload)
676677 if err != nil {
677678 return err
678679 }
679680 result := tr.probe.apply(output)
680681 if !result.Accepted {
681682 if !tr.probe.resetPending {
682- if err := tr.Send(ports.Frame{Type: ports.MsgOutputResetRequest, Payload: ports.MarshalOutputResetRequest(protocol.OutputResetRequest{})}); err != nil {
683+ if err := tr.Send(wire.Frame{Type: wire.MsgOutputResetRequest, Payload: wire.MarshalOutputResetRequest(protocol.OutputResetRequest{})}); err != nil {
683684 return err
684685 }
685686 tr.probe.resetPending = true
@@ -690,11 +691,11 @@ func processOutputFrame(tr *harnessTransport, frame ports.Frame) error {
690691 if !result.StateBearing {
691692 return nil
692693 }
693- payload, err := ports.MarshalAck(result.Ack)
694+ payload, err := wire.MarshalAck(result.Ack)
694695 if err != nil {
695696 return err
696697 }
697- if err := tr.Send(ports.Frame{Type: ports.MsgAck, Payload: payload}); err != nil {
698+ if err := tr.Send(wire.Frame{Type: wire.MsgAck, Payload: payload}); err != nil {
698699 return err
699700 }
700701 tr.probe.resetPending = false
@@ -714,7 +715,7 @@ func awaitOutputContains(ctx context.Context, tr *harnessTransport, want string)
714715 if err != nil {
715716 return err
716717 }
717- if frame.Type != ports.MsgOutput {
718+ if frame.Type != wire.MsgOutput {
718719 continue
719720 }
720721 if err := processOutputFrame(tr, frame); err != nil {
@@ -738,7 +739,7 @@ func awaitRemotePickerPreview(ctx context.Context, tr *harnessTransport, marker
738739 if err != nil {
739740 return fmt.Errorf("no rendered remote preview after %d output frames (loading=%t): %w", tr.probe.outputFrames, sawLoading, err)
740741 }
741- if frame.Type != ports.MsgOutput {
742+ if frame.Type != wire.MsgOutput {
742743 continue
743744 }
744745 if err := processOutputFrame(tr, frame); err != nil {
@@ -768,7 +769,7 @@ func remotePreviewContains(preview protocol.RemotePreview, want string) bool {
768769 }
769770
770771 func sendInputAndAwait(ctx context.Context, tr *harnessTransport, command, want string) error {
771- if err := tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(protocol.Input{Data: []byte(command + "\n")})}); err != nil {
772+ if err := tr.Send(wire.Frame{Type: wire.MsgInput, Payload: wire.MarshalInput(protocol.Input{Data: []byte(command + "\n")})}); err != nil {
772773 return err
773774 }
774775 deadline, cancel := context.WithTimeout(ctx, 5*time.Second)
@@ -779,7 +780,7 @@ func sendInputAndAwait(ctx context.Context, tr *harnessTransport, command, want
779780 if err != nil {
780781 return fmt.Errorf("waiting for marker after %d output frames/%d bytes: %w", tr.probe.outputFrames, tr.probe.outputBytes, err)
781782 }
782- if frame.Type != ports.MsgOutput {
783+ if frame.Type != wire.MsgOutput {
783784 continue
784785 }
785786 if err := processOutputFrame(tr, frame); err != nil {
@@ -801,11 +802,11 @@ func sendCommand(ctx context.Context, tr *harnessTransport, slug string) error {
801802 Attached: true,
802803 Slug: slug,
803804 }
804- payload, err := ports.MarshalCommandRequest(request)
805+ payload, err := wire.MarshalCommandRequest(request)
805806 if err != nil {
806807 return err
807808 }
808- if err := tr.Send(ports.Frame{Type: ports.MsgCommand, Payload: payload}); err != nil {
809+ if err := tr.Send(wire.Frame{Type: wire.MsgCommand, Payload: payload}); err != nil {
809810 return err
810811 }
811812 deadline, cancel := context.WithTimeout(ctx, 5*time.Second)
@@ -815,16 +816,16 @@ func sendCommand(ctx context.Context, tr *harnessTransport, slug string) error {
815816 if err != nil {
816817 return err
817818 }
818- if frame.Type == ports.MsgOutput {
819+ if frame.Type == wire.MsgOutput {
819820 if err := processOutputFrame(tr, frame); err != nil {
820821 return err
821822 }
822823 continue
823824 }
824- if frame.Type != ports.MsgCommandResult {
825+ if frame.Type != wire.MsgCommandResult {
825826 continue
826827 }
827- result, err := ports.UnmarshalCommandResult(frame.Payload)
828+ result, err := wire.UnmarshalCommandResult(frame.Payload)
828829 if err != nil {
829830 return err
830831 }
@@ -836,7 +837,7 @@ func sendCommand(ctx context.Context, tr *harnessTransport, slug string) error {
836837 }
837838
838839 func detach(ctx context.Context, tr *harnessTransport) error {
839- if err := tr.Send(ports.Frame{Type: ports.MsgDetach, Payload: ports.MarshalDetach(protocol.Detach{})}); err != nil {
840+ if err := tr.Send(wire.Frame{Type: wire.MsgDetach, Payload: wire.MarshalDetach(protocol.Detach{})}); err != nil {
840841 return err
841842 }
842843 deadline, cancel := context.WithTimeout(ctx, 5*time.Second)
@@ -846,7 +847,7 @@ func detach(ctx context.Context, tr *harnessTransport) error {
846847 if err != nil {
847848 return err
848849 }
849- if frame.Type == ports.MsgDetached {
850+ if frame.Type == wire.MsgDetached {
850851 return nil
851852 }
852853 }
@@ -871,18 +872,18 @@ func expectNoSuchTarget(ctx context.Context, tr *harnessTransport, target domain
871872 RemoteTarget: &target,
872873 EnvironmentPolicy: protocol.EnvironmentPolicyDaemonOwned,
873874 }
874- payload := ports.MarshalHello(hello)
875- if err := tr.Send(ports.Frame{Type: ports.MsgHello, Payload: payload}); err != nil {
875+ payload := wire.MarshalHello(hello)
876+ if err := tr.Send(wire.Frame{Type: wire.MsgHello, Payload: payload}); err != nil {
876877 return err
877878 }
878879 frame, err := receiveWithTimeout(ctx, tr)
879880 if err != nil {
880881 return err
881882 }
882- if frame.Type != ports.MsgError {
883+ if frame.Type != wire.MsgError {
883884 return fmt.Errorf("stale target returned frame type %d", frame.Type)
884885 }
885- message, err := ports.UnmarshalErrorMsg(frame.Payload)
886+ message, err := wire.UnmarshalErrorMsg(frame.Payload)
886887 if err != nil {
887888 return err
888889 }
@@ -1002,7 +1003,7 @@ func assertNoDirectHandoff(tr *harnessTransport) error {
10021003 queued := len(tr.frames)
10031004 for range queued {
10041005 outcome := <-tr.frames
1005- if outcome.frame.Type == ports.MsgAttachTarget {
1006+ if outcome.frame.Type == wire.MsgAttachTarget {
10061007 tr.probe.recordIncoming(outcome.frame)
10071008 } else {
10081009 tr.queued = append(tr.queued, outcome)
@@ -1014,7 +1015,7 @@ func assertNoDirectHandoff(tr *harnessTransport) error {
10141015 return nil
10151016 }
10161017
1017-func receiveWithTimeout(ctx context.Context, tr *harnessTransport) (ports.Frame, error) {
1018+func receiveWithTimeout(ctx context.Context, tr *harnessTransport) (wire.Frame, error) {
10181019 if len(tr.queued) != 0 {
10191020 outcome := tr.queued[0]
10201021 tr.queued = tr.queued[1:]
@@ -1025,7 +1026,7 @@ func receiveWithTimeout(ctx context.Context, tr *harnessTransport) (ports.Frame,
10251026 }
10261027 select {
10271028 case <-ctx.Done():
1028- return ports.Frame{}, ctx.Err()
1029+ return wire.Frame{}, ctx.Err()
10291030 case outcome := <-tr.frames:
10301031 if outcome.err == nil {
10311032 tr.probe.recordIncoming(outcome.frame)
M scripts/remote-picker-harness/main_test.go +4 -4
@@ -5,7 +5,7 @@ import (
55 "strings"
66 "testing"
77
8- "github.com/bnema/vev/internal/ports"
8+ "github.com/bnema/vev/internal/protocol/wire"
99 )
1010
1111 func TestBoundedStderrRetainsAtMostDiagnosticLimit(t *testing.T) {
@@ -50,13 +50,13 @@ func TestAssertNoDirectHandoffProcessesInitialFramesAndRetainsOutput(t *testing.
5050 probe: &visualProbe{},
5151 frames: make(chan harnessFrame, 2),
5252 }
53- tr.frames <- harnessFrame{frame: ports.Frame{Type: ports.MsgOutput}}
54- tr.frames <- harnessFrame{frame: ports.Frame{Type: ports.MsgAttachTarget}}
53+ tr.frames <- harnessFrame{frame: wire.Frame{Type: wire.MsgOutput}}
54+ tr.frames <- harnessFrame{frame: wire.Frame{Type: wire.MsgAttachTarget}}
5555
5656 if err := assertNoDirectHandoff(tr); err == nil {
5757 t.Fatal("direct handoff was not rejected")
5858 }
59- if len(tr.queued) != 1 || tr.queued[0].frame.Type != ports.MsgOutput {
59+ if len(tr.queued) != 1 || tr.queued[0].frame.Type != wire.MsgOutput {
6060 t.Fatalf("queued frames = %#v, want one retained output", tr.queued)
6161 }
6262 if tr.probe.unexpectedHandoffs != 1 {
M scripts/remote-picker-harness/visual_probe.go +3 -3
@@ -11,8 +11,8 @@ import (
1111
1212 vt "github.com/bnema/vev-vt"
1313 "github.com/bnema/vev/internal/domain"
14- "github.com/bnema/vev/internal/ports"
1514 "github.com/bnema/vev/internal/protocol"
15+ "github.com/bnema/vev/internal/protocol/wire"
1616 )
1717
1818 const (
@@ -237,8 +237,8 @@ func (p *visualProbe) contains(want string) bool {
237237 return strings.Contains(p.text(), want)
238238 }
239239
240-func (p *visualProbe) recordIncoming(frame ports.Frame) {
241- if p == nil || frame.Type != ports.MsgAttachTarget {
240+func (p *visualProbe) recordIncoming(frame wire.Frame) {
241+ if p == nil || frame.Type != wire.MsgAttachTarget {
242242 return
243243 }
244244 p.unexpectedHandoffs++
M scripts/remote-picker-harness/visual_probe_test.go +11 -11
@@ -8,8 +8,8 @@ import (
88 "testing"
99
1010 "github.com/bnema/vev/internal/domain"
11- "github.com/bnema/vev/internal/ports"
1211 "github.com/bnema/vev/internal/protocol"
12+ "github.com/bnema/vev/internal/protocol/wire"
1313 "github.com/stretchr/testify/require"
1414 )
1515
@@ -19,24 +19,24 @@ const (
1919 )
2020
2121 type probeTestTransport struct {
22- sent []ports.Frame
22+ sent []wire.Frame
2323 }
2424
25-func (t *probeTestTransport) Send(frame ports.Frame) error {
25+func (t *probeTestTransport) Send(frame wire.Frame) error {
2626 t.sent = append(t.sent, frame)
2727 return nil
2828 }
2929
30-func (*probeTestTransport) Recv() (ports.Frame, error) { return ports.Frame{}, errors.New("not used") }
31-func (*probeTestTransport) Close() error { return nil }
30+func (*probeTestTransport) Recv() (wire.Frame, error) { return wire.Frame{}, errors.New("not used") }
31+func (*probeTestTransport) Close() error { return nil }
3232
33-func probeTestOutput(t *testing.T, output protocol.Output) ports.Frame {
33+func probeTestOutput(t *testing.T, output protocol.Output) wire.Frame {
3434 t.Helper()
35- payload, err := ports.MarshalOutput(output)
35+ payload, err := wire.MarshalOutput(output)
3636 if err != nil {
3737 t.Fatalf("marshal output: %v", err)
3838 }
39- return ports.Frame{Type: ports.MsgOutput, Payload: payload}
39+ return wire.Frame{Type: wire.MsgOutput, Payload: payload}
4040 }
4141
4242 func TestVisualProbePersistsFullAndIncrementalOutput(t *testing.T) {
@@ -240,9 +240,9 @@ func TestProcessOutputFrameACKsOnlyAcceptedStateBearingFrames(t *testing.T) {
240240 var ackCount, resetCount int
241241 for _, frame := range transport.sent {
242242 switch frame.Type {
243- case ports.MsgAck:
243+ case wire.MsgAck:
244244 ackCount++
245- case ports.MsgOutputResetRequest:
245+ case wire.MsgOutputResetRequest:
246246 resetCount++
247247 }
248248 }
@@ -268,7 +268,7 @@ func TestCaptureUnifiedPickerRowsRequiresLocalAndRemoteEntries(t *testing.T) {
268268
269269 func TestVisualProbeRecordsUnexpectedAttachTarget(t *testing.T) {
270270 probe := newVisualProbe(domain.Size{Cols: probeTestCols, Rows: probeTestRows})
271- probe.recordIncoming(ports.Frame{Type: ports.MsgAttachTarget})
271+ probe.recordIncoming(wire.Frame{Type: wire.MsgAttachTarget})
272272
273273 if probe.unexpectedHandoffs != 1 {
274274 t.Fatalf("unexpected handoffs = %d, want 1", probe.unexpectedHandoffs)
M transport_replay_integration_test.go +6 -5
@@ -12,6 +12,7 @@ import (
1212 "github.com/bnema/vev/internal/adapters/sshstdio"
1313 "github.com/bnema/vev/internal/ports"
1414 "github.com/bnema/vev/internal/protocol"
15+ "github.com/bnema/vev/internal/protocol/wire"
1516 "github.com/bnema/vev/internal/testutil/replaytest"
1617 "github.com/stretchr/testify/require"
1718 )
@@ -39,7 +40,7 @@ func TestTransportReplayIntegration(t *testing.T) {
3940 got, err := receiver.Recv()
4041 require.NoError(t, err)
4142 require.Equal(t, want, got)
42- output, err := ports.UnmarshalOutput(got.Payload)
43+ output, err := wire.UnmarshalOutput(got.Payload)
4344 require.NoError(t, err)
4445 bytes = append(bytes, output.Data...)
4546 terminal.Write(output.Data)
@@ -102,7 +103,7 @@ func TestThemeGenerationTransportSequences(t *testing.T) {
102103 done := make(chan error, 1)
103104 go func() {
104105 for _, snapshot := range sequence {
105- if err := sender.Send(ports.Frame{Type: ports.MsgTheme, Payload: ports.MarshalTheme(snapshot)}); err != nil {
106+ if err := sender.Send(wire.Frame{Type: wire.MsgTheme, Payload: wire.MarshalTheme(snapshot)}); err != nil {
106107 done <- err
107108 return
108109 }
@@ -113,9 +114,9 @@ func TestThemeGenerationTransportSequences(t *testing.T) {
113114 for _, want := range sequence {
114115 frame, err := receiver.Recv()
115116 require.NoError(t, err)
116- require.Equal(t, ports.MsgTheme, frame.Type)
117- require.Equal(t, ports.MarshalTheme(want), frame.Payload, "theme payload must be preserved byte-for-byte before decode")
118- got, err := ports.UnmarshalTheme(frame.Payload)
117+ require.Equal(t, wire.MsgTheme, frame.Type)
118+ require.Equal(t, wire.MarshalTheme(want), frame.Payload, "theme payload must be preserved byte-for-byte before decode")
119+ got, err := wire.UnmarshalTheme(frame.Payload)
119120 require.NoError(t, err)
120121 require.Equal(t, want, got)
121122 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close