Bundles the in-flight work into the first tagged release. See CHANGELOG.md `[0.0.1] - 2026-05-14` for the full per-change list. Highlights: - Sidebar / chrome stability: clamp absolute cursor positioning and printable bytes to the viewport so long-running TUIs (claude, codex) can't spray into the right rail; bound tab bar's row clear to the viewport width so the rail isn't wiped on every tab redraw; flag scroll escapes (RI/IND/NEL/SU/SD/IL/DL) and clamp `CSI 0/1/2 J`/`K` to viewport columns. - Palette: "Spawn process…" form, macros (`sw `, `k `, `sp `), kill entries mark the focused tab, dead agents drop out of the switch list. - Sidebar: split into Processes (session-wide) + Agent Tree (per-active-agent) sections; relaunch indicator; Ctrl+W/S walks the combined list, Ctrl+A/D steps tabs. - MCP: protocol handshake (`initialize`, `tools/list`, `tools/call`, `ping`), `mcp_injection.kind = cli_override / config_env` so codex and opencode pick up the server with no file writes, `lifecycle` help topic and tool-description cleanup-duty pointers. - Lifecycle: orchestrator-spawned children cascade-killed when the parent dies; orchestrator-injected prompts end with CR + delayed Enter so claude submits cleanly.
This commit is contained in:
@@ -16,12 +16,13 @@
|
||||
},
|
||||
{ "type": "wait_text", "contains": "RIBURST READY", "timeout_ms": 5000 },
|
||||
{ "type": "wait_stable", "timeout_ms": 2000 },
|
||||
{ "type": "assert_contains", "contains": "Session tree" },
|
||||
{ "type": "assert_contains", "contains": "Processes" },
|
||||
{ "type": "assert_contains", "contains": "Agent Tree" },
|
||||
{ "type": "assert_contains", "contains": "● riburst" },
|
||||
{ "type": "assert_contains", "contains": "Scratchpads" },
|
||||
{
|
||||
"type": "assert_regex",
|
||||
"regex": "(?s)Session tree[^\\n]*\\n[^─\\n]*─[─]+[^\\n]*\\n[^●\\n]*● riburst",
|
||||
"regex": "(?s)Processes[^\\n]*\\n[^─\\n]*─[─]+[^\\n]*\\n[^●\\n]*● riburst",
|
||||
"timeout_ms": 2000
|
||||
}
|
||||
]
|
||||
|
||||
27
internal/harness/scenarios/sidebar_survives_wide_writes.json
Normal file
27
internal/harness/scenarios/sidebar_survives_wide_writes.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "sidebar_survives_wide_writes",
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"scripts": [
|
||||
{
|
||||
"name": "widewrite",
|
||||
"body": "#!/bin/sh\n# Reproduces a long-running TUI whose internal column state drifted\n# past the viewport width (the symptom seen in fucked-up-terminal.txt:\n# claude's input box drew a horizontal divider all the way to the host\n# edge, overwriting the sidebar). The widths here are: cols=120,\n# sidebarCols=28, so the viewport is 91 cols wide and the sidebar\n# border lives at col 92. Anything the child writes at col >= 92 lands\n# in the sidebar unless patterm defensively clamps it.\n#\n# After WIDE READY, emit 12 throw-away chunks to exhaust the focus\n# snapshot replay budget (8 chunks) so the wide-write clobber goes\n# through the *incremental* viewport renderer path. That's the path\n# that long-running sessions stay on — without clamping it can spray\n# bytes into the sidebar.\nprintf 'WIDE READY\\n'\ni=0\nwhile [ $i -lt 12 ]; do\n printf 'tick %d\\n' \"$i\"\n i=$((i + 1))\n sleep 0.05\ndone\nprintf 'PRE-CLOBBER\\n'\nsleep 0.2\nprintf '\\033[5;95HCLOBBER-CUP'\nsleep 0.1\nprintf '\\033[7;100HCLOBBER-CUP2'\nsleep 0.1\nprintf '\\033[9;1H'\nprintf '\\033[110GCLOBBER-CHA'\nprintf '\\nDONE\\n'\nsleep 5\n"
|
||||
}
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"type": "mcp_call",
|
||||
"method": "spawn_process",
|
||||
"params": { "kind": "command", "argv": ["widewrite"], "name": "widewrite" }
|
||||
},
|
||||
{ "type": "wait_text", "contains": "WIDE READY", "timeout_ms": 5000 },
|
||||
{ "type": "wait_text", "contains": "DONE", "timeout_ms": 5000 },
|
||||
{ "type": "wait_stable", "timeout_ms": 2000 },
|
||||
{ "type": "assert_contains", "contains": "Agent Tree" },
|
||||
{ "type": "assert_contains", "contains": "Processes" },
|
||||
{ "type": "assert_contains", "contains": "Scratchpads" },
|
||||
{ "type": "assert_contains", "contains": "● widewrite" },
|
||||
{ "type": "assert_not_contains", "contains": "CLOBBER-CUP" },
|
||||
{ "type": "assert_not_contains", "contains": "CLOBBER-CHA" }
|
||||
]
|
||||
}
|
||||
31
internal/harness/scenarios/spawn_process_form.json
Normal file
31
internal/harness/scenarios/spawn_process_form.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "spawn_process_form",
|
||||
"cols": 100,
|
||||
"rows": 30,
|
||||
"scripts": [
|
||||
{
|
||||
"name": "formfixture",
|
||||
"body": "#!/bin/sh\necho FORM-READY\nsleep 5\n"
|
||||
}
|
||||
],
|
||||
"steps": [
|
||||
{ "type": "wait_stable", "timeout_ms": 3000 },
|
||||
{ "type": "send_chord", "chord": "ctrl-k" },
|
||||
{ "type": "send_text", "text": "Spawn process" },
|
||||
{ "type": "send_chord", "chord": "enter" },
|
||||
{ "type": "wait_text", "contains": "Spawn process", "timeout_ms": 3000 },
|
||||
{ "type": "send_text", "text": "formfixture" },
|
||||
{ "type": "send_chord", "chord": "tab" },
|
||||
{ "type": "send_chord", "chord": "space" },
|
||||
{ "type": "send_chord", "chord": "enter" },
|
||||
{ "type": "wait_text", "contains": "FORM-READY", "timeout_ms": 5000 },
|
||||
{
|
||||
"type": "assert_mcp",
|
||||
"method": "list_processes",
|
||||
"path": "0.status",
|
||||
"equals": "running"
|
||||
},
|
||||
{ "type": "assert_contains", "contains": "Processes" },
|
||||
{ "type": "assert_contains", "contains": "⟳" }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user