diff --git a/.gitignore b/.gitignore index db4f5384..8bc3b5d3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ # -- clean-house skill run history / skip ledger (machine-local) -------- /governance/decisions/.clean-house-state.md +# -- frame0 wireframe local↔Frame0 id mapping (machine-local) ----------- +**/*.idmap.json + # -- Flutter / Dart (single package at repo root) ----------------------- /.dart_tool/ /.packages diff --git a/.pql/changelog/ticket_history/2026-06.sql b/.pql/changelog/ticket_history/2026-06.sql index 15fceb96..9ed7bc87 100644 --- a/.pql/changelog/ticket_history/2026-06.sql +++ b/.pql/changelog/ticket_history/2026-06.sql @@ -499,3 +499,7 @@ INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-256', 'status', 'in_progress', 'done', NULL, '2026-06-06 19:53:07', '2026-06-06 19:53:07', '2026-06-06 19:53:07', NULL, '2c8e4bc4e093620c9313dbf09b6f48f0', 1) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-208', 'status', 'in_progress', 'done', NULL, '2026-06-06 19:53:07', '2026-06-06 19:53:07', '2026-06-06 19:53:07', NULL, '83aab9acdaff92bd1018b0591291abb7', 1) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-54', 'status', 'backlog', 'ready', NULL, '2026-06-06 20:00:45', '2026-06-06 20:00:45', '2026-06-06 20:00:45', NULL, '0634bad8a7ba2f618e9c27c6b4014544', 1) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-54', 'description', 'Bottom panel or context tab showing daemon logs, build output, extension logs, and pql sync output. Filterable by source. Auto-scrolls to latest. Useful for debugging extension and IPC issues.', 'Bottom panel or context tab showing daemon logs, build output, extension logs, and pql sync output. Filterable by source. Auto-scrolls to latest. Useful for debugging extension and IPC issues. + +UX design (2026-06-06, D-87): the panel is a bottom OUTPUT DOCK, read-only, two tabs — Output (the Logger stream, filter by source/level/text, auto-scroll) + Problems (moved out of the sidebar; no duplication). Toggled by a single status-bar widget that REPLACES the app-status indicator (merged health+log: green check when clean, warn/error counts when not, chevron for open state) — opens with click or Cmd/Ctrl+J. Needs a bounded in-memory ring sink on the Logger (no history today). Layout amends D-47 (dock pushes Claude up, capped so Claude stays >=50%). Terminal is NOT in the dock — kept first-class in the editor pane, tracked by T-258. Resolves Q-28. Wireframe: docs/design/wireframes/output-dock/.', NULL, '2026-06-06 20:56:04', '2026-06-06 20:56:04', '2026-06-06 20:56:04', NULL, 'c1df82ae7d048d460fa63288d00ad78b', 1) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-36', 'status', 'backlog', 'review', NULL, '2026-06-06 20:56:20', '2026-06-06 20:56:20', '2026-06-06 20:56:20', NULL, 'bcdc7e0811d809ddd533e12867c111d5', 1) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-06.sql b/.pql/changelog/tickets/2026-06.sql index ec76b193..6d975c4b 100644 --- a/.pql/changelog/tickets/2026-06.sql +++ b/.pql/changelog/tickets/2026-06.sql @@ -1062,3 +1062,12 @@ Finding: the IPC/CLI foundation (D-1, D-6, D-56; built under T-99 incl. the C cl Plus: a governance decision on WHICH agent model is the dogfood target (hosted stream-json session vs external CLI driver vs both) and an MCP follow-up. The pleasant surprise per the probe: the hard part (a live, correct, single-process IPC contract with proper 0/1/3 exit codes) is done; what''s missing is the last mile that puts the tool in the agent''s hands. NOTE: the report says ''daemon'' but D-56 dissolved it — this is the in-process IPC server; the socket contract is unchanged.', 'done', 'high', NULL, NULL, 'D-6', '2026-06-02 18:13:52', '2026-06-06 19:53:07', NULL, '3575a67bc5f4df7fddc67269641dfe2b', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-256', 'task', 'T-208', 'Install affordance: distinguish a dev-tree clide from a production-installed build', 'Flagged during T-212 (2026-06-06). The Install-clide-command affordance copies whatever CliInstaller resolves as the bundled C client into ~/.local/bin, and ''make run'' points CLIDE_CLI_BIN at the dev-tree client (native//clide). On a normal user machine that is correct: the bundled client beside the GUI runner IS the production build. But on a clide DEV system the clide on PATH should point at the properly-installed production build, not a dev-tree binary or stale build artefact. CliInstaller.inspect only classifies missing / staleGui (points into the Flutter bundle) / installed, and treats any non-GUI binary as installed without distinguishing dev-tree vs a real install. Good enough for now per the user; the affordance works for end users and fixes the stale-GUI-symlink footgun. Decide desired dev-machine behaviour (e.g. detect dev tree via CLIDE_PROJECT / repo-relative path and warn instead of silently installing, or prefer an already-correct production build) and refine inspect() accordingly.', 'done', 'low', NULL, NULL, NULL, '2026-06-06 10:07:35', '2026-06-06 19:53:07', NULL, 'a736e06701cbdb90f6256e2dab2485f8', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-54', 'story', NULL, 'output and log panel', 'Bottom panel or context tab showing daemon logs, build output, extension logs, and pql sync output. Filterable by source. Auto-scrolls to latest. Useful for debugging extension and IPC issues.', 'ready', 'medium', NULL, NULL, NULL, '2026-04-23 20:32:06', '2026-06-06 20:00:45', NULL, '0865a967d2c535bfdcac7f5f7bd3749a', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-258', 'story', NULL, 'Terminal as a first-class editor-pane surface (swap vs split)', 'Spun off from the T-54 dock design (D-87), which scoped the bottom dock to read-only output (logs + problems) and explicitly kept the terminal OUT of it. Terminals are core functionality, not an afterthought: the terminal should live in the editor pane (above Claude, D-49 editor-mode) as a first-class peer of editor and diff — not in a read-only log strip. + +First-class means: reachable by a dedicated keybinding (e.g. Ctrl+`), multiple terminals, and tmux-backed persistence (D-41/D-75) so swapping away never stops a running terminal — you swap back and the build is still streaming. Today the terminal is a generic workspace tab; this moves it into the editor-mode family. + +OPEN QUESTION (decide here): clide''s editor-mode is swap (one surface above Claude at a time), so you cannot currently watch a terminal AND edit a file side-by-side. Options: (a) swap-only, leaning on tmux persistence + a fast toggle; or (b) allow the editor pane to split so a terminal can sit beside editor/diff. This is a facet of Q-27 (two-editor split) — resolve them together. Relates to D-47 (Claude-is-home), D-48 (chrome budget), D-49 (editor-mode), D-84 (diff placement, the existing editor-mode peer).', 'backlog', 'medium', NULL, NULL, 'D-49', '2026-06-06 20:55:49', '2026-06-06 20:55:49', NULL, '52adad3337fdc5ca6f8dbb237f31e5a7', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-54', 'story', NULL, 'output and log panel', 'Bottom panel or context tab showing daemon logs, build output, extension logs, and pql sync output. Filterable by source. Auto-scrolls to latest. Useful for debugging extension and IPC issues. + +UX design (2026-06-06, D-87): the panel is a bottom OUTPUT DOCK, read-only, two tabs — Output (the Logger stream, filter by source/level/text, auto-scroll) + Problems (moved out of the sidebar; no duplication). Toggled by a single status-bar widget that REPLACES the app-status indicator (merged health+log: green check when clean, warn/error counts when not, chevron for open state) — opens with click or Cmd/Ctrl+J. Needs a bounded in-memory ring sink on the Logger (no history today). Layout amends D-47 (dock pushes Claude up, capped so Claude stays >=50%). Terminal is NOT in the dock — kept first-class in the editor pane, tracked by T-258. Resolves Q-28. Wireframe: docs/design/wireframes/output-dock/.', 'ready', 'medium', NULL, NULL, NULL, '2026-04-23 20:32:06', '2026-06-06 20:56:04', NULL, 'd7252d09c6f9f8a8533ef74dfc1a094b', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-36', 'story', 'T-7', 'Context auto-behavior: right panel reacts to Claude references', 'Parse file references from Claude output. Swap viewer content when right panel is open. Badge on spine when collapsed. Live-sync viewer when editing .md.', 'review', 'low', NULL, NULL, 'D-50', '2026-04-22 20:34:09', '2026-06-06 20:56:20', NULL, '014c81c57e20079eb3c67ff0e338ca1f', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); diff --git a/docs/design/wireframes/output-dock/output-log-dock.json b/docs/design/wireframes/output-dock/output-log-dock.json new file mode 100644 index 00000000..cae42609 --- /dev/null +++ b/docs/design/wireframes/output-dock/output-log-dock.json @@ -0,0 +1,243 @@ +{ + "name": "T-54 Output Log Dock", + "shapes": { + "title": { + "type": "Text", + "left": 40, "top": 8, + "text": "T-54 — Output / Log dock · click the status bar to toggle (open state shown)", + "fontColor": "#c8d0e0", "fontSize": 16 + }, + + "win": { + "type": "Rectangle", + "left": 40, "top": 40, "width": 1280, "height": 800, + "fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [8, 8, 8, 8] + }, + "chrome": { + "type": "Rectangle", "parent": "win", + "left": 40, "top": 40, "width": 1280, "height": 24, + "fillColor": "#21262f", "strokeColor": "#333340", "corners": [8, 8, 0, 0] + }, + "chrome-label": { + "type": "Text", "parent": "chrome", + "left": 54, "top": 45, "text": "clide · postmeridiem/clide", "fontColor": "#6a7280", "fontSize": 12 + }, + + "sidebar": { + "type": "Rectangle", "parent": "win", + "left": 40, "top": 64, "width": 200, "height": 500, + "fillColor": "#21262f", "strokeColor": "#333340", "corners": [0, 0, 0, 0] + }, + "sidebar-label": { + "type": "Text", "parent": "sidebar", + "left": 54, "top": 76, "text": "tickets · files · git · pql", "fontColor": "#6a7280", "fontSize": 12 + }, + + "workspace": { + "type": "Rectangle", "parent": "win", + "left": 240, "top": 64, "width": 740, "height": 500, + "fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [0, 0, 0, 0] + }, + "ws-label": { + "type": "Text", "parent": "workspace", + "left": 256, "top": 76, "text": "Claude (home) — editor · diff · terminal lift above it (D-49 editor-mode)", "fontColor": "#6a7280", "fontSize": 12 + }, + "ws-msg": { + "type": "Rectangle", "parent": "workspace", + "left": 256, "top": 110, "width": 560, "height": 70, + "fillColor": "#21262f", "strokeColor": "#333340", "corners": [6, 6, 6, 6] + }, + "ws-prompt": { + "type": "Rectangle", "parent": "workspace", + "left": 256, "top": 500, "width": 708, "height": 44, + "fillColor": "#2a3040", "strokeColor": "#c8d8f0", "corners": [8, 8, 8, 8] + }, + "ws-prompt-label": { + "type": "Text", "parent": "ws-prompt", + "left": 270, "top": 514, "text": "Ask Claude…", "fontColor": "#6a7280", "fontSize": 13 + }, + + "context": { + "type": "Rectangle", "parent": "win", + "left": 980, "top": 64, "width": 340, "height": 500, + "fillColor": "#21262f", "strokeColor": "#333340", "corners": [0, 0, 0, 0] + }, + "context-label": { + "type": "Text", "parent": "context", + "left": 994, "top": 76, "text": "context / reader", "fontColor": "#6a7280", "fontSize": 12 + }, + + "dock": { + "type": "Rectangle", "parent": "win", + "left": 40, "top": 564, "width": 1280, "height": 250, + "fillColor": "#21262f", "strokeColor": "#333340", "corners": [0, 0, 0, 0] + }, + "dock-accent": { + "type": "Rectangle", "parent": "dock", + "left": 40, "top": 564, "width": 1280, "height": 2, + "fillColor": "#c8d8f0", "strokeColor": "#c8d8f0", "corners": [0, 0, 0, 0] + }, + "dock-header": { + "type": "Rectangle", "parent": "dock", + "left": 40, "top": 566, "width": 1280, "height": 32, + "fillColor": "#2a3040", "strokeColor": "#333340", "corners": [0, 0, 0, 0] + }, + "tab-output": { + "type": "Rectangle", "parent": "dock-header", + "left": 52, "top": 570, "width": 78, "height": 24, + "fillColor": "#1a1e24", "strokeColor": "#c8d8f0", "corners": [4, 4, 4, 4] + }, + "tab-output-label": { + "type": "Text", "parent": "tab-output", + "left": 66, "top": 575, "text": "Output", "fontColor": "#c8d8f0", "fontSize": 13 + }, + "tab-problems": { + "type": "Text", "parent": "dock-header", + "left": 148, "top": 575, "text": "Problems 2", "fontColor": "#6a7280", "fontSize": 13 + }, + + "filter-box": { + "type": "Rectangle", "parent": "dock-header", + "left": 820, "top": 570, "width": 150, "height": 24, + "fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [4, 4, 4, 4] + }, + "filter-label": { + "type": "Text", "parent": "filter-box", + "left": 830, "top": 575, "text": "Filter…", "fontColor": "#6a7280", "fontSize": 12 + }, + "source-dd": { + "type": "Rectangle", "parent": "dock-header", + "left": 980, "top": 570, "width": 122, "height": 24, + "fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [4, 4, 4, 4] + }, + "source-label": { + "type": "Text", "parent": "source-dd", + "left": 990, "top": 575, "text": "Source: all ▾", "fontColor": "#c8d0e0", "fontSize": 12 + }, + "level-dd": { + "type": "Rectangle", "parent": "dock-header", + "left": 1112, "top": 570, "width": 116, "height": 24, + "fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [4, 4, 4, 4] + }, + "level-label": { + "type": "Text", "parent": "level-dd", + "left": 1122, "top": 575, "text": "Level: info ▾", "fontColor": "#c8d0e0", "fontSize": 12 + }, + "clear-btn": { + "type": "Rectangle", "parent": "dock-header", + "left": 1238, "top": 570, "width": 70, "height": 24, + "fillColor": "#1a1e24", "strokeColor": "#333340", "corners": [4, 4, 4, 4] + }, + "clear-label": { + "type": "Text", "parent": "clear-btn", + "left": 1250, "top": 575, "text": "Clear", "fontColor": "#6a7280", "fontSize": 12 + }, + + "err-row-bg": { + "type": "Rectangle", "parent": "dock", + "left": 42, "top": 690, "width": 1276, "height": 46, + "fillColor": "#2a1f22", "strokeColor": "#2a1f22", "corners": [0, 0, 0, 0] + }, + + "l1-t": { "type": "Text", "parent": "dock", "left": 56, "top": 612, "text": "10:42:03.114", "fontColor": "#6a7280", "fontSize": 12 }, + "l1-lv": { "type": "Text", "parent": "dock", "left": 158, "top": 612, "text": "INFO", "fontColor": "#7f8a9c", "fontSize": 12 }, + "l1-src": { "type": "Text", "parent": "dock", "left": 222, "top": 612, "text": "ipc", "fontColor": "#6a7280", "fontSize": 12 }, + "l1-msg": { "type": "Text", "parent": "dock", "left": 320, "top": 612, "text": "IPC server listening at $XDG_RUNTIME_DIR/clide/8f3a…sock", "fontColor": "#c8d0e0", "fontSize": 12 }, + + "l2-t": { "type": "Text", "parent": "dock", "left": 56, "top": 638, "text": "10:42:03.140", "fontColor": "#6a7280", "fontSize": 12 }, + "l2-lv": { "type": "Text", "parent": "dock", "left": 158, "top": 638, "text": "INFO", "fontColor": "#7f8a9c", "fontSize": 12 }, + "l2-src": { "type": "Text", "parent": "dock", "left": 222, "top": 638, "text": "mcp", "fontColor": "#6a7280", "fontSize": 12 }, + "l2-msg": { "type": "Text", "parent": "dock", "left": 320, "top": 638, "text": "MCP/SSE listening at http://127.0.0.1:54310", "fontColor": "#c8d0e0", "fontSize": 12 }, + + "l3-t": { "type": "Text", "parent": "dock", "left": 56, "top": 664, "text": "10:42:04.880", "fontColor": "#6a7280", "fontSize": 12 }, + "l3-lv": { "type": "Text", "parent": "dock", "left": 158, "top": 664, "text": "DEBUG", "fontColor": "#5a6270", "fontSize": 12 }, + "l3-src": { "type": "Text", "parent": "dock", "left": 222, "top": 664, "text": "pane", "fontColor": "#6a7280", "fontSize": 12 }, + "l3-msg": { "type": "Text", "parent": "dock", "left": 320, "top": 664, "text": "spawned pane p1 (tmux clide:main)", "fontColor": "#9aa3b2", "fontSize": 12 }, + + "l4-t": { "type": "Text", "parent": "dock", "left": 56, "top": 696, "text": "10:42:06.330", "fontColor": "#6a7280", "fontSize": 12 }, + "l4-lv": { "type": "Text", "parent": "dock", "left": 158, "top": 696, "text": "ERROR", "fontColor": "#f06c6f", "fontSize": 12 }, + "l4-src": { "type": "Text", "parent": "dock", "left": 222, "top": 696, "text": "extensions", "fontColor": "#6a7280", "fontSize": 12 }, + "l4-msg": { "type": "Text", "parent": "dock", "left": 320, "top": 696, "text": "builtin.foo failed to activate: MissingDep('lua')", "fontColor": "#f0a6a8", "fontSize": 12 }, + "l4-stack": { "type": "Text", "parent": "dock", "left": 320, "top": 716, "text": " at LuaExtension.activate (lua_host.dart:88) › expand for full trace", "fontColor": "#6a7280", "fontSize": 11 }, + + "l5-t": { "type": "Text", "parent": "dock", "left": 56, "top": 744, "text": "10:42:07.002", "fontColor": "#6a7280", "fontSize": 12 }, + "l5-lv": { "type": "Text", "parent": "dock", "left": 158, "top": 744, "text": "WARN", "fontColor": "#d08447", "fontSize": 12 }, + "l5-src": { "type": "Text", "parent": "dock", "left": 222, "top": 744, "text": "pql", "fontColor": "#6a7280", "fontSize": 12 }, + "l5-msg": { "type": "Text", "parent": "dock", "left": 320, "top": 744, "text": "decisions sync: 1 broken ref in architecture.md", "fontColor": "#e0b48c", "fontSize": 12 }, + + "l6-t": { "type": "Text", "parent": "dock", "left": 56, "top": 770, "text": "10:42:07.115", "fontColor": "#6a7280", "fontSize": 12 }, + "l6-lv": { "type": "Text", "parent": "dock", "left": 158, "top": 770, "text": "INFO", "fontColor": "#7f8a9c", "fontSize": 12 }, + "l6-src": { "type": "Text", "parent": "dock", "left": 222, "top": 770, "text": "git", "fontColor": "#6a7280", "fontSize": 12 }, + "l6-msg": { "type": "Text", "parent": "dock", "left": 320, "top": 770, "text": "status: 3 changed, 0 staged", "fontColor": "#c8d0e0", "fontSize": 12 }, + + "follow-pill": { + "type": "Rectangle", "parent": "dock", + "left": 1180, "top": 776, "width": 124, "height": 24, + "fillColor": "#1a1e24", "strokeColor": "#00ab9a", "corners": [12, 12, 12, 12] + }, + "follow-label": { + "type": "Text", "parent": "follow-pill", + "left": 1196, "top": 781, "text": "● Following", "fontColor": "#00ab9a", "fontSize": 12 + }, + + "statusbar": { + "type": "Rectangle", "parent": "win", + "left": 40, "top": 814, "width": 1280, "height": 26, + "fillColor": "#2a3040", "strokeColor": "#333340", "corners": [0, 0, 8, 8] + }, + "sb-branch": { "type": "Text", "parent": "statusbar", "left": 56, "top": 820, "text": "⎇ main", "fontColor": "#6a7280", "fontSize": 12 }, + "sb-toggle": { + "type": "Rectangle", "parent": "statusbar", + "left": 1120, "top": 816, "width": 184, "height": 22, + "fillColor": "#1a1e24", "strokeColor": "#c8d8f0", "corners": [4, 4, 4, 4] + }, + "sb-toggle-label": { + "type": "Text", "parent": "sb-toggle", + "left": 1132, "top": 820, "text": "▼ Output ⚠ 1 ✕ 1", "fontColor": "#c8d8f0", "fontSize": 12 + }, + + "closed-title": { + "type": "Text", + "left": 40, "top": 872, "text": "Collapsed + log clean — same widget now reads as the health indicator (green ✓), chevron flips to ▲:", "fontColor": "#6a7280", "fontSize": 13 + }, + "closed-bar": { + "type": "Rectangle", + "left": 40, "top": 898, "width": 1280, "height": 26, + "fillColor": "#2a3040", "strokeColor": "#333340", "corners": [6, 6, 6, 6] + }, + "closed-branch": { "type": "Text", "parent": "closed-bar", "left": 56, "top": 904, "text": "⎇ main", "fontColor": "#6a7280", "fontSize": 12 }, + "closed-toggle": { + "type": "Rectangle", "parent": "closed-bar", + "left": 1120, "top": 900, "width": 184, "height": 22, + "fillColor": "#1a1e24", "strokeColor": "#00ab9a", "corners": [4, 4, 4, 4] + }, + "closed-toggle-label": { + "type": "Text", "parent": "closed-toggle", + "left": 1132, "top": 904, "text": "▲ Output ✓ clean", "fontColor": "#00ab9a", "fontSize": 12 + }, + + "ann-dock": { + "type": "Text", + "left": 1360, "top": 580, + "text": "BOTTOM DOCK — read-only output\nOutput (logs) + Problems (diagnostics).\nResizable height; remembers last size.\nResolves Q-28: dock = logs/problems only.\nTerminal is NOT here — it's a first-class\nwork surface in the editor pane (D-49),\ntmux-backed so it never stops on swap.", + "fontColor": "#c8d8f0", "fontSize": 13 + }, + "ann-toggle": { + "type": "Text", + "left": 1360, "top": 760, + "text": "HEALTH + LOG — ONE WIDGET\nReplaces the old app-status indicator, so\nthe bar doesn't gain a segment. Green ✓\nwhen the log is clean; ⚠/✕ counts when not.\nClick (or ⌘J) toggles the dock; ▲/▼ = state.", + "fontColor": "#c8d8f0", "fontSize": 13 + }, + "ann-follow": { + "type": "Text", + "left": 1360, "top": 690, + "text": "AUTO-SCROLL\nFollows the tail; scrolling up pauses\nfollow and shows 'Jump to latest ↓'.", + "fontColor": "#c8d0e0", "fontSize": 13 + } + }, + "connectors": { + "c-dock": { "tailId": "ann-dock", "headId": "dock-header", "strokeColor": "#c8d8f0" }, + "c-toggle": { "tailId": "ann-toggle", "headId": "sb-toggle", "strokeColor": "#c8d8f0" }, + "c-follow": { "tailId": "ann-follow", "headId": "follow-pill", "strokeColor": "#c8d8f0" } + } +} diff --git a/docs/design/wireframes/output-dock/output-log-dock.png b/docs/design/wireframes/output-dock/output-log-dock.png new file mode 100644 index 00000000..c7bef9fd Binary files /dev/null and b/docs/design/wireframes/output-dock/output-log-dock.png differ diff --git a/governance/README.md b/governance/README.md index 6116c9b9..ea66e209 100644 --- a/governance/README.md +++ b/governance/README.md @@ -128,6 +128,7 @@ You might also want, project-permitting: - [D-84: Diff view placement — editor-mode inline above Claude, spawned from the git sidebar](decisions/architecture.md#d-84-diff-view-placement--editor-mode-inline-above-claude-spawned-from-the-git-sidebar) — _architecture_ - [D-85: Event bus delivery — bounded ring-buffer back-pressure; in-memory cursor retention, bus-owned if persisted](decisions/architecture.md#d-85-event-bus-delivery--bounded-ring-buffer-back-pressure-in-memory-cursor-retention-bus-owned-if-persisted) — _architecture_ - [D-86: MCP tool surface — full clide namespace generated from the co-registered command registry](decisions/architecture.md#d-86-mcp-tool-surface--full-clide-namespace-generated-from-the-co-registered-command-registry) — _architecture_ +- [D-87: Output/log dock — bottom, toggled, read-only (logs + problems)](decisions/architecture.md#d-87-outputlog-dock--bottom-toggled-read-only-logs--problems) — _architecture_ ## Open questions @@ -151,7 +152,6 @@ You might also want, project-permitting: - [Q-25: Body text face — mono everywhere vs Josefin Sans UI + mono code](questions/architecture.md#q-25-body-text-face--mono-everywhere-vs-josefin-sans-ui--mono-code) — _architecture_ - [Q-26: Small screen layout (< 1000px)](questions/architecture.md#q-26-small-screen-layout--1000px) — _architecture_ - [Q-27: Two-editor split](questions/architecture.md#q-27-two-editor-split) — _architecture_ -- [Q-28: Terminal strip scope — shell only or logs/errors/tests](questions/architecture.md#q-28-terminal-strip-scope--shell-only-or-logserrorstests) — _architecture_ - [Q-29: Branch picker location](questions/architecture.md#q-29-branch-picker-location) — _architecture_ - [Q-30: Focus behavior when editor is dirty and viewer is peeked](questions/architecture.md#q-30-focus-behavior-when-editor-is-dirty-and-viewer-is-peeked) — _architecture_ - [Q-31: XWayland fallback for frameless — proper Wayland protocol needed](questions/architecture.md#q-31-xwayland-fallback-for-frameless--proper-wayland-protocol-needed) — _architecture_ @@ -164,6 +164,7 @@ You might also want, project-permitting: - [Q-19: (withdrawn)](questions/process.md#q-19-withdrawn) — _process_ - [Q-21: Pql absorbs planning vs keeps separate](questions/architecture.md#q-21-pql-absorbs-planning-vs-keeps-separate) — _architecture_ - [Q-22: Ticket persistence strategy](questions/architecture.md#q-22-ticket-persistence-strategy) — _architecture_ +- [Q-28: Terminal strip scope — shell only or logs/errors/tests](questions/architecture.md#q-28-terminal-strip-scope--shell-only-or-logserrorstests) — _architecture_ - [Q-32: MCP tool surface — minimum slash-ide or extended clide tools?](questions/architecture.md#q-32-mcp-tool-surface--minimum-slash-ide-or-extended-clide-tools) — _architecture_ - [Q-33: MCP transport — SSE, WebSocket, stdio, or all?](questions/architecture.md#q-33-mcp-transport--sse-websocket-stdio-or-all) — _architecture_ diff --git a/governance/decisions/architecture.md b/governance/decisions/architecture.md index 2fe393df..5edf1f2f 100644 --- a/governance/decisions/architecture.md +++ b/governance/decisions/architecture.md @@ -140,6 +140,7 @@ Core, rendering, IPC, kernel, panel manager. ### D-47: Interaction model — Claude-is-home layout - **Date:** 2026-04-22 +- **Amendment (2026-06-06):** Rule (1) (prompt-bar Y invariant across all states) gains one documented exception: the bottom **output dock** ([D-87](#d-87-outputlog-dock--bottom-toggled-read-only-logs--problems)) may re-baseline the prompt bar by pushing Claude up when it opens, capped so Claude stays the largest surface (≥ 50% of the middle column). Closed dock → original baseline. The dock is the *only* surface permitted to move the prompt-bar Y; everything else still obeys rule (1). - **Decision:** The prompt bar is pinned to a fixed Y-position in the middle column; every other surface makes room *around* Claude — never on top, never pushing the prompt off-Y. Three hard rules: (1) prompt bar Y-position is invariant across all states (open, collapsed, focus, editor, viewer); (2) the three bottom strips (left icon rail, app strip, right icon rail) align to one continuous horizontal line; (3) Claude is always the largest surface when present. The three-column layout from [D-11](#d-11-panel-manager-is-kernel-layout-is-data-three-column-is-a-preset) is refined: left = overview (tickets, decisions, files, git, PRs), middle = Claude (+ optional editor above), right = context (viewer, pql graph, links, images). Both side panels have a bottom icon rail for section switching; keyboard: `⌥1–5` (left), context-type switcher (right). - **Rationale:** "Claude is home" means the prompt never moves, regardless of what opens or closes around it. Every layout mutation respects this invariant. The three-column refinement assigns purpose to columns rather than leaving them generic. - **Cost:** The prompt bar invariant constrains future layout presets — any preset that repositions Claude must explicitly break this rule. Editor mode (see [D-49](#d-49-editor-mode-inline-above-claude-viewer-swap)) is the only case where another surface shares the middle column, and it opens *above* Claude rather than displacing it. @@ -433,3 +434,19 @@ Core, rendering, IPC, kernel, panel manager. - **Raised by:** 2026-06-06 — walking Q-32 during the T-208 wind-down; user chose the full namespace but constrained it to single-registry generation to avoid a second maintenance front, noting the extensions-first pattern needs that anyway. --- + +### D-87: Output/log dock — bottom, toggled, read-only (logs + problems) +- **Date:** 2026-06-06 +- **Status:** accepted +- **Decision:** clide gains a **bottom output dock** — a toggled, **read-only** panel hosting two tabs: **Output** (the kernel log stream) and **Problems** (diagnostics). Resolves [Q-28](../questions/architecture.md#q-28-terminal-strip-scope--shell-only-or-logserrorstests): the bottom strip hosts logs/problems, **not** the terminal. + - **Toggle = one merged status-bar widget.** Clicking it (or `⌘J` / `Ctrl+J`) opens/closes the dock. The widget **replaces the separate app-status indicator** rather than adding a segment (chrome budget, [D-48](#d-48-chrome-budget--no-tabs-no-breadcrumbs-keyboard-first)): it shows green `✓` when clean, `⚠`/`✕` diagnostic counts when not, and a `▲`/`▼` chevron for dock state. The badge means problem counts are visible **without** opening the dock. + - **Output tab:** the `Logger` stream (subsystems: ipc, mcp, extensions, pql, git, pane, …), filterable by **source + level + text**, auto-scrolling (follow tail; scrolling up pauses follow and shows "jump to latest"). + - **Problems tab:** diagnostics, **moved out of the left sidebar** — no duplication, and the dock's full width suits `severity · file:line · message` rows the 180–400px sidebar truncated. The status badge replaces the sidebar panel's always-visible glance. + - **Retention:** add a bounded in-memory **ring sink** to the `Logger` (drop-oldest, fixed cap). The logger today only live-broadcasts with no history, so late openers would see nothing; the ring serves "the last N records" on open. Same drop-oldest shape as the [D-85](#d-85-event-bus-delivery--bounded-ring-buffer-back-pressure-in-memory-cursor-retention-bus-owned-if-persisted) event ring. + - **Layout:** the dock occupies the bottom of the workspace column and pushes Claude **up**; the prompt bar rides up with it. Height is resizable (top edge) and open/closed + height persist per workspace. This is an explicit exception to [D-47](#d-47-interaction-model--claude-is-home-layout) rule (1) — see its amendment. +- **Rationale:** Q-28 asked whether the bottom strip should host logs/errors/tests. The answer splits **by interaction**: the dock is **passive** (you skim it) so it earns the always-present-but-collapsed bottom slot; the **terminal is active** (you work in it) so it stays a first-class surface in the editor pane ([D-49](#d-49-editor-mode--inline-above-claude-viewer-swap)), never demoted into a log strip. Merging health into the toggle widget delivers a new capability with **zero new persistent chrome** and keeps the at-a-glance count alive while the dock is closed. Problems moves in because the width fits and the badge covers the glance it used to provide from the sidebar. +- **Cost / risk:** Breaks D-47's literal prompt-bar Y-invariance — mitigated by the explicit amendment, the ≥50% cap (Claude stays the largest surface), and the dock only moving on a deliberate user toggle. Adds bounded log-buffer memory. Relocating Problems costs muscle memory + the slot move. Small-screen behaviour interacts with [Q-26](../questions/architecture.md#q-26-small-screen-layout--1000px): under a short window the dock + ≥50% cap may force a smaller dock or a modal — deferred to Q-26. +- **Cross-reference:** [D-47](#d-47-interaction-model--claude-is-home-layout) (amended — prompt-Y exception), [D-48](#d-48-chrome-budget--no-tabs-no-breadcrumbs-keyboard-first) (merged widget adds no segment), [D-49](#d-49-editor-mode--inline-above-claude-viewer-swap) (where the terminal lives instead), [D-51](#d-51-panel-collapse--12px-spine-with-badge) (collapse-with-badge pattern the toggle echoes), [D-85](#d-85-event-bus-delivery--bounded-ring-buffer-back-pressure-in-memory-cursor-retention-bus-owned-if-persisted) (drop-oldest ring reused), [Q-26](../questions/architecture.md#q-26-small-screen-layout--1000px). Resolves [Q-28](../questions/architecture.md#q-28-terminal-strip-scope--shell-only-or-logserrorstests). Implemented by T-54; the terminal's editor-pane home is tracked separately. +- **Raised by:** 2026-06-06 — T-54 UX design session (Frame0 wireframe under `docs/design/wireframes/output-dock/`). User chose a status-bar-toggled bottom dock scoped to read-only output, Problems folded in, terminal kept first-class in the editor pane. + +--- diff --git a/governance/questions/architecture.md b/governance/questions/architecture.md index f05a980d..3fd233ef 100644 --- a/governance/questions/architecture.md +++ b/governance/questions/architecture.md @@ -90,7 +90,8 @@ ticket persistence. - **Source:** 2026-04-22 interaction model spec (Wireframe — Flows v3), open question 2. ### Q-28: Terminal strip scope — shell only or logs/errors/tests -- **Status:** Open +- **Status:** Resolved → [D-87](../decisions/architecture.md#d-87-outputlog-dock--bottom-toggled-read-only-logs--problems) +- **Resolved (2026-06-06):** Split by interaction. A bottom **output dock** hosts **read-only** output — logs + problems — toggled from a merged health/log status-bar widget (D-87). The **terminal is NOT in the strip**: it's an interactive surface, kept first-class in the editor pane (D-49). So "both, later" became "logs/problems yes, terminal no." - **Question:** Is the app strip (bottom bar) purely a terminal shell + status, or does it also host tabs for logs, errors, and test output? Probably both, later. - **Context:** The interaction model spec defines the app strip as 14px with terminal shell + daemon indicator + branch; expanding on focus. If it grows to host logs/errors/tests, it becomes a mini-panel with its own tab model. - **Source:** 2026-04-22 interaction model spec (Wireframe — Flows v3), open question 3.