v button — run the project's default build+test as a bg jobLocal one-keystroke CI: the pager's v button detects the project's
conventional build+test entry point ("default stuff": ./ci.sh,
./scripts/ci.sh, cmake+ninja+ctest, ./configure && make && make test,
and the per-language equivalents), runs it as a detached background job in
the worktree, and puts the verdict into the per-repo cache. Separate from
the qemu runner fleet (CI-001/CI-003) — this is the local leg; the
fleet can later reuse the same detector script inside its VMs.
ci MUST BE A VIEW — a views/-tree
read-only verb like status/log/todo (Views is the open category;
only the mutation basis is closed). The ∞ click mints the normal
view spell, the pager pushes the ci view like any other: it starts
or attaches to the run and renders the log tail (TODOs 7-9). The
_pagerSpell reserved-word interception built on 2026-08-04 is
WRONG and must be removed — no pager-local dispatch, no special
case. The extension-script entry (jab ci.js) stays for the CLI
and the CI-001 fleet.v in the pager kicks off the detected default build+test for the
current worktree as a background job; the pager stays interactive.[ ∞] (2-cell, frame conventions of TODO-005),
tone #5883a7 (a new view/theme.js BTN slot; the ci name is taken
by the commit ✓ — pick a fresh name, e.g. run).r refreshes (re-reads the file,
stays at the end while the job appends). The log already lands in
a file, so the view is a tail over it, no second capture path../ci.sh./scripts/ci.shCMakeLists.txt → cmake + ninja + ctest -j16./configure && make && make testMakefile → make && make testCargo.toml → cargo test; go.mod → go test ./...;
package.json → npm test; pyproject.toml/setup.py → pytestbe/: probe the ladder, print the chosen
command line; exit distinct on "nothing detected".v keybinding invoking the runner for the current context
worktree; re-press while running reports "in progress".[ ∞] #5883a7 at the FILE
panel's end, the post ✓ moved to the COMMIT panel's end; wire the
click to the runner, the face to the verdict memo.r
re-reads and re-tails.── PASS rc=0 ── /
── FAIL rc=N ── in the verdict tone, ⋯ running mid-run;
render-only, never written to the log file. The hunk BANNER
stays the command line as-is._pagerSpell reserved-word layer; the ∞ mints the
plain view spell and the pager pushes the ci view normally.#e1351e fail, #1fe033 ok. The status is
REMEMBERED per wt in a {wt: status} map the runner writes at
settle (beside the logs, outside every watched tree, so no
self-staling rev bump); the board reads the map cold — no
watcher needed to show the last verdict.r2, the ci VIEW (supersedes r1's reserved-word wiring):
views/ci/ci.js — a views/-tree read-only verb: bare ci (context tree), ci //WT (the board's form), ci <path>; all ride shared/ci.js's ONE runner. The view forks-or-ATTACHES (CI.ensure — a fresh verdict replays, r re-runs), tails the log file (4 KB cap, positioned at the END), renders the ── PASS rc=0 ──/── FAIL rc=N ── footer in the verdict tones, ⋯ running mid-run; the banner stays the command line.shared/viewmark.js — the GENERIC per-view ~1 s tick (tick/end/take): only the topmost marked view re-runs, scroll pins to the last page until a scroll key, unmarked views never wake. loop.js also records the initial view's launch invocation (r/tick used to re-derive a spell from the banner — fatal for ci, whose banner is a command)._pagerSpell layer is DELETED (both call sites); the v key, status-bar badge, board layout, detection ladder, spawnFds capture and the rev-keyed memo survive.⋯ running forever (log without .rc); $TMP/be-ci has no reaper; the key wait is the pre-existing 100 ms VTIME spin; Enter on the ci banner row derives nonsense (refresh fixed, that path not); BTN_FACE_ASCII/PANELW dead wiring.CI-004: ci as a view — log tail, 1s view tick, per-wt verdict map