This document is the stateless source of truth for the ShellQL TUI build-out. Start every new session by reading this file. Update task status here when work completes.
Repos:
- TUI library:
fissible/shellframe(https://github.com/fissible/shellframe) - App:
fissible/shellql(local at~/lib/fissible/shellql, not yet on GitHub)
| Symbol | Size | Time estimate |
|---|---|---|
| XS | Tiny | < 1 hour |
| S | Small | 1–2 hours |
| M | Medium | ~half day |
| L | Large | ~1 day |
| XL | X-Large | 2–3 days |
Define conventions before building anything. These are docs/interfaces, not code.
| # | Task | Effort | GH Issue | Status |
|---|---|---|---|---|
| 1 | Component contract (render, sizing, focus, input, state) | S | #1 | closed |
| 2 | Layout contract (vstack, hstack, fixed, fill regions) | S | #2 | closed |
| 3 | Focus model (traversal, trapping, delegation) | S | #3 | closed |
Framework-level behavior. Must not be duplicated inside individual widgets.
| # | Task | Effort | GH Issue | Status |
|---|---|---|---|---|
| 4 | Keyboard input mapping module | M | #4 | closed |
| 5 | Selection model module | S | #5 | closed |
| 6 | Cursor model module | M | #6 | closed |
| 7 | Clipping and measurement helpers | S | #7 | closed |
Build bottom-up. Each depends only on shared modules from Phase 2.
| # | Task | Effort | GH Issue | Status | Deps |
|---|---|---|---|---|---|
| 8 | Text primitive (render, clip, align, style) | M | #8 | closed | 7 |
| 9 | Box/Panel (border, title, padding, focus state) | M | #9 | closed | 7,8 |
| 10 | Scroll container (V+H scroll, clipping, pg/home/end) | L | #10 | closed | 7 |
| 11 | Selectable list (items, selection, keyboard, scroll) | M | #11 | closed | 5,10 |
| 12 | Input field (single-line, cursor, insert/delete, placeholder) | M | #12 | closed | 6 |
| 13 | Tab bar (labels, active, overflow, keyboard) | S | #13 | closed | 3,5 |
| 14 | Modal/dialog (overlay, focus trap, dismiss/confirm) | M | #14 | closed | 3,9 |
| 15 | Tree view (expand/collapse, selection, keyboard, indent) | L | #15 | closed | 5,10,11 |
| 16 | Text editor (multiline, cursor, scroll, submit hook) | L | #16 | closed | 6,10,12 |
| 17 | Data grid (rows/cols, sticky header, H+V scroll, col width) | XL | #17 | closed | 5,7,10 |
Generic shell usable by ShellQL and future shellframe apps.
| # | Task | Effort | GH Issue | Status | Deps |
|---|---|---|---|---|---|
| 18 | App shell (region layout, focus switching, screen routing, modal layer) | L | #18 | closed | 1,2,3,9,14 |
Build against
SHQL_MOCK=1. No sqlite3 calls. Validates the framework.
| # | Task | Effort | GH Issue | Status | Deps |
|---|---|---|---|---|---|
| 19 | Welcome screen (recent files list, open action, empty state) | M | shellql#1 | closed | 11,18 |
| 20 | Schema browser (sidebar tree + main pane detail) | M | shellql#2 | closed | 15,18 |
| 21 | Table view (tab bar, data grid, structure tab) | L | shellql#3 | closed | 13,17,18 |
| 22 | Query screen (editor + results grid + status area) | M | shellql#4 | closed | 16,17,18 |
| 23 | Record inspector (modal key/value scroll panel) | S | shellql#5 | closed | 14,18 |
Wire real sqlite3 behind the adapter seam. Tracking moved to
shellql/PLAN.md. All tasks completed 2026-03-22 (shellql#6–9).
| # | Task | Effort | GH Issue | Status | Deps |
|---|---|---|---|---|---|
| 24 | SQLite adapter: src/db.sh (list, describe, fetch, query) |
L | shellql#7 | closed | - |
| 25 | Mock adapter: src/db_mock.sh (fixture data) |
S | shellql#6 | closed | - |
| 26 | CLI entry point: bin/shql (arg parse + mode dispatch) |
M | shellql#8 | closed | 24 |
| 27 | Discovery mode (recent/known databases) | S | (done inline) | closed | 24,26 |
| 28 | Integration tests (real sqlite3, all CLI modes) | M | shellql#9 | closed | 24,26 |
Additional interactive widgets that build on the Phase 1–3 primitives. All dependencies are already shipped — pure widget work.
| # | Task | Effort | GH Issue | Status | Deps |
|---|---|---|---|---|---|
| 22 | Menu bar: horizontal bar + dropdown panel + submenu nesting; v2 contract (init/render/on_key/on_focus/size); bash 3.2-compatible label→variable naming; unit + integration tests + showcase entry |
L | #22 | closed | 4, 5, 7, 9 |
Keyboard hardening, diff rendering, and mouse support. Independent of ShellQL app phases — can be tackled in any order after Phase 4. Tasks A→C→D→E form one dependency chain (input → mouse parse → hit-test → routing). Tasks B→F form a second chain (dirty-region → framebuffer diff).
| # | Task | Effort | GH Issue | Status | Deps |
|---|---|---|---|---|---|
| A | Input hardening: generic CSI drain path (prevents buffer leakage from unknown sequences), F1–F12 constants + recognition, modifier+arrow sequences (\x1b[1;2A etc.) — all changes in src/input.sh + src/keymap.sh |
S | #29 | closed | 4 |
| B | Dirty-region rendering: widget dirty flags + conditional re-render in app loop; shellframe_screen_clear only called when full repaint is needed; render fns still write to /dev/tty — no API break |
M | #30 | closed | 18 |
| C | Mouse: shellframe_mouse_enter/exit in screen.sh; SGR mouse sequence parsing in shellframe_read_key; SHELLFRAME_MOUSE_COL/ROW/BUTTON/ACTION output vars |
S | #32 | closed | A |
| D | Widget hit-test registry: shellframe_widget_register name top left width height + shellframe_widget_at row col; new module src/hitbox.sh |
M | #31 | closed | 9, 18 |
| E | Mouse routing in app shell + on_mouse handler per widget (click-to-focus, click-to-select in lists, scroll-wheel in scroll views) |
M | #34 | closed | C, D |
| F | Framebuffer diff rendering: _SF_FRAME_CURR/PREV[row*COLS+col] flat indexed arrays; all render fns write to framebuffer instead of /dev/tty; shellframe_screen_flush diffs and emits only changed cells. See migration note in src/screen.sh. |
XL | #33 | closed | B |
| Milestone | Condition | Status |
|---|---|---|
| M1: Shellframe ready | Phase 1–4 all closed | closed |
| M2: Mock app complete | Phase 5 all closed, mock screens working | closed (5/5 screens done) |
| M3: ShellQL v0.1 | Phase 6 all closed, integration tests passing | closed (tracked in shellql/PLAN.md) |
| M4: Platform enhancements | Phase 7 all closed; mouse, diff rendering, full F-key support | closed |
Phase 1 (contracts)
│
├── Phase 2 (shared modules)
│ │
│ └── Phase 3 (primitives)
│ │
│ └── Phase 4 (app shell) ──→ Phase 5 (mock screens)
│ │ │
│ │ Phase 6 (sqlite)
│ │ │
│ │ shql v0.1 alpha
│ │
│ └── Phase 7 (platform enhancements) — parallel track
│ A (input hardening)
│ ├── C (mouse parse) → D (hit-test) → E (routing)
│ └── B (dirty-region) → F (framebuffer diff)
└── (focus model feeds Phase 4 directly)
Before adding any new widget or screen:
- Is this a reusable primitive or a one-off?
- Is scrolling handled by the shared scroll container?
- Is focus handled by the framework focus model?
- Is text editing logic duplicated anywhere?
- Is layout logic embedded in a screen instead of layout primitives?
- Is selection behavior duplicated?
- Is this ShellQL-specific when it should live in shellframe?
Update this section at the end of each session.
Last updated: 2026-03-16 (session 5)
- shellql repo stubbed and pushed to GitHub (https://github.com/fissible/shellql)
- All 28 GitHub issues created: shellframe #1–18, shellql #1–9
- PROJECT.md is the master tracking sheet; shellql/PLAN.md cross-references shellframe issues
- Existing shellframe widgets (table, action-list, confirm, alert) are complete and tested
- Phase 1 complete: #1 component contract, #2 layout contract, #3 focus model written to
docs/contracts/ - Phase 2 complete: #5 selection model, #7 clipping helpers, #4 keyboard input mapping, #6 cursor model (259/259 assertions)
src/input.shextended: 14 key constants and 4-byte ESC sequence support inshellframe_read_keysrc/keymap.sh:shellframe_keyname,shellframe_keymap_bind/lookup,shellframe_keymap_default_nav/editsrc/cursor.sh: full text cursor model — init, move, insert, backspace, delete, kill ops
- Phase 3 complete: #8 text, #9 panel, #10 scroll, #11 list, #12 input-field, #13 tab-bar (330/330 assertions across 11 test files)
- All are v2 composable components (render/on_key/on_focus/size contract)
src/text.sh:_shellframe_text_align,_shellframe_text_wrap_words,shellframe_text_render,shellframe_text_sizesrc/scroll.sh: context-keyed V+H scroll state,shellframe_scroll_move/ensure_row/ensure_col/resizesrc/panel.sh: single/double/rounded/none borders, title alignment, focus highlightsrc/widgets/tab-bar.sh: horizontal tabs with reverse-video active highlight, left/right navsrc/widgets/input-field.sh: single-line edit using cursor.sh, all standard edit keys, mask modesrc/widgets/list.sh: scrollable list using selection.sh + scroll.sh, optional multiselect- Key decisions:
shellframe_sel_move ctx downalways moves 1 step (page_size only applies to page_up/page_down); field scroll is computed at render time from cursor position
- ptyunit migration complete (2026-03-16): shellframe now uses ptyunit as a git submodule (
tests/ptyunit/);tests/assert.sh,tests/run.sh,tests/pty_run.pyremoved; all test files updated toptyunit_test_begin/ptyunit_test_summary; Docker matrix updated. 352/352 assertions pass (330 unit + 22 integration). Run withbash tests/ptyunit/run.sh. - Phase 3 #14 Modal/dialog complete (2026-03-16):
src/widgets/modal.sh— centered panel overlay, message body, optional input field, button row (Left/Right/Tab cycle, Enter confirms, Esc dismisses). SHELLFRAME_MODAL_RESULT set on rc=2. 29/29 assertions. - Phase 4 #18 App shell complete (2026-03-16):
src/shell.sh—shellframe_shellv2 composable runtime.shellframe_shell_regionregisters named regions;shellframe_shell_focus_setqueues focus by name; Tab/Shift-Tab traversal; key dispatch + screen routing viaPREFIX_SCREEN_<region>_on_key/action/quitcallbacks. 30/30 assertions. M1 milestone: Phases 1–4 all closed. - Phase 3 #15 Tree view complete (2026-03-15):
src/widgets/tree.sh— scrollable tree with expand/collapse, keyboard navigation, indent rendering. Parallel arrays (ITEMS/DEPTHS/HASCHILDREN) in pre-order. Space/Right expand, Left collapses or jumps to parent. SHELLFRAME_TREE_RESULT set on rc=2. 46/46 assertions. 457/457 total. - Phase 3 #16 Text editor complete (2026-03-16):
src/widgets/editor.sh— multiline editor rewritten to support soft word wrap (default) and no-wrap modes. Wrap mode: vmap (_SHELLFRAME_ED_${ctx}_VMAP) maps content rows to visual rows via_shellframe_ed_line_segments(soft wrap at last space ≤ width, hard wrap fallback); up/down move by visual row preserving vis_col. No-wrap mode: shared HSCROLL per context, lazy cursor-anchored scroll (only moves when cursor leaves viewport). 119/119 assertions. 576/576 total. - Editor polish (2026-03-16):
- Bracketed paste (
\033[?2004h):shellframe_raw_enter/exitenable/disable bracketed paste mode;shellframe_read_keyrewritten to loop until CSI final byte (handles 6-byte\033[200~/\033[201~); editoron_keyhandler batches the entire paste via_shellframe_ed_insert_text— one vmap rebuild regardless of paste size. 588/588 assertions. - Render flicker fix:
shellframe_editor_rendernow accumulates entire frame into a string and writes once (printf '%s' "$_buf" >/dev/tty); per-character cursor-row padding loop replaced withprintf '%*s';_shellframe_ed_vrow_countgained output-var form to eliminate$()subshell in page navigation. - Footer/doc labels: Ctrl-K/U/W changed from "kill" to "clear" (plain English).
- Wrap cursor boundary fix: vrow movement functions previously clamped
new_coltoseg_start + seg_len(exclusive end), which equals the start of the next segment;cursor_to_vrow's "last matching segment wins" rule then resolved the position to the wrong visual row, causing cursor jumps and invisible-character artifacts. Fixed by clamping intermediate segments toseg_start + seg_len - 1and last segments toline_len. 592/592 assertions.
- Bracketed paste (
- Editor goal column (2026-03-16):
_SHELLFRAME_ED_${ctx}_GOAL_COLadded toshellframe_editor_init. All four vertical movement functions (move_up, move_down, page_up, page_down) store vis_col on first move and reuse it on subsequent vertical moves, so the cursor no longer snaps to col 0 after passing through blank or shorter lines. Non-vertical keys reset GOAL_COL to -1. 610/610 assertions (+18 new goal-col tests). - Phase 3 #17 Data grid complete (2026-03-16):
src/widgets/grid.sh— v2 composable data grid. Flat 1DSHELLFRAME_GRID_DATA[row*COLS+col]array. Sticky header (bold/white labels, only drawn when height ≥ 3 and headers set).│column separators between every pair of adjacent visible columns;SHELLFRAME_GRID_PK_COLS(int, default 0): separator after column PK_COLS-1 becomes┃(data rows) /╋(header junction) to visually mark the PK boundary. Header─separator row uses┼/╋junctions at separator x-positions. V scroll via selection.sh + scroll.sh row axis; H scroll via scroll.sh column axis (Left/Right pan 1 column). Conservativevcols=1init;shellframe_scroll_resizein render updates actual visible-column count. Cursor row: reverse video. Optional multiselect (Space). 52/52 assertions. 640/640 total. - M1 milestone achieved: Phase 3 fully complete (#8–#17 all closed). Phase 4 (#18 app shell) was already closed. All of Phases 1–4 are done.
- Widget showcase added (2026-03-16):
docs/showcase.md— visual gallery with ASCII art + code for every widget (confirm, alert, action-list, list single/multi-select, modal prompt, editor, shellframe_app multi-screen, shellframe_shell composable pane layout). Linked from README "Going deeper". 662/662 assertions still pass. - Phase 5 progress (2026-03-16): shellql#1 welcome, shellql#2 schema browser, shellql#3 table view all closed. App navigates welcome → schema → table → schema.
- Grid widget polished: H-scroll
_trailing_vis_colsfix, 1-char left cell padding, right end-of-data│/┘border, cursor highlight suppressed when unfocused. - Tab bar: inactive=reverse video (persistent white bar), active=bold+clear bg, fill=reverse video.
- Table screen: gap row below tab bar; ↓ from tab bar focuses body; ↑ at top of body returns focus to tab bar;
[/]switch tabs from anywhere. - All shellframe widget changes committed in shellframe repo.
- Grid widget polished: H-scroll
- showcase.md corrections (2026-03-16): Fixed two bugs —
if (( rc == 0 ))→if (( rc == 2 ))in list example (Enter returns 2),shellframe_editor_text→shellframe_editor_get_text. Addedmeta="$5"to action-list_draw_rowsignature; inline comment onshellframe_list_initsecond arg. - Test coverage audit (2026-03-17):
shellframe_apphas zero coverage — backlogged as shellframe#21 (effort S)- Added
examples/modal.sh+tests/integration/test-modal.sh(6 assertions) andtests/integration/test-editor.sh(6 assertions) - Added
assert_not_containsto ptyunit submodule - Tab-bar and shell (unit-tested only, no integration example yet) remain a known gap
- Confirm/alert/action-list have integration tests but no unit tests; recommended path is refactor to expose v2 internals (
_render+_on_key) and keep monolithic wrappers — unit-testable state machine, backwards-compatible callers, aligns with LEGO philosophy - 674/674 assertions pass
- Phase 3.5 #22 Menu bar backlogged (2026-03-17): shellframe#22 — horizontal menu bar + dropdown + submenu. Deps: panel.sh, clip.sh, selection.sh, input/keymap (all shipped). Bash 3.2 label→variable naming convention (
SHELLFRAME_MENU_FILE=(...)). Result path inSHELLFRAME_MENU_RESULT(e.g."File|Open Recent|file1.db"). Effort L. Deliverables:src/widgets/menu-bar.sh, unit tests, example, integration tests, showcase entry. - Phase 5.5 Record inspector closed (shellql#5):
src/screens/inspector.sh— two-column key/value overlay,ceil(N/2)scroll model; Enter on data row triggers it via_shql_TABLE_body_action. - Phase 5.4 Query screen closed (shellql#4) (2026-03-17):
src/screens/query.sh— editor 30% /─divider / results grid split; Ctrl-D runs query, auto-focuses results; Tab: editor→results→stop; Shift-Tab: results→editor→tabbar→stop; auto-focuses editor on Query tab entry. 19 unit assertions. - shellframe change (2026-03-17):
src/shell.sh— Tab/Shift-Tab now offered to focused region'son_keybefore cycling focus; returning 0 consumes the key and suppresses the default advance/retreat. Backward-compatible: existing handlers that return 1 continue to get default behaviour. - M2 milestone achieved: All Phase 5 mock screens complete (shellql#1–5 all closed).
- Next task: Phase 6 — SQLite integration (shellql#6–8): mock adapter cleanup,
src/db.shreal adapter, CLI argument parsing (bin/shql). - MCP intrusion remediated (2026-03-22): A third-party
dual-graphMCP server overwroteCLAUDE.mdin both shellframe and shellql repos with its own "Dual-Graph Context Policy", and added.dual-graph/to both.gitignorefiles. A prior session reverted the CLAUDE.md replacements and pushed. This session removed.dual-graph/directories and.claude/settings.local.jsonhook configs from both repos. All 775 assertions still pass. - Coverage improvement branch
feature/coverage-improvement(2026-03-22) [shellframe#21 + plandocs/superpowers/plans/2026-03-22-coverage-improvement.md]:- All 3 phases complete: Phase 1 (refactor confirm/action-list/alert to extract
_on_key/_render+ unit tests), Phase 2 (new tests for diff-view, app.sh, screen.sh, table), Phase 3 (branch coverage for panel, modal, shell, grid, tab-bar, text). - 917/917 assertions pass on bash 3.2 (local) and bash 5.x (Docker).
- Coverage: 58% (2398/4145 code lines) measured via
bash tests/ptyunit/coverage.sh --src=srcunder Docker bash 5. Baseline was 44% on bash 3.2. On bash 3.2, LINENO is dropped at nesting depth ≥3 in PS4 traces, so widget coverage shows 0% locally — always use Docker bash 5 for accurate numbers. - The 70% target in the plan was aspirational; monolithic keyboard event loops in confirm/action-list/table/diff-view (~700 lines) require PTY input and cannot be traced by the coverage tool. Effective coverage of unit-testable code is ~71%.
- Both branches fully merged to
main(PR #25 coverage improvement, PR #26 ptyunit Homebrew migration). - shellframe#21 can be closed.
- All 3 phases complete: Phase 1 (refactor confirm/action-list/alert to extract
- ptyunit Homebrew migration complete (2026-03-23):
feature/ptyunit-homebrewmerged as PR #26. Removestests/ptyunit/git submodule; addsbootstrap.sh+tests/run.sh; all test files updated tosource "$PTYUNIT_HOME/assert.sh". Docker matrix mounts host ptyunit via-v. CI usesbootstrap-command: bash bootstrap.sh.- Worktrees
feature/coverage-improvementandfeature/ptyunit-homebrewremoved; local branches deleted. - Run tests:
bash tests/run.sh --unit(no submodule init needed; requiresbash bootstrap.shon first use).
- diff-view render coverage added (2026-03-23):
- 23 new assertions in
tests/unit/test-diff-view.shcovering_shellframe_dv_clip_ansi,_shellframe_dv_render_pane(all 7 row types + HIDE_FILE_HDR path),shellframe_diff_view_render(with/without footer),shellframe_diff_view_render_side.widgets/diff-view.sh: 11% → 76%. Total unit assertions: 880/880. Overall coverage: 64% (up from 58%). - HTML report:
coverage/2026_03_23_05_19_32.html(linked fromcoverage/index.html).
- 23 new assertions in
mainis clean: 880/880 assertions, no dirty worktrees, no stale branches. Next: shellql Phase 6 (SQLite integration, shellql#6–8).- table widget unit coverage (2026-03-22): Extracted
_shellframe_table_on_keyand_shellframe_table_scroll_checkfrom monolithicshellframe_table(same pattern as action-list). Addedtests/unit/test-table.sh— 27 new assertions covering navigation, action cycling, confirm/quit, unhandled keys, and scroll boundary logic. Total: 907/907 assertions pass..DS_Storeadded to.gitignore. - editor + shell coverage increase (2026-03-23): Added 28 new unit assertions (921 → 949).
test-editor.sh: 20 assertions —get_textout_var form,_shellframe_ed_is_printable(3 cases),_shellframe_ed_insert_stringempty no-op,_shellframe_ed_line_segmentswidth=0,_shellframe_ed_vrow_countout_var form,rightat EOL of last line,ctrl-kat EOL of last line (no-op), no-wrapmove_up/move_downgoal-col preservation, no-wrappage_down/page_up.test-shell.sh: 8 assertions —_shellframe_shell_drawcoverage: re-registers regions, calls region render fns, dispatcheson_focus 1/0, and appliesFOCUS_REQUESTfrom old ring before firing on_focus. 949/949 assertions pass. - confirm + diff-view coverage increase (2026-03-23): Added 14 new unit assertions (907 → 921).
test-confirm.sh: 4 new assertions for uppercase aliasesH(Yes),L(No),C(confirm) — branches in_shellframe_confirm_on_keythat were present but untested.test-diff-view.sh: 10 new assertions covering HL_ENABLED ctx path (left + right), hdr row status variants (deleted/addedlabel on left/right pane — 4 cases),render_sideright pane with footer,shellframe_diff_view_renderwith RIGHT_FOOTER and LEFT_DATE. 921/921 assertions pass. - ptyunit upgraded to v1.1.0 (2026-03-23): ptyunit cut a new release (v1.1.0) containing
fix(coverage): skip function declaration lines; add version + file links to HTML report. Updated Homebrew formula (/opt/homebrew/Library/Taps/fissible/homebrew-tap/Formula/ptyunit.rb) to new tarball URL + sha256.brew upgrade fissible/tap/ptyunitapplied cleanly. 949/949 assertions pass. - ptyunit v1.1.1 + coverage report (2026-03-23): Discovered Python 3.14 argparse incompatibility in
coverage_report.py—%in--minhelp string raisedValueError: badly formed help string. Fixed by escaping as%%. Cut ptyunit v1.1.1, updated homebrew-tap formula, pushed. Coverage run producedcoverage/2026_03_23_00_08_12.html(linked as default inindex.html). Result: 70% total coverage (2770/3957 lines), up from 64%. Highlights: diff-view 81%, editor 82%, grid 94%, modal 91%. Low floors (confirm 11%, action-list 18%, table 17%) are keyboard event loops requiring PTY — cannot be unit-traced. - Phase 3.5 #22 Menu bar complete (2026-03-23):
src/widgets/menu-bar.sh— v2 composable widget. Horizontal bar + double-border dropdown + one-level submenu. Data model: SHELLFRAME_MENU_NAMES + SHELLFRAME_MENU_ arrays + @VARNAME:Label sigil for submenus. State machine: idle/bar/dropdown/submenu. shellframe_menubar_open for hotkey plug-in seam. SHELLFRAME_MENUBAR_RESULT on rc=2 (empty=dismiss, non-empty=selection path). Unit + integration tests + example + showcase entry. UX fix: spatial navigation model — Up from BAR releases focus upward (rc=2 empty); Up at first dropdown item closes to bar. 58 unit assertions, 7 integration tests. #22 closed. All tests pass (1007/1007 unit). mainis clean (2026-03-23): 1007/1007 unit assertions, no dirty worktrees, no stale branches. 17 commits ahead of origin (unpushed).- Next: PM decision — Phase 7 platform enhancements or other prioritised work.
- ptyunit upgraded to v1.3.0 (2026-03-24): Updated
homebrew-tapformula from v1.0.0 → v1.3.0 (also picked up theVERSIONfile install added in v1.1.1). Resolved rebase conflict with upstream v1.1.1 commit, pushed to GitHub.brew upgrade fissible/tap/ptyunitapplied cleanly (1.1.1 → 1.3.0). 1009/1009 unit assertions pass. - Coverage restored + improved to 70% (2026-03-25):
- Root cause of regression: ptyunit v1.3.0 switched coverage tracing from
BASH_XTRACEFD=2toBASH_XTRACEFD=3. Widget test files that doexec 3>/dev/null(to discard TUI render output) were silently killing the trace for their entire file. Previous 70% reading was measured with an older ptyunit version; v1.3.0 baseline measured 56.6%. - Fix — fd dup technique: Added to 8 affected test files (
test-menu-bar.sh,test-diff-view.sh,test-grid.sh,test-modal.sh,test-panel.sh,test-tab-bar.sh,test-table.sh,test-confirm.sh):exec 4>&3 2>/dev/null || true # dup trace fd; no-op outside coverage mode exec 3>/dev/null # discard widget render output BASH_XTRACEFD=4 # keep trace on fd 4, safe from >&3 redirects
- New test file:
tests/unit/test-screen.sh— 8 assertions coveringshellframe_screen_clear,cursor_hide/show,raw_save,raw_enter,raw_exit,screen_exit.screen.sh: 0% → 77%. - Extracted draw-row helpers to module level (enables direct unit testing without PTY):
_shellframe_al_default_draw_rowfromaction-list.sh— 5 new assertions intest-action-list.sh_shellframe_confirm_draw_buttonsfromconfirm.sh— 6 new assertions intest-confirm.sh_shellframe_tbl_default_draw_rowfromtable.sh— 5 new assertions intest-table.sh
- Result: 70% total coverage (3058/4356 lines), 1033/1033 assertions pass. HTML report:
coverage/2026_03_25_07_21_10.html. - Highlights after fix: menu-bar.sh 51% (from ~1%), grid 89%, modal 92%, tab-bar 92%, panel 84%.
- Remaining low floors: action-list 24%, confirm 22%, table 20% — keyboard event loops requiring PTY input; not unit-traceable.
- Root cause of regression: ptyunit v1.3.0 switched coverage tracing from
- Coverage pass #2 — render tests (2026-03-25):
- Added fd dup setup + render tests to
test-alert.sh(was missing fd dup; render tests existed),test-list.sh(newshellframe_list_rendertests),test-input-field.sh(newshellframe_field_render+_shellframe_field_is_printabletests). - Result: 84% total coverage (3658/4356 lines), 1052/1052 assertions pass.
- Highlights: alert.sh 55%→89%, list.sh →98%, input-field.sh 54%→85%, screen.sh →100%.
- Added fd dup setup + render tests to
- Phase 7B dirty-region rendering complete (2026-03-25): shellframe#30 closed.
src/shell.sh:_SHELLFRAME_SHELL_DIRTYflag,shellframe_shell_mark_dirty(),_shellframe_shell_draw_if_dirty(). Event loop skips_shellframe_shell_drawunless widget marks dirty. Tab/Shift-Tab always mark dirty (focus change always visible).- All 7 V2 composable widgets call
shellframe_shell_mark_dirtybefore rc=0/2: list, input-field, editor, tab-bar, tree, grid, menu-bar. - Dirty integration tests added to all 7 widget test files + test-shell.sh. 1075/1075 assertions pass.
- Docker matrix blocked by Docker Desktop file-sharing config (
/opt/homebrew/opt/ptyunit/libexecnot shared) — pre-existing infra issue, not a code regression. - Merged
feature/phase-7b-dirty-region→main.
- Phase 7A input hardening complete (2026-03-25): shellframe#29 closed. F1–F12 constants (SS3 + CSI variants), 12 modifier+arrow constants (Shift/Alt/Ctrl × Up/Down/Left/Right),
shellframe_keynameentries for all new sequences, CSI drain path documented. 61 new assertions. Cherry-picked from worktree agent onto main. - Phase 7D hitbox registry complete (2026-03-25): shellframe#31 closed. New
src/hitbox.sh: parallel-array bounding-box registry, last-registered-wins overlap, out_var form forshellframe_widget_at, selectiveshellframe_widget_clear. 15 unit assertions. 1151/1151 total assertions pass. - Phase 7 status: A ✓, B ✓, D ✓ — C (#32, mouse) now unblocked (deps A done); F (#33, framebuffer diff) now unblocked (deps B done); E (#34, mouse routing) waits for C+D (both done when C ships).
- Phase 7C mouse parsing complete (2026-03-25): shellframe#32 closed.
src/screen.sh:shellframe_mouse_enter/exit(SGR 1000+1006).src/input.sh:SHELLFRAME_KEY_MOUSEsentinel,SHELLFRAME_MOUSE_{BUTTON,COL,ROW,ACTION}globals, SGR parse branch in CSI drain. 15 new assertions (8 subprocess IO-validation). 1166/1166 pass. - Phase 7 status: A ✓, B ✓, C ✓, D ✓ — E (#34, mouse routing) now unblocked (deps C+D both done); F (#33, framebuffer diff, deps B done) also unblocked.
- Phase 7E mouse routing complete (2026-03-25): shellframe#34 closed.
shellframe_sel_set(selection.sh), hitbox auto-sync inshellframe_shell_region, SGR parse in_shellframe_shell_read_key, mouse dispatch branch in event loop,shellframe_mouse_enter/exitinshellframe_shell.shellframe_list_on_mouse(click-to-select + scroll-wheel) andshellframe_scroll_on_mouse(generic scroll-wheel). 7 PTY IO-validation integration tests. 1253/1253 pass. - Phase 7 status: A ✓, B ✓, C ✓, D ✓, E ✓ — only F (#33, framebuffer diff, XL) remains.
- M4 milestone near: All Phase 7 tasks except F done. F is a large architectural change (all render fns write to framebuffer instead of /dev/tty). PM should decide whether to cut a release before tackling F.
- Phase 7F framebuffer diff rendering complete (2026-03-25): shellframe#33 closed. Core infrastructure in
src/screen.sh:shellframe_fb_frame_start,shellframe_fb_put,shellframe_fb_print,shellframe_fb_fill,shellframe_fb_print_ansi,shellframe_screen_flush(CURR/PREV diff, dirty list, erasure detection). All 10 composable render fns migrated (panel, text, split, list, tree, grid, tab-bar, input-field, modal, menu-bar).src/shell.shintegrates frame_start+flush around each draw cycle. 7 unit test files updated (source screen.sh, fb_frame_start+flush wrappers, macOS-compatibletr -d '\033' | sedANSI stripping). 15 new framebuffer unit tests in test-screen.sh. 1271/1271 total assertions pass. - Phase 7 status: A ✓, B ✓, C ✓, D ✓, E ✓, F ✓ — all closed. M4 milestone: closed.
- Next: PM decision — cut v0.3.0 release, begin ShellQL app phases, or other work.
- Follow-up items (note for PM): diff-view.sh deferred from 7F (uses buffer-building approach, needs architectural restructuring for framebuffer); docker matrix blocked by Docker Desktop file-sharing config (pre-existing, not from 7F).
- CI fix for Phase 7F integration tests (2026-03-26): PR #35 merged. Root cause:
examples/modal.shusedtput cols/tput lineswhich requireTERM; GitHub Actions does not setTERM, so child exited within init_delay. Fix: replaced withstty size </dev/tty(reads kernel PTY window size via TIOCGWINSZ, TERM-independent). Both macOS (bash 3.2) and Ubuntu (bash 5.x) CI now green. - Unit test coverage pass #3 (2026-03-26): Added 255 new assertions across 4 test files (
test-scroll.sh,test-split.sh,test-tree.sh,test-text.sh). Bugs found and fixed during authoring:|| trueclobbering$?in error-path checks (use; _rc=$?instead); split_regions stub captured$6=focusinstead of$2=top; node_to_view tests passed view-row indices instead of node indices; hidden-node expected-1but function returns0;_shellframe_tree_sync_statecalled with 3 args (signature isctx node_idx, 2 args); render assertions expected spaces in non-selected rows butshellframe_screen_flushdiff optimization skips cells matching default_prev=' '. All 1233/1233 unit assertions pass.- What's next: PM decision — cut v0.3.0 release or continue coverage / shellql work.
- ShellQL UX polish session (2026-03-27):
- SGR mouse modifier decoding:
src/input.sh+src/shell.sh— parser now extracts Shift/Meta/Ctrl from the SGR button byte bitmask. New globals:SHELLFRAME_MOUSE_SHIFT,SHELLFRAME_MOUSE_META,SHELLFRAME_MOUSE_CTRL. Base button preserved inSHELLFRAME_MOUSE_BUTTONvia& ~28mask. Backward compatible — all existing_button == 2/<= 2/> 2checks work unchanged. - Shift+click context menus: shellql
table.sh— all 4 right-click trigger points (sidebar, tabbar, data grid, query results) now also fire on Shift+left-click. Workaround for iTerm2/macOS intercepting right-click and Ctrl+click. - Editor
on_mouse:src/widgets/editor.sh— newshellframe_editor_on_mousemaps screen coordinates to cursor position (both wrap and no-wrap modes). Enables click-to-position cursor in query editor. - Editor click-to-activate: shellql
table.shquery mouse handler — clicking already-focused editor pane activates edit mode + positions cursor. Clicking results pane or losing content focus deactivates edit mode. - Results panel unfocused when detail open: shellql
query.sh— Results panel border demoted to single/unfocused style when Row Detail is active. - 1358/1358 shellframe assertions pass, 432/432 shellql assertions pass.
- SGR mouse modifier decoding:
- ShellQL v1.0 issue specs drafted (2026-03-27):
- Specs at
shellql/docs/v1-issue-specs.md— 13 issues across shellframe (#36-38) and shellql (#13-22). - Shellframe primitives needed: form widget (#36, M), toast/flash (#37, S), autocomplete layer (#38, M).
- ShellQL features: insert (#13), update (#14), delete (#15), truncate (#16), drop table (#17), create table template (#18), CSV export (#19), SQL type-ahead (#20), enriched context menus (#21), Esc hierarchy fix (#22).
- Dependency graph and build order documented. Total estimated effort ~5-6 days.
- Action for PM: Review specs, adjust scope, create GitHub issues. Suggested build order: form+toast (parallel) → DML → DDL → export → autocomplete.
- Specs at
- Editor deferred-write fix (2026-03-27):
shellframe_editor_renderwrote directly to fd 3, bypassing the framebuffer. When switching from a data-tab grid to a query-tab editor,shellframe_screen_flushfound stale grid cells in_SF_FRAME_PREVat the editor's screen positions, treated them as erasures, and overwrote editor content with\033[0m(terminal-default spaces). Fix: editor now claims its rows in CURR viashellframe_fb_filland stores rendered output in_SHELLFRAME_EDITOR_DEFERRED_BUF;_shellframe_shell_drawwrites the deferred buffer to fd 3 aftershellframe_screen_flush. Also removed subshell forks from_shellframe_ed_line_segmentsand_shellframe_ed_vrow_count(out_var pattern) to reduce typing latency. 1233/1233 assertions pass. - UX improvements session (2026-03-28):
- Configurable mouse scroll speed:
SHELLFRAME_SCROLL_MOUSE_STEPglobal (default 3). Applied to genericshellframe_scroll_on_mouse,shellframe_grid_on_mouse, andshellframe_list_on_mouse. - Vertical scrollbar widget: New
src/widgets/scrollbar.sh— proportional thumb, renders░/█track, auto-hides when content fits. Added to sidebar, data tables, and query result grids in shellql. - Footer row count: Centered "Rows X–Y of N" in shellql footer when viewing data or query results.
- Tab close buttons + overflow clipping: Each tab shows "x " close button; overflow tabs are clipped with ellipsis.
_shql_tab_fitscorrected to match render math. - Context menu widget: New
src/widgets/context-menu.sh— floating pop-over menu with border, keyboard nav, scroll support, auto-positioning. Integrated in shellql sidebar, tabbar, data grid, and query results. - Scroll coalescing (deferred):
read -t 0approach prototyped in_shellframe_shell_draw_if_dirtyto coalesce rapid mouse events, but disabled pending crash investigation. TODO comment marks the approach. - Crash diagnostic trap:
bin/shqlEXIT trap logs crash context to/tmp/shql-crash.logwhen exit code is non-zero. - Scroll crash: User reported app crashing on data table scroll after scrollbar+footer changes. Extensive investigation found no
set -uviolation. Defensive guards added (min width/height for scrollbar,:-defaults). Crash NOT yet verified as fixed — user needs to test. - 1370/1370 shellframe assertions pass, 403/403 shellql unit assertions pass.
- Configurable mouse scroll speed:
- Welcome tiles + mouse fixes session (2026-03-28):
- Mouse-driven screen transitions:
src/shell.shevent loop now checks_SHELLFRAME_SHELL_NEXTafter mouse event dispatch. Previously only the keyboard rc=2 path handled screen transitions — mouse clicks that set_SHELLFRAME_SHELL_NEXTwere silently ignored. - Enter key → rc=2 convention: Welcome tiles
on_keynow returnsrc=2for Enter so the shell loop's action+transition path fires. Calling_actiondirectly and returningrc=0bypassed the transition check. - Context menu release fix:
shellframe_cmenu_on_mousereturnedrc=1(outside-click) for release events, causing consumers to dismiss the menu on Shift+click button release. Now returnsrc=0(no-op) for non-press events. - Modal mouse support: New
shellframe_modal_on_mouse— hit-tests centered button row, returnsrc=2withSHELLFRAME_MODAL_RESULTon button click. Clicks outside modal bounds dismiss withresult=-1. Wired into welcome screen delete confirmation. - Welcome tile background fix: All
shellframe_fb_fillcalls in tile render now use$SHQL_THEME_CONTENT_BGinstead of""to prevent blue color bleed from header/footer. - 1370/1370 shellframe assertions pass, 446/446 shellql assertions pass.
- Mouse-driven screen transitions:
- Row-based framebuffer complete (2026-04-01): shellframe#39 closed. Replaced per-cell
_SF_FRAME_CURR[idx]with per-row_SF_ROW_CURR[row]positioned fragment accumulation. Allfb_*functions are O(1) string appends (no per-character loops).shellframe_screen_flushdiffs whole row strings, emits oneprintfper changed row instead of one per changed cell. Editor deferred-write workaround (_SHELLFRAME_EDITOR_DEFERRED_BUF) removed — editor writes per-row fragments directly like every other widget. DIRECT_RENDER typing fast-path unchanged. Test ANSI-strip pattern fixed across all test files (two-steptr+sedreplaced with singlesedmatching full ESC+[sequences). 1397/1397 assertions pass (1323 unit + 74 integration). Spec:docs/superpowers/specs/2026-04-01-row-framebuffer-design.md.- Next: PM decision on next work item. diff-view.sh still uses its own buffer-building approach (deferred from 7F, now also from #39).
- diff-view framebuffer migration complete (2026-04-02):
src/widgets/diff-view.shmigrated from manual_bufaccumulation +printf >&3to row-based framebuffer API (shellframe_fb_fill,shellframe_fb_print_ansi). All three render paths migrated:_shellframe_dv_render_pane(per-row fb writes),shellframe_diff_view_renderfooter,shellframe_diff_view_render_sidefooter. Zero>&3writes remain — all composable widgets now use the framebuffer uniformly. Test helper_dv_captureupdated to usefb_frame_start+screen_flush. 1323/1323 unit assertions pass.- Also committed: ANSI attribute reset fixes in
panel.sh,grid.sh,scrollbar.sh—\033[m/\033[39m/\033[22m\033[39mafter border/separator chars to prevent style bleed into adjacent cell content. - Next: PM decision — cut v0.4.0 release (
bash release.sh minor), ShellQL Phase 6, or v1.0 issue specs.
- Also committed: ANSI attribute reset fixes in
- Autocomplete layer complete (2026-04-02): shellframe#38 closed. New
src/widgets/autocomplete.sh— composable autocomplete overlay for input-field and editor contexts. Provider callback contract (provider_fn prefix out_array_name), "auto" and "tab" trigger modes, popup delegated toshellframe_cmenu_*. Bug caught in final review: editor-mode accept wrote to_SHELLFRAME_ED_${ctx}_LINE_${row}— corrected to_L${row}. 62 unit assertions + 3 PTY integration tests +examples/autocomplete.sh+ showcase entry. 1462/1462 assertions pass (1385 unit + 77 integration). Branchfeature/autocomplete-layermerged tomain. Issue #38 closed.- Next: PM decision — cut v0.4.0 release (
bash release.sh minor), start ShellQL DML work (shellql#13–17), or form widget (#36).
- Next: PM decision — cut v0.4.0 release (
- Sheet navigation primitive complete (2026-04-02): shellframe#27 closed. New
src/sheet.sh— partial overlay aboveshellframe_shellscreen. Frozen, dimmed back-strip at row 1; configurable sheet height (SHELLFRAME_SHEET_HEIGHT); wizard-style internal transitions via_SHELLFRAME_SHEET_NEXT; Esc + Up-from-topmost +shellframe_sheet_popdismissal paths. Registry swap pattern (sheet_SHELLFRAME_SHEET_*↔ shell_SHELLFRAME_SHELL_*) isolates sheet focus from parent.src/screen.shgains_SF_ROW_OFFSETapplied by all 4shellframe_fb_*functions during region dispatch.src/shell.shgets two delegation guards (_shellframe_shell_draw+ key loop). Tab/Shift-Tab reserved at sheet level (always cycle focus, not offered to regions).examples/sheet.sh: two-step wizard (Name+Email → City+Zip). 8 PTY integration tests + 24 new unit assertions. 1511/1511 assertions pass (1397+114 via new tests). Spec:docs/superpowers/specs/2026-04-02-sheet-navigation-design.md.- Known limitation: Submit action setting both
_SHELLFRAME_SHEET_NEXT="__POP__"and_SHELLFRAME_SHELL_NEXT="__QUIT__"causes__QUIT__to be processed before__POP__runs throughshellframe_sheet_draw— sheet state left dirty on exit. Safe for single-run scripts; documented inexamples/sheet.sh. - Next: PM decision — cut v0.4.0 release, form widget (#36), or ShellQL DML work (shellql#13–17).
- Known limitation: Submit action setting both