diff --git a/docs/reference/terminal-session-correctness-handoff-2026-08-07/finalized-design.html b/docs/reference/terminal-session-correctness-handoff-2026-08-07/finalized-design.html new file mode 100644 index 00000000000..afe8bcbbc86 --- /dev/null +++ b/docs/reference/terminal-session-correctness-handoff-2026-08-07/finalized-design.html @@ -0,0 +1,275 @@ + + + + + + Terminal Architecture — Finalized Design + + + +
+
+

Terminal Architecture — Finalized Design

+

Synthesis of the “one protocol, three routes” proposal with a control-plane track it does not cover

+
+ +
+
Adopt
Data-plane proposal
+
+1 track
Control plane (missing)
+
REFUTED
“Delete the lease”
+
~374
Lines of reconciliation
+
~390
Remoteness branches
+
+ +
+

Verdict in one paragraph

+

The proposal is correct and its root cause is real — I verified it. Adopt it. But it is a data-plane design: framing, encoding, credit windows, backpressure. The bug that started this work is a control-plane bug, and none of its thirteen steps touch the records involved. The two tracks are independent and can run in parallel. My shipped work is a stopgap on the second track, framed around a symptom rather than a cause, and most of it should be deleted once the real fix lands.

+
+ +

1 · Two planes, one system

+

Nearly every disagreement here dissolves once you separate these.

+
+
What each plane owns⤢ zoom
+

proposal covers this

proposal is silent

CONTROL PLANE — who owns what

which pane owns
which shell

what survives
a restart

what may be
destroyed

DATA PLANE — moving bytes

framing · encoding

credit windows
backpressure

reconnect & resume

13 steps

0 steps
2 → 19 → 20 survives all 13

proposal covers this

proposal is silent

CONTROL PLANE — who owns what

which pane owns
which shell

what survives
a restart

what may be
destroyed

DATA PLANE — moving bytes

framing · encoding

credit windows
backpressure

reconnect & resume

13 steps

0 steps
2 → 19 → 20 survives all 13

+
+

Verified: none of the ladder’s core files reference the lease or binding records at all. You could ship all thirteen steps and still get 19 ghost panes on the third reconnect.

+ +

2 · The data-plane root cause — verified

+

This is the strongest finding in either document, and it is correct.

+

Orca copied a well-known persistent protocol’s 13-byte frame header, and kept two of its nine message types:

+
export const MessageType = { Regular: 1, KeepAlive: 9 } as const
+// dropped: Control · Ack · Disconnect · ReplayRequest · Pause · Resume · None
+

The seven dropped are exactly the ones that make the protocol persistent. The ACK field that survived feeds only unackedTimestamps — a seq→timestamp map read solely by the death timer. Nothing is retained; nothing is replayed.

+
+
Why three ledgers exist⤢ zoom
+

transport gives
no delivery guarantee

SSH stack rebuilds
retain + replay

runtime RPC rebuilds
retain + replay

daemon rebuilds it
lossily on purpose

three ledgers · three sets of bugs
4,847 lines in one of them

transport gives
no delivery guarantee

SSH stack rebuilds
retain + replay

runtime RPC rebuilds
retain + replay

daemon rebuilds it
lossily on purpose

three ledgers · three sets of bugs
4,847 lines in one of them

+
+
+

This reframes the duplication as three teams correctly solving a missing primitive, not carelessness. Fix the transport and the duplication loses its reason to exist. That is a genuine root cause — the framing I shipped earlier (“identity compared with the wrong key”) named a symptom class instead.

+
+ +

3 · Your principle, confirmed twice over

+

“SSH should behave like local, plus its own reconnection logic.” The codebase already proves it — for the other routes.

+
function getProvider(connectionId) {
+  if (!connectionId) return localProvider          // local AND daemon — one slot
+  const provider = sshProviders.get(connectionId)  // SSH — a parallel registry
+}
+
+
Three strategies, one exception⤢ zoom
+

the exception

SSH
neither

rides the local path
wearing a connectionId tag

~390 remoteness branches
+ 25 instanceof checks

works today

daemon
substitutes

swap the provider slot
0 branches added

paired runtime
encapsulates

hidden behind a transport
mostly negative guards

the exception

SSH
neither

rides the local path
wearing a connectionId tag

~390 remoteness branches
+ 25 instanceof checks

works today

daemon
substitutes

swap the provider slot
0 branches added

paired runtime
encapsulates

hidden behind a transport
mostly negative guards

+
+

The daemon is a 3,604-line provider that adds zero branches to the shared spine. SSH gets a second registry, and the tag then leaks outward as a condition ~390 call sites re-derive independently. Your instinct was not a preference — SSH is the only route that broke a pattern the codebase already implements correctly twice.

+ +

4 · What I got wrong

+ + + + + + + + +
ClaimStatusEvidence
“Identity compared with the wrong key” is the root causeSymptomThe real cause is one layer down — a transport with no delivery guarantee, and a tag instead of a substitution
My change consolidatesInverted~310 of ~405 added lines are SSH-only special case; every new function in the largest file is lease machinery
The superseded-keystroke fence protects reconnectInert thereIts maps have two writers, both spawn paths; relay reattach never populates them, so the guard permits every reattached PTY
“Less code proves a better design”Too bluntThe right control-plane fix is roughly net-neutral in lines; the payoff is a dissolved failure class, not a smaller diff
+
+

The fence failure is the thesis in miniature

+

A guard placed on the shared spine silently does not apply to the one route that does not ride the spine. That is not a coding slip — it is the predicted failure mode of tag-based divergence, and it happened to me while I was writing the fix for it.

+
+ +

5 · The control-plane defect

+

Two questions, one record, keyed for neither.

+
+
The key inversion⤢ zoom
+

today: one record

two different questions

which shell does
this pane own?

which shells exist
that nobody owns?

lease
keyed by (target, ptyId)
pane identity is optional payload

pane uniqueness is not structural
→ bolt on supersession, ranking,
rollback, dual matchers

~374 lines of reconciliation
9 states where records disagree

today: one record

two different questions

which shell does
this pane own?

which shells exist
that nobody owns?

lease
keyed by (target, ptyId)
pane identity is optional payload

pane uniqueness is not structural
→ bolt on supersession, ranking,
rollback, dual matchers

~374 lines of reconciliation
9 states where records disagree

+
+

Because the lease is keyed by shell rather than pane, “one pane, one live shell” is not expressible — it has to be enforced. My +209 lines are that enforcement: supersession, rank comparison, deferral, rollback. They exist only to compensate for the key.

+ +

Why the obvious fixes are both wrong

+ + + + + + +
ProposalVerdictWhy
Merge the lease into the pane bindingBlockedA lease must be able to name a live shell no pane owns, and must outlive the binding — expiry deletes the binding, then authorizes recovery for 30s
Delete the lease; let the host be authoritativeRefutedSee below — tested adversarially and it failed on all five probes
+
+
Why the host cannot be the authority⤢ zoom
+

no

no

no

make the remote host
the source of truth

does it hold
the state durably?

PTY table is an in-memory Map
no persistence layer at all

can the client
read pane identity back?

listProcesses and attach never return it
attach only compares, then throws
the one RPC that returns it has 0 callers

is the host reachable
when it matters?

the decisive one:
a synchronous disk read during quit
cannot become an RPC to a host that is down

no

no

no

make the remote host
the source of truth

does it hold
the state durably?

PTY table is an in-memory Map
no persistence layer at all

can the client
read pane identity back?

listProcesses and attach never return it
attach only compares, then throws
the one RPC that returns it has 0 callers

is the host reachable
when it matters?

the decisive one:
a synchronous disk read during quit
cannot become an RPC to a host that is down

+
+

I verified all three personally. The client-side record is load-bearing precisely because it works offline.

+ +

6 · The proposed control-plane fix

+

Don’t merge the records and don’t delete one. Give each question the key that answers it.

+
+
Split by question, not by transport⤢ zoom
+

after

OWNERSHIP
keyed by (host, pane)
same key local already uses

INVENTORY
keyed by (host, ptyId)
shells with no owner

which shell does
this pane own?

which shells exist
that nobody owns?

one pane, one shell
structural, not enforced

reattach work list ·
disconnected cleanup UI

supersession, ranking, rollback,
dual matchers all become unnecessary

after

OWNERSHIP
keyed by (host, pane)
same key local already uses

INVENTORY
keyed by (host, ptyId)
shells with no owner

which shell does
this pane own?

which shells exist
that nobody owns?

one pane, one shell
structural, not enforced

reattach work list ·
disconnected cleanup UI

supersession, ranking, rollback,
dual matchers all become unnecessary

+
+

Ownership becomes the same shape for local, daemon and SSH — one record per pane per host, with transport-specific payload (lease state and timestamps for SSH, nothing extra for local). That is your principle applied at the record layer. Inventory becomes its own small list, because “a live shell nobody owns” is genuinely a different fact, not a degenerate ownership row.

+

The 30-second recovery grant survives as a terminal state on the pane-keyed record, so nothing load-bearing is lost. And the offline consumers keep reading local disk synchronously, exactly as today.

+ +

7 · Where the proposal needs correcting

+ + + + + + + + +
Claim in the proposalAssessment
“Main is no longer in the data path at all” for a phone on an SSH hostOverstated — the pivot step forwards the host’s loopback socket to a port on your machine. Pairing, E2EE and the broker remain main’s. The ledger duplication collapses; the hop does not.
Target is “one wire contract”Incomplete — no step migrates the local daemon onto it. After the final step you have two protocols, not one. There is an unstated additional step, and it is large.
“The method layer is already transport-agnostic”True, and it volunteers its own counterexample. But 191 remoteness branches sit in the renderer above that boundary — including five separate re-declarations of one predicate — and no step addresses them.
Measured encoding cost (wire and CPU)Honestly reported — it states plainly that the absolute throughput is not a bottleneck, and argues from where the cost is paid instead. Not oversold.
+ +

8 · Combined roadmap

+
+
Two independent tracks⤢ zoom
+

independent — run in parallel

CONTROL PLANE — added here

pin behaviour with
oracles that bite

re-key ownership
by pane

split out
orphan inventory

delete the
compensating code

DATA PLANE — adopt as written

see it · capability hello

unify types · ledger ·
chunk-ack · lanes

binary payloads

boring first connect

SSH becomes
a port-forward

independent — run in parallel

CONTROL PLANE — added here

pin behaviour with
oracles that bite

re-key ownership
by pane

split out
orphan inventory

delete the
compensating code

DATA PLANE — adopt as written

see it · capability hello

unify types · ledger ·
chunk-ack · lanes

binary payloads

boring first connect

SSH becomes
a port-forward

+
+

The tracks touch disjoint files, so they can proceed simultaneously. The control-plane track should land its oracles first, because re-keying a durable record is a migration with a corruption story, and the existing tests pin the current key rather than the intended behaviour.

+ +

9 · What happens to the shipped work

+ + + + + + + + + + +
ChangeNowAfter the control-plane track
Reattach binds, never createsHolds the lineKeep — correct at any key
Pane-keyed lease supersession (+209)Holds the lineDelete — the key makes it structural
Corrupted-lease healingNeeded for existing installsFolds into the migration
“Needs re-establishing” ≠ “expired”Fixes duplicate agent resumeKeep — an error-taxonomy fix, orthogonal
Three-valued livenessCorrect generalizationKeep
Superseded-keystroke fenceInert on reattachFix or withdraw — decision needed
+ +

10 · Verification status

+

Stated explicitly, because two earlier claims in this program were retracted after checking.

+ + + + + + + + + + + + +
FindingStatus
Only 2 of 9 message types kept; ACK feeds the death timer onlyPersonally verified
Daemon substitutes via the provider slot; SSH uses a parallel registryPersonally verified
Pane maps have two writers, both spawn; fence inert on reattachPersonally verified
Relay PTY table is in-memory only; identity RPC has no production callerPersonally verified
Lease consulted from a synchronous, offline disk pathPersonally verified
Method layer transport-agnostic (0 / 0 / 27 references)Personally verified
~390 branch count, LOC ratios, the 9 disagreement states, ~374 reconciliation linesAgent-reported, spot-checked
Encoding throughput measurements in the proposalNot re-measured
+ +

11 · Decisions needed

+ + + + + + + + +
#Decision
1Adopt the data-plane proposal as written? My review found no defect in its reasoning — only two overstated claims and one missing step. It is independent of everything below.
2Approve the control-plane track? Re-keying a durable record needs a migration and a corruption story. It is roughly net-neutral in lines and deletes a failure class rather than a file.
3The inert fence — populate the pane maps on the reattach path, or withdraw the guard and its test? Shipping it as-is means a test asserting a protection that does not apply where it matters.
4The open PRs — merge as a stopgap that keeps the reported bug closed, or hold them and go straight at the control-plane track?
+ +
+ Companion to report.html (evidence for the shipped work). Click any diagram to zoom · scroll to scale · drag to pan. +
+
+ +
×
scroll to zoom · drag to pan · Esc to close
+ + + +