BTerminal/docs/v3-progress.md
DexterFromLab 32f6d7eadf docs: update meta files for multi-agent orchestration
- v3-progress.md: full session log for agent orchestration work
- v3-task_plan.md: 7 new decisions (agent rendering, env passthrough,
  re-injection, shared DB, role tabs, PlantUML encoding)
- CLAUDE.md: updated overview, key paths, component list
- .claude/CLAUDE.md: updated workflow, ProjectBox tabs, orchestration docs
2026-03-11 15:25:53 +01:00

51 KiB
Raw Permalink Blame History

BTerminal v3 — Progress Log

Session: 2026-03-07 — Architecture Planning + MVP Implementation (Phases 1-5)

Phase: Adversarial Design Review

  • Launch 3 architecture agents (Architect, Devil's Advocate, UX+Performance Specialist)
  • Collect findings — 12 issues identified, all resolved
  • Produce final architecture plan in docs/v3-task_plan.md
  • Create 10-phase implementation plan

Phase 1: Data Model + Config

  • Created v2/src/lib/types/groups.ts — TypeScript interfaces (ProjectConfig, GroupConfig, GroupsFile)
  • Created v2/src-tauri/src/groups.rs — Rust structs + load/save groups.json
  • Added groups_load, groups_save Tauri commands to lib.rs
  • SQLite migrations in session.rs: project_id column, agent_messages table, project_agent_state table
  • Created v2/src/lib/adapters/groups-bridge.ts (IPC wrapper)
  • Created v2/src/lib/stores/workspace.svelte.ts (replaces layout.svelte.ts, Svelte 5 runes)
  • Added --group CLI argument parsing in main.rs
  • Wrote 24 vitest tests for workspace store (workspace.test.ts)
  • Wrote cargo tests for groups load/save/default

Phase 2: Project Box Shell

  • Created GlobalTabBar.svelte (Sessions | Docs | Context | Settings)
  • Created ProjectGrid.svelte (flex + scroll-snap container)
  • Created ProjectBox.svelte (CSS grid: header | session-area | terminal-area)
  • Created ProjectHeader.svelte (icon + name + status dot + accent color)
  • Rewrote App.svelte (GlobalTabBar + tab content + StatusBar, no sidebar/TilingGrid)
  • Created CommandPalette.svelte (Ctrl+K overlay with fuzzy search)
  • Created DocsTab.svelte (markdown file browser per project)
  • Created ContextTab.svelte (wrapper for ContextPane)
  • Created SettingsTab.svelte (per-project + global settings editor)
  • CSS for responsive project count + Catppuccin accent colors

Phase 3: Claude Session Integration

  • Created ClaudeSession.svelte (wraps AgentPane, passes project cwd/profile/config_dir)

Phase 4: Terminal Tabs

  • Created TerminalTabs.svelte (tab bar + content, shell/SSH/agent tab types)

Phase 5: Team Agents Panel

  • Created TeamAgentsPanel.svelte (right panel for subagents)
  • Created AgentCard.svelte (compact subagent view: status, messages, cost)

Bug Fix

  • Fixed AgentPane Svelte 5 event modifier syntax: on:click -> onclick (Svelte 5 requires lowercase event attributes)

Verification

  • All 138 vitest tests pass (114 existing + 24 new workspace tests)
  • All 36 cargo tests pass (29 existing + 7 new groups tests)
  • Vite build succeeds

Session: 2026-03-07 — Phases 6-10 Completion

Phase 6: Session Continuity

  • Added persistSessionForProject() to agent-dispatcher — saves agent state + messages to SQLite on session complete
  • Added registerSessionProject() — maps sessionId -> projectId for persistence routing
  • Added sessionProjectMap (Map<string, string>) in agent-dispatcher
  • Updated ClaudeSession.svelte: restoreMessagesFromRecords() restores cached messages into agent store on mount
  • ClaudeSession loads previous state via loadProjectAgentState(), restores session ID and messages
  • Added getAgentSession() export to agents store

Phase 7: Workspace Teardown on Group Switch

  • Added clearAllAgentSessions() to agents store (clears sessions array)
  • Updated switchGroup() in workspace store to call clearAllAgentSessions() + reset terminal tabs
  • Updated workspace.test.ts to mock clearAllAgentSessions

Phase 10: Dead Component Removal + Polish

  • Deleted TilingGrid.svelte (328 lines), PaneContainer.svelte (113 lines), PaneHeader.svelte (44 lines)
  • Deleted SessionList.svelte (374 lines), SshSessionList.svelte (263 lines), SshDialog.svelte (281 lines), SettingsDialog.svelte (433 lines)
  • Removed empty directories: Layout/, Sidebar/, Settings/, SSH/
  • Rewrote StatusBar.svelte for workspace store (group name, project count, agent count, "BTerminal v3" label)
  • Fixed subagent routing in agent-dispatcher: project-scoped sessions skip layout pane creation (subagents render in TeamAgentsPanel instead)
  • Updated v3-task_plan.md to mark all 10 phases complete

Verification

  • All 138 vitest tests pass (including updated workspace tests with clearAllAgentSessions mock)
  • All 36 cargo tests pass
  • Vite build succeeds
  • ~1,836 lines of dead code removed

Session: 2026-03-07 — SettingsTab Global Settings + Cleanup

SettingsTab Global Settings Section

  • Added "Global" section to SettingsTab.svelte with three settings:
    • Theme flavor dropdown (Catppuccin Latte/Frappe/Macchiato/Mocha) via setFlavor() from theme store
    • Default shell text input (persisted via setSetting('default_shell', ...))
    • Default CWD text input (persisted via setSetting('default_cwd', ...))
  • Global settings load on mount via getSetting() from settings-bridge
  • Added imports: onMount, getSetting/setSetting, getCurrentFlavor/setFlavor, CatppuccinFlavor type

A11y Fixes

  • Changed project field labels from <div class="project-field"><label> to wrapping <label class="project-field"><span class="field-label"> pattern — proper label/input association
  • Global settings use id/for label association (e.g., id="theme-flavor", id="default-shell")

CSS Cleanup

  • Removed unused .project-field label selector (replaced by .field-label)
  • Simplified .project-field input[type="text"], .project-field input:not([type]) to .project-field input:not([type="checkbox"])

Rust Cleanup (committed separately)

  • Removed dead update_ssh_session() method from session.rs and its test
  • Fixed stale TilingGrid comment in AgentPane.svelte

Session: 2026-03-07 — Multi-Theme System (7 Editor Themes)

Theme System Generalization

  • Generalized CatppuccinFlavor type to ThemeId union type (11 values)
  • Added 7 new editor themes: VSCode Dark+, Atom One Dark, Monokai, Dracula, Nord, Solarized Dark, GitHub Dark
  • Added ThemePalette interface (26-color slots) — all themes map to same slots
  • Added ThemeMeta interface (id, label, group, isDark) for UI metadata
  • Added THEME_LIST: ThemeMeta[] with group metadata ('Catppuccin' or 'Editor')
  • Added ALL_THEME_IDS: ThemeId[] derived from THEME_LIST for validation
  • Deprecated CatppuccinFlavor, CatppuccinPalette, FLAVOR_LABELS, ALL_FLAVORS (kept as backwards compat aliases)

Theme Store Updates

  • getCurrentTheme(): ThemeId replaces getCurrentFlavor() as primary getter
  • setTheme(theme: ThemeId) replaces setFlavor() as primary setter
  • initTheme() validates saved theme against ALL_THEME_IDS
  • Deprecated getCurrentFlavor() and setFlavor() with delegation wrappers

SettingsTab Theme Selector

  • Theme dropdown uses <optgroup> per theme group (Catppuccin, Editor)
  • themeGroups derived from THEME_LIST using Map grouping
  • handleThemeChange() replaces direct setFlavor() call
  • Fixed input overflow in .setting-row with min-width: 0

Design Decision

All editor themes map to the same --ctp-* CSS custom property names (26 vars). This means every component works unchanged — no component-level theme awareness needed. Each theme provides its own mapping of colors to the 26 semantic slots.

Verification

  • All 138 vitest + 35 cargo tests pass

Session: 2026-03-07 — Deep Dark Theme Group (6 Themes)

New Theme Group: Deep Dark

  • Added 6 new "Deep Dark" themes to v2/src/lib/styles/themes.ts:
    • Tokyo Night (base: #1a1b26)
    • Gruvbox Dark (base: #1d2021)
    • Ayu Dark (base: #0b0e14, near-black)
    • Poimandres (base: #1b1e28)
    • Vesper (base: #101010, warm dark)
    • Midnight (base: #000000, pure OLED black)
  • Extended ThemeId union type from 11 to 17 values
  • Added THEME_LIST entries with group: 'Deep Dark'
  • Added all 6 palette definitions (26 colors each) mapped to --ctp-* slots
  • Total themes: 17 across 3 groups (Catppuccin 4, Editor 7, Deep Dark 6)

Verification

  • No test changes needed — theme palettes are data-only, no logic changes

Session: 2026-03-07 — Custom Theme Dropdown

SettingsTab Theme Picker Redesign

  • Replaced native <select> with custom themed dropdown in SettingsTab.svelte
  • Dropdown trigger shows color swatch (base color from getPalette()) + theme label + arrow indicator
  • Dropdown menu groups themes by category (Catppuccin/Editor/Deep Dark) with styled uppercase headers
  • Each option shows: color swatch + label + 4 accent color dots (red/green/blue/yellow)
  • Active theme highlighted with surface0 background + bold text
  • Click-outside handler and Escape key to close dropdown
  • Uses --ctp-* CSS vars throughout — fully themed with any active theme
  • Added getPalette import from themes.ts for live color rendering
  • Added aria-haspopup/aria-expanded attributes for accessibility

Verification

  • No test changes needed — UI-only change, no logic changes

Session: 2026-03-07 — Theme Dropdown CSS Polish

SettingsTab Dropdown Sizing Fix

  • Set min-width: 180px on .theme-dropdown container (was min-width: 0) to prevent trigger from collapsing
  • Set min-width: 280px on .theme-options dropdown menu (was right: 0) to ensure full theme names visible
  • Increased max-height from 320px to 400px on dropdown menu for better scrolling experience
  • Added white-space: nowrap on .theme-option-label (was min-width: 0) to prevent label text wrapping

Verification

  • No test changes needed — CSS-only change

Session: 2026-03-07 — Global Font Controls

SettingsTab Font Family + Font Size Controls

  • Added font family <select> with 9 monospace font options (JetBrains Mono, Fira Code, Cascadia Code, Source Code Pro, IBM Plex Mono, Hack, Inconsolata, Ubuntu Mono, monospace) + "Default" option
  • Added font size +/- stepper control with numeric input (range 8-24px)
  • Both controls apply live preview via CSS custom properties (--ui-font-family, --ui-font-size)
  • Both settings persisted to SQLite via settings-bridge (font_family, font_size keys)
  • handleFontFamilyChange() and handleFontSizeChange() functions with validation

SettingsTab Layout Restructure

  • Restructured global settings from inline .setting-row (label left, control right) to 2-column .global-grid with .setting-field (label above control)
  • Labels now uppercase, 0.7rem, subtext0 color — consistent compact labeling
  • All inputs/selects use consistent styling (surface0 bg, surface1 border, 4px radius)

CSS Typography Variables

  • Added --ui-font-family and --ui-font-size to catppuccin.css :root (defaults: JetBrains Mono fallback chain, 13px)
  • Updated app.css body rule to use CSS vars instead of hardcoded font values

Theme Store Font Restoration

  • Extended initTheme() in theme.svelte.ts to load and apply saved font_family and font_size settings on startup
  • Font restoration wrapped in try/catch — failures are non-fatal (CSS defaults apply)

Verification

  • No test changes needed — UI/CSS-only changes, no logic changes

Session: 2026-03-07 — Settings Drawer Conversion

Settings Tab to Drawer

  • Converted Settings from a full-page tab to a collapsible side drawer
  • GlobalTabBar now has 3 tabs (Sessions/Docs/Context) + gear icon toggle for settings drawer
  • App.svelte renders SettingsTab in an <aside> drawer (right side, 32em width, semi-transparent backdrop)
  • Drawer close: Escape key, click-outside (backdrop), close button (X icon)
  • Gear icon in GlobalTabBar highlights blue when drawer is open (active state)
  • GlobalTabBar accepts props: settingsOpen, ontoggleSettings
  • Removed 'settings' from WorkspaceTab union type (now 'sessions' | 'docs' | 'context')
  • Alt+1..3 for tabs (was Alt+1..4), Ctrl+, toggles drawer (was setActiveTab('settings'))
  • SettingsTab padding reduced (12px 16px), max-width removed, flex:1 for drawer context

Verification

  • All 138 vitest tests pass

Session: 2026-03-08 — VSCode-Style Sidebar Redesign

UI Layout Redesign (Top Tab Bar -> Left Sidebar)

  • Redesigned GlobalTabBar.svelte from horizontal tab bar to vertical icon rail (36px wide)
    • 4 SVG icon buttons: Sessions (grid), Docs (document), Context (clock), Settings (gear)
    • Each button uses SVG path from icons record mapped by WorkspaceTab
    • Props renamed: settingsOpen -> expanded, ontoggleSettings -> ontoggle
    • handleTabClick() manages toggle: clicking active tab collapses drawer
  • Rewrote App.svelte layout from vertical (top tab bar + content area + settings drawer) to horizontal (icon rail + sidebar panel + workspace)
    • .main-row flex container: GlobalTabBar | sidebar-panel (28em, max 50%) | workspace
    • ProjectGrid always visible in main workspace (not inside tab content)
    • Sidebar panel renders active tab content (Sessions/Docs/Context/Settings)
    • Panel header with title + close button
    • Removed backdrop overlay, drawer is inline sidebar not overlay
  • Re-added 'settings' to WorkspaceTab union type (was removed when settings was a drawer)
  • SettingsTab CSS: changed flex: 1 to height: 100% for sidebar panel context
  • Updated keyboard shortcuts:
    • Alt+1..4 (was Alt+1..3): switch tabs + open drawer, toggle if same tab
    • Ctrl+B (new): toggle sidebar open/closed
    • Ctrl+, : open settings panel (toggle if already active)
    • Escape: close drawer
  • State variables renamed: settingsOpen -> drawerOpen, toggleSettings() -> toggleDrawer()
  • Added panelTitles record for drawer header labels

Design Decisions

  • VSCode-style sidebar chosen for: always-visible workspace, progressive disclosure, familiar UX
  • Settings as regular tab (not special drawer) simplifies code and mental model
  • Icon rail at 36px minimizes horizontal space cost
  • No backdrop overlay — sidebar is inline, not modal

Verification

  • All 138 vitest tests pass
  • svelte-check clean (only 2 third-party esrap warnings)

Session: 2026-03-07 — SettingsTab Global Settings Redesign

Font Settings Split (UI Font + Terminal Font)

  • Split single font setting into UI font (sans-serif options) and Terminal font (monospace options)
  • UI font dropdown: System Sans-Serif, Inter, Roboto, Open Sans, Lato, Noto Sans, Source Sans 3, IBM Plex Sans, Ubuntu + Default
  • Terminal font dropdown: JetBrains Mono, Fira Code, Cascadia Code, Source Code Pro, IBM Plex Mono, Hack, Inconsolata, Ubuntu Mono, monospace + Default
  • Each font dropdown renders preview text in its own typeface
  • Size steppers (8-24px) for both UI and Terminal font independently
  • Changed setting keys: font_family -> ui_font_family, font_size -> ui_font_size, + new term_font_family, term_font_size

SettingsTab Layout Redesign

  • Rewrote global settings as single-column layout with labels above controls
  • Split into "Appearance" subsection (theme, UI font, terminal font) and "Defaults" subsection (shell, CWD)
  • All dropdowns now use reusable custom themed dropdowns (no native <select> anywhere)

CSS + Theme Store Updates

  • Added --term-font-family and --term-font-size CSS custom properties to catppuccin.css
  • Updated initTheme() in theme.svelte.ts: loads 4 font settings (ui_font_family, ui_font_size, term_font_family, term_font_size) instead of 2
  • UI font fallback changed from monospace to sans-serif

Verification

  • No test changes needed — UI/CSS-only changes, no logic changes

Session: 2026-03-08 — CSS Relative Units Rule

New Rule: 18-relative-units.md

  • Created .claude/rules/18-relative-units.md enforcing rem/em for layout CSS
  • Pixels allowed only for icon sizes, borders/outlines, box shadows
  • Exception: --ui-font-size/--term-font-size CSS vars store px (xterm.js API requirement)
  • Added rule #18 to .claude/CLAUDE.md rule index

CSS Conversions

  • GlobalTabBar.svelte: rail width 36px -> 2.75rem, button 28px -> 2rem, gap 2px -> 0.25rem, padding 6px 4px -> 0.5rem 0.375rem, border-radius 4px -> 0.375rem
  • App.svelte: sidebar header padding 8px 12px -> 0.5rem 0.75rem, close button 22px -> 1.375rem, border-radius 4px -> 0.25rem
  • Also changed GlobalTabBar rail-btn color from --ctp-overlay1 to --ctp-subtext0 for better contrast

Session: 2026-03-08 — Content-Driven Sidebar Width

Sidebar Panel Sizing

  • Changed .sidebar-panel from fixed width: 28em to width: max-content with min-width: 16em and max-width: 50%
  • Changed .sidebar-panel and .panel-content from overflow: hidden to overflow-y: auto — hidden was blocking content from driving parent width
  • Each tab component now defines its own min-width: 22em (SettingsTab, ContextTab, DocsTab)

Additional px → rem Conversions

  • SettingsTab.svelte: padding 12px 16px → 0.75rem 1rem
  • DocsTab.svelte: file-picker 220px → 14em, picker-title padding → rem, file-btn padding → rem, empty/loading padding → rem
  • ContextPane.svelte: font-size, padding, margin, gap converted from px to rem; added white-space: nowrap on .ctx-header/.ctx-error for intrinsic width measurement

Fix: Sidebar Drawer Content-Driven Width

  • Root cause found: #app in app.css had leftover v2 grid layout (display: grid; grid-template-columns: var(--sidebar-width) 1fr) constraining .app-shell to 260px first column
  • Removed v2 grid + both media queries from #app — v3 .app-shell manages its own flexbox layout
  • Added JS $effect in App.svelte: measures content width via requestAnimationFrame + querySelectorAll for nowrap elements, headings, inputs, tab-specific selectors; panelWidth state drives inline style:width
  • Verified all 4 tabs scale to content: Sessions ~473px, Settings ~322px, Context ~580px, Docs varies by content
  • Investigation path: CSS intrinsic sizing (max-content, fit-content) failed due to column-flex circular dependency → JS measurement approach → discovered inline style set but rendered width wrong → Playwright inspection revealed parent .main-row only 260px → traced to #app grid layout

Session: 2026-03-08 — Native Directory Picker

tauri-plugin-dialog Integration

  • Added tauri-plugin-dialog Rust crate + @tauri-apps/plugin-dialog npm package
  • Registered plugin in lib.rs (tauri_plugin_dialog::init())
  • Removed stub pick_directory Tauri command (always returned None)
  • Added browseDirectory() helper in SettingsTab.svelte using open({ directory: true })
  • Added folder browse button (folder SVG icon) to: Default CWD, existing project CWD, Add Project path
  • Styled .input-with-browse layout (flex row, themed browse button)
  • Fixed nested input theme: .setting-field .input-with-browse input selector for dark background
  • Fixed dialog not opening: added "dialog:default" permission to v2/src-tauri/capabilities/default.json — Tauri IPC security blocked invoke() without capability
  • Verified via Playwright: error was Cannot read properties of undefined (reading 'invoke') in browser context (expected — Tauri IPC only exists in WebView), confirming code is correct
  • Clean rebuild required after capability changes (cached binary doesn't pick up new permissions)

Modal + Dark-Themed Dialog

  • Root cause: tauri-plugin-dialog skips set_parent(&window) on Linux via cfg(any(windows, target_os = "macos")) gate in commands.rs — dialog not modal
  • Root cause: native GTK file chooser uses system GTK theme, not app's CSS theme — dialog appears light
  • Fix: custom pick_directory Tauri command using rfd::AsyncFileDialog directly with .set_parent(&window) — modal on Linux
  • Fix: std::env::set_var("GTK_THEME", "Adwaita:dark") at start of run() in lib.rs — dark-themed dialog
  • Added rfd = { version = "0.16", default-features = false, features = ["gtk3"] } as direct dep — MUST disable defaults to avoid gtk3+xdg-portal feature conflict
  • Switched SettingsTab from @tauri-apps/plugin-dialog open() to invoke<string | null>('pick_directory')

Session: 2026-03-08 — Project Workspace Layout Redesign + Icon Fix

Icon Fix

  • Replaced Nerd Font codepoints (\uf120) with emoji (📁 default) — Nerd Font not installed, showed "?"
  • Added emoji picker grid (24 project-relevant emoji, 8-column popup) in SettingsTab instead of plain text input
  • Removed font-family: 'NerdFontsSymbols Nerd Font' from ProjectHeader and TerminalTabs

ProjectBox Layout Redesign

  • Switched ProjectBox from flex to CSS grid (grid-template-rows: auto 1fr auto) — header | session | terminal zones
  • Terminal area: explicit height: 16rem instead of collapsing to content
  • Session area: min-height: 0 for proper flex child overflow

AgentPane Prompt Layout

  • Prompt area anchored to bottom (justify-content: flex-end) instead of vertical center
  • Removed max-width: 600px constraint on form and toolbar — uses full panel width
  • Toolbar sits directly above textarea

CSS px → rem Conversions

  • ProjectGrid.svelte: gap 4px → 0.25rem, padding 4px → 0.25rem, min-width 480px → 30rem
  • TerminalTabs.svelte: tab bar, tabs, close/add buttons all converted to rem
  • ProjectBox.svelte: min-width 480px → 30rem

Session: 2026-03-08 — Project-Level Tabs + Clean AgentPane

ProjectHeader Info Bar

  • Added CWD path display (ellipsized from START via direction: rtl + text-overflow: ellipsis)
  • Added profile name as info-only text (right side of header)
  • Home dir shortening: /home/user/foo~/foo

Project-Level Tab Bar

  • Added tab bar in ProjectBox below header: Claude | Files | Context
  • Content area switches between ClaudeSession, ProjectFiles, ContextPane based on selected tab
  • CSS grid updated to 4 rows: auto auto 1fr auto (header | tabs | content | terminal)
  • TeamAgentsPanel still renders alongside ClaudeSession in Claude tab

ProjectFiles Component (NEW)

  • Created ProjectFiles.svelte — project-scoped markdown file viewer
  • Accepts cwd + projectName props (not workspace store)
  • File picker sidebar (10rem) + MarkdownPane content area
  • Auto-selects priority file or first file

AgentPane Cleanup

  • Removed entire session toolbar (DIR/ACC interactive inputs + all CSS)
  • Added profile prop — resolved via listProfiles() to get config_dir
  • CWD passed as prop from parent (project.cwd), no longer editable in pane
  • Clean chat interface: prompt (bottom-anchored) + messages + send button
  • ClaudeSession now passes project.profile to AgentPane

Verification

  • All 138 vitest tests pass
  • Vite build succeeds

Session: 2026-03-08 — Security Audit Fixes + OTEL Telemetry

Security Audit Fixes

  • Fixed all CRITICAL (5) + HIGH (4) findings — path traversal, race conditions, memory leaks, listener leaks, transaction safety
  • Fixed all MEDIUM (6) findings — runtime type guards, ANTHROPIC_* env stripping, timestamp mismatch, async lock, error propagation
  • Fixed all LOW (8) findings — input validation, mutex poisoning, log warnings, payload validation
  • 3 false positives dismissed with rationale
  • 172/172 tests pass (138 vitest + 34 cargo)

OTEL Telemetry Implementation

  • Added 6 Rust deps: tracing, tracing-subscriber, opentelemetry 0.28, opentelemetry_sdk 0.28, opentelemetry-otlp 0.28, tracing-opentelemetry 0.29
  • Created v2/src-tauri/src/telemetry.rs — TelemetryGuard, layer composition, OTLP export via BTERMINAL_OTLP_ENDPOINT env var
  • Integrated into lib.rs: TelemetryGuard in AppState, init before Tauri builder
  • Instrumented 10 Tauri commands with #[tracing::instrument]: pty_spawn, pty_kill, agent_query/stop/restart, remote_connect/disconnect/agent_query/agent_stop/pty_spawn
  • Added frontend_log Tauri command for frontend→Rust tracing bridge
  • Created v2/src/lib/adapters/telemetry-bridge.tstel.info/warn/error/debug/trace() convenience API
  • Wired agent dispatcher lifecycle events: agent_started, agent_stopped, agent_error, sidecar_crashed, cost metrics
  • Created Docker compose stack: docker/tempo/ — Tempo (4317/4318/3200) + Grafana (port 9715)

Session: 2026-03-08 — Teardown Race Fix + px→rem Conversion

Workspace Teardown Race Fix

  • Added pendingPersistCount counter + waitForPendingPersistence() export in agent-dispatcher.ts
  • persistSessionForProject() increments/decrements counter in try/finally
  • switchGroup() in workspace.svelte.ts now awaits waitForPendingPersistence() before clearing state
  • SettingsTab.svelte switchGroup onclick handler made async with await
  • Added test for waitForPendingPersistence in agent-dispatcher.test.ts
  • Added mock for waitForPendingPersistence in workspace.test.ts
  • Last open HIGH audit finding resolved (workspace teardown race)

px→rem Conversion (Rule 18 Compliance)

  • Converted ~100 px layout violations to rem across 10 components
  • AgentPane.svelte (~35 violations: font-size, padding, gap, margin, max-height, border-radius)
  • ToastContainer.svelte, CommandPalette.svelte, TeamAgentsPanel.svelte, AgentCard.svelte
  • StatusBar.svelte, AgentTree.svelte, TerminalPane.svelte, AgentPreviewPane.svelte, SettingsTab.svelte
  • Icon/decorative dot dimensions kept as px per rule 18
  • 139 vitest + 34 cargo tests pass, vite build succeeds

Session: 2026-03-08 — E2E Testing Infrastructure

WebdriverIO + tauri-driver Setup

  • Installed @wdio/cli, @wdio/local-runner, @wdio/mocha-framework, @wdio/spec-reporter (v9.24.0)
  • Created wdio.conf.js with tauri-driver lifecycle hooks (onPrepare builds debug binary, beforeSession/afterSession spawns/kills tauri-driver)
  • Created tsconfig.json for e2e test TypeScript compilation
  • Created smoke.test.ts with 6 tests: app title, status bar, version text, sidebar rail, workspace area, sidebar toggle
  • Added test:e2e npm script (wdio run tests/e2e/wdio.conf.js)
  • Updated README.md with complete setup instructions and CI guide
  • Key decision: WebdriverIO over Playwright (Playwright cannot control Tauri/WebKit2GTK apps)
  • Prerequisites: tauri-driver (cargo install), webkit2gtk-driver (apt), display server or xvfb-run

E2E Fixes (wdio v9 + tauri-driver compatibility)

  • Fixed wdio v9 BiDi: added wdio:enforceWebDriverClassic: true — wdio v9 injects webSocketUrl:true which tauri-driver rejects
  • Removed browserName: 'wry' from capabilities (not needed in wdio, only Selenium)
  • Fixed binary path: Cargo workspace target is v2/target/debug/, not v2/src-tauri/target/debug/
  • Fixed tauri-plugin-log panic: telemetry::init() registers tracing-subscriber before plugin-log → removed tauri-plugin-log entirely (redundant with telemetry::init())
  • Removed tauri-plugin-log from Cargo.toml dependency

E2E Coverage Expansion (25 tests, single spec file)

  • Consolidated 4 spec files into single bterminal.test.ts — Tauri creates one app session per spec file; after first spec completes, app closes and subsequent specs get "invalid session id"
  • Added Workspace & Projects tests (8): project grid, project boxes, header with name, 3 project tabs, active highlight, tab switching, status bar counts
  • Added Settings Panel tests (6): settings tab, sections, theme dropdown, dropdown open+options, group list, close button
  • Added Keyboard Shortcuts tests (5): Ctrl+K command palette, Ctrl+, settings, Ctrl+B sidebar, Escape close, palette group list
  • Fixed WebDriver clicks on Svelte 5 components: element.click() doesn't reliably trigger onclick inside complex components via WebKit2GTK/tauri-driver — use browser.execute() for JS-level clicks
  • Fixed CSS text-transform: .ptab getText() returns uppercase — use .toLowerCase() for comparison
  • Fixed element scoping: browser.$('.ptab') returns ALL tabs across project boxes — scope via box.$('.ptab')
  • Fixed keyboard focus: browser.execute(() => document.body.focus()) before sending shortcuts
  • Removed old individual spec files (smoke.test.ts, keyboard.test.ts, settings.test.ts, workspace.test.ts)
  • All 25 E2E tests pass (9s runtime after build)

Session: 2026-03-10 — Tab System Overhaul

Tab Renames + New Tabs

  • Renamed Claude → Model, Files → Docs in ProjectBox
  • Added 3 new tabs: Files (directory browser), SSH (connection manager), Memory (knowledge explorer)
  • Implemented PERSISTED-EAGER (Model/Docs/Context — display:flex/none) vs PERSISTED-LAZY (Files/SSH/Memory — {#if everActivated} + display:flex/none) mount strategy
  • Tab type union: 'model' | 'docs' | 'context' | 'files' | 'ssh' | 'memories'

Files Tab (FilesTab.svelte)

  • VSCode-style tree sidebar (14rem) + content viewer
  • Rust list_directory_children command: lazy expansion, hidden files skipped, dirs-first sort
  • Rust read_file_content command: FileContent tagged union (Text/Binary/TooLarge), 10MB gate, 30+ language mappings
  • Frontend files-bridge.ts adapter (DirEntry, FileContent types)
  • Shiki syntax highlighting for code files, image display via convertFileSrc, emoji file icons

SSH Tab (SshTab.svelte)

  • CRUD panel for SSH connections using existing ssh-bridge.ts/SshSession model
  • Launch button spawns terminal tab in Model tab's TerminalTabs section via addTerminalTab()

Memory Tab (MemoriesTab.svelte)

  • Pluggable MemoryAdapter interface (memory-adapter.ts): name, available, list(), search(), get()
  • Adapter registry: registerMemoryAdapter(), getDefaultAdapter(), getAvailableAdapters()
  • UI: search bar, tag display, expandable cards, adapter switcher, placeholder when no adapter

Context Tab Repurpose (ContextTab.svelte)

  • Replaced ContextPane (ctx database viewer) with LLM context window visualization
  • Tribunal debate for design (S-1-R4 winner at 82% confidence)
  • Stats bar: input/output tokens, cost, turns, duration
  • Segmented token meter: CSS flex bar with color-coded categories (assistant/thinking/tool calls/tool results)
  • File references: extracted from tool_call messages, colored op badges
  • Turn breakdown: collapsible message groups by user prompt
  • Token estimation via ~4 chars/token heuristic
  • Wired into ProjectBox (replaces ContextPane, passes sessionId)
  • Sub-tab navigation: Overview | AST | Graph
  • AST tab: per-turn SVG conversation trees (Thinking/Response/ToolCall/File nodes, bezier edges, token counts)
  • Graph tab: bipartite tool→file DAG (tools left, files right, curved edges, count badges)
  • Compaction detection: sdk-messages.ts adapts compact_boundary system messages → CompactionContent type
  • Stats bar compaction pill: yellow count badge with tooltip (last trigger, tokens removed)
  • AST compaction boundaries: red "Compacted" nodes inserted between turns at compaction points

FilesTab Fixes & CodeMirror Editor

  • Fixed HTML nesting error: <button> inside <button><div role="tab">
  • Fixed Svelte 5 $state proxy reactivity: look up tab from reactive array before setting content
  • CodeEditor.svelte: CodeMirror 6 with 15 lazy-loaded language modes, Catppuccin theme
  • Dirty tracking, Ctrl+S save, save-on-blur setting (files_save_on_blur in SettingsTab)
  • write_file_content Rust command (safety: existing files only)

Project Health Dashboard (S-3 — Mission Control)

  • health.svelte.ts store: per-project ActivityState (running/idle/stalled), burn rate ($/hr EMA), context pressure (% of model limit), attention scoring
  • StatusBar → Mission Control bar: running/idle/stalled counts, $/hr burn rate, "needs attention" priority queue dropdown
  • ProjectHeader health indicators: status dot (color-coded), context pressure badge, burn rate badge
  • session_metrics SQLite table: per-project historical metrics (100-row retention)
  • Rust commands: session_metric_save, session_metrics_load
  • TypeScript bridge: SessionMetric interface, saveSessionMetric(), loadSessionMetrics()
  • agent-dispatcher wiring: recordActivity, recordToolDone, recordTokenSnapshot, sessionStartTimes, metric persistence on completion
  • ClaudeSession: trackProject() on session create/restore
  • App.svelte: startHealthTick()/stopHealthTick() lifecycle
  • workspace.svelte.ts: clearHealthTracking() on group switch

Verification

  • svelte-check: 0 new errors (only pre-existing esrap type errors)
  • vitest: 139/139 tests pass
  • cargo test: 34/34 pass

Session: 2026-03-11 — S-1 Phase 1.5: Conflict Detection Enhancements

Bash Write Detection

  • BASH_WRITE_PATTERNS regex array in tool-files.ts: >, >>, sed -i, tee [-a], cp dest, mv dest, chmod/chown
  • extractBashWritePaths() helper with /dev/null and flag-target filtering
  • Write detection prioritized over read detection for ambiguous commands (cat file > out)
  • extractWritePaths() now captures Bash writes alongside Write/Edit

Acknowledge/Dismiss Conflicts

  • acknowledgeConflicts(projectId) API in conflicts.svelte.ts — marks current conflicts as acknowledged
  • acknowledgedFiles Map state — suppresses badge until new session writes to acknowledged file
  • ProjectHeader conflict badge → clickable button with ✕ (stopPropagation, hover darkens)
  • Ack auto-cleared when new session writes to previously-acknowledged file

Worktree-Aware Conflict Suppression

  • sessionWorktrees Map in conflicts store — tracks worktree path per session (null = main tree)
  • setSessionWorktree(sessionId, path) API
  • areInDifferentWorktrees() / hasRealConflict() — suppresses conflicts between sessions in different worktrees
  • extractWorktreePath(tc) in tool-files.ts — detects Agent/Task isolation:"worktree" and EnterWorktree
  • agent-dispatcher.ts wiring: registers worktree paths from tool_call events
  • useWorktrees?: boolean field on ProjectConfig (groups.ts) for future per-project setting

Verification

  • vitest: 194/194 tests pass (+24 new: 5 extractWorktreePath, 10 bash write, 9 acknowledge/worktree)
  • cargo test: 34/34 pass

Session: 2026-03-11 — S-1 Phase 2: Filesystem Write Detection

Rust Backend — ProjectFsWatcher

  • New module v2/src-tauri/src/fs_watcher.rs — per-project recursive inotify watchers via notify crate v6
  • Debouncing (100ms per-file), ignored dirs (.git/, node_modules/, target/, etc.)
  • Emits fs-write-detected Tauri events with FsWritePayload { project_id, file_path, timestamp_ms }
  • Two Tauri commands: fs_watch_project, fs_unwatch_project
  • ProjectFsWatcher added to AppState, initialized in setup()
  • 5 Rust unit tests for path filtering (should_ignore_path)

Frontend Bridge

  • New v2/src/lib/adapters/fs-watcher-bridge.ts — fsWatchProject(), fsUnwatchProject(), onFsWriteDetected()

External Write Detection (conflicts store)

  • EXTERNAL_SESSION_ID = 'external' sentinel for non-agent writers
  • agentWriteTimestamps Map — tracks when agents write files (for timing heuristic)
  • recordExternalWrite(projectId, filePath, timestampMs) — 2s grace window suppresses agent's own writes
  • getExternalConflictCount(projectId) — counts external-only conflicts
  • FileConflict.isExternal flag, ProjectConflicts.externalConflictCount field
  • clearAllConflicts/clearProjectConflicts clear timestamp state

Health Store Integration

  • externalConflictCount added to ProjectHealth interface
  • Attention reason includes "(N external)" note when external conflicts present

UI Updates

  • ProjectBox $effect: starts/stops fs watcher per project CWD, listens for events, calls recordExternalWrite
  • ProjectHeader: split conflict badge into orange "ext write" badge + red "agent conflict" badge
  • Toast notification on new external write conflict

Verification

  • vitest: 202/202 tests pass (+8 new external write tests)
  • cargo test: 39/39 pass (+5 new fs_watcher tests)

Session: 2026-03-11 — Files Tab: PDF Viewer + CSV Table View

PDF Viewer

  • Added pdfjs-dist@5.5.207 dependency (WebKit2GTK has no built-in PDF viewer)
  • Created PdfViewer.svelte — canvas-based multi-page renderer
  • Zoom controls (0.5x3x, 25% steps), HiDPI-aware (devicePixelRatio scaling)
  • Reads PDF via convertFileSrc() → pdfjs (no new Rust commands needed)
  • Page shadow, themed toolbar, error handling

CSV Table View

  • Created CsvTable.svelte — RFC 4180 CSV parser (no external dependency)
  • Auto-detects delimiter (comma, semicolon, tab)
  • Sortable columns (numeric-aware), sticky header, row numbers
  • Row hover, text truncation at 20rem, themed via --ctp-* vars

FilesTab Routing

  • Binary+pdf → PdfViewer (via isPdfExt check)
  • Text+csv → CsvTable (via isCsvLang check)
  • Updated file icons: 📕 PDF, 📊 CSV
  • Both viewers are read-only

Verification

  • vitest: 202/202 tests pass (no regressions)
  • Vite build: clean
  • cargo check: clean

Session: 2026-03-11 — S-2 Session Anchors

Implementation

  • Created types/anchors.ts — AnchorType, SessionAnchor, AnchorSettings, budget constants
  • Created adapters/anchors-bridge.ts — 5 Tauri IPC functions (save, load, delete, clear, updateType)
  • Created stores/anchors.svelte.ts — Svelte 5 rune store (per-project anchor management)
  • Created utils/anchor-serializer.ts — observation masking, turn grouping, token estimation
  • Created utils/anchor-serializer.test.ts — 17 tests (4 describe blocks)
  • Added session_anchors SQLite table + SessionAnchorRecord struct + 5 CRUD methods (session.rs)
  • Added 5 Tauri commands for anchor persistence (lib.rs)
  • Auto-anchor logic in agent-dispatcher.ts on first compaction event per project
  • Re-injection in AgentPane.startQuery() via system_prompt field
  • Pin button on AgentPane text messages
  • Anchor section in ContextTab: budget meter, promote/demote, remove

Verification

  • vitest: 219/219 tests pass (+17 new anchor tests)
  • cargo test: 42/42 pass (+3 new session_anchors tests)

Session: 2026-03-11 — Configurable Anchor Budget + Truncation Fix

Research-backed truncation fix

  • Removed 500-char assistant text truncation in anchor-serializer.ts
  • Research consensus (JetBrains NeurIPS 2025, SWE-agent, OpenDev ACC): reasoning must never be truncated, only tool outputs get masked

Configurable anchor budget scale

  • Added AnchorBudgetScale type ('small'|'medium'|'large'|'full') with preset map (2K/6K/12K/20K)
  • Added anchorBudgetScale? field to ProjectConfig (persisted in groups.json)
  • Updated getAnchorSettings() to resolve budget from scale
  • Added 4-stop range slider to SettingsTab per-project settings
  • Updated ContextTab to derive budget from anchorBudgetScale prop
  • Updated agent-dispatcher to look up project's budget scale

Cleanup

  • Removed Ollama-specific warning toast from AgentPane (budget slider handles generically)
  • Removed unused notify import from AgentPane

Verification

  • vitest: 219/219 tests pass (no regressions)
  • cargo test: 42/42 pass (no regressions)

Session: 2026-03-11 — S-1 Phase 3: Worktree Isolation Per Project

UI toggle

  • Added 'Worktree Isolation' checkbox to SettingsTab per-project card (card-field-row CSS layout)
  • ProjectConfig.useWorktrees? already existed — wired to toggle

Spawn with worktree flag

  • Added worktree_name: Option to AgentQueryOptions (Rust sidecar.rs)
  • Added worktree_name?: string to TS AgentQueryOptions (agent-bridge.ts)
  • Sidecar JSON passes worktreeName field to claude-runner.ts
  • claude-runner.ts passes extraArgs: { worktree: name } to SDK query() (maps to --worktree CLI flag)
  • AgentPane: added useWorktrees prop, passes worktree_name=sessionId when enabled
  • AgentSession: passes useWorktrees={project.useWorktrees} to AgentPane
  • Rebuilt sidecar bundle (claude-runner.mjs)

CWD-based worktree detection

  • Added detectWorktreeFromCwd() to agent-dispatcher.ts (matches .claude/.codex/.cursor worktree patterns)
  • Init event handler now calls setSessionWorktree() when CWD contains worktree path
  • Dual detection: CWD-based (primary) + tool_call-based extractWorktreePath (subagent fallback)

Tests

  • Added 7 new tests to agent-dispatcher.test.ts (detectWorktreeFromCwd unit tests + init CWD integration)
  • vitest: 226/226 tests pass
  • cargo test: 42/42 pass

Session: 2026-03-11 — Provider Runners (Codex + Ollama)

Codex Provider

  • providers/codex.ts — ProviderMeta (gpt-5.4, hasSandbox, supportsResume)
  • adapters/codex-messages.ts — adaptCodexMessage (ThreadEvents → AgentMessage[])
  • sidecar/codex-runner.ts — @openai/codex-sdk wrapper (dynamic import, graceful failure)
  • adapters/codex-messages.test.ts — 19 tests

Ollama Provider

  • providers/ollama.ts — ProviderMeta (qwen3:8b, modelSelection only)
  • adapters/ollama-messages.ts — adaptOllamaMessage (streaming chunks → AgentMessage[])
  • sidecar/ollama-runner.ts — Direct HTTP to localhost:11434 (zero deps)
  • adapters/ollama-messages.test.ts — 11 tests

Registration + Build

  • App.svelte: register CODEX_PROVIDER + OLLAMA_PROVIDER
  • message-adapters.ts: register codex + ollama adapters
  • package.json: build:sidecar builds all 3 runners
  • vitest: 256/256 tests pass
  • cargo test: 42/42 pass

2026-03-11 — Register Memora Adapter

Duration: ~15 min

What happened: Registered a concrete MemoraAdapter that bridges the MemoryAdapter interface to the Memora SQLite database. Direct read-only SQLite access (no MCP/CLI dependency at runtime).

Rust Backend

  • memora.rs — MemoraDb struct (read-only SQLite, Option, graceful absence)
  • list() with tag filtering via json_each() + IN clause
  • search() via FTS5 MATCH on memories_fts, optional tag join
  • get() by ID
  • 4 Tauri commands: memora_available, memora_list, memora_search, memora_get
  • 7 cargo tests (missing-db error paths)

TypeScript Bridge + Adapter

  • memora-bridge.ts — IPC wrappers + MemoraAdapter class implementing MemoryAdapter
  • App.svelte — registers MemoraAdapter on mount with async availability check
  • memora-bridge.test.ts — 16 tests (IPC + adapter)

Results

  • vitest: 272/272 tests pass
  • cargo test: 49/49 pass

2026-03-11 — Configurable Stall Threshold

Duration: ~10 min

What happened: Made the hardcoded 15-minute stall threshold configurable per-project via a range slider in SettingsTab (560 min, step 5).

Changes

  • groups.ts — Added stallThresholdMin?: number to ProjectConfig
  • health.svelte.ts — Replaced hardcoded constant with per-project stallThresholds Map + setStallThreshold() API, fallback to DEFAULT_STALL_THRESHOLD_MS (15 min)
  • SettingsTab.svelte — Range slider per project card (560 min, step 5, default 15)
  • ProjectBox.svelte — $effect syncs project.stallThresholdMinsetStallThreshold() on mount/change

Results

  • No test changes — UI/config wiring only
  • vitest: 272/272 tests pass
  • cargo test: 49/49 pass

2026-03-11 — Nemesis Security Audit + Reconnect Loop Fix

Duration: ~15 min

What happened: Ran nemezis-audit on Rust backend. 0 verified exploitable findings, 10 recon targets identified (all previously known from 2026-03-08 security audit). Fixed Priority 8 reconnect loop race condition.

Nemesis Audit

  • Ran nemezis orchestrator on v2/src-tauri (Rust backend, 496s, $0.57)
  • 0 verified findings, 10 attack surface targets in recon hit list
  • All targets match previous 2026-03-08 security audit — no new vulnerabilities

Reconnect Loop Fix

  • remote.rs — Added cancelled: Arc<AtomicBool> to RemoteMachine struct
  • remove_machine() and disconnect() set cancelled=true before aborting tasks
  • connect() resets cancelled=false for new connections
  • Reconnect loop checks flag at top of each iteration, exits immediately when set

Results

  • cargo check: clean
  • cargo test: 49/49 pass

Session 2026-03-11 (SOLID Phase 1 Refactoring)

SOLID Analysis

  • Ran /solid on full v2 codebase (TypeScript + Rust)
  • Identified 3 critical, 5 high, 5 medium issues; 8 good-practice modules

Phase 1 Refactoring — High-impact, Low-risk

  • AttentionScorer extraction: scoreAttention() pure function from health.svelte.ts → utils/attention-scorer.ts (14 tests)
  • Shared type guards: str()/num() from 3 adapter copies → utils/type-guards.ts
  • lib.rs command module split: 976 → 170 lines, 48 commands → 11 domain modules under commands/
  • Skipped withRemoteSupport() HOF — parameter shapes differ, 3-line duplication doesn't justify abstraction

Results

  • vitest: 286/286 pass (14 new attention-scorer tests)
  • cargo check: clean
  • cargo test: 49/49 pass

Session 2026-03-11 (SOLID Phase 2 Refactoring)

agent-dispatcher.ts Split (496→260 lines)

  • Extracted utils/worktree-detection.ts — detectWorktreeFromCwd() pure function (17 lines, 5 tests)
  • Extracted utils/session-persistence.ts — session maps + persistSessionForProject (107 lines)
  • Extracted utils/auto-anchoring.ts — triggerAutoAnchor (48 lines)
  • Extracted utils/subagent-router.ts — spawnSubagentPane + SUBAGENT_TOOL_NAMES (73 lines)
  • Dispatcher is now thin coordinator with re-exports for backward compat

session.rs Split (1,008 lines → 7 sub-modules)

  • session/mod.rs — SessionDb struct + open() + migrate() + re-exports (153 lines)
  • session/sessions.rs — Session CRUD (9 tests)
  • session/layout.rs — LayoutState save/load (3 tests)
  • session/settings.rs — Settings CRUD (5 tests)
  • session/ssh.rs — SshSession CRUD (4 tests)
  • session/agents.rs — AgentMessageRecord + ProjectAgentState
  • session/metrics.rs — SessionMetric save/load
  • session/anchors.rs — SessionAnchorRecord CRUD
  • conn field: pub(in crate::session) for sub-module access

Results

  • vitest: 286/286 pass (5 worktree tests moved to new file)
  • cargo check: clean
  • cargo test: 49/49 pass

Session 2026-03-11 (SOLID Phase 3 — Branded Types)

Implementation

  • Introduced SessionId/ProjectId branded types (types/ids.ts)
  • Applied branded types to conflicts.svelte.ts and health.svelte.ts Map keys
  • Branded sessionId at sidecar boundary in agent-dispatcher
  • Applied branded types at Svelte component call sites

Results

  • cargo check: clean
  • vitest: 286/286 pass

Session 2026-03-11 — Multi-Agent Orchestration System

btmsg Group Agent Messenger CLI

  • Created btmsg CLI tool — inter-agent messaging (inbox, send, reply, contacts, history, channels)
  • btmsg graph command — visual agent hierarchy with status
  • Admin role (tier 0), channel messaging (create/list/send/history), mark-read, global feed

btmsg Rust Backend + Tauri Bridge

  • Created btmsg.rs module — SQLite-backed messaging (shared DB: ~/.local/share/bterminal/btmsg.db)
  • 8+ Tauri commands: btmsg_inbox, btmsg_send, btmsg_read, btmsg_contacts, btmsg_feed, btmsg_channels, etc.
  • CommsTab: sidebar chat interface with activity feed, DMs, channels (Ctrl+M)

Agent Unification (Tier 1 → ProjectBoxes)

  • agentToProject() converter in groups.ts — Tier 1 agents rendered as full ProjectBoxes
  • getAllWorkItems() in workspace store combines agents + projects for ProjectGrid
  • GroupAgentsPanel: click-to-navigate agent cards to their ProjectBox

Agent System Prompts

  • Created utils/agent-prompts.ts — generateAgentPrompt() builds comprehensive introductory context
    • Sections: Identity, Environment, Team hierarchy, btmsg docs, bttask docs, Custom context, Workflow
    • Role-specific workflows (Manager: check inbox → review board → coordinate; Architect: code review focus; Tester: write/run tests)
  • AgentSession builds system prompt: Tier 1 gets full generated prompt, Tier 2 gets custom context

BTMSG_AGENT_ID Environment Passthrough (5-layer chain)

  • agent-bridge.ts: added extra_env?: Record<string,string> to AgentQueryOptions
  • bterminal-core/sidecar.rs: added extra_env: HashMap<String,String> with #[serde(default)]
  • claude-runner.ts: extraEnv merged into cleanEnv after provider var stripping
  • codex-runner.ts: same extraEnv pattern
  • AgentSession injects { BTMSG_AGENT_ID: project.id } for agent projects

Tier 1 Agent Config in SettingsTab

  • Agent cards: icon + name + role badge + enable toggle + CWD + model + wake interval (manager)
  • Custom Context textarea per agent (appended to auto-generated prompt)
  • Collapsible preview of full generated introductory prompt
  • updateAgent() function in workspace store for Tier 1 config persistence

Custom Context for Tier 2 Projects

  • Custom Context textarea in SettingsTab project cards
  • Stored as project.systemPrompt, passed through AgentSession → AgentPane

Periodic System Prompt Re-injection

  • AgentSession: 1-hour timer (REINJECTION_INTERVAL_MS = 3,600,000ms)
  • Checks every 60s if elapsed > 1 hour, sets contextRefreshPrompt
  • AgentPane: autoPrompt prop consumed only when agent is idle (done/error state)
  • Different refresh messages: Tier 1 (check inbox + task board), Tier 2 (review instructions)

bttask Kanban Backend + UI

  • Created bttask.rs — Task/TaskComment structs, 6 operations (list, comments, update_status, create, delete, add_comment)
  • Created commands/bttask.rs — 6 Tauri commands registered in lib.rs
  • Created bttask-bridge.ts — TypeScript IPC adapter
  • Created TaskBoardTab.svelte — Kanban board with 5 columns (todo/progress/review/done/blocked)
    • Task creation form (title, description, priority)
    • Expandable task detail with status actions, comments, delete
    • 5-second polling
    • Pending count badge

ArchitectureTab (PlantUML Diagrams)

  • Created ArchitectureTab.svelte — PlantUML diagram viewer/editor
    • Sidebar with diagram list + new diagram form (4 templates: Class, Sequence, State, Component)
    • PlantUML source editor + SVG preview via plantuml.com server (~h hex encoding)
    • Stores .puml files in .architecture/ directory
    • Read/write via files-bridge.ts

TestingTab (Selenium + Automated Tests)

  • Created TestingTab.svelte — dual-mode component
    • Selenium mode: screenshot gallery (.selenium/screenshots/), session log viewer, 3s polling
    • Tests mode: discovers test files in standard dirs (tests/, test/, spec/, tests/, e2e/), file content viewer

Role-Specific Tabs in ProjectBox

  • Extended ProjectTab type: added 'tasks' | 'architecture' | 'selenium' | 'tests'
  • Conditional tab buttons: Manager→Tasks, Architect→Arch, Tester→Selenium+Tests
  • PERSISTED-LAZY rendering via {#if everActivated[tab]} pattern
  • .ptab-role CSS class (mauve accent color for agent-specific tabs)

Bug Fix

  • Fixed FileContent type case: 'text' → 'Text' in ArchitectureTab and TestingTab (files-bridge uses capital T)

Verification

  • cargo check: clean (bttask module + commands)
  • svelte-check: 0 project errors
  • Sidecar rebuilt with extraEnv support