Beagle SCM
JSQUE-021: resident session exhausts file descriptors — verb load dies mid-session
Context
2026-07-18 live, a long-running jab work pager session (the day's WORK-002/WORK-003 clicking): a spell died with err: the verb 'get' failed to load from /home/gritzko/src/journal/work/WORK-003/jsrc/verbs/get/get.js: Error: No — gritzko confirms the full underlying error is "No file descriptors available". The resident process leaked fds until require itself could not open a .js file.
- The loader message is the WORK-003 plain-words registry fix doing its job; the status line TRUNCATED the cause at "Error: No" — the full text must survive to the user.
- Every click/spell in the resident loop opens stores (packs), wtlogs, dirs; nothing visibly closes them per drive — JSQUE-002's pay-once process turns any per-drive open into a leak.
- The native side already has known fd leaks the session would amplify: ABC-013 (FSW watches never removed, kqueue leaks an fd per watch), ABC-014 (PACKClose leaks fd + PAGE mapping + index mmap on error paths).
- MEASURED (orchestrator pty probe, 2026-07-18,
jab work from work/WORK-003, j/k navigation only): 212 fds at launch, then +1 fd per keypress — 90 keys → 290 fds, ALL 80 new fds are re-opens of the LAUNCH tree's .be (/proc/<pid>/fd readlink); the work view re-opens the wtlog on every redraw and never closes it.
- Baseline hog: the 212 launch fds are
200 `/.be/beagle-ext/*.keeper` pack files ALL held open simultaneously by the store reader — the open set scales with store history, so long sessions start near the ulimit before the per-keypress leak even begins.
Goals
A resident session's fd count stays bounded across arbitrarily many spells/clicks; verb loads never die of fd exhaustion.
- Red-first repro: drive a resident session (pty or direct loop harness) through N spells, watch
/proc/self/fd count — today it grows per drive; after the fix it plateaus.
- Find the actual leak site(s): per-drive store/pack/wtlog/dir opens without close, FSW watches, pty/child fds — measure, don't guess.
- Error display: the status line must carry the full cause line, not a width-truncated "Error: No".
Constraints
- RULING (gritzko, chat 2026-07-18): "why does jk open a file? should just rerender a buffer" — j/k navigation must RERENDER from the in-memory buffer; no file opens, no data-pass re-run per keypress. Data refresh happens on explicit events (launch, spell completion), not on cursor motion.
- Native-side fixes belong to ABC-013/ABC-014 — if the leak is wholly native, this ticket wires the JS-side close discipline and links the rest there; don't fork into libabc without need.
- Rulings in force: errors in plain words; no unrelated edits; the JSQUE-020 queue-drop rework may collide — check it before restructuring the loop.
WIP
Design decisions
TODOs
Blockers and bummers
Outcome