Update project configuration and documentation
ober
59e6405c4c9508559ab0f40ea7f0e588bb527e1a
new file mode 100644 --- /dev/null +++ b/.jpkgignore @@ -0,0 +1,11 @@ +vendor/ +build/ +dist/ +.jpkg/ +target/ +node_modules/ +__pycache__/ +*.so +*.o +*.a +.git/ --- a/AGENTS.md +++ b/AGENTS.md @@ -371,7 +371,6 @@ improved versions of the above. When working in a Jerboa project, **ONLY modify files in the current repo** unless the user explicitly names another path. Common sibling repos that exist but must NOT be touched without explicit instruction: -- `~/mine/jerboa-emacs` — **NEVER touch**. Another model owns it. - `~/mine/jerboa-mcp` — Legacy node MCP, superseded. The active MCP server now lives in THIS repo at `mcp/` + `data/`. Don't modify the legacy repo unless told. - `~/mine/jerboa-shell` — Only modify when user explicitly says to work there. - `~/mine/gerbil-mcp` — **NEVER touch**. Deprecated. --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # jerbuild bundles Chez Scheme and the Jerboa stdlib. When a sibling Jerboa # checkout is present, use its patched jerboa-native-rs crate for the encrypted # log backend; otherwise fall back to the native crate bundled with jerbuild. -JERBOA_VERSION ?= v0.2.3 +JERBOA_VERSION ?= v0.2.8 JERBOA_TOOL_DIR ?= $(CURDIR)/.jerboa/bin JERBUILD ?= $(shell if [ -x ./jerbuild ] && [ -x ./jerboa ]; then \ printf '%s\n' ./jerbuild; \ new file mode 100644 --- /dev/null +++ b/handoff-corruption.md @@ -0,0 +1,133 @@ +# Handoff: Fix TUI display corruption (stale cells, forced C-l) + +## Task + +Make the jerboa-signal TUI self-heal from terminal display corruption instead of +requiring the user to press `C-l` manually. + +## Symptom (evidence: ~/signal.png) + +The screenshot shows the same 3-column layout (contacts / thread / details) +repeated **three times, stacked vertically**, each copy scrolled to a different +point. That is the signature of stale cells from previous frames remaining on +the physical terminal: the TUI thinks the screen is already correct and its +incremental repaint never touches those cells again. Pressing `C-l` (force +full repaint) clears it — the user reports being "forced to always hit C-l". + +## Environment / stack + +Three independent incremental-diff renderers are stacked: + +``` +Emacs vterm → tmux → jerboa-signal (termbox2, vendored in vendor/termbox2) +``` + +vterm and tmux are **external to this repo — do not attempt to fix them**. +(A `tmux-problem.md` exists in the sibling `jerboa-term` repo as background; +do not modify that repo.) The fix must make jerboa-signal stop depending on +its cell-diff staying in sync with the real terminal. + +## Root cause (verified in code) + +- `draw!` (`signal/tui/main.ss:2567-2609`) fully repaints termbox2's **back + buffer** every frame: `tb-set-clear-attrs!` + `tb-clear!` + full-screen + `fill-rect!` + all panels. The Scheme side is NOT the problem — every frame + is a complete frame. +- `tb-present!` then emits terminal escape sequences **only for cells that + differ** between front and back buffers (termbox2's cell-diff optimization). +- When tmux/vterm drops or mangles one escape sequence (cursor addressing, + erase, scroll region), the physical terminal diverges from termbox2's front + buffer. From then on the diff considers the corrupted cells "already + correct" and never rewrites them. A mangled cursor-home also explains the + "same frame repeated at a vertical offset" pattern in the screenshot. +- The only recovery is `tb_invalidate()`, which marks every cell dirty and + forces a full non-differential repaint on the next `tb_present()`. +- `tb-invalidate!` is currently called from exactly **two** sites (verified by + grep — there are no others): + 1. Resize event: `signal/tui/main.ss:1384-1387` (also updates w/h in state) + 2. `C-l` key: `signal/tui/main.ss:1390-1392` (plus sets "Screen redrawn." + status) +- The event loop (`signal/tui/main.ss:397-408`) redraws only when dirty and + otherwise blocks in `tb-peek-event` with `*idle-poll-ms* = 250` + (`signal/tui/main.ss:394`). Nothing ever triggers a full repaint on a timer. + +So: corruption between resize events persists forever, until the user hits C-l. + +## Verified code map + +| What | Where | +|---|---| +| Event loop (draw-when-dirty, 250ms idle poll) | `signal/tui/main.ss:394-408` | +| `handle-event!` — resize → `tb-invalidate!` | `signal/tui/main.ss:1382-1387` | +| `handle-event!` — `C-l` → `tb-invalidate!` | `signal/tui/main.ss:1390-1392` | +| `draw!` — full back-buffer repaint per frame | `signal/tui/main.ss:2567-2609` | +| FFI wrappers `tb-clear!` / `tb-present!` / `tb-invalidate!` | `signal/tui/ffi.ss:222-224` | +| C shim → real termbox2 `tb_invalidate()` | `signal/tui/signal_tui_shim.c:61` | + +## Constraints — do NOT + +- Do NOT remove or weaken the existing `C-l` handler or the resize-time + `tb-invalidate!`; they stay as-is. +- Do NOT modify the C shim (`signal_tui_shim.c`) or anything under `vendor/`. +- Do NOT touch the logdb/crypto code or any tests unrelated to the TUI loop. +- Do NOT modify any sibling repository (`jerboa-term`, `jerboa-shell`, etc.). +- Keep the change inside `signal/tui/main.ss` if possible. + +## Recommended fix: periodic self-healing invalidate + +Call `tb-invalidate!` on a wall-clock interval (e.g. every ~10 seconds) so any +desync self-heals without user input. `C-l` remains as the instant manual +escape hatch. + +Sketch, in `event-loop` (`signal/tui/main.ss:397-408`): + +```scheme +(def *invalidate-interval-s* 10) ;; force a full repaint this often + +(def (event-loop state actor) + (let loop ([dirty? #t] [last-invalidate (real-time)]) + (let* ([actor-dirty? (handle-actor-events! state actor)] + [send-dirty? (handle-send-events! state)] + [timer-dirty? (expire-typing-indicators! state)] + [now (real-time)] + [invalidate? (>= (- now last-invalidate) *invalidate-interval-s*)]) + (when (or dirty? actor-dirty? send-dirty? timer-dirty?) + (when invalidate? (tb-invalidate!)) ;; <-- the fix + (draw! state) + (tb-present!)) + (let* ([ev (tb-peek-event *idle-poll-ms*)] + [event-dirty? (and ev (begin (handle-event! state actor ev) #t))] + [last-invalidate (if invalidate? now last-invalidate)]) + (unless (tui-state-quit? state) + (loop event-dirty? last-invalidate)))))) +``` + +Notes for the implementer: + +- `(real-time)` is already used in this file (see + `expire-typing-indicators!`, line 411) — no new imports needed. +- 10 s is a suggestion; anything in the 5–30 s range is fine. The cost of a + full repaint of a small TUI frame is negligible. +- Optional cheap add-on (not required): also call `tb-invalidate!` on mode + transitions (chat ↔ new-message/search/attach/captcha/theme), since those + repaint the whole screen anyway. Only do this if it stays a 1–2 line change. + +## Acceptance criteria + +1. `make binary` succeeds (this host is macOS — do NOT run `make docker-build`; + that is the Linux pipeline). +2. `make test` passes in full. +3. Manual check: run the TUI inside tmux (ideally under Emacs vterm), resize + the pane repeatedly, scroll, and switch modes. Any corruption that appears + must clear by itself within the invalidate interval, without pressing C-l. +4. `C-l` still forces an immediate repaint and shows "Screen redrawn.". +5. Grep confirms `tb-invalidate!` call sites are now: resize, C-l, and the + new periodic one — nothing else removed. + +## If the edit "has no effect" + +Delete stale compiled artifacts and rebuild: + +```bash +find lib -name "*.so" -delete && find lib -name "*.wpo" -delete && make build +``` new file mode 100644 --- /dev/null +++ b/signal.md @@ -0,0 +1,74 @@ +╭─── Claude Code v2.1.218 ─────────────────────────────────────────────────────╮ +│ │ Tips for getting │ +│ Welcome back Jaime! │ started │ +│ │ Run /init to create a … │ +│ ▐▛███▜▌ │ ─────────────────────── │ +│ ▝▜█████▛▘ │ What's new │ +│ ▘▘ ▝▝ │ Changed `/code-review`… │ +│ Sonnet 5 · Claude Pro · jaimef@linbsd.org's │ Added screen-reader an… │ +│ Organization │ Fixed Windows paths wi… │ +│ ~/mine/jerboa-signal │ /release-notes for more │ +╰──────────────────────────────────────────────────────────────────────────────╯ + + +❯ Please see ~/signal.md, in the jsh mux, it renders with corruption, and I'm + forced to always hit C-l + + Searched for 1 pattern, read 1 file, listed 1 directory (ctrl+o to expand) + +⏺ No signal.md exists anywhere near ~. There is a ~/signal.png modified today + though — likely what you meant. Let me look at it. + + Read 1 file (ctrl+o to expand) + +⏺ No signal.png corruption is evident there (looks like a clean render) — that's + probably not what you meant either. There's a staged kimi3.md in this repo + that matches the terminal status bar in that screenshot ("6:get kimi3"). Let + me check that instead. + + Read 1 file (ctrl+o to expand) + +⏺ That matches — findings A1/A2 in this doc are exactly the rendering bugs + (scroll arrows overwriting rows), which explains the corruption forcing C-l. + Let me quickly confirm there's no separate signal.md elsewhere before acting + on this. + + Listed 1 directory (ctrl+o to expand) + +⏺ No signal.md anywhere — kimi3.md findings A1/A2 are the rendering bugs causing + the corruption you're describing. Let me look at the actual code and fix + them. + + Read 1 file (ctrl+o to expand) + ⎿ Interrupted · What should Claude do instead? + +❯ Please see ~/signal.png, in the jsh mux, it renders with corruption, and I'm + forced to always hit C-l + +⏺ That screenshot itself is the evidence — reading it again with + rendering-corruption specifically in mind: it shows the same 3-column layout + (contacts / thread / details) repeated three times stacked vertically, with + the same Larissa conversation just scrolled to different points each time. + That's the signature of a TUI that doesn't fully clear the screen on + resize/redraw — leftover panels from a previous (taller) frame stay on screen + underneath the new one, which is exactly what C-l (force full clear+redraw) + papers over. Let me check how the mux/resize handling works in the code. + + Searched for 7 patterns, read 1 file, listed 2 directories (ctrl+o to expand) + +⏺ There's a tmux-problem.md right in jerboa-term — that's very likely + documenting this exact issue. Let me read it. + + Read 1 file (ctrl+o to expand) + +⏺ This document (about tmux+vterm+TUI issues generally) confirms the stack: + Emacs vterm → tmux → jerboa-signal's termbox2 UI. That's three independent + incremental-diff renderers stacked (termbox2's cell-diff, tmux's pane-diff, + libvterm's grid), and any single dropped/misparsed escape sequence in that + chain leaves stale cells that only a full non-differential repaint clears — + which is exactly what C-l does today (tb-invalidate!, confirmed at + signal/tui/main.ss:1390-1392). I can't fix vterm or tmux from this repo, but I + can make jerboa-signal stop depending on the diff staying in sync. Let me + check the render loop. + + Searched for 2 patterns, read 1 file (ctrl+o to expand) ⎿ Interrupted · What should Claude do instead? \ No newline at end of file