053ae6ef
docs: the run header is a delta, after the size gate refused the absolute one
a73x 2026-09-04 17:00
Commit message
docs/superpowers/plans/2026-09-04-cells-on-the-wire.md
| Old | New | ||
|---|---|---|---|
| @@ -20,7 +20,8 @@ | |||
| 20 | - Any hand-run rig exports an isolated `XDG_STATE_HOME` and `XDG_RUNTIME_DIR` first. | 20 | - Any hand-run rig exports an isolated `XDG_STATE_HOME` and `XDG_RUNTIME_DIR` first. |
| 21 | - Never quote a speed number from the dev tree; byte counts are fine. | 21 | - Never quote a speed number from the dev tree; byte counts are fine. |
| 22 | - Frame numbers: `snapshot = 0x95`, `delta = 0x96`, `scrollback_chunk = 0x97`; `0x81`, `0x85`, `0x87` retired and never reused. | 22 | - Frame numbers: `snapshot = 0x95`, `delta = 0x96`, `scrollback_chunk = 0x97`; `0x81`, `0x85`, `0x87` retired and never reused. |
| 23 | - Colour packing: `0` none, `(1 << 24) | index` palette, `(2 << 24) | r << 16 | g << 8 | b` RGB. Run flags bits 0–10 = ghostty `Style.Flags` bit order; bit 15 = `ascii`. Cell head = `wide << 6 | text_len`, `text_len ≤ 63`. | 23 | - Colour packing in memory: `0` none, `(1 << 24) | index` palette, `(2 << 24) | r << 16 | g << 8 | b` RGB. Style flags bits 0–10 = ghostty `Style.Flags` bit order. Cell head = `wide << 6 | text_len`, `text_len ≤ 63`. |
| 24 | - Run header (revised 2026-09-04 after the Task 2 gate refused the 16-byte absolute form): `u16 LE count` ++ `u8 mask` ++ present fields — mask bit0 `flags` (u16 LE follows), bit1 `fg`, bit2 `bg`, bit3 `ul`, bit7 `ascii`; a present colour is `u8 code` (0 none, 1 palette + 1 index byte, 2 RGB + 3 bytes). Absent fields are UNCHANGED from the previous run of the same row; the first run of a row starts from the default style. Task 1's golden bytes were rewritten to this form in Task 2's fix round. | ||
| 24 | 25 | ||
| 25 | --- | 26 | --- |
| 26 | 27 | ||
docs/superpowers/specs/2026-09-04-cells-on-the-wire-design.md
| Old | New | ||
|---|---|---|---|
| @@ -70,16 +70,27 @@ Cells past `ncells` up to the grid width are default-style blanks, so the | |||
| 70 | encoder stops at the last cell that is not one (a cell whose content is a | 70 | encoder stops at the last cell that is not one (a cell whose content is a |
| 71 | bare background colour is NOT blank). | 71 | bare background colour is NOT blank). |
| 72 | 72 | ||
| 73 | A **run** is `RunHeader` (16 B: `u16 LE count`, `u16 LE flags`, `u32 LE fg`, | 73 | A **run** is a header that carries only what CHANGED since the previous run |
| 74 | `u32 LE bg`, `u32 LE ul`) followed by `count` cells: | 74 | of the same row — the shape SGR has, which is why VT rows are small — then |
| 75 | 75 | `count` cells. The first run of a row starts from the default style; a row | |
| 76 | is self-contained, so nothing carries between rows or frames. | ||
| 77 | |||
| 78 | - `u16 LE count` ++ `u8 mask` ++ the present fields in order. `mask` bit 0: | ||
| 79 | `flags` present, `u16 LE` follows; bit 1: `fg` present; bit 2: `bg` | ||
| 80 | present; bit 3: `ul` present; bit 7: `ascii`. A present colour is `u8 | ||
| 81 | code` — 0 none, 1 palette then one index byte, 2 RGB then three bytes. | ||
| 82 | A default first run is 3 bytes; a palette foreground change is 5. | ||
| 83 | (The first cut was a 16-byte absolute header, and the measurement gate | ||
| 84 | refused it: vim 1.45×, htop 1.56× — see decisions.md, 2026-09-04.) | ||
| 76 | - `flags` bits 0–10 are ghostty's `Style.Flags` verbatim — bold 0, italic 1, | 85 | - `flags` bits 0–10 are ghostty's `Style.Flags` verbatim — bold 0, italic 1, |
| 77 | faint 2, blink 3, inverse 4, invisible 5, strikethrough 6, overline 7, | 86 | faint 2, blink 3, inverse 4, invisible 5, strikethrough 6, overline 7, |
| 78 | underline style 8–10 (none 0, single 1, double 2, curly 3, dotted 4, | 87 | underline style 8–10 (none 0, single 1, double 2, curly 3, dotted 4, |
| 79 | dashed 5). Bit 15 is `ascii`: every cell of the run is ONE byte in | 88 | dashed 5). `ascii` means every cell of the run is ONE byte in |
| 80 | `0x20..0x7E`, narrow, with no per-cell header. | 89 | `0x20..0x7E`, narrow, with no per-cell header. |
| 81 | - Colours are packed as `wasm_core.packColor` does today: `0` none, | 90 | - In memory (`CellStyle`, the grid, the wasm viewport) colours stay packed |
| 82 | `1<<24 | index` palette, `2<<24 | r<<16 | g<<8 | b` RGB. | 91 | as `wasm_core.packColor` does today: `0` none, `1<<24 | index` palette, |
| 92 | `2<<24 | r<<16 | g<<8 | b` RGB; the wire spells the same three cases as | ||
| 93 | the code byte above. | ||
| 83 | - A non-`ascii` cell is `u8 head` = `wide << 6 | text_len` then `text_len` | 94 | - A non-`ascii` cell is `u8 head` = `wide << 6 | text_len` then `text_len` |
| 84 | bytes of UTF-8 — the whole grapheme cluster, capped at 63 bytes (the | 95 | bytes of UTF-8 — the whole grapheme cluster, capped at 63 bytes (the |
| 85 | encoder keeps the leading codepoints that fit). `wide` is 0 narrow, 1 | 96 | encoder keeps the leading codepoints that fit). `wide` is 0 narrow, 1 |