| … | … | @@ -2718,6 +2718,22 @@ SessionState's truncate / reorg / named-session machinery. |
| 2718 | 2718 | script drives a JS Duplex subclass and keetd's: write / end / push / destroy / pause / |
| 2719 | 2719 | resume / listeners, plus when `_open` / `_write` / `_read` / `_final` / `_destroy` |
| 2720 | 2720 | call back. The ordered events and callbacks must be identical. |
| 2721 | - **4e-3b DONE 2026-10-04** (keetd 1313020 + 2 gate commits). |
| 2722 | - **Gate `tools/ss/duplex-gate.sh`:** 22 op scripts, JS streamx vs `ss::Duplex`, |
| 2723 | identical traces; stable over 4 runs. |
| 2724 | - **Drill `m5g4e3b`: 20/20 red.** The first run's 3 survivors were script gaps and |
| 2725 | became cases: |
| 2726 | - a push to exactly the highWaterMark; |
| 2727 | - a `once('data')`, with listenerCount; |
| 2728 | - `drained` resolving in a microtask. |
| 2729 | - **Measured (Bare 1.28):** an exception thrown in a microtask is HELD. The checkpoint |
| 2730 | continues, then the immediates, and `uncaughtException` comes at the end of the loop |
| 2731 | iteration. `ss::Loop` holds it the same way (`reportUncaught` / `deliverUncaught`), |
| 2732 | which is how bare-events throws an unheard 'error'. |
| 2733 | - **TRAP:** a JS harness that flushes with a 0 ms timer RACES that delivery (whether |
| 2734 | the timer is due depends on the clock). Flush with `setImmediate`. |
| 2735 | - **Divergence:** microtasks and callbacks hold a stream WEAKLY (an alive token). JS |
| 2736 | would keep an unreferenced stream alive until they ran. |
| 2721 | 2737 | - **4e-3c `ss/secret-stream`:** |
| 2722 | 2738 | - `Handshake`; |
| 2723 | 2739 | - `NoiseSecretStream` over a Duplex, with a raw-stream interface (write → drained, |