# VT Conformance Matrix This document is the **single source of truth** for what Ntilde supports (and intentionally does not) in terms of VT/xterm/DEC behavior. It is designed to be: - **Actionable**: each row maps to code areas + tests. - **Auditable**: β€œSupported” means there is verification evidence (replay, unit test, external suite). - **Maintainable**: when behavior changes, update **one row** and its linked tests. --- ## Status legend | Status | Meaning | Verification requirement | |---|---|---| | βœ… Supported | Implemented and correct for the documented scope | At least 1 automated test (replay/unit/external) | | ⚠ Partial | Implemented but with limitations, known deviations or an explicit qualification gap | Automated test + deviation note | | πŸ§ͺ Experimental | Works but not yet stable/guaranteed | Optional tests; can change | | ❌ Not supported | Not implemented | N/A | | 🚫 Won’t support | Intentionally not supported | Rationale required | **Verification types** - **Replay**: `*.rec β†’ *.snap` (golden) - **Unit**: targeted parser/buffer and App input tests (including headless UI events) - **Unit (render)**: pixel assertions through the production draw path - **External**: external suites (e.g., VTTEST) captured into replays - **External (native)**: isolated clipboard fixtures using production services and independent OS peers; see `docs/qa/clipboard-native.md` and `docs/qa/clipboard-primary.md` A recognized query or negotiated flag alone does not qualify its behavior. Buffer, input, render and native evidence establish different scopes; none substitutes for the others. Partial rows name their remaining implementation or qualification limit. Report evidence kinds retain the existing unit/replay/external/fuzz/manual categories; App, render and native qualifiers describe scope within those categories. --- ## Terminal model assumptions - Default size: **80Γ—24** unless specified - Default TERM exposed by PTY layer: **xterm-256color** - PTYs inherit the caller locale. If LC_ALL, LC_CTYPE and LANG are all unset/empty, the Unix native host supplies LC_CTYPE=UTF-8 on macOS or LANG=C.UTF-8 on Linux; Windows adds no locale fallback (`src/Ntilde.App/native/src/lib.rs`); deterministic capture fixtures must set and record their own environment. - Parser/buffer tests assert terminal state. Render tests separately qualify pixels, App tests qualify input dispatch, and native fixtures qualify OS clipboard exchange. A buffer snapshot alone does not establish those behaviors. --- ## How to use this matrix 1. When you implement a feature, add/update a row with: - **Status** - **Evidence** (test path) - **Code ownership** (file(s)/module) 2. When a user reports a bug: - Add a row or update status to ⚠ Partial - Add a replay test reproducing it 3. Before releases: - Ensure β€œβœ… Supported” rows have at least one automated verification link. --- ## 1) Input parsing & state machine | Feature / Sequence | Spec / Notes | Status | Evidence | Ownership (code) | Known deviations | |---|---|---:|---|---|---| | C0 controls (BEL, BS, HT, LF, CR) | Basic control chars | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/TabSystemTests.cs` | `src/Ntilde.VT/AnsiParser.cs`, `src/Ntilde.VT/TerminalBuffer.cs` | HT now follows stored tab stops instead of inserting spaces; broader C0 coverage is still only partially audited | | C1 via 7-bit ESC (ESC @.._) | β€œ7-bit C1” translation | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/AnsiParserHardeningTests.cs` | `src/Ntilde.VT/AnsiParser.cs` | Recognizes CSI/OSC/DCS/APC plus IND/NEL/RI; unsupported `ESC @.._` controls are ignored with recovery rather than fully implemented | | 8-bit C1 bytes (0x80–0x9F) | If supported, must be explicit | ❌ Not supported | β€” | `src/Ntilde.VT/AnsiParser.cs` | Legacy raw-byte C1 transport is not qualified. The string parser recognizes decoded U+0090/U+009B/U+009D/U+009F introducers and U+009C ST; this does not establish how a PTY byte decoder handles isolated 0x80–0x9F bytes. | | String terminators (ST = ESC \\, BEL) | OSC/APC termination rules | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/AnsiParserHardeningTests.cs` | `src/Ntilde.VT/AnsiParser.cs` | OSC accepts BEL and ST; DCS/APC also accept BEL as permissive recovery behavior, not strict spec compliance | | Unknown sequence handling | Ignore/print/strict? | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/AnsiParserHardeningTests.cs` | `src/Ntilde.VT/AnsiParser.cs` | Unknown `ESC @.._` sequences are ignored and parser resumes on the next valid escape or printable content | | Error recovery on malformed sequences | Robustness | ⚠ Partial | Fuzz/Unit: `tests/Ntilde.App.Tests/AnsiParserHardeningTests.cs`, `tests/Ntilde.App.Tests/AnsiCorpusReplayTests.cs`, `tests/Ntilde.VT.Tests/DcsTruncationStateTests.cs`, `tests/Ntilde.VT.Tests/StringSequenceCapTests.cs` | `src/Ntilde.VT/AnsiParser.cs` | Malformed OSC/CSI/DCS/APC recover across chunk boundaries and nested ESC, but this is a best-effort parser policy rather than full conformance coverage; DCS lost-input state survives chunks/snapshot JSON and import clipping without changing capped SIXEL decoding | --- ## 2) Cursor movement & positioning (CSI) | Feature / CSI | Notes | Status | Evidence | Ownership | Known deviations | |---|---|---:|---|---|---| | CUU/CUD/CUF/CUB (A/B/C/D) | Cursor up/down/forward/back | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/PendingWrapTests.cs` | Parser+Buffer | Explicit CUF movement is covered via pending-wrap reset; dedicated CUU/CUD/CUB targeted coverage is still missing | | CUP / HVP (H/f) | Positioning, default params | βœ… Supported | Unit + Replay: `tests/Ntilde.App.Tests/CursorPositioningCompletionTests.cs`, `tests/Ntilde.App.Tests/ReplayTests/RegressionTests.cs` | Parser+Buffer | | | CHA (G) | Horizontal absolute | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | | | CPL (F) | Previous line and column reset | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | | | CNL (E) | Next line and column reset | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | | | CHT (I) | Cursor forward tabulation | βœ… Supported | Unit: `tests/Ntilde.App.Tests/TabSystemTests.cs` | Parser+Buffer | | | CBT (Z) | Cursor backward tabulation | βœ… Supported | Unit: `tests/Ntilde.App.Tests/TabSystemTests.cs` | Parser+Buffer | | | VPA/HPA (d/G/`) | Absolute row/col | βœ… Supported | Unit: `tests/Ntilde.App.Tests/CursorPositioningCompletionTests.cs` | Parser+Buffer | | | HPR/VPR (a/e) | Relative row/col | βœ… Supported | Unit: `tests/Ntilde.App.Tests/CursorPositioningCompletionTests.cs` | Parser+Buffer | | --- ## 3) Erase & insert/delete (CSI) | Feature / CSI | Notes | Status | Evidence | Ownership | Known deviations | |---|---|---:|---|---|---| | ED (J) | 0/1/2 erase display | βœ… Supported | Replay: `tests/Ntilde.App.Tests/ReplayTests/RegressionTests.cs`, `tests/Ntilde.App.Tests/Fixtures/Replay/vttest_cursor.rec`; Unit: `tests/Ntilde.Platform.Tests/Ssh/NativeSshTerminalParityTests.cs` | Parser+Buffer | | | DECSED (? J) | Selective display erase | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/SelectiveErasureTests.cs`, `tests/Ntilde.VT.Tests/CharacterProtectionStateTests.cs`, `tests/Ntilde.VT.Tests/SelectiveEraseHistoryTests.cs` | Parser+Buffer | Modes 0/1/2 erase their ED region except characters written under DECSCA 1, leaving the cursor, pen and saved lines alone; while nothing on the screen is protected they are exactly ED. Mode 2 retires intersecting inline images exactly as ED 2 does; images are never protected. Undefined private mode 3 is ignored and preserves scrollback, both screen banks and image anchors; ordinary ED 3 still clears history. Rows scrolled into history lose protection. | | DECSEL (? K) | Selective line erase | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/SelectiveEraseHistoryTests.cs`, `tests/Ntilde.VT.Tests/SelectiveErasureTests.cs` | Parser+Buffer | Modes 0/1/2 still alias EL: unlike DECSED and DECSERA, DECSEL does not yet spare DECSCA-protected characters (pinned by `Decsel_is_still_an_EL_alias`). Undefined private mode 3 remains ignored. | | DECERA ($ z) | Erase rectangular stored text | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RectangularEraseTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`; Replay (VT): `tests/Ntilde.VT.Tests/RectangularEraseTests.cs` | Parser+Buffer | Exact unprefixed four-border DECERA supports omitted/zero parameters, page clipping, origin-mode row translation and bounds outside scrolling margins. Cursor, pen and row wrapping remain intact. Erasure clears cell visual/side-table attributes with existing erase colors and atomic wide/OSC66 glyph handling. Version1 full-state and actual recording snapshot/tail tests pass. Image-plane erasure is unqualified and leaves image ownership untouched; DECERA erases protected characters too (protection is not read-only); DECSERA is the selective form. | | DECSERA ($ {) | Selective erase rectangular stored text | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/SelectiveErasureTests.cs`, `tests/Ntilde.VT.Tests/CharacterProtectionStateTests.cs` | Parser+Buffer | The exact four-border `CSI Pt;Pl;Pb;Pr $ {` spelling takes DECERA's defaults, page clipping, DECOM translation, erase colors and wide/OSC66 handling but spares characters written under DECSCA 1; with nothing protected in the rectangle it is DECERA. Cursor, pen and row wrapping stay intact; the inline-image plane is untouched. Full-state and recording checkpoints carry the protection. | | DECSCA (" q) | Select character protection attribute | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/CharacterProtectionStateTests.cs`, `tests/Ntilde.VT.Tests/SelectiveErasureTests.cs` | Parser+Buffer | `CSI 1 " q` protects later characters from DECSED and DECSERA; 0, 2 and an omitted value do not; other values and extra parameters are ignored. A separate pen mode, not SGR: SGR resets leave it alone, DECSC/DECRC save it per screen, DECSTR and RIS reset it, and entering the alternate screen keeps it. Stored per screen cell (a wide glyph's protection lives on its anchor and reads on both halves only while the pair is intact), kept through ICH/DCH/IRM, scrolling, resize/reflow, full-state export and recording checkpoints; ordinary erases and overwrites clear it. Rows scrolled into history lose protection. ISO SPA/EPA protection, DECRQSS and DECSEL are not covered; ANSI/text export carries no protection. | | DECFRA ($ x) | Fill rectangular stored text | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RectangleCopyFillTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | Exact unprefixed `CSI Pch;Pt;Pl;Pb;Pr $ x` fills the inclusive rectangle with one character (32-126 through the in-use GL set, 160-255 as Latin-1; any other code ignores the command) carrying the live SGR pen (attributes, literal/index/default colors, underline decoration). Omitted/zero borders, page clipping and origin-mode row translation follow DECERA; scrolling margins do not clip. Cursor, pen, pending wrap and row wrapping remain intact; hyperlinks and extended text are cleared, and intersected wide/scaled units are retired whole. Filled characters are written under the live DECSCA pen. Image-plane fill and non-ASCII fill characters beyond Latin-1 are unqualified. | | DECCRA ($ v) | Copy rectangular stored text | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RectangleCopyFillTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | Exact unprefixed `CSI Pts;Pls;Pbs;Prs;Pps;Ptd;Pld;Ppd $ v` copies the clipped source rectangle to the destination origin as if the source were read completely first, so overlapping copies cannot smear. Characters, attributes, colors, extended text, hyperlinks and underline decoration travel with the cells; the live pen is not applied. Omitted/zero parameters, page clipping and origin-mode row translation follow DECERA; a destination past the page clips. Only page 1 exists: a source or destination page other than 0/1 ignores the command. A wide glyph cut by the source or page edge copies as a blank; scaled-text metadata is not copied. DECSCA protection travels with the copied characters (a cut wide half copies as an unprotected blank). Image-plane copy and multiple pages are unqualified. | | DECCARA ($ r) | Change attributes in a rectangular area | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RectangularAttributeTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | Exact unprefixed identity; Pt;Pl;Pb;Pr default to the page and clip to it, DECOM offsets rows by the top margin and columns by the left margin, margins do not clip. Selectors 0, 1, 4, 5, 7, 22, 24, 25, 27 set or clear bold, underline, blink and inverse in order; other selectors (including xterm's 8/28) are ignored and an omitted list is selector 0. Text, colors, hyperlinks, extended text, cursor and the SGR pen are untouched; clearing underline drops its style and color but keeps overline; a wide glyph changes as one unit. The default extent is the DECSACE stream (first row from Pl, whole middle rows, last row through Pr); Pl > Pr or Pt > Pb is ignored. In the stream extent cells past a row's written extent (unoccupied) are left alone while the rectangle changes and occupies them. Protection is untouched (attributes only); OSC 66 block atomicity is not modelled. | | DECRARA ($ t) | Reverse attributes in a rectangular area | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RectangularAttributeTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | Same identity, borders and extent as DECCARA. Selectors 0, 1, 4, 5, 7 toggle the named attribute (0 or an omitted list toggles all four, a repeated selector cancels); 22/24/25/27 and other selectors are ignored. A second identical application restores the area. Toggling underline off drops its style and color. Unoccupied cells follow the same stream/rectangle rule. Protection is untouched (attributes only). | | DECSACE (* x) | Select attribute change extent | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RectangularAttributeTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | Ps 0 or omitted and 1 select the stream (default), 2 the rectangle; other values, extra parameters and subparameters are ignored. RIS restores the stream, DECSTR keeps the selection, state transfer carries it. The setting is not readable (no DECRQSS) and is independent of margins, origin mode, the pen and protection. | | DECRQCRA (* y) | Request checksum of rectangular area | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RectangleChecksumTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`, `tests/Ntilde.VT.Tests/QueryVariantAuditTests.cs` | Parser+Buffer+App | **Off by default**: answered only when the `AllowRectangleChecksumReport` setting is on, because a one-cell checksum names that cell's character and lets a program read the screen back; otherwise silent. When on, `CSI Pi;Pg;Pt;Pl;Pb;Pr * y` replies `DCS Pi ! ~ XXXX ST`: four uppercase hex digits of the negated 16-bit sum of each cell's Unicode scalar plus bold 0x80, blink 0x40, reverse 0x20 and underline 0x10. Blanks count as spaces, a wide glyph once at its lead cell; combining marks, colors and other attributes add nothing. Omitted Pi is 0; Pg is not interpreted; DECERA's defaults, DECOM row offset and page clipping apply, and an inverted rectangle sums nothing. Only the exact unprefixed identity with at most six plain parameters is admitted; the request changes no state. Agreement with a particular xterm `checksumExtension` or DEC terminal is not claimed. Direct panes only (persistent sessions stay silent). | | EL (K) | 0/1/2 erase line | βœ… Supported | Unit: `tests/Ntilde.Platform.Tests/Ssh/NativeSshTerminalParityTests.cs` | Parser+Buffer | | | ICH ( @ ) | Insert chars | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RowMetadataShiftTests.cs`, `tests/Ntilde.VT.Tests/UnderlineStyleTests.cs`, `tests/Ntilde.VT.Tests/Osc66GridTests.cs` | Parser+Buffer | Character/side-metadata shifts and whole-block erasure are tested; combined edit, resize and scrollback sequences are not comprehensively qualified. | | DCH (P) | Delete chars | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RowMetadataShiftTests.cs`, `tests/Ntilde.VT.Tests/UnderlineStyleTests.cs`, `tests/Ntilde.VT.Tests/Osc66GridTests.cs` | Parser+Buffer | Character/side-metadata shifts and whole-block erasure are tested; combined edit, resize and scrollback sequences are not comprehensively qualified. | | DECIC (' }) | Insert columns | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/ColumnEditingTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | Exact unprefixed one-parameter `CSI Pn ' }` (omitted/zero = 1) inserts blank columns at the cursor column on every row of the top/bottom scrolling region, only while the cursor row is inside it; columns pushed past the right edge are lost. With DECLRMM/DECSLRM margins it acts only while the cursor column is between them and moves only the columns between them; without them the page edges are the horizontal margins. Cursor, pen, pending wrap, scrollback and the image plane stay untouched. Introduced blanks carry the live colors and no visual attribute, as ICH/DCH blanks do; shifted cells keep attributes, literal/index colors, extended text, hyperlinks, underline decoration and DECSCA protection, and a wide glyph the shift would split is retired whole. Full-state round trip and chunked input are tested. Image-plane shifting is unqualified. | | DECDC (' ~) | Delete columns | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/ColumnEditingTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | Exact unprefixed one-parameter `CSI Pn ' ~` (omitted/zero = 1) deletes columns at the cursor column on every row of the top/bottom scrolling region, only while the cursor row is inside it; blank columns enter at the right edge. With DECLRMM/DECSLRM margins it acts only while the cursor column is between them and moves only the columns between them; without them the page edges are the horizontal margins. Cursor, pen, pending wrap, scrollback and the image plane stay untouched. Introduced blanks carry the live colors and no visual attribute, as ICH/DCH blanks do; shifted cells keep attributes, literal/index colors, extended text, hyperlinks, underline decoration and DECSCA protection, and a wide glyph the shift would split is retired whole. Full-state round trip and chunked input are tested. Image-plane shifting is unqualified. | | SL (SP @) | Scroll left | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/ColumnEditingTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | Exact unprefixed one-parameter `CSI Pn SP @` (omitted/zero = 1) shifts every row of the top/bottom scrolling region left from the left margin whatever the cursor column, only while the cursor row is inside the region (xterm's rule); blank columns enter at the right edge. With DECLRMM/DECSLRM margins it acts only while the cursor column is between them and moves only the columns between them; without them the page edges are the horizontal margins. Cursor, pen, pending wrap, scrollback and the image plane stay untouched. Introduced blanks carry the live colors and no visual attribute, as ICH/DCH blanks do; shifted cells keep attributes, literal/index colors, extended text, hyperlinks, underline decoration and DECSCA protection, and a wide glyph the shift would split is retired whole. Full-state round trip and chunked input are tested. Image-plane shifting is unqualified. | | SR (SP A) | Scroll right | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/ColumnEditingTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | Exact unprefixed one-parameter `CSI Pn SP A` (omitted/zero = 1) shifts every row of the top/bottom scrolling region right from the left margin whatever the cursor column, only while the cursor row is inside the region (xterm's rule); columns pushed past the right edge are lost. With DECLRMM/DECSLRM margins it acts only while the cursor column is between them and moves only the columns between them; without them the page edges are the horizontal margins. Cursor, pen, pending wrap, scrollback and the image plane stay untouched. Introduced blanks carry the live colors and no visual attribute, as ICH/DCH blanks do; shifted cells keep attributes, literal/index colors, extended text, hyperlinks, underline decoration and DECSCA protection, and a wide glyph the shift would split is retired whole. Full-state round trip and chunked input are tested. Image-plane shifting is unqualified. | | IL (L) / DL (M) | Insert/delete lines | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/Osc66FlowTests.cs`; Replay: `tests/Ntilde.App.Tests/ReplayTests/ScrollAndWrapReplayTests.cs` | Parser+Buffer | Whole-block line edits and regional scrolling have regression coverage; arbitrary mixed line-edit/margin/resize sequences are not comprehensively qualified. | | ECH (X) | Erase chars | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/UnderlineStyleTests.cs`, `tests/Ntilde.VT.Tests/Osc66GridTests.cs` | Parser+Buffer | Erasure clears underline metadata and whole text-size blocks; arbitrary mixed erase/resize sequences are not comprehensively qualified. | | REP (b) | Repeat preceding graphic character | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/RepeatCharacterTests.cs` | Parser+Buffer | Repeats only an immediately preceding graphic character (xterm rule); count clamped to one line | --- ## 4) Scrolling, margins, and origin mode | Feature | Notes | Status | Evidence | Ownership | Known deviations | |---|---|---:|---|---|---| | DECSTBM (CSI t;b r) | Set top/bottom margins | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/ScrollAndWrapCorrectnessTests.cs`; Replay: `tests/Ntilde.App.Tests/ReplayTests/ScrollAndWrapReplayTests.cs` | Parser+Buffer | Invalid margins and scroll-at-margin boundaries are tested; all combinations with line edits, resize and retained scrollback are not comprehensively qualified. | | DECRQSS top/bottom margin query | Exact DCS $q r ST; xterm-compatible DCS 1$r top;bottom r ST | βœ… Supported | Unit/Replay: `tests/Ntilde.VT.Tests/DecrqssMarginQueryTests.cs`; Contract: `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessTerminalSessionTests.cs`; MCP: `tests/Ntilde.McpServer.Tests/V2ToolsTests.cs` | Parser | Only complete exact `r` with standard ST succeeds. Current absolute margins are reported without mutation, including after resize/reset/screen changes. Truncation, unknown legacy completeness, BEL recovery and invalid margins remain negative; settings other than `r`, `m` and `SP q` are unavailable. Follows xterm status polarity, not the VT510 manual's opposite documented status. Source/scripted-transport evidence only; frozen Q12 observations/grades are unchanged. | | DECRQSS left/right margin query | Exact DCS $q s ST; DCS 1$r Pl;Pr s ST | βœ… Supported | Unit/Replay: `tests/Ntilde.VT.Tests/DecrqssLeftRightMarginTests.cs`; Contract: `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser | Only complete exact `s` with standard ST succeeds. Reports current absolute one-based margins whether or not DECLRMM is set (full width when unset), never origin-relative, without mutation; the reply replayed after `CSI ? 69 h` restores them. Follows xterm, which gates `s` on the VT level. Source/scripted-transport evidence only. | | DECRQSS SGR query | Exact DCS $q m ST; xterm-compatible DCS 1$r 0;… m ST | βœ… Supported | Unit/Replay: `tests/Ntilde.VT.Tests/DecrqssSettingReportTests.cs`; Contract: `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessTerminalSessionTests.cs`; MCP: `tests/Ntilde.McpServer.Tests/V2ToolsTests.cs` | Parser | Reports the active pen with a leading `0`, so replaying the report reproduces it on any prior pen (tested). Underline styles use `4:n`; extended colors use the semicolon `38/48/58;5;n` and `;2;r;g;b` forms. Palette indexes 0-15 report as short ANSI codes where they exist, and theme-snapped truecolor reports its snapped index, as the pen stores it. Only exact `m` with standard ST succeeds; BEL recovery and truncation stay negative. Source/scripted-transport evidence only. | | DECRQSS cursor style query | Exact DCS $q SP q ST; DCS 1$r Ps SP q ST | βœ… Supported | Unit/Replay: `tests/Ntilde.VT.Tests/DecrqssSettingReportTests.cs`; Contract: `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessTerminalSessionTests.cs`; MCP: `tests/Ntilde.McpServer.Tests/V2ToolsTests.cs` | Parser | Reports `0` while the host's configured cursor is shown (DECSCUSR 0 restores it), otherwise xterm's 1-6 shape-and-blink code. VT does not know the host's configured shape, so it never reports it as a 1-6 value. Only exact `SP q` with standard ST succeeds. Source/scripted-transport evidence only. | | XTGETTCAP capability query | DCS + q hex-name[;hex-name…] ST; one DCS 1+r name=value ST (or DCS 1+r name ST for a flag) per name, DCS 0+r ST for a name not in the table | βœ… Supported | Unit/Replay: `tests/Ntilde.VT.Tests/TerminalCapabilityTests.cs`, `tests/Ntilde.VT.Tests/QueryVariantAuditTests.cs`; Contract: `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessTerminalSessionTests.cs`; MCP: `tests/Ntilde.McpServer.Tests/V2ToolsTests.cs` | Parser | Answers only from `TerminalCapabilities` (#844): `Co`/`colors`, `RGB`, `Tc`, `setrgbf`/`setrgbb`, `Su`, `Smulx`, `Setulc`, `Sync`, `Ss`/`Se`, `Ms`, `Enmg`/`Dsmg`/`Clmg`/`Cmg`, `hs`, `tsl`/`fsl`. Every string value is expanded and run through the parser by a test. Names are case-sensitive; `TN` and capabilities inherited from `xterm-256color` (such as `kcuu1`) stay negative. A truncated, BEL-terminated, malformed or more-than-16-name list gets one `DCS 0+r ST`; only validated hex is echoed. `Ms` is answered only while the host accepts OSC 52 writes, so mux sessions never advertise it. Checked with Neovim 0.12.6 (`docs/qa/terminfo/2026-11-05-neovim-capability-check.md`). | | XTSMGRAPHICS graphics attributes | CSI ? Pi ; Pa ; Pv S; xterm's reply CSI ? Pi ; 0 ; Pv S on success (width ; height for geometry), CSI ? Pi ; Ps S otherwise | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/XtsmgraphicsTests.cs`, `tests/Ntilde.Rendering.Tests/SixelDecoderTests.cs`; Contract: `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessTerminalSessionTests.cs`; MCP: `tests/Ntilde.McpServer.Tests/V2ToolsTests.cs` | Parser | Reports the Sixel decoder's real limits (#872): 4096 color registers, fixed (set succeeds only for 4096, so notcurses' attempts to set 256 or 1024 get Ps 3), and a 2000 x 2000 pixel clip. The geometry read is the text area in Sixel pixels (columns and rows times the cell size Sixel placement uses, so an image that wide fills the pane), capped at 2000 per side. It is the same text area `CSI 14 t` reports (device pixels on a scaled display), capped at 2000. geometry reset and set fail, as in xterm. ReGIS (Pi 3) is not supported (Ps 1). Only the exact three-parameter form is answered, so a two-value geometry set stays silent, as in xterm. Persistent sessions answer from the daemon with the window's cell size. The probe never scrolls. | | IND (ESC D) / RI (ESC M) | Index / Reverse index | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/ScrollAndWrapCorrectnessTests.cs`; Replay: `tests/Ntilde.App.Tests/ReplayTests/ScrollAndWrapReplayTests.cs` | Parser+Buffer | Regional index/reverse-index and bottom-margin wrap are tested; arbitrary combinations with resize and retained scrollback are not comprehensively qualified. | | DECOM (origin mode) | Cursor relative to margins | βœ… Supported | Unit: `tests/Ntilde.App.Tests/DecModeTests.cs` | Parser+Buffer | | | DECLRMM (CSI ? 69 h/l) | Left/right margin mode | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/LeftRightMarginTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | Off by default; DECRQM reports 1/2. Setting it makes `CSI Pl;Pr s` DECSLRM; resetting it, RIS, DECSTR, DECALN and a resize restore full-width margins (RIS also resets the mode). State transfer carries the mode and the margins; a full-width payload is unchanged. | | DECSLRM (CSI Pl;Pr s) | Set left/right margins | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/LeftRightMarginTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs` | Parser+Buffer | Only while DECLRMM is set (otherwise `CSI s` stays SCOSC). Omitted/zero Pl is 1; omitted, zero or oversized Pr is the last column; Pl not left of Pr is ignored; applied margins home the cursor (the top-left margin corner under DECOM). The margins bound printing and autowrap (a wrap returns to the left margin and is not marked as a soft wrap), CR/NEL/CNL/CPL, CUF/CUB/BS, HT, CUP/HVP/CHA/HPA and CPR under DECOM, IND/RI/LF at the region edges (outside the margins they neither scroll nor move there), SU/SD, IL/DL, ICH/DCH and insert mode, DECIC/DECDC/SL/SR and DECOM rectangle columns. Partial-width scrolls never feed scrollback; side tables move with their cells, wide glyphs cut by a margin are retired, and images overlapping the moved band are removed. ED/EL/ECH stay full-width; DECRQSS `s` reports the margins; DECBI/DECFI are not implemented. | | Wraparound DECAWM | Auto wrap | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/PendingWrapTests.cs`, `tests/Ntilde.App.Tests/ScrollAndWrapCorrectnessTests.cs` | Buffer | Pending wrap, wide glyphs and combining marks have regressions. A width-changing selector arriving after a last-column base is not guaranteed to retroactively reflow it. | | Reverse-wraparound (CSI ? 45 h / l) | xterm semantics: with auto-wrap (?7) also set, BS and CUB at the left edge move to the last column of the previous row when that row ended by a soft wrap. A pending wrap is consumed before the cursor moves left. Off by default; never crosses a hard line break, the top row or the top margin of the active scrolling region; reset by RIS and DECSTR; reported by DECRQM; carried through state export/import and recordings | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/ReverseWrapModeTests.cs` | Buffer | Measured by terminfo.dev `cursor.reverse-wrap` (see `docs/qa/QA_TERMINFO.md`). | | Animated whole-line wheel scrolling | SmoothScrolling setting, enabled by default | βœ… Supported | Unit (App): `tests/Ntilde.App.Tests/Input/TerminalViewSmoothScrollTests.cs` | `src/Ntilde.App/Shell/TerminalView.Scrolling.cs` | Wheel targets are eased through integer row offsets. This is visible animation, with mid-gesture accumulation and cancellation tests; it does not provide continuous pixel offsets or DECSCLM. | | Continuous pixel-level scrolling | Fractional viewport offsets | ❌ Not supported | β€” | App view | Viewport scroll offsets and animation steps are whole rows. | | DECSCLM smooth-scroll mode | CSI ? 4 h/l | ❌ Not supported | β€” | VT+App | No tracked private mode 4 or terminal-controlled smooth-scroll behavior. ANSI mode 4 is insert mode (IRM), a separate feature. | --- ## 5) Screen buffers & modes (DEC private modes) | Mode | CSI | Notes | Status | Evidence | Ownership | Known deviations | |---|---|---|---:|---|---|---| | Alternate screen | ?1049 / ?47 / ?1047 | Switch + save/restore cursor | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/SavedStateQualificationTests.cs`, `tests/Ntilde.VT.Tests/AlternateScreenModeTests.cs`, `tests/Ntilde.App.Tests/AlternateScreenTests.cs`; Replay: `tests/Ntilde.App.Tests/ReplayTests/AlternateScreenReplayTests.cs`, `tests/Ntilde.App.Tests/ReplayTests/NativeSshReplayParityTests.cs` | Buffer | Main scrollback is preserved and alt-screen output never enters scrollback. `?47` and `?1047` reuse the retained alternate buffer/state on entry. Following xterm, `?1047l` clears the active alternate cells/row metadata before returning to main; while main is already active it does not clear a stashed alternate. `?1049` clears and homes on entry. Nested/redundant alt-screen enters are treated as no-op, and a `?1049` save is consumed by the first exit from alt-screen regardless of whether that exit uses `?47l`, `?1047l`, or `?1049l`. | | RIS screen reset | ESC c | Return to main; clear both retained screen banks, history and row metadata with default pens/colors; reset saved banks, pending wrap and DECTCEM cursor visibility | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RisCursorVisibilityTests.cs`, `tests/Ntilde.VT.Tests/FullResetScreenTests.cs`, `tests/Ntilde.VT.Tests/SavedStateQualificationTests.cs`, `tests/Ntilde.VT.Tests/KittyImageLifecycleTests.cs`, `tests/Ntilde.App.Tests/RenderTests/CursorResetRenderTests.cs`; Replay: `tests/Ntilde.VT.Tests/FullResetScreenTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessSoftResetTests.cs`; Live: `docs/qa/terminfo/2026-11-04-reset-observations.md` | Parser+Buffer+Replay | Four initial-state controls reproduce the old main/alternate clearing defect. Inherited autowrap on/off converges to the existing wrap-on policy. Main/alternate and chunked RIS restore the visible cursor, including DECRQM25, immutable render cuts, replay and mux attach. Both-screen image retirement remains snapshot-pinned. Owning state/transport coverage and the fresh 40x140 disposable-App case verify both banks/default witnesses with completed source/cleanup evidence; broad power-on state and legacy device hints remain unqualified. Published RIS negatives stay frozen. See `docs/qa/terminfo/ris-cursor-visibility.md` for the source-backed visibility correction. See `docs/qa/terminfo/reset-observations.md`. | | DECSTR (soft reset) | CSI ! p | Preserve contents, scrollback, active screen and current cursor position; reset margins, origin/insert/application-cursor/application-keypad modes, visibility, cursor presentation, SGR and character sets; reset the active saved cursor to home | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/SavedStateQualificationTests.cs`, `tests/Ntilde.VT.Tests/SoftResetAndCursorSaveTests.cs`, `tests/Ntilde.App.Tests/RenderTests/CursorResetRenderTests.cs`; Replay: `tests/Ntilde.VT.Tests/SavedCursorReplayTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessSoftResetTests.cs` | Parser+Buffer+App | Uses xterm-compatible autowrap-on defaults. Tabs, mouse/focus/paste/keyboard reporting, in-band resize and SIXEL display mode/metrics survive. Current pending wrap is cleared. Cursor shape/blink return to the presenting host preference through an optional default-appearance marker. Unsupported DEC hardware modes are outside the model. | | Cursor save/restore (1048) | CSI ? 1048 h / CSI ? 1048 l | Aliases DECSC/DECRC: per-screen position, rendition, pending wrap, origin mode and G0-G3/GL; does not switch screens | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/SavedStateQualificationTests.cs`, `tests/Ntilde.VT.Tests/SoftResetAndCursorSaveTests.cs`; Replay: `tests/Ntilde.VT.Tests/SavedCursorReplayTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessSoftResetTests.cs` | Parser+Buffer+Replay | Shares the existing ESC 7/8 and 1049 cursor banks. Restores pending wrap while leaving the current DECAWM autowrap mode unchanged, matching xterm. New replay snapshots carry a complete terminal/parser cut including both screens and the armed 1049 restore; legacy snapshots use default banks. Mode 1048 is an action without a persistent set/reset flag, so DECRQM continues reporting 0. Legacy mux cursor snapshots omit the added mode/charset context and retain legacy restore behavior for those fields. | | Reverse video (DECSCNM) | ?5 h/l, CSI ? 5 $ p | Screen-wide reverse video; terminfo `flash` (`\E[?5h$<100/>\E[?5l`) visual bell | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/ReverseVideoModeTests.cs`; Unit (App): `tests/Ntilde.App.Tests/RenderTests/ReverseVideoRenderTests.cs`, `tests/Ntilde.App.Tests/RenderTests/RenderCacheEffectivenessTests.cs`, `tests/Ntilde.App.Tests/RenderTests/DrawPathAllocationTests.cs`; Replay: `tests/Ntilde.App.Tests/ReplayTests/ReverseVideoReplayTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessReverseVideoTests.cs` | Buffer+Renderer | xterm semantics: the default foreground and background trade places for the whole screen; explicit RGB and indexed colors keep their values; SGR 7 swaps each cell's resolved pair as usual, so default-colored inverse cells appear normal. A cursor or cursor-text color equal to a default follows the swap; derived selection, search and cursor-text colors are derived from the swapped pair, explicit theme selection colors are kept. VT presents the swapped theme in the render snapshot (cached, no per-frame allocation); a change repaints every row once. DECRQM reports live state; DECSTR preserves it and RIS clears it (VT510/xterm); state transfer, mux attach and replay snapshots carry it. The mux text client renders default colors through its host terminal and does not reproduce the swap. Independent of the App's own BEL visual-bell setting. Manual `tput flash` / Vim `:set vb` check not yet recorded. | | Show cursor | ?25 | | βœ… Supported | Unit: `tests/Ntilde.App.Tests/DecModeTests.cs`; Replay: `tests/Ntilde.App.Tests/ReplayTests/ReplayV2Tests.cs` | Buffer+Renderer | | | Application cursor keys | ?1 | Impacts input mapping | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/Input/TerminalInputModeEncoderTests.cs`, `tests/Ntilde.App.Tests/CommandAssist/TerminalViewKeyHandlingTests.cs`; Unit/Code: `ReplayV2Tests` | Parser+Input | Unmodified arrows and Home/End switch between CSI and SS3 with the mode; with a modifier held they always use xterm's `CSI 1;m` form (e.g. Ctrl+Left `CSI 1;5D`), as xterm does, since SS3 has no parameter slot | | Application keypad | ESC = / ESC >; CSI ? 66 h/l and $p | Unmodified auxiliary-keypad digits, arithmetic, decimal/comma and physical keypad Enter use SS3 application frames | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/ApplicationKeypadModeTests.cs`, `tests/Ntilde.App.Tests/Input/ApplicationKeypadRoutingTests.cs`; owned child: `docs/qa/terminfo/2026-11-04-input-observations.md` | Parser+Input+Replay | VT owns mode state, clone, complete snapshots and legacy replay defaults; DECSTR/RIS restore numeric mode. Kitty reporting takes precedence. Routed Avalonia key/text events pin exact writes and one-shot duplicate-text suppression. Logical PC navigation and modified keys retain existing mappings; unavailable native NumLock state, VT52, extra keypad keys, broadcast mapping and other runtime platforms remain unqualified. A retained macOS run observes normal key/text/up and eighteen application-keypad events through the generated routed-event path into the real local child; hardware input remains separate. See `docs/qa/terminfo/application-keypad.md`. | | Focus event reporting | ?1004 | Emits `CSI I` / `CSI O` on focus transitions | βœ… Supported | Unit: `tests/Ntilde.App.Tests/DecModeTests.cs`, `tests/Ntilde.App.Tests/Input/TerminalInputModeEncoderTests.cs`; Unit (App): `tests/Ntilde.App.Tests/CommandAssist/TerminalViewKeyHandlingTests.cs` | VT+App input | Mode-gated CSI I/O encoding and raised headless UI focus events are tested. Native window-manager focus delivery on every desktop is outside this evidence. | | Alternate scroll | ?1007 | On the alternate screen with mouse reporting off, each wheel notch sends one cursor key: `ESC O A` / `ESC O B` under application cursor mode (`?1`), `CSI A` / `CSI B` otherwise. DECSET/DECRST override the `AlternateScroll` setting (default on), and DECRQM reports the effective value | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/AlternateScrollModeTests.cs`, `tests/Ntilde.VT.Tests/DecrqmTests.cs`; Contract: `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`; Unit (App): `tests/Ntilde.App.Tests/Input/AlternateScrollWheelTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessAlternateScrollTests.cs`; Replay: `tests/Ntilde.VT.Tests/AlternateScrollModeTests.cs`, `tests/Ntilde.VT.Tests/StateTransfer/ParityCorpus.cs` | VT+App input | Mouse reporting wins; the main screen keeps scrolling scrollback. Sub-notch high-resolution deltas accumulate to whole notches, one key per notch regardless of `WheelLinesPerNotch`. Modifiers are not carried, and keys are always the legacy cursor encoding, even with kitty keyboard flags pushed. RIS returns to the setting; DECSTR keeps the application's choice, as in xterm. State transfer, mux attach and replay carry only the application's DECSET/DECRST: each presenting pane applies its own setting, and a mux daemon's own DECRQM reply assumes the default on. Wheel input is not broadcast to sibling panes. | | Bracketed paste | ?2004 | Input feature | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/Input/TerminalInputSenderTests.cs`, `tests/Ntilde.App.Tests/MouseReportingTests.cs` | Input layer | Markers, line-ending normalization and removal of embedded terminators are tested. Native clipboard acquisition and every paste gesture are separate qualification scopes; arbitrary payload fidelity is not claimed because sanitization deliberately changes content. | | Mouse reporting | ?9/1000/1002/1003/1006 etc | Click (`?1000`) and drag-motion (`?1002`) tracking, plus hover motion with no buttons held under any-event tracking (`?1003`, issue #269); SGR (`?1006`) and legacy X10 coordinate encodings both supported. Motion reports are coalesced to at most one per distinct terminal cell (tracked last-reported cell, reset on buffer attach/resize, alternate-screen switch, pointer exit, and `?1002`/`?1003` mode changes observed between two motion events) so a stationary-but-jittery pointer or a TUI toggling tracking modes doesn't flood the PTY | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/X10MouseTrackingTests.cs`, `tests/Ntilde.App.Tests/Input/X10MouseRoutingTests.cs`, `tests/Ntilde.Mux.Tests/TextClient/TextClientRendererTests.cs`, `tests/Ntilde.App.Tests/Input/TerminalInputModeEncoderTests.cs`, `tests/Ntilde.App.Tests/Input/TerminalViewMouseMotionTests.cs`, `tests/Ntilde.App.Tests/Input/LegacyMouseByteInputTests.cs`, `tests/Ntilde.App.Tests/Input/LegacyMouseByteInputPtyTests.cs`, `tests/Ntilde.Mux.Tests/Client/ByteInputTransportTests.cs`, `tests/Ntilde.Platform.Tests/Ssh/SshByteInputForwardingTests.cs`; owned child: `docs/qa/terminfo/2026-11-04-input-observations.md` | Input layer | `?1001` (highlight tracking) and urxvt (`?1015`)/pixel-position (`?1016`) mouse coordinate encodings are not implemented; only X10 and SGR (`?1006`) coordinate encodings are supported. Coordinates are viewport-relative and 1-based on every report path (motion, press, release, wheel); the legacy X10 encoding clamps coordinates to its 223 ceiling rather than promoting the report to the SGR form the application never requested. Legacy high bytes use optional byte-preserving PTY/SSH/mux input; old text-only peers retain ASCII reports and refuse high bytes. A real POSIX raw child verifies six exact high-coordinate octets; DECSET 9 is separately tested as modifier-free three-button presses only; parser tracking selectors are mutually exclusive and any recognized tracking reset disables tracking. Mux text output resets before enabling its target. A retained macOS run observes eighteen generated mouse cases with independent raw child bytes, mode controls, suppression and coalescing. Native pointer events, Windows and foreign-console input decoding remain unqualified. See `docs/qa/terminfo/mouse-byte-input.md` and `docs/qa/terminfo/x10-tracking.md`. | | Cursor style | CSI Ps SP q | DECSCUSR block/beam/underline + blink state | βœ… Supported | Unit: `tests/Ntilde.App.Tests/OscUxTests.cs`, `tests/Ntilde.VT.Tests/CursorStyleInvalidationTests.cs`; Unit (App): `tests/Ntilde.App.Tests/Core/TerminalViewCursorBlinkTests.cs` | Parser+Renderer | Style-only changes invalidate once per batch. App timers honor explicit blinking/steady choices over host defaults until reset, while preserving focus/visibility/lifetime gates. Ps 0 (or omitted) returns to the host's configured cursor, as kitty, foot, WezTerm, Ghostty, Alacritty and Windows Terminal do; xterm treats it as a blinking block. | | Color-scheme query | CSI ? 996 n | Contour dark/light preference | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/ColorSchemeReportingTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`; Unit (App): `tests/Ntilde.App.Tests/Controls/PaneParserWiringTests.cs` | Parser+App | Reports CSI ? 997 ; 1 n for dark or 2 for light, derived from the effective terminal background, including profile overrides. Uses the same perceived RGB luma threshold as UI theme classification; alpha is ignored and an unset background uses black. It reports the terminal preference, not the raw OS appearance. Persistent spawns carry an optional initial background before startup output; absent legacy values retain the dark fallback until presentation arrives. An updated daemon is needed for launch-time color replies. | | Color-scheme notifications | CSI ? 2031 h/l, CSI ? 2031 $ p | Opt-in background preference updates | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/ColorSchemeReportingTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`; Unit (App): `tests/Ntilde.App.Tests/Controls/PaneParserWiringTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessColorSchemeTests.cs` | Parser+App+Mux | Off by default. Only effective-background transitions between dark and light send a report; enabling, unchanged classification and unrelated settings are silent. DECRQM reports live subscription state. DECSTR preserves it, RIS clears it; complete parser snapshots retain it while host colors remain configuration. Shared-session replies originate in the daemon. Palette edits that do not change dark/light classification do not send updates. | | Kitty keyboard protocol | CSI ? u / CSI > Pm u / CSI < Pm u / CSI = Pm ; Pm u | Progressive-enhancement flags: query, push, pop, set. Per-screen-buffer flag stacks (depth 32, oldest evicted on overflow); RIS clears both | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/KittyKeyboardProtocolTests.cs`, `tests/Ntilde.VT.Tests/KittyKeyboardEventTests.cs`, `tests/Ntilde.App.Tests/Input/KittyKeyEventEncodingTests.cs`, `tests/Ntilde.App.Tests/Input/KittyKeyEventRoutingTests.cs`, `tests/Ntilde.App.Tests/Input/KittyKeyboardEncodingTests.cs`, `tests/Ntilde.App.Tests/Input/KittyAlternateKeyRoutingTests.cs`, `tests/Ntilde.App.Tests/Input/KittyAllKeyRoutingTests.cs`, `tests/Ntilde.App.Tests/Input/KittyAssociatedTextRoutingTests.cs`, `tests/Ntilde.App.Tests/Input/KittyAssociatedTextEncodingTests.cs`, `tests/Ntilde.VT.Tests/KittyAssociatedTextProtocolTests.cs`, `tests/Ntilde.VT.Tests/KittyAllKeyProtocolTests.cs`, `tests/Ntilde.Platform.Tests/Ssh/NativeSshSessionTests.cs`, `tests/Ntilde.VT.Tests/KittyAlternateKeyProtocolTests.cs`, `tests/Ntilde.App.Tests/Controls/MuxPaneSharingTests.cs`, `tests/Ntilde.App.Tests/AnsiParserHardeningTests.cs`, `tests/Ntilde.App.Tests/CommandAssist/TerminalViewKeyHandlingTests.cs` | Parser+Input | Scope: disambiguate escape codes (`0b1`), report event types (`0b10`), alternate keys (`0b100`), all keys (`0b1000`), and associated text (`0b10000`). Alternate-key fields enrich keys already encoded by another enhancement; flag 4 alone does not change legacy bytes. Available scalar framework symbols supply layout-aware primary/shifted values, and physical positions supply PC-101 base-layout values; equal or unavailable optional values are omitted. Shifted values appear only while Shift is held. Captured identity survives repeat/release and later layout changes. Alt-produced symbols are excluded from primary derivation. Missing/control/combining/format/multi-scalar/malformed symbols keep the legacy primary; shifted punctuation also keeps that primary because Avalonia exposes no unshifted layout glyph for that event. No native keyboard-map query is added; this does not claim complete layout normalization. Repeat/release events use modifier subparameters `:2`/`:3`; press is the default. Releases are paired with forwarded presses and discarded on focus/session/buffer/keyboard-stack changes; repeats retain the pressed logical key across layout changes. Tracking starts after the input call returns, but the existing fire-and-forget transport has no child-delivery acknowledgement and saturated input queues can lose events. Without flag 8, Enter/Tab/Backspace keep their unmodified initial recovery bytes, report negotiated repeats, and have no releases; ordinary text, IME and AltGr stay on TextInput. Flag 8 implies disambiguation and canonical ordinary/recovery/modifier/keypad/media key frames; paired releases still require flag 2. Left/right modifier releases use the current framework modifier mask. All-key mode and unhandled legacy text keydowns capture their flag state and one-shot text policy, so later ceiling/stack/kill-switch changes do not duplicate frames or lose legacy text; a matching key release or focus/session/buffer change clears correlation so a later text-only commit is admitted by current policy, while consumed legacy keys retain their prior text-only behavior. Generation tokens prevent nested callbacks from re-arming or overwriting a consumed/newer text decision. With flag 8 alone, unmatched plain text callbacks (including text-only IME commits) are suppressed. With flags 8+16, actual generated text is encoded as invariant scalar values in CSI-u frames; releases never carry text. Valid forwarded payloads notify the typing observer once for markless history and assist refresh; rejected payloads and key-only fallbacks do not. Text-producing keydowns remain unhandled for the native text callback, with one pending frame and one input-priority fallback job per view. If text arrives, the frame receives its actual payload; if no callback arrives, the key frame is flushed before the next key/release or at the next dispatcher turn. Late or unassociated text uses key code zero. Input-context changes drop unsent old-context frames; old-stack deferred keys cannot acquire a new-stack release. Complete malformed UTF-16 and all C0/C1 payload controls are rejected as a whole; combining/format scalars are allowed in text. Adjacent single-unit backend callbacks can complete one surrogate pair, retaining only one high unit and its original mode across packet key events; context reset or a new known key discards an incomplete fragment, and unmatched halves never become replacement payloads. Flag 16 without flag 8 has no text effect (the protocol leaves that combination undefined). Available key symbols supply primary identity even without flag 4, while missing/invalid metadata retains the existing logical fallback. Unavailable key codes and lock-state bits are not invented; a key without logical or physical identity cannot acquire a repeat/release slot. Undefined future bits (`0b100000` and above) are accepted but masked out on push/set, so the `CSI ? u` reply only advertises flags actually honored. Key encoding covers Esc, modified Enter/Tab/Backspace, and ctrl/alt/super combinations on the spec's legacy-text keys; keypad text remains text when flag 8 is absent and the legacy application-keypad mode does not claim that unmodified key. Without event or all-key reporting, functional keys keep their legacy encodings, which for arrows/Home/End/Insert/Delete/PgUp/PgDn/F1-F12 carry modifiers in xterm's PC-style form (e.g. Ctrl+Left is `CSI 1;5D`, Ctrl+Delete `CSI 3;5~`). With event reporting, supported functional keys use canonical CSI frames (F3 uses `13~` to avoid CPR collisions), independent of application cursor mode. AltGr on Windows non-US layouts is reported as Control+Alt; without flag 8, the encoder carves that combination out (falls through to legacy/text) so composed characters are not swallowed - the accepted trade-off is that literal Ctrl+Alt+<key> shortcuts on US layouts also lose kitty encoding, same as the pre-existing Alt-sends-ESC carve-out. Gated end-to-end by `TerminalSettings.EnableKittyKeyboardProtocol` (default on): when off, the App-side encoder is skipped unconditionally and the `CSI ? u` reply always reports flags 0 regardless of the pushed stack state. Persistent spawn requests carry the host keyboard ceiling before startup output is parsed, including the disabled setting; absent launch capabilities default to flag 1. Persistent sessions intersect the supported flag masks of interactive clients; old daemons/clients without the optional mask stay at flag 1, and read-only observers do not reduce capabilities. An attaching GUI receives its negotiated mask before the snapshot is offered; the GUI applies later capability notifications before subsequent output; raw requested stacks survive a temporary ceiling reduction. The tab-broadcast path in `MainWindow` builds its own input independently of this encoder and always sends legacy encoding to broadcast targets regardless of their own flag state. | | xterm modifyOtherKeys | CSI > 4 ; Pv m / CSI > 4 m / CSI > 4 ; m / CSI > m / CSI > 4 n | Key-modifier resource 4 (XTMODKEYS): levels 0, 1 and 2, one level per screen buffer; the App encodes claimed chords as `CSI 27 ; m ; code ~` | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/ModifyOtherKeysProtocolTests.cs`, `tests/Ntilde.App.Tests/Input/ModifyOtherKeysEncodingTests.cs`, `tests/Ntilde.App.Tests/Input/ModifyOtherKeysRoutingTests.cs`, `tests/Ntilde.App.Tests/Input/ModifyOtherKeysBroadcastTests.cs`, `tests/Ntilde.VT.Tests/StateTransfer/ParityCorpus.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessModifyOtherKeysTests.cs` | Parser+Input+Mux | Issue #829. Key sets follow xterm input.c (`ModifyOtherKeys`/`allowedCharModifiers`) with metaSendsEscape and the DEC backarrow default: level 1 leaves the X11 Ctrl control aliases (letters, `@ [ \ ] ^ _`, backquote, Space, `2`-`8` and `/`), Shift-only text, every Alt chord, Backspace, Escape, Space and Shift+Tab legacy, and encodes the remaining Ctrl/Ctrl+Shift ordinary keys, Shift/Ctrl Enter and Ctrl+Tab; level 2 encodes every Ctrl/Alt chord on ordinary keys (Ctrl+A `CSI 27;5;97~`, Ctrl+Shift+A `CSI 27;6;65~`, Alt+A `CSI 27;3;97~`, Ctrl+I/M/[ distinct from Tab/Enter/Esc), Shift+Space, modified Enter/Escape, modified Tab except Shift+Tab (`CSI Z`), and Shift/Alt Backspace as 127 and Ctrl+Shift Backspace as 8 (plain Ctrl+Backspace stays legacy). Deviations: Shift alone on a printable key stays text at level 2 (xterm sends `CSI 27;2;65~`; Vim, tmux and Emacs strip that Shift, and this keeps IME/dead-key/non-Latin text on TextInput); Ctrl+Alt (AltGr) and Meta/Super/Cmd chords are never encoded; codes come from US-layout virtual keys like the legacy Ctrl/Alt encoders; level 3, `formatOtherKeys: 1` (`CSI code ; m u`, which would collide with kitty CSI-u frames), xterm's colon ignore-mask extension and every other key-modifier resource are not implemented and leave the level unchanged; xterm keeps one session-wide value while Ntilde keeps main/alternate levels like the kitty stacks. Kitty flags, when active on the screen, win and modifyOtherKeys is not consulted; level 0 is byte-identical to legacy. UI shortcuts, Command Assist's interceptor, Enter-to-reconnect and Ctrl+C-copies-selection run first. A text-capable encoded chord reserves one-shot TextInput suppression. RIS and DECSTR reset both levels to 0 (xterm restores the resource on soft reset too) and so does the OSC 133 prompt transient reset. Complete state snapshots carry optional `mok_main`/`mok_alt`; legacy peers import 0 and unsupported values clamp to 0. Tab broadcast encodes per receiving pane: Ctrl chords and modified Enter/Tab/Backspace/Escape take the receiver's level, using its effective kitty flags (zero under the kill switch) for precedence. Text-producing chords stay on the text broadcast unless the focused pane's own level consumes the keydown (Shift+Space at level 2), in which case each sibling gets its encoding or the space exactly once: a window-level one-shot gate, with the focused view's suppression lifetime (consumed by the next text callback, expired by the key's release or the next keydown), skips any trailing TextInput a backend still raises. Alt chords stay unbroadcast and kitty panes keep legacy bytes. Input-host ceiling, as kitty's supported-flag mask: `ModifyOtherKeysState.IsSupported` keeps the requested levels but makes the effective level, the `CSI ? 4 m` reply and the encoder 0 while off. A shared session ANDs it over interactive clients' optional `MuxPresentation.ModifyOtherKeysEnabled` (the GUI presents true; the `ntilde mux attach` text client, which forwards legacy input, and older clients do not; read-only observers take no part) and sends it to opted-in clients on the ordered `keyboardCapabilities` frame (`modifyOtherKeysSupported`, before the attach snapshot and before subsequent output), which GUI panes apply on the delivery thread; an older daemon's frame omits it and leaves the GUI's support on. Keypad keys are identified by physical position, so keypad comma/equals/Enter reported under ordinary logical keys stay legacy. Manual Vim (`keyprotocol` mok2) and live generated-input qualification are not yet recorded. | | xterm modifyOtherKeys query | CSI ? 4 m β†’ CSI > 4 ; Pv m | XTQMODKEYS for resource 4 reports the active screen's level | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`, `tests/Ntilde.VT.Tests/ModifyOtherKeysProtocolTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessModifyOtherKeysTests.cs` | Parser | Each listed resource is answered in order; resources other than 4, a parameterless `CSI ? m`, colon parameters and intermediates stay silent rather than guessing values Ntilde does not track. Shared sessions answer from the daemon, reporting 0 while an attached interactive client (such as the text client) cannot encode modifyOtherKeys. | | Synchronized output | CSI ? 2026 h/l | Defer invalidation until reset or bounded timeout | βœ… Supported | Unit: `tests/Ntilde.App.Tests/Core/SynchronizedRenderingTests.cs`, `tests/Ntilde.VT.Tests/DecrqmTests.cs` | VT buffer+App presentation | Defers invalidation, not parser state mutation. A 200 ms timeout can end the synchronized interval; the host must service timeout flushing. No atomic native compositor-frame guarantee. | | DECRQM / DECRPM (mode query) | CSI Ps $ p / CSI ? Ps $ p β†’ CSI [?] Ps ; Pm $ y | Reports live set/reset state for private modes 1, 5, 6, 7, 25, 45, 47, 66, 80, 1000, 1002, 1003, 1004, 1006, 1007 (effective alternate-scroll value), 1047, 1049, 2004, 2026, 2031, 2048 and host-gated 5522, plus ANSI modes 4 (IRM), 12 (SRM), 20 (LNM); unrecognized modes report `Pm=0`. Lets apps (e.g. probing `?2026` synchronized output) detect real support instead of assuming it from unanswered queries | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/DecrqmTests.cs`, `tests/Ntilde.VT.Tests/ReverseVideoModeTests.cs`, `tests/Ntilde.VT.Tests/ColorSchemeReportingTests.cs`, `tests/Ntilde.VT.Tests/InBandResizeTests.cs`, `tests/Ntilde.VT.Tests/KittyClipboardProtocolTests.cs` | Parser | Mode 5522 reports unrecognized when the presenting host has not enabled MIME clipboard handling. `?9001` (ConPTY passthrough) is accepted by `CSI ? Ps h/l` but has no tracked boolean state, so DECRQM reports it as `Pm=0` (not recognized) rather than guessing. Dispatch keys on the `$` intermediate exactly, so `CSI ! p` performs DECSTR without a reply; bare `CSI p` is ignored. | | XTVERSION (terminal name/version) | CSI > q / CSI > 0 q β†’ DCS > | Ntilde <version> ST | Reports the terminal's name (matching the `TERM_PROGRAM=Ntilde` the PTY exports) and the build's version without `+` build metadata | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/XtVersionTests.cs` | Parser | Shares the `q` final byte with DECSCUSR (`CSI Ps SP q`); dispatch keys on the `>` leader with no intermediates, and Ps other than 0 stays silent. | | Tertiary device attributes | CSI = c / CSI = 0 c β†’ DCS ! | 00000000 ST | Reports a fixed unassigned, nonunique all-zero unit identifier, following xterm's zero site/serial convention | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/TertiaryDeviceAttributeTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`, `tests/Ntilde.App.Tests/AnsiParserHardeningTests.cs`, `tests/Ntilde.McpServer.Tests/V2ToolsTests.cs`; Replay: `tests/Ntilde.VT.Tests/TertiaryDeviceAttributeTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessTerminalSessionTests.cs` | Parser | Default or one numeric-zero parameter only; seven-bit/C1 CSI accepted, seven-bit response. Nonzero/extra/colon parameters and intermediates remain silent. No host/hardware identity I/O, unique identifier or complete VT400 conformance is claimed. See `docs/qa/terminfo/exact-query-audit.md`. | | Text-area size query | CSI 18 t β†’ CSI 8 ; rows ; cols t | Reports current VT text-area dimensions in characters, without a subscription or pixel metrics | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/ExactWindowQueryTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`; Replay: `tests/Ntilde.VT.Tests/ExactWindowQueryTests.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessTerminalSessionTests.cs` | Parser | Exact bare single-parameter spelling; leader/intermediate variants and extra/colon parameters stay silent. Reports VT geometry rather than kernel/native-window measurements. See `docs/qa/terminfo/exact-query-audit.md` for all eighteen Q07 dispositions and per-setting Q12 failure evidence. | | XTWINOPS title push | CSI 22 ; Ps t (Ps 0/omitted both, 1 icon name, 2 window title) | Saves the raw window title and/or icon name on a session-wide stack, so Vim's `t_ST` can restore it on exit | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/XtWinOpsTitleStackTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`; State transfer: `tests/Ntilde.VT.Tests/StateTransfer/ParityCorpus.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessTitleStackTests.cs`, `tests/Ntilde.Mux.Tests/Cli/MuxCliTests.cs`; App: `tests/Ntilde.App.Tests/Core/MainWindowAgentMetadataTests.cs`, `tests/Ntilde.App.Tests/Controls/MuxPaneTests.cs` | Parser | xterm semantics: 10 entries, the oldest discarded on overflow; a one-sided push copies the other half from the entry below or leaves it absent. Bare spelling only; other selectors, colon/extra parameters and a nonzero third parameter (xterm direct slot access) are ignored. Session-wide across alternate screens; DECSTR keeps it, RIS clears it (xterm keeps it); complete mux/replay snapshots carry it. Titles over 8 Ki chars are not retained. Before any OSC 0/1/2 the saved title is empty. See `docs/qa/terminfo/osc-title-payloads.md`. | | XTWINOPS title pop | CSI 23 ; Ps t (Ps 0/omitted both, 1 icon name, 2 window title) | Restores the saved raw title through the ordinary title callback, so the App tab label and mux listing see the exact saved value | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/XtWinOpsTitleStackTests.cs`, `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`; State transfer: `tests/Ntilde.VT.Tests/StateTransfer/ParityCorpus.cs`; Mux: `tests/Ntilde.Mux.Tests/Headless/HeadlessTitleStackTests.cs`, `tests/Ntilde.Mux.Tests/Cli/MuxCliTests.cs`; App: `tests/Ntilde.App.Tests/Core/MainWindowAgentMetadataTests.cs`, `tests/Ntilde.App.Tests/Controls/MuxPaneTests.cs` | Parser+App+Mux | An empty pop is a no-op. Every title restore raises the callback, even with an unchanged value; an empty saved title clears to the host fallback label. Icon restores update VT state only (no host icon presentation). Same spelling and lifecycle limits as push. A manual Vim check has not been recorded. | | Kitty in-band resize / CSI 14 t / CSI 16 t | Mode 2048, `CSI 48 ; rows ; cols ; heightPx ; widthPx t`, `CSI 14 t` -> `CSI 4 ; heightPx ; widthPx t`, `CSI 16 t` -> `CSI 6 ; cellHeightPx ; cellWidthPx t` | Terminal-to-child reports are written to stdin only while the client has set mode 2048 (unsolicited reports would be garbage on its stdin); pixel sizes come from the live cell metrics, in the unit images are placed in: the host's device-pixel cell (logical cell times render scaling) when it set one, so on a 2x display a direct pane answers `CSI 16 t` with the same 17 x 37 a persistent pane's daemon does, and an image sized from it fills the cells it was drawn for. This is the only resize signal clients like terminal-browser get on Windows (no SIGWINCH, no console polling) | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/InBandResizeTests.cs`, `tests/Ntilde.VT.Tests/ImageCellPixelGeometryTests.cs`, `tests/Ntilde.App.Tests/Controls/DirectPaneImageCellGeometryTests.cs` | Parser+App | Other xterm window ops (iconify, resize-by-cells, ...) are ignored rather than answered; title push/pop (`CSI 22/23 t`) has its own rows. The `rows:1`/`rows:2` sub-parameter form of the start/end events is not emitted; plain numeric rows are, which in-band-resize clients parse identically. | | DSR / CPR / DECXCPR | CSI Ps n / CSI ? Ps n | Operating status (`CSI 5 n` -> `CSI 0 n`) and cursor position. The bare request answers `CSI r ; c R`; the DEC private request `CSI ? 6 n` (DECXCPR) answers in the private form `CSI ? r ; c R`, because a client that asked the private question parses for a private answer and would otherwise drop the reply or mistake it for an unsolicited CPR. Both frames honour DECOM: with origin mode set the row is reported relative to the top of the scrolling region, matching the frame CUP used to set it | βœ… Supported | Unit: `tests/Ntilde.App.Tests/AnsiParserHardeningTests.cs`, `tests/Ntilde.App.Tests/VttestScenarioTests.cs` | Parser | DEC's page parameter is omitted from the DECXCPR reply (`CSI ? r ; c R`, not `CSI ? r ; c ; p R`), as xterm omits it. The other private status reports - `?15` printer, `?25` UDK, `?26` keyboard, `?53` locator - are not implemented and stay silent rather than guessing. `CSI > Ps n` (xterm disable-key-modifiers) is not a status request: `CSI > 4 n` turns modifyOtherKeys off and other Ps are ignored | --- ## 6) SGR attributes & colors | Feature | CSI | Notes | Status | Evidence | Ownership | Known deviations | |---|---|---:|---|---|---| | Basic SGR (0,1,2,3,4,5,7,9,22,23,24,25,27,29) | | Bold/dim/italic/underline/blink/reverse/strike | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/SgrAttributeTests.cs`; render: `tests/Ntilde.App.Tests/RenderTests/TextBlinkRenderTests.cs`; clock/lifetime: `tests/Ntilde.App.Tests/Core/TerminalViewTextBlinkTests.cs`; owned pixels: `docs/qa/terminfo/2026-11-04-input-observations.md`; VTTEST: sgr scenario | Parser+Buffer+Renderer | Underline style/color tracked separately. SGR 5 text blink uses an App-owned monotonic 530 ms half-period; blink-off/hidden ink preserves backgrounds and animated rows cannot reuse stale pictures. Deterministic phase and visible-view timer tests cover the shared draw path; the retained macOS offscreen run records 25 passing text-pixel comparisons, including explicit-RGB bold under JetBrains Mono NL 14. The historical bold negative stays frozen; desktop cadence, other fonts and runtime platforms remain unqualified. | | 8/16 colors | 30–37/90–97, 40–47/100–107 | | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/ThemeSwitchLiveStyleStateTests.cs`; Replay: `tests/Ntilde.App.Tests/ReplayTests/RegressionTests.cs` | Parser+Buffer | Indexed SGR/theme migration has regression coverage; exhaustive foreground/background and bright/bold interactions across themes are not pixel-qualified. | | 256-color | 38;5;N / 48;5;N | | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RuntimeColorTests.cs`; Unit (render): `tests/Ntilde.App.Tests/RenderTests/RuntimeColorRenderTests.cs` | Parser+Buffer | Indexed cells, palette mutation and live-cache pixels are tested; the complete foreground/background index cross-product is not pixel-qualified. | | Truecolor | 38;2;r;g;b / 48;2;r;g;b | | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/SgrAttributeTests.cs`, `tests/Ntilde.VT.Tests/RuntimeColorTests.cs` | Parser+Buffer | Semicolon RGB cells and colon-form foreground parsing have regressions; complete foreground/background syntax and rendered RGB combinations are not comprehensively qualified. | | Underline styles and color | 4:0-4:5, 21, 58, 59 | xterm, kitty | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/UnderlineStyleTests.cs`; render: `tests/Ntilde.App.Tests/RenderTests/UnderlineStyleRenderTests.cs`; export: `tests/Ntilde.VT.Tests/Export/TerminalExporterUnderlineTests.cs` | Parser+Buffer+Renderer | Straight/double/curly/dotted/dashed, own RGB or palette color (#270). The ANSI exporters (mux text client, export) write them in colon form, in a sequence of their own after the main SGR | | Overline on | 53 | Independent of underline; 0 resets | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/OverlineTests.cs`; render: `tests/Ntilde.App.Tests/RenderTests/UnderlineStyleRenderTests.cs` | Parser+Buffer+Renderer | Sparse decoration metadata preserves the 12-byte cell layout. | | Overline off | 55 | Preserves underline style and color | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/OverlineTests.cs`; render: `tests/Ntilde.App.Tests/RenderTests/UnderlineStyleRenderTests.cs` | Parser+Buffer+Renderer | Clears only overline on subsequent writes. | --- ## 7) Tabs | Feature | Notes | Status | Evidence | Ownership | Known deviations | |---|---|---:|---|---|---| | HT (tab) movement | | βœ… Supported | Unit: `tests/Ntilde.App.Tests/TabSystemTests.cs` | Parser+Buffer | Custom tab stops are clipped on width shrink; columns exposed by width growth start with default 8-column tab stops | | Tab stops set/clear | ESC H, CSI g | βœ… Supported | Unit: `tests/Ntilde.App.Tests/TabSystemTests.cs` | Parser+Buffer | `CSI g` supports current-stop clear (`0`/default) and clear-all (`3`); other parameters are ignored | --- ## 8) OSC sequences | OSC | Purpose | Status | Evidence | Ownership | Known deviations | |---|---|---:|---|---|---| | OSC 0/2 | Set title | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/OscTitleTests.cs`, `tests/Ntilde.VT.Tests/StringSequenceCapTests.cs`; Unit (Mux): `tests/Ntilde.Mux.Tests/Cli/MuxCliTests.cs`; Unit (App): `tests/Ntilde.App.Tests/Core/MainWindowAgentMetadataTests.cs`, `tests/Ntilde.App.Tests/Core/TabTitleResolutionTests.cs`, `tests/Ntilde.App.Tests/Controls/PaneParserWiringTests.cs`, `tests/Ntilde.App.Tests/Shell/Mux/MuxCommandLsAllTests.cs` | VT+App+Mux | Exact bounded title payloads include empty/whitespace requests, surrounding spaces, Unicode and split input. Headless App metadata observes raw replacement plus existing profile/cwd fallback and title precedence. Mux text listings choose command fallback, preserving raw JSON and App text cleaning/caps. Titles are also VT state for the XTWINOPS title stack (`CSI 22/23 t`), whose pop raises the same callback; OSC 0 sets the icon name too. Host icon presentation, native window-manager title effects and an empty visible tab label remain unqualified; frozen owned title observations are unchanged. | | OSC 1 | Set icon name | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/XtWinOpsTitleStackTests.cs`, `tests/Ntilde.VT.Tests/OscTitleTests.cs` | Parser | Exact raw icon-name state for the XTWINOPS title stack (`CSI 22/23 ; 1 t`); OSC 0 sets it with the title. No title callback and no host icon presentation. | | OSC 7 | CWD reporting | βœ… Supported | Unit: `tests/Ntilde.App.Tests/OscUxTests.cs` | Parser+App | | | OSC 10/11 | Foreground/background color set/query | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RuntimeColorTests.cs`, `tests/Ntilde.App.Tests/OscUxTests.cs` | Parser+Buffer+Renderer | Numeric #RGB/#RRGGBB/#RRRGGGBBB/#RRRRGGGGBBBB, rgb: (1–4 hex digits/channel), finite rgbi: [0,1]; X11 named colors are not supported. Overrides affect existing default-colored cells, remain separate from saved host themes, and mask host color queries until reset. Query fallback remains fg C0C0C0 / bg 000000 when unset | | OSC 4/104 | Palette set/query and reset | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RuntimeColorTests.cs`, `tests/Ntilde.App.Tests/RenderTests/RuntimeColorRenderTests.cs` | Parser+Buffer+Renderer | All 256 indexed colors; numeric values only; multiple index/value pairs and per-index/all reset. At most 256 query replies per OSC; later mutations still apply after the reply cap. Explicit RGB cells stay unchanged; X11 names and special palette indices above 255 are not implemented | | OSC 12/17/19 | Cursor and selection color set/query | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/RuntimeColorTests.cs`, `tests/Ntilde.App.Tests/RenderTests/RuntimeColorRenderTests.cs` | Parser+Buffer+Renderer | Numeric values only; unset derived selection colors have no concrete query reply. Other dynamic slots and OSC 21 are not implemented | | OSC 110/111/112/117/119 | Reset runtime dynamic colors | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/RuntimeColorTests.cs` | Parser+Buffer | Empty payload resets the corresponding override to the current host default | | OSC 30001 | Save session colors | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/RuntimeColorTests.cs`, `tests/Ntilde.Mux.Tests/Headless/HeadlessColorSchemeTests.cs`, `tests/Ntilde.App.Tests/RenderTests/RuntimeColorRenderTests.cs` | Parser+Buffer+Renderer | Session-wide across screens; newest 16 frames retained, oldest discarded on overflow, empty pop is silent. Saves foreground/background/cursor/selection overrides and all 256 palette overrides; unset values continue following the receiving/current host theme. DECSTR preserves; RIS clears; complete mux/replay snapshots carry deep copies. Host theme metadata remains separate | | OSC 30101 | Restore session colors | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/RuntimeColorTests.cs`, `tests/Ntilde.Mux.Tests/Headless/HeadlessColorSchemeTests.cs`, `tests/Ntilde.App.Tests/RenderTests/RuntimeColorRenderTests.cs` | Parser+Buffer+Renderer | Session-wide across screens; newest 16 frames retained, oldest discarded on overflow, empty pop is silent. Saves foreground/background/cursor/selection overrides and all 256 palette overrides; unset values continue following the receiving/current host theme. DECSTR preserves; RIS clears; complete mux/replay snapshots carry deep copies. Host theme metadata remains separate | | OSC 66 | Text sizing | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/Osc66ProtocolTests.cs`, `tests/Ntilde.VT.Tests/Osc66GridTests.cs`, `tests/Ntilde.VT.Tests/Osc66StateTests.cs`, `tests/Ntilde.VT.Tests/Osc66ReplayTests.cs`, pixels: `tests/Ntilde.App.Tests/RenderTests/Osc66RenderTests.cs` | Parser+Buffer+Renderer | `s` 1–7, `w` 0–7, `n`/`d` 0–15 with nonzero denominator greater than numerator, `v`/`h` 0–2; safe Unicode payload up to 4096 UTF-8 bytes and metadata up to 256 characters. Explicit width packs the entire payload into one block; automatic width scales graphemes individually. Fractions and alignment affect ink only. Immutable rectangular side metadata preserves the 12-byte cell ABI; an additional 16 MiB metadata budget bounds retained blocks. Overwrite, erase, shifts and line edits preserve or erase whole blocks; lower rows are skipped by ordinary text. Complete state transfer, legacy replay checkpoints and render snapshots preserve geometry, including a retained lower fragment whose anchor aged out. Rows containing blocks are physical layout barriers on resize; ordinary lines outside them reflow. Blocks clipped by the new physical width or too tall for the new screen are discarded whole. GUI rendering clips the scaled/shaped text and decorations per row; cursor keeps its ordinary cell shape. ANSI snapshot export emits OSC 66, while an unnegotiated mux text client uses a bounded normal-size fallback. No claim of exact font-pixel parity with Kitty. | | OSC 5522 | MIME clipboard | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/KittyClipboardProtocolTests.cs`, `tests/Ntilde.App.Tests/Shell/KittyClipboardSessionTests.cs`; External (native): `docs/qa/clipboard-native.md`, `docs/qa/clipboard-primary.md` | Parser+App | Direct desktop/SSH panes: binary reads/writes, contiguous chunks and aliases; strict padded base64, 64 MiB decoded write/retained-read budget plus a 128 MiB native MIME/alias expansion budget, 128 MIME types, 8192-character packets and 2048-character metadata. Read data requires explicit consent; Allow once stays the default and named password requests can be approved for this terminal session. MIME listings do not. Writes share `AllowOsc52ClipboardWrite`. Linux local-X11 PRIMARY uses an independent App-owned XCB connection with bounded INCR transfers and location-specific consent; other systems return ENOSYS without falling back. Mux/headless hosts do not advertise. Session read grants match the full program-supplied name and password, retain only bounded hashes in memory (32 grants), and are revoked on terminal reset, replacement or disposal. Paste event passwords remain separate, content-bound and single-use within 15 seconds. No password grants are saved to disk. Host-assembled transfers/grants are not snapshot state; raw client OSC packets can remain in recorded PTY output or an incomplete parser accumulator. Native fixtures qualify isolated OS-peer byte exchange on macOS/Windows/Linux and local-X11 PRIMARY, not arbitrary editors, rich-text presentation, consent-dialog gestures or native Wayland PRIMARY. One native data snapshot serves each read. Replies wait for bounded queue admission (5 seconds per packet) and never claim DONE after a missing DATA chunk. Avalonia has no item-size preflight API: its first native value allocation is not covered by the managed conversion/retention budget. | | OSC 52 | Clipboard | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/OscUxTests.cs`, `tests/Ntilde.App.Tests/Controls/PaneParserWiringTests.cs` | Parser+App | Write-only (issue #268): valid base64 decodes and raises `AnsiParser.OnClipboardWrite`, capped at 1 MiB decoded (rejected on encoded length before decoding, re-checked after); invalid base64 dropped silently; a sequence with no payload separator at all is ignored rather than clearing the clipboard. Targets `c`/`p` both map to the system clipboard, other selection chars ignored. Query (`?`) always gets an empty-payload denial reply, never real clipboard contents β€” clipboard READ is unsupported by design (security). The echoed selection parameter is whitelisted to `c p q s 0-7` and length-capped first: responses go to the child process's stdin, so an unsanitized echo was a command-injection primitive (PR #280 review). Gated by `TerminalSettings.AllowOsc52ClipboardWrite` (default true, single global setting; per-profile/SSH-scoped opt-in is future work) | | OSC 8 | Hyperlinks | βœ… Supported | Unit: `tests/Ntilde.App.Tests/OscUxTests.cs` | Parser+Renderer+UI | Ctrl-click open path is app-level | | OSC 9 / 9;4 | Desktop notification / progress reporting | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/Osc9NotificationProgressTests.cs`, `tests/Ntilde.App.Tests/TerminalProgressPresentationTests.cs` | Parser+App | #271. `9;` flattens/caps (200 chars) and raises `OnDesktopNotification`; `9;4;[;]` clamps pct 0-100 and raises `OnProgressReported` (state 0-4, pct null when omitted; malformed payloads dropped; bare `4` without `;` is notification text). App side: focus-gated in-app toast (`Osc9NotificationsEnabled`, default false, same policy as long-command notifications), per-tab progress β€” thin bar in vertical headers, trailing suffix in horizontal titles β€” and Windows taskbar progress mirroring the selected tab (`Osc9ProgressReportingEnabled`, default true). Progress withdraws on state 0, pane exit, and pane close | | OSC 133 | Shell integration lifecycle | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/OscShellIntegrationTests.cs` | Parser+Command Assist | Supports A/B/C/D markers; C's command from base64, plain text or kitty's `cmdline_url` (fish 4); other semantic prompt extensions not audited | | OSC 1337 | iTerm2 inline images | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/DecoderIndependentImagePlacementTests.cs`, `tests/Ntilde.VT.Tests/ITerm2MultipartImageTests.cs`, `tests/Ntilde.VT.Tests/ImageHeaderProbeTests.cs`, `tests/Ntilde.Rendering.Tests/ImageHeaderProbeSkiaTests.cs`, `tests/Ntilde.Rendering.Tests/InlineImageEndToEndTests.cs`, `tests/Ntilde.VT.Tests/ITermAspectIntentTests.cs`, `tests/Ntilde.App.Tests/RenderTests/ITermAspectRenderTests.cs`, `tests/Ntilde.VT.Tests/ImageCellPixelGeometryTests.cs`, `tests/Ntilde.Mux.Tests/Headless/HeadlessImagePolicyTests.cs`, `tests/Ntilde.App.Tests/Controls/MuxPaneTests.cs`; Manual: `docs/qa/QA_GRAPHICS.md`, `docs/qa/terminfo/2026-11-06-persistent-images-check.md` | Parser+Renderer | Requires `inline=1`, in the single `File=args:payload` form or the multipart form (`MultipartFile=args`, `FilePart=` chunks, `FileEnd`) that `imgcat` sends by default; no `name=`/`size=` enforcement. The multipart payload is capped at the single-sequence limit and placed at the cursor at `FileEnd`; output between parts leaves the transfer pending, a new `MultipartFile` drops an unfinished one, RIS ends it, and complete snapshots carry it, so a client attaching between parts places the same image. Persistent sessions assemble the multipart form only under image policy 2; policy-1 sessions (and peers from before it) ignore it on every side. Image pixels and `Npx` sizes map to cells in the `CSI 16 t` unit (device pixels on a scaled display, not iTerm2's points). Default/explicit aspect preservation and explicit-zero stretch have real parser/Skia/offscreen pixel controls across wide/tall sources and DPI, including clipping, history and snapshot-pinned clear. Legacy/SIXEL policy and complete snapshots carry retained iTerm2 images where the parser retains image sources, and persistent sessions spawned with the image policy show them (#838). Native-pixel/default sizing, other OSC 1337 variants (file downloads, `inline=0`) and owned runtime image pixels remain unqualified. See `docs/qa/terminfo/iterm-aspect-fit.md`. Placement follows the image header (`ImageHeaderProbe`), never the decoder: an undecodable body with a valid header keeps its cells, and a format the probe cannot size is ignored (#838). | | OSC 1339 | Windows conpty tunnel | πŸ§ͺ Experimental | Manual | Parser+Win | | --- ## 9) Kitty graphics / APC | Feature | Notes | Status | Evidence | Ownership | Known deviations | |---|---|---:|---|---|---| | Kitty graphics protocol | APC / OSC forms | ⚠ Partial | Unit: `tests/Ntilde.VT.Tests/DecoderIndependentImagePlacementTests.cs`, `tests/Ntilde.VT.Tests/ImageHeaderProbeTests.cs`, `tests/Ntilde.Rendering.Tests/ImageHeaderProbeSkiaTests.cs`, `tests/Ntilde.App.Tests/GraphicsTests.cs`, `tests/Ntilde.App.Tests/AnsiParserHardeningTests.cs`, `tests/Ntilde.Rendering.Tests/InlineImageEndToEndTests.cs`, `tests/Ntilde.VT.Tests/KittyImageLifecycleTests.cs`, `tests/Ntilde.VT.Tests/KittyBase64PayloadTests.cs`, `tests/Ntilde.Rendering.Tests/KittyImageLifecyclePixelTests.cs`, `tests/Ntilde.App.Tests/RenderTests/KittyImageLifecycleRenderTests.cs`, `tests/Ntilde.VT.Tests/KittyAspectSnapshotTests.cs`, `tests/Ntilde.App.Tests/RenderTests/KittyAspectRenderTests.cs`, `tests/Ntilde.VT.Tests/ImageCellPixelGeometryTests.cs`, `tests/Ntilde.Mux.Tests/Headless/HeadlessImagePolicyTests.cs`, `tests/Ntilde.Mux.Tests/Server/ImagePolicyAdmissionTests.cs`, `tests/Ntilde.App.Tests/Controls/MuxPaneTests.cs`; Manual: `docs/qa/terminfo/2026-11-06-persistent-images-check.md` | Parser+Renderer | Native APC (non-tunneled) on Windows decodes behind `TerminalSettings.AllowNativeKittyGraphics` (default on) β€” historically probes got `ERR`/images skipped; modern ConPTY passes APC through intact (live-verified 2026-09). Payload formats: container (PNG/...) plus raw `f=24`/`f=32` sized by `s=`/`v=`, `o=z` zlib inflation, and `t=f` file transport read through the host-injected `ReadFileBytes` delegate (App-side reader confined to `%TEMP%`, 64 MB cap). `t=s` (shm) skips gracefully. Basic lifecycle separates transmit-only `a=t`, transmit/display `a=T` and retained display `a=p`. Unsigned ids, terminal-allocated `I` numbers, placement ids, quiet policy and basic `a/A/i/I/n/N` deletion have exact regression coverage, bounded storage and snapshot-safe handle retirement. Offscreen rendered pixels pin cursor-anchored aspect fit inside the existing cell envelope, including DPI snapping, snapshot clipping, history translation and cache-safe replacement; source dimensions remain distinct from reserved cells. Without `c=`/`r=`, image pixels and `w=`/`h=` map to cells in the `CSI 16 t` unit (device pixels on a scaled display, as in kitty), so a client that sized its image from that reply fills the cells it meant; rendered native-pixel fidelity stays unqualified. Legacy extra layout keys (including `U=1`) still do not qualify Unicode placeholders, animation or extended crop/layout/delete actions. Complete snapshots carry retained Kitty assets and placements where the parser retains image sources (`tests/Ntilde.VT.Tests/StateTransfer/ImageStateTransferTests.cs`). Persistent sessions spawned with the image policy (#838) carry Kitty graphics: the daemon answers queries and commands itself, with no decoder, as a direct pane does (native APC included, so #838's probe gets `OK`), every client places the same images, and reattach restores them; `t=f` stays unavailable there and sessions from earlier versions stay SIXEL-only. Live owned end-to-end image evidence remains unqualified. Valid leading-G base64 bytes are preserved across native/tunneled and chunked payloads; malformed extra prefixes remain rejected. See `docs/qa/terminfo/kitty-image-lifecycle.md` and `docs/qa/terminfo/kitty-payload-base64.md` and `docs/qa/terminfo/kitty-aspect-fit.md`. Placement, cell span and replies follow the image header (`ImageHeaderProbe`: PNG, JPEG, GIF, WebP; raw `s`/`v`), never the decoder: an undecodable body with a valid header keeps its cells and gets `OK`, and an unsizable format gets `EINVAL:Image decode failed` (#838). | | Placement, z-index, scrolling | Complex interactions | ⚠ Partial | Unit: `tests/Ntilde.Rendering.Tests/InlineImageEndToEndTests.cs`; Unit (App): `tests/Ntilde.App.Tests/RenderTests/RetiredImageDisposalTests.cs` | Buffer+Renderer | Cursor-anchored image replacement, alternate-screen ownership and bitmap retirement are tested. General placement IDs, z-index composition and Unicode-placeholder placement are not implemented; retirement waits for older in-flight render snapshots. | --- ## 10) SIXEL | Feature | Notes | Status | Evidence | Ownership | Known deviations | |---|---|---:|---|---|---| | SIXEL decode/render | Wired via `Ntilde.Rendering.SkiaImageDecoder` | βœ… Supported | Unit: `tests/Ntilde.Rendering.Tests/SixelDecoderTests.cs`, `tests/Ntilde.Rendering.Tests/SkiaImageDecoderTests.cs` | Decoder+Renderer | | | SIXEL scrolling behavior | DECSDM (`?80`), xterm polarity | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/SixelScrollingTests.cs`, `tests/Ntilde.Mux.Tests/Headless/HeadlessSixelScrollingTests.cs`, `tests/Ntilde.App.Tests/RenderTests/SixelScrollingRenderTests.cs` | Parser+Buffer+Renderer | Default scrolling reserves rows without writing text cells or wrapping wide images. Set `?80` places at page origin without moving the text cursor; reset restores scrolling. Regional and alternate-screen scrolling clip discarded pixels; right-edge clipping survives widening. Shared sessions receive initial pixel metrics at spawn and ordered updates afterward; GUI panes decode new SIXELs while the daemon measures geometry. Attach snapshots restore retained SIXEL source, placement and clipping for both screens without moving the cursor. Per pane/session, source retention is capped at 256 entries and 2 Mi UTF-16 characters; oldest or oversized sources are omitted when this budget is reached. Active captures share an additional bounded source-reference allowance of 256 entries and 2 Mi characters (at most 8 MiB retained payloads plus capture pins), preserving copied undecoded images through their final draw. Updated GUI/daemon required for shared layout parity. Existing below-image cursor and immediate trailing-newline policy is retained. Cursor-right extension `?8452` is not implemented; placement remains cell-aligned. | --- ## 11) Clipboard, selection, and hyperlinking | Feature | Notes | Status | Evidence | Ownership | Known deviations | |---|---|---:|---|---|---| | MIME clipboard paste notifications | DEC mode 5522 | βœ… Supported | Unit: `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`, `tests/Ntilde.VT.Tests/KittyClipboardProtocolTests.cs`, `tests/Ntilde.App.Tests/Shell/KittyClipboardSessionTests.cs` | Parser+App | Host opt-in, DECRQM/set/reset; MIME listings replace ordinary paste and bracketed paste. Expiring one-use token belongs to one TTY and the default clipboard. Unsupported primary selection returns ENOSYS. Headless/mux hosts report unrecognized. | | Selection model | UI behavior | ⚠ Partial | Unit (App): `tests/Ntilde.App.Tests/CommandAssist/TerminalViewKeyHandlingTests.cs`; Unit (render): `tests/Ntilde.App.Tests/RenderTests/ThemeRenderColorPixelTests.cs` | App | Programmatically established selections exercise copy shortcuts and selection pixels. These tests do not qualify pointer-driven word/line/drag selection or native clipboard delivery on every desktop. | | Copy on select | Configurable | ❌ Not supported | β€” | UI | | | Hyperlinks | OSC 8 | βœ… Supported | Unit: `tests/Ntilde.App.Tests/OscUxTests.cs` | Parser+UI | Ctrl-click open path is app-level | --- ## 12) Unicode width, graphemes, and font behavior | Feature | Notes | Status | Evidence | Ownership | Known deviations | |---|---|---:|---|---|---| | wcwidth-like width | CJK/emoji width | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/WidthTests.cs`, `tests/Ntilde.App.Tests/UnicodeWidthModelV2Tests.cs`; Replay: `tests/Ntilde.App.Tests/Fixtures/Replay/mixed_unicode.rec` | Buffer+Renderer | Deterministic 0/1/2-cell model for combining marks, emoji modifiers, ZWJ emoji, variation selectors, and regional-indicator flags. No Unicode-version pin or full UAX #11 conformance table is documented yet. | | Combining marks | Grapheme clusters | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/GraphemeAttachmentTests.cs`, `tests/Ntilde.App.Tests/SurrogateTests.cs`, `tests/Ntilde.App.Tests/UnicodeWidthModelV2Tests.cs`, `tests/Ntilde.App.Tests/ScrollAndWrapCorrectnessTests.cs` | Buffer+Renderer | Combining/variation attachment is covered for common terminal cases, including pending-wrap boundaries. Full extended-grapheme-cluster conformance is not claimed. | | ZWJ emoji sequences | | ⚠ Partial | Unit: `tests/Ntilde.App.Tests/GraphemeAttachmentTests.cs`, `tests/Ntilde.App.Tests/WidthTests.cs`, `tests/Ntilde.App.Tests/UnicodeWidthModelV2Tests.cs` | Buffer+Renderer | Chunked ZWJ families, emoji modifiers, VS15/VS16, and chunked regional-indicator flag pairs are covered. Remaining gaps: cursor-addressing CSI remains cell-oriented, and width-changing selectors that arrive after a base glyph already placed at the last column are not guaranteed to retroactively reflow. | --- ## 13) Verification inventory ### External suites - VTTEST capture adapter: `tests/Ntilde.ExternalSuites/` Recordings: `tests/Replays/Vttest/*.rec` ### Replay suites (goldens) - Add all `.rec/.snap` pairs under: `tests/Ntilde.App.Tests/Fixtures/Replay/` ### Unit tests - Parser/buffer: `tests/Ntilde.VT.Tests/` and legacy `tests/Ntilde.App.Tests/` - Input/UI and pixels: `tests/Ntilde.App.Tests/Input/`, `tests/Ntilde.App.Tests/RenderTests/` - Decoder/rendering: `tests/Ntilde.Rendering.Tests/` - Native clipboard qualification: `tests/Ntilde.Clipboard.NativeHost/`, `docs/qa/clipboard-native.md`, `docs/qa/clipboard-primary.md` ### Collector observations - The historical owned terminfo.dev run remains **78 supported, 0 unsupported, 177 inconclusive**. `docs/qa/terminfo/2026-11-02-inconclusive-triage.md` classifies the inconclusive observations without regrading them; suiteComplete remains false and identity remains unverified; they are neither proof of implementation nor a list of missing features. Its pinned source revision describes that historical audit, not the current matrix. ### Generated report and guards - After any matrix edit, regenerate `src/Ntilde.App/Resources/vt-conformance-report.json`: its digest covers the entire Markdown file. Use the repository build wrapper to run `src/Ntilde.Conformance` with `--validate --report src/Ntilde.App/Resources/vt-conformance-report.json`, then `--validate --check-report` for the same path. - `tests/Ntilde.Platform.Tests/Conformance/VtConformanceToolTests.cs` validates parsing/evidence; `tests/Ntilde.App.Tests/VtReportCliTests.cs` checks the shipped artifact against fresh output. Catalogue-backed names/statuses/evidence must also match `src/Ntilde.VtContract/vt-capabilities.json` and `tests/Ntilde.VT.Tests/VtCapabilityContractTests.cs`. These guards do not prove behavioral conformance for every row. - Do not regenerate historical collector receipts or triage provenance merely because this current matrix changes. --- ## 14) Maintenance rules (non-negotiable) 1. **No β€œβœ… Supported” without evidence.** 2. Evidence must be a stable path in the repo. 3. If behavior differs from xterm/wezterm, document it under β€œKnown deviations”. 4. When you fix a deviation, update the row and add/adjust tests. 5. Every new OSC/APC/CSI feature must add a row here. --- ## 15) Roadmap linkage When a feature is planned but not implemented, add a row with: - Status: ❌ Not supported - Evidence: β€œPlanned” - Link: roadmap item / issue number Example: - `❌ Not supported` β†’ β€œRoadmap: M4 Font & Text Excellence”