chore(parity): close out the Ink-parity loop — reconcile ledger, record stop decision (#66)

13 mediums fixed+merged across sweeps 1-10 (G32, G33, G39, G44, G45, G46,
G52, G58, G59, G63, G64, G68) plus the ~22 earlier gaps. Maintainer paused
the open-ended loop at diminishing returns: latest sweeps surface byte-level/
edge-case divergences (G68 ANSI order is visually identical; G67 refuted as an
unavoidable Vue-vs-React semantic). Open mediums G69/G70 and the LOW tail are
recorded but deferred by decision.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Yunfei He
2026-05-30 16:56:49 +08:00
committed by GitHub
parent 6b09a55a6d
commit 5b681aa677
+3 -1
View File
@@ -21,6 +21,8 @@
Refuted (NOT gaps): ~~`exit()` second-wins — vue-tui is already guarded~~ **REVERSED by sweep-4: it IS last-wins vs Ink first-wins → now tracked as G33**; kitty key-release printable-text suppression — Ink behaves the same.
> **🛑 LOOP STOPPED by maintainer (2026-05-30) — diminishing returns.** 13 mediums fixed+merged (G32, G33, G39, G44, G45, G46, G52, G58, G59, G63, G64, G68 — plus the ~22 earlier gaps G01–G05, G08–G23). The high-value, user-visible parity is done. The maintainer paused the open-ended "run until a sweep is medium-clean" loop after observing that the latest sweeps surface byte-level / edge-case divergences (e.g. G68 ANSI nesting order is visually identical; G67 was refuted as an unavoidable Vue-vs-React semantic; G64/G67 were corrections of vue-tui's own earlier fixes), and that a byte-level audit between a Vue and a React runtime may never return "clean." **Open mediums deferred by decision (NOT done): G69 (Static immediate render), G70 (non-interactive SR hard-wrap).** LOW tail (G25–G31, G34–G43, G47–G51, G53–G57, G60–G62, G65–G66, G71–G73 + the SR squashTextContent transform-drop note) remains recorded and unfixed by maintainer decision. To resume: pick up G69/G70 (sketches in their rows) or re-triage under a stricter "observable user-facing impact only" bar.
## Decisions log
Non-obvious calls made while fixing gaps, recorded for review in the final report.
@@ -116,7 +118,7 @@ Non-obvious calls made while fixing gaps, recorded for review in the final repor
| G65 | app-exit-instances-animation-sr | SR unchanged-frame skip compares WRAPPED output, not Ink's UNWRAPPED linearization — diverges on resize with identical content — sweep-9 LOW | P3 | todo | — | — |
| G66 | app-exit-instances-animation-sr | app.waitUntilRenderFlush() lacks Ink's unmount/unmounting short-circuit to awaitExit() — sweep-9 LOW | P3 | todo | — | — |
| G67 | box-layout-border | ~~flexDirection/flexWrap reset on dynamic removal vs Ink keep-stale~~ **REFUTED (codex, sweep-10):** Ink's `<Box>` injects row/nowrap defaults BEFORE the style spread, so OMITTING the prop → row (common case, which vue-tui's G19 reset already matches); Ink keeps stale ONLY for explicit `flexDirection={undefined}` (rare), which Vue CANNOT distinguish from omission. Keeping stale would break the common case. UNAVOIDABLE Vue-vs-React semantic — KEEP G19's reset-to-default. **FLAG for maintainer.** | P1 | refuted | — | — |
| G68 | text-wrap-transform | Text style ANSI nesting/order: vue-tui builds ONE chained chalk (color→bg→dim→bold…); Ink applies each style as a SEPARATE nested wrap in a different order (dim→color→bg→bold…) — bytes diverge for multi-style Text (MEDIUM, sweep-10) | P1 | pr-open | `fix/parity-text-ansi-order` | #65 |
| G68 | text-wrap-transform | Text style ANSI nesting/order: vue-tui builds ONE chained chalk (color→bg→dim→bold…); Ink applies each style as a SEPARATE nested wrap in a different order (dim→color→bg→bold…) — bytes diverge for multi-style Text (MEDIUM, sweep-10) | P1 | merged | `fix/parity-text-ansi-order` | #65 |
| G69 | render-lifecycle-reconciler | `<Static>` output is throttled, not immediate; Ink has an isStaticDirty/onImmediateRender escape hatch that paints freshly-appended Static children synchronously at commit before they can be sliced out (MEDIUM, sweep-10) | P1 | todo | — | — |
| G70 | app-exit-instances-animation-sr | Non-interactive (and debug) screen-reader output is hard-wrapped at terminal width; Ink keeps it unwrapped (wrap only in the interactive SR branch) — diverges from vue-tui's own renderToString (MEDIUM, sweep-10) | P1 | todo | — | — |
| G71 | focus | useFocusManager().activeId is null (not undefined) when nothing is focused — sweep-10 LOW | P3 | todo | — | — |