Files
Jinwoo-H d4890a78bc fix(terminal): stop gating OutputSpan on the desktop multiplex path
The OutputSpan gate is right for `terminal.subscribe` and wrong for
`terminal.multiplex`. Keying spans off a declared `outputSpan` capability treats
"peer did not declare it" as "peer cannot decode opcode 15", which is false on
multiplex: opcode 15 shipped in v1.4.147 (c0f0810dd9, 2026-07-19) and every
capability a peer could declare to prove it is younger — `ackOutput` by 9 days,
`ackOutputSourceRanges` by 10. So the gate read the entire installed base as
incapable and downgraded it.

The downgrade cannot be made safe there. A plain Output frame carries only
`(data, seq)`, so a seq-tracking peer reconstructs a run as `seq - data.length`,
which a transformed run (`rawLength !== data.length`) contradicts by definition.
Three failures reproduced through the real dispatcher against the shipped desktop
capability set:

- a zero-byte absorbed-query frame fails `TerminalSourceRangeLedger.canAccept`
  (`encodedBytes > 0`), parks in `ackPendingOutput`, and head-blocks the flush
  loop on the same head forever — all later output is lost;
- every transformed emission reads as a dropped-frame gap in `detectOutputGap`
  and triggers `requestResyncSnapshot`;
- a >48 KiB transformed run takes the `shouldDelayFinalSeq` arm, which omits
  `sourceRanges`, flipping the ledger's mapping mode and detaching the stream.

Multiplex now always frames transformed runs as OutputSpan; the vestigial
`outputSpan` capability is removed from the multiplex schema, stream state, and
the renderer's subscribe frame. Subscribe keeps the gate: mobile ignores Output
`seq` outright, which is what makes the downgrade lossless there — that property,
not the capability flag, is the licence, so the chunker option is now
`transformedRuns: 'span' | 'downgrade-to-text'` and the unsound call site cannot
be written by accident.

Coverage for the fallback arm was absent — every fixture was a 14-character
string, so the multi-chunk downgrade never ran and deleting a yield (48 KiB of
silently dropped output) passed everything. Adds over-cap, zero-byte and
per-frame-seq contracts plus a 600-case fuzz, transformed and multi-chunk
transformed steps to the cross-version journey (which pins current code against
the real v1.4.173 release client), and repairs the JS benchmark that the required
options argument broke.

Refs STA-3482
2026-08-05 19:26:12 -04:00
..