docs: update meta files for SOLID Phase 2 refactoring
This commit is contained in:
parent
9c94272ca7
commit
584a38d096
5 changed files with 39 additions and 10 deletions
|
|
@ -5,7 +5,7 @@
|
|||
- v1 is a single-file Python app (`bterminal.py`). Changes are localized.
|
||||
- v2 docs are in `docs/`. Architecture decisions are in `docs/task_plan.md`.
|
||||
- v2 Phases 1-7 + multi-machine (A-D) + profiles/skills complete. Extras: SSH, ctx, themes, detached mode, auto-updater, shiki, copy/paste, session resume, drag-resize, session groups, Deno sidecar, Claude profiles, skill discovery.
|
||||
- v3 Mission Control (All Phases 1-10 Complete + S-1 Phase 1/1.5/2/3 + S-2 Session Anchors + Provider Adapter Pattern + Provider Runners + Memora Adapter): project groups, workspace store, 12 Workspace components, session continuity, workspace teardown, file overlap conflict detection, inotify-based external write detection, multi-provider adapter pattern (3 phases + Codex/Ollama runners), worktree isolation, session anchors, Memora adapter (read-only SQLite), dead v2 component cleanup. 286 vitest + 49 cargo tests.
|
||||
- v3 Mission Control (All Phases 1-10 Complete + S-1 Phase 1/1.5/2/3 + S-2 Session Anchors + Provider Adapter Pattern + Provider Runners + Memora Adapter + SOLID Phase 2): project groups, workspace store, 12 Workspace components, session continuity, workspace teardown, file overlap conflict detection, inotify-based external write detection, multi-provider adapter pattern (3 phases + Codex/Ollama runners), worktree isolation, session anchors, Memora adapter (read-only SQLite), SOLID refactoring (agent-dispatcher split → 4 utils, session.rs split → 7 sub-modules), dead v2 component cleanup. 286 vitest + 49 cargo tests.
|
||||
- v3 docs: `docs/v3-task_plan.md`, `docs/v3-findings.md`, `docs/v3-progress.md`.
|
||||
- Consult Memora (tag: `bterminal`) before making architectural changes.
|
||||
|
||||
|
|
@ -34,9 +34,9 @@
|
|||
- Agent sessions use `@anthropic-ai/claude-agent-sdk` query() function (migrated from raw CLI spawning due to piped stdio hang bug). SDK handles subprocess management internally. All output goes through the adapter layer (`src/lib/adapters/claude-messages.ts` via `message-adapters.ts` registry) — SDK message format matches CLI stream-json. Multi-provider support: message-adapters.ts routes by ProviderId to provider-specific parsers (claude-messages.ts, codex-messages.ts, ollama-messages.ts — all 3 registered).
|
||||
- Sidecar uses per-provider runner bundles (`sidecar/dist/{provider}-runner.mjs`). Currently only `claude-runner.mjs` exists. SidecarManager.resolve_sidecar_for_provider(provider) finds the right runner file. Deno preferred (faster startup), Node.js fallback. Communicates with Rust via stdio NDJSON. Claude CLI auto-detected at startup via `findClaudeCli()` — checks ~/.local/bin/claude, ~/.claude/local/claude, /usr/local/bin/claude, /usr/bin/claude, then `which claude`. Path passed to SDK via `pathToClaudeCodeExecutable` option. Agents error immediately if CLI not found. Provider env var stripping: strip_provider_env_var() strips CLAUDE*/CODEX*/OLLAMA* vars (whitelists CLAUDE_CODE_EXPERIMENTAL_*). Dual-layer: (1) Rust env_clear() + clean_env, (2) JS runner SDK `env` option. Session stop uses AbortController.abort(). `agent-runner-deno.ts` exists as standalone alternative runner but is NOT used by SidecarManager.
|
||||
- AgentPane does NOT stop agents in onDestroy — onDestroy fires on layout remounts, not just explicit close. Stop-on-close is handled externally (was TilingGrid in v2, now workspace teardown in v3).
|
||||
- Agent dispatcher (`src/lib/agent-dispatcher.ts`) is a singleton that routes sidecar events to the agent store. Provider-aware: sessionProviderMap routes messages through adaptMessage(provider, event) from message-adapters.ts. Handles subagent routing (project-scoped sessions skip layout pane, render in TeamAgentsPanel; detached mode creates layout pane). Session persistence via registerSessionProject(sessionId, projectId, provider) + persistSessionForProject() (saves state + messages to SQLite on complete).
|
||||
- Agent dispatcher (`src/lib/agent-dispatcher.ts`) is a thin coordinator (260 lines) routing sidecar events to the agent store. Delegates to extracted modules: `utils/session-persistence.ts` (session-project maps, persistSessionForProject), `utils/subagent-router.ts` (spawn + route subagent panes), `utils/auto-anchoring.ts` (triggerAutoAnchor on compaction), `utils/worktree-detection.ts` (detectWorktreeFromCwd pure function). Provider-aware via message-adapters.ts.
|
||||
- AgentQueryOptions supports `provider` field (defaults to 'claude', flows Rust -> sidecar), `provider_config` blob (Rust passes through as serde_json::Value), `permission_mode` (defaults to 'bypassPermissions'), `setting_sources` (defaults to ['user', 'project']), `system_prompt`, `model`, `claude_config_dir` (for multi-account), `additional_directories`, `worktree_name` (when set, passed as `extraArgs: { worktree: name }` to SDK → `--worktree <name>` CLI flag).
|
||||
- Worktree isolation (S-1 Phase 3): Per-project `useWorktrees` toggle in SettingsTab. When enabled, AgentPane passes `worktree_name=sessionId` in queryAgent(). Agent runs in `<repo>/.claude/worktrees/<sessionId>/`. CWD-based detection: agent-dispatcher.ts `detectWorktreeFromCwd()` matches `.claude/worktrees/`, `.codex/worktrees/`, `.cursor/worktrees/` patterns on init events → calls `setSessionWorktree()` for conflict suppression. Dual detection: CWD-based (primary, from init event) + tool_call-based `extractWorktreePath()` (subagent fallback).
|
||||
- Worktree isolation (S-1 Phase 3): Per-project `useWorktrees` toggle in SettingsTab. When enabled, AgentPane passes `worktree_name=sessionId` in queryAgent(). Agent runs in `<repo>/.claude/worktrees/<sessionId>/`. CWD-based detection: `utils/worktree-detection.ts` `detectWorktreeFromCwd()` matches `.claude/worktrees/`, `.codex/worktrees/`, `.cursor/worktrees/` patterns on init events → calls `setSessionWorktree()` for conflict suppression. Dual detection: CWD-based (primary, from init event) + tool_call-based `extractWorktreePath()` (subagent fallback).
|
||||
- Claude profiles: claude_list_profiles() reads ~/.config/switcher/profiles/ with profile.toml metadata. Profile set per-project in Settings (project.profile field), passed through AgentSession -> AgentPane `profile` prop -> resolved to config_dir for SDK. Profile name shown as info-only in ProjectHeader.
|
||||
- ProjectBox has project-level tab bar: Model | Docs | Context | Files | SSH | Memory. Three mount strategies: PERSISTED-EAGER (Model, Docs, Context — always mounted, display:flex/none), PERSISTED-LAZY (Files, SSH, Memory — mount on first activation via {#if everActivated} + display:flex/none). Tab type: `'model' | 'docs' | 'context' | 'files' | 'ssh' | 'memories'`. Model tab = AgentSession+TeamAgentsPanel. Docs tab = ProjectFiles (markdown viewer). Context tab = ContextTab.svelte (LLM context window visualization: stats bar, segmented token meter, file references, turn breakdown; reads from agent store via sessionId prop; replaced old ContextPane ctx database viewer). Files tab = FilesTab.svelte (VSCode-style directory tree + CodeMirror 6 editor with 15 language modes, dirty tracking, Ctrl+S save, save-on-blur setting, image display via convertFileSrc, 10MB gate; CodeEditor.svelte wrapper; PdfViewer.svelte for PDF files via pdfjs-dist with canvas multi-page rendering + zoom 0.5x–3x; CsvTable.svelte for CSV with RFC 4180 parser, delimiter auto-detect, sortable columns). SSH tab = SshTab.svelte (CRUD for SSH connections, launch spawns terminal tab in Model tab). Memory tab = MemoriesTab.svelte (pluggable via MemoryAdapter interface in memory-adapter.ts; MemoraAdapter registered at startup, reads ~/.local/share/memora/memories.db via Rust memora.rs). Rust backend: list_directory_children + read_file_content + write_file_content (FileContent tagged union: Text/Binary/TooLarge). Frontend bridge: files-bridge.ts.
|
||||
- ProjectHeader shows CWD (ellipsized from START via `direction: rtl`) + profile name as info-only text on right side. AgentPane no longer has DIR/ACC toolbar — CWD and profile are props from parent.
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
- Layout store persists to SQLite on every addPane/removePane/setPreset/setPaneGroup change (fire-and-forget). Restores on app startup via `restoreFromDb()`.
|
||||
- Session groups: Pane.group? field in layout store, group_name column in sessions table, collapsible group headers in sidebar. Right-click pane to set group.
|
||||
- File watcher uses notify crate v6, watches parent directory (NonRecursive), emits `file-changed` Tauri events.
|
||||
- Settings use key-value `settings` table in SQLite (session.rs). Frontend: `settings-bridge.ts` adapter. v3 uses SettingsTab.svelte rendered in sidebar drawer panel (v2 SettingsDialog.svelte deleted in P10). SettingsTab has two sections: Global (single-column layout, split into Appearance [theme dropdown, UI font dropdown with sans-serif options + size stepper, Terminal font dropdown with monospace options + size stepper] and Defaults [shell, CWD] — all custom themed dropdowns, no native `<select>`, all persisted via settings-bridge with keys: theme, ui_font_family, ui_font_size, term_font_family, term_font_size, default_shell, default_cwd) and Group/Project CRUD.
|
||||
- Settings use key-value `settings` table in SQLite (session/settings.rs). Frontend: `settings-bridge.ts` adapter. v3 uses SettingsTab.svelte rendered in sidebar drawer panel (v2 SettingsDialog.svelte deleted in P10). SettingsTab has two sections: Global (single-column layout, split into Appearance [theme dropdown, UI font dropdown with sans-serif options + size stepper, Terminal font dropdown with monospace options + size stepper] and Defaults [shell, CWD] — all custom themed dropdowns, no native `<select>`, all persisted via settings-bridge with keys: theme, ui_font_family, ui_font_size, term_font_family, term_font_size, default_shell, default_cwd) and Group/Project CRUD.
|
||||
- Notifications use ephemeral toast system: `notifications.svelte.ts` store (max 5, 4s auto-dismiss), `ToastContainer.svelte` display. Agent dispatcher emits toasts on agent complete/error/crash.
|
||||
- StatusBar → Mission Control bar: running/idle/stalled agent counts (color-coded), total $/hr burn rate, "needs attention" dropdown priority queue (up to 5 cards sorted by urgency score, click-to-focus), total tokens + cost. Uses health.svelte.ts store (not workspace store for health signals).
|
||||
- health.svelte.ts store: per-project health tracking via ProjectTracker map. ActivityState = inactive|running|idle|stalled (configurable per-project via stallThresholdMin in ProjectConfig, default 15 min, range 5–60 min step 5, synced via setStallThreshold() API). Burn rate from 5-min EMA costSnapshots. Context pressure = tokens/model limit. File conflict count from conflicts.svelte.ts. Attention scoring: stalled=100, error=90, ctx>90%=80, file_conflict=70, ctx>75%=40. 5-second tick timer (auto-stop/start). API: trackProject(), recordActivity(), recordToolDone(), recordTokenSnapshot(), getProjectHealth(), getAttentionQueue(), getHealthAggregates().
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- **Reconnect loop race in RemoteManager** — orphaned reconnect tasks continued running after `remove_machine()` or `disconnect()`. Added `cancelled: Arc<AtomicBool>` flag to `RemoteMachine`; set on removal/disconnect, checked each reconnect iteration. `connect()` resets flag for new connections (remote.rs)
|
||||
|
||||
### Changed
|
||||
- **agent-dispatcher.ts split (SOLID Phase 2)** — 496→260 lines. Extracted 4 modules: `utils/worktree-detection.ts` (pure function), `utils/session-persistence.ts` (session maps + persist), `utils/auto-anchoring.ts` (compaction anchor), `utils/subagent-router.ts` (spawn + route). Dispatcher is now a thin coordinator
|
||||
- **session.rs split (SOLID Phase 2)** — 1008-line monolith split into 7 sub-modules under `session/` directory: mod.rs (struct + migrate), sessions.rs, layout.rs, settings.rs, ssh.rs, agents.rs, metrics.rs, anchors.rs. `pub(in crate::session)` conn visibility. 21 new cargo tests
|
||||
- **lib.rs command module split** — 976-line monolith with 48 Tauri commands split into 11 domain modules under `src-tauri/src/commands/` (pty, agent, watcher, session, persistence, knowledge, claude, groups, files, remote, misc). lib.rs reduced to ~170 lines (AppState + setup + handler registration)
|
||||
- **Attention scorer extraction** — `scoreAttention()` pure function extracted from inline health store code to `utils/attention-scorer.ts` with 14 tests. Priority chain: stalled > error > context critical > file conflict > context high
|
||||
- **Shared type guards** — deduplicated `str()`/`num()` runtime guards from claude-messages.ts, codex-messages.ts, ollama-messages.ts into shared `utils/type-guards.ts`
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ Terminal emulator with SSH and Claude Code session management. v1 (GTK3+VTE Pyth
|
|||
| `v2/src-tauri/src/sidecar.rs` | SidecarManager (thin re-export from bterminal-core) |
|
||||
| `v2/src-tauri/src/event_sink.rs` | TauriEventSink (implements EventSink for AppHandle) |
|
||||
| `v2/src-tauri/src/remote.rs` | RemoteManager (WebSocket client connections to relays) |
|
||||
| `v2/src-tauri/src/session.rs` | SessionDb (rusqlite, sessions + layout + settings + ssh_sessions + agent_messages + project_agent_state + session_anchors) |
|
||||
| `v2/src-tauri/src/session/` | SessionDb module: mod.rs (struct + migrate), sessions.rs, layout.rs, settings.rs, ssh.rs, agents.rs, metrics.rs, anchors.rs |
|
||||
| `v2/src-tauri/src/watcher.rs` | FileWatcherManager (notify crate, file change events) |
|
||||
| `v2/src-tauri/src/ctx.rs` | CtxDb (read-only access to ~/.claude-context/context.db) |
|
||||
| `v2/src-tauri/src/memora.rs` | MemoraDb (read-only access to ~/.local/share/memora/memories.db, FTS5 search) |
|
||||
|
|
@ -64,7 +64,11 @@ Terminal emulator with SSH and Claude Code session management. v1 (GTK3+VTE Pyth
|
|||
| `v2/src/lib/providers/ollama.ts` | Ollama provider metadata constant (OLLAMA_PROVIDER, qwen3:8b default) |
|
||||
| `v2/src/lib/adapters/codex-messages.ts` | Codex message adapter (ThreadEvent parser) |
|
||||
| `v2/src/lib/adapters/ollama-messages.ts` | Ollama message adapter (streaming chunk parser) |
|
||||
| `v2/src/lib/agent-dispatcher.ts` | Routes sidecar events to agent store + subagent routing + session persistence + toast notifications + CWD-based worktree detection |
|
||||
| `v2/src/lib/agent-dispatcher.ts` | Thin coordinator: routes sidecar events to agent store, delegates to extracted modules |
|
||||
| `v2/src/lib/utils/session-persistence.ts` | Session-project maps + persistSessionForProject + waitForPendingPersistence |
|
||||
| `v2/src/lib/utils/auto-anchoring.ts` | triggerAutoAnchor on first compaction event |
|
||||
| `v2/src/lib/utils/subagent-router.ts` | Subagent pane creation + toolUseToChildPane routing |
|
||||
| `v2/src/lib/utils/worktree-detection.ts` | detectWorktreeFromCwd pure function (3 provider patterns) |
|
||||
| `v2/src/lib/adapters/file-bridge.ts` | File watcher IPC wrapper |
|
||||
| `v2/src/lib/adapters/settings-bridge.ts` | Settings IPC wrapper (get/set/list) |
|
||||
| `v2/src/lib/adapters/ctx-bridge.ts` | ctx database IPC wrapper |
|
||||
|
|
|
|||
6
TODO.md
6
TODO.md
|
|
@ -7,12 +7,12 @@
|
|||
- [ ] **Multi-machine real-world testing** -- Test bterminal-relay with 2 machines.
|
||||
- [ ] **Multi-machine TLS/certificate pinning** -- TLS support for bterminal-relay + certificate pinning in RemoteManager.
|
||||
- [ ] **Agent Teams real-world testing** -- Env var whitelist fix done. 3 test sessions ran ($1.10, $0.69, $1.70) but model didn't spawn subagents — needs complex multi-part prompts to trigger delegation. Test with CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1.
|
||||
- [ ] **SOLID Phase 2 — agent-dispatcher.ts** -- God module (496 lines, 9 concerns). Extract event routing, session persistence, subagent routing into separate modules.
|
||||
- [ ] **SOLID Phase 2 — session.rs** -- Mixed concerns (1008 lines, 8 tables). Split by table lifecycle.
|
||||
- [ ] **SOLID Phase 3 — Primitive obsession** -- SessionId, ProjectId, FilePath as raw strings across 15+ files. Introduce value objects.
|
||||
|
||||
## Completed
|
||||
|
||||
- [x] **SOLID Phase 2 — agent-dispatcher.ts split** -- 496→260 lines. Extracted 4 modules: utils/worktree-detection.ts (pure function, 5 tests), utils/session-persistence.ts (session maps + persist), utils/auto-anchoring.ts (compaction anchor), utils/subagent-router.ts (spawn + route). Dispatcher is thin coordinator. 286 vitest + 49 cargo tests. | Done: 2026-03-11
|
||||
- [x] **SOLID Phase 2 — session.rs split** -- 1008→7 sub-modules under session/ directory (mod.rs, sessions.rs, layout.rs, settings.rs, ssh.rs, agents.rs, metrics.rs, anchors.rs). pub(in crate::session) conn visibility. 21 new cargo tests. 49 cargo tests total. | Done: 2026-03-11
|
||||
- [x] **SOLID Phase 1 Refactoring** -- Extracted AttentionScorer pure function (14 tests), shared str()/num() type guards, split lib.rs (976→170 lines, 11 command modules). 286 vitest + 49 cargo tests. | Done: 2026-03-11
|
||||
- [x] **Configurable stall threshold** -- Per-project range slider (5–60 min, step 5) in SettingsTab. `stallThresholdMin` in ProjectConfig, `setStallThreshold()` API in health store, ProjectBox $effect sync. Adaptive suggestions deferred (needs 50+ sessions in session_metrics). | Done: 2026-03-11
|
||||
- [x] **Register Memora adapter** -- MemoraAdapter (memora-bridge.ts) implements MemoryAdapter, reads ~/.local/share/memora/memories.db via Rust memora.rs (read-only SQLite, FTS5 search). 4 Tauri commands, 16 vitest + 7 cargo tests. 272 vitest + 49 cargo total. | Done: 2026-03-11
|
||||
|
|
@ -21,5 +21,3 @@
|
|||
- [x] **S-2 — Session Anchors + Configurable Budget** -- Preserves important turns through compaction chains. Auto-anchors first 3 turns (observation-masked — reasoning preserved in full per research). Configurable budget via AnchorBudgetScale slider (Small=2K, Medium=6K, Large=12K, Full=20K) in SettingsTab per-project. Manual pin, promote/demote in ContextTab. Re-injection via system_prompt. 219 vitest + 42 cargo tests. | Done: 2026-03-11
|
||||
- [x] **Agent provider adapter pattern** -- Multi-provider support (Claude, Codex, Ollama) via 3-phase adapter pattern. Core abstraction, Settings UI, Sidecar routing. 5 new files, 4 renames, 20+ modified. 202 vitest + 42 cargo tests. | Done: 2026-03-11
|
||||
- [x] **Files tab PDF viewer + CSV table** -- PdfViewer.svelte (pdfjs-dist 5.5.207, canvas multi-page, zoom 0.5x–3x, HiDPI). CsvTable.svelte (RFC 4180 parser, delimiter auto-detect, sortable columns, sticky header). | Done: 2026-03-11
|
||||
- [x] **Filesystem Write Detection (S-1 Phase 2)** -- inotify-based file change detection via notify crate (fs_watcher.rs). Timing heuristic (2s grace) for PID attribution. 202/202 vitest + 42/42 cargo tests. | Done: 2026-03-11
|
||||
- [x] **Conflict Detection Enhancements (S-1 Phase 1.5)** -- Bash write detection, acknowledge/dismiss conflicts, worktree-aware suppression. 194/194 tests. | Done: 2026-03-11
|
||||
|
|
|
|||
|
|
@ -764,3 +764,28 @@ Ran nemezis-audit on Rust backend. 0 verified exploitable findings, 10 recon tar
|
|||
- [x] vitest: 286/286 pass (14 new attention-scorer tests)
|
||||
- [x] cargo check: clean
|
||||
- [x] cargo test: 49/49 pass
|
||||
|
||||
### Session 2026-03-11 (SOLID Phase 2 Refactoring)
|
||||
|
||||
#### agent-dispatcher.ts Split (496→260 lines)
|
||||
- [x] Extracted utils/worktree-detection.ts — detectWorktreeFromCwd() pure function (17 lines, 5 tests)
|
||||
- [x] Extracted utils/session-persistence.ts — session maps + persistSessionForProject (107 lines)
|
||||
- [x] Extracted utils/auto-anchoring.ts — triggerAutoAnchor (48 lines)
|
||||
- [x] Extracted utils/subagent-router.ts — spawnSubagentPane + SUBAGENT_TOOL_NAMES (73 lines)
|
||||
- [x] Dispatcher is now thin coordinator with re-exports for backward compat
|
||||
|
||||
#### session.rs Split (1,008 lines → 7 sub-modules)
|
||||
- [x] session/mod.rs — SessionDb struct + open() + migrate() + re-exports (153 lines)
|
||||
- [x] session/sessions.rs — Session CRUD (9 tests)
|
||||
- [x] session/layout.rs — LayoutState save/load (3 tests)
|
||||
- [x] session/settings.rs — Settings CRUD (5 tests)
|
||||
- [x] session/ssh.rs — SshSession CRUD (4 tests)
|
||||
- [x] session/agents.rs — AgentMessageRecord + ProjectAgentState
|
||||
- [x] session/metrics.rs — SessionMetric save/load
|
||||
- [x] session/anchors.rs — SessionAnchorRecord CRUD
|
||||
- [x] conn field: pub(in crate::session) for sub-module access
|
||||
|
||||
#### Results
|
||||
- [x] vitest: 286/286 pass (5 worktree tests moved to new file)
|
||||
- [x] cargo check: clean
|
||||
- [x] cargo test: 49/49 pass
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue