research/ecc2-codebase-analysis.md
Date: 2026-03-26
Subject: ecc-tui v0.1.0 — Agentic IDE Control Plane
Total Lines: 4,417 across 15 .rs files
ECC2 is a Rust TUI application that orchestrates AI coding agent sessions. It uses:
| Module | Lines | Purpose |
|---|---|---|
session/ | 1,974 | Session lifecycle, persistence, runtime, output |
tui/ | 1,613 | Dashboard, app loop, custom widgets |
observability/ | 409 | Tool call risk scoring and logging |
config/ | 144 | Configuration (TOML file) |
main.rs | 142 | CLI entry point |
worktree/ | 99 | Git worktree management |
comms/ | 36 | Inter-agent messaging (send only) |
session/runtime.rs — dedicated OS thread for SQLite writes from async context via mpsc::unbounded_channel with oneshot acknowledgements. Clean solution to the "SQLite from async" problem.Pending → {Running, Failed, Stopped}, Running → {Idle, Completed, Failed, Stopped}, etc.OUTPUT_BUFFER_LIMIT = 1000 lines per session with automatic eviction.| Metric | Value |
|---|---|
| Total lines | 4,417 |
| Test functions | 29 |
unwrap() calls | 3 |
unsafe blocks | 0 |
| TODO/FIXME comments | 0 |
| Max file size | 1,273 lines (dashboard.rs) |
Assessment: The codebase is clean. Only 3 unwrap() calls (2 in tests, 1 in config default()), zero unsafe, and all modules use proper anyhow::Result error propagation. The dashboard.rs file at 1,273 lines exceeds the repo's 800-line max-file guideline, but it is still manageable at the current scope.
comms/mod.rs (36 lines) has send() but no receive(), poll(), inbox(), or subscribe(). The messages table exists in SQLite, but nothing reads from it. The inter-agent messaging story is half-built.
Impact: Agents cannot coordinate. The TaskHandoff, Query, Response, and Conflict message types are defined but unusable.
dashboard.rs:495 — new_session() logs "New session dialog requested" but does nothing. Users must use the CLI (ecc start --task "...") to create sessions; the TUI dashboard cannot.
session/manager.rs — agent_program() only supports "claude". The CLI accepts --agent but anything other than "claude" fails. No codex, opencode, or custom agent support.
Config::load() reads ~/.claude/ecc2.toml only. The implementation lacks environment variable overrides (e.g., ECC_DB_PATH, ECC_WORKTREE_ROOT) and CLI flags for configuration.
git2git2 = "0.20" is still declared in Cargo.toml, but the worktree module shells out to the git CLI instead. That makes git2 a strong removal candidate rather than an already-completed cleanup.
SessionMetrics tracks tokens, cost, duration, tool_calls, files_changed per session. But there's no aggregate view: total cost across sessions, average duration, top tools by usage, etc. The Metrics pane in the dashboard shows per-session detail only.
session/daemon.rs runs an infinite loop checking session timeouts. No health endpoint, no log rotation, no PID file, no signal handling for graceful shutdown. Ctrl+C during daemon mode kills the process uncleanly.
34 test functions across 10 source modules:
| Module | Tests | Coverage Focus |
|---|---|---|
main.rs | 1 | CLI parsing |
config/mod.rs | 5 | Defaults, deserialization, legacy fallback |
observability/mod.rs | 5 | Risk scoring, persistence, pagination |
session/daemon.rs | 2 | Crash recovery / liveness handling |
session/manager.rs | 4 | Session lifecycle, resume, stop, latest status |
session/output.rs | 2 | Ring buffer, broadcast |
session/runtime.rs | 1 | Output capture persistence/events |
session/store.rs | 3 | Buffer window, migration, state transitions |
tui/dashboard.rs | 8 | Rendering, selection, pane navigation, scrolling |
tui/widgets.rs | 3 | Token meter rendering and thresholds |
Direct coverage gaps:
comms/mod.rs — 0 testsworktree/mod.rs — 0 testsThe core I/O-heavy paths are no longer completely untested: manager.rs, runtime.rs, and daemon.rs each have targeted tests. The remaining gap is breadth rather than total absence, especially around comms/, worktree/, and more adversarial process/worktree failure cases.
tokio::process::Command with explicit Stdio::piped() — no shell injection vectors.rm -rf, git push --force origin main, file access to .env/secrets.claude --print. If the task contains shell metacharacters, it could be exploited depending on how Command handles argument quoting. Currently safe (arguments are not shell-interpreted), but worth auditing.| Crate | Version | Latest | Notes |
|---|---|---|---|
| ratatui | 0.29 | 0.30.0 | Update available |
| crossterm | 0.28 | 0.29.0 | Update available |
| rusqlite | 0.32 | 0.39.0 | Update available |
| tokio | 1 | 1.50.0 | Update available |
| serde | 1 | 1.0.228 | Update available |
| clap | 4 | 4.6.0 | Update available |
| chrono | 0.4 | 0.4.44 | Update available |
| uuid | 1 | 1.22.0 | Update available |
git2 is still present in Cargo.toml even though the worktree module shells out to the git CLI. Several other dependencies are outdated; either remove git2 or start using it before the next release.
Config::load() — ECC_DB_PATH, ECC_WORKTREE_ROOT, ECC_DEFAULT_AGENT. Standard practice for CLI tools.comms::receive() / comms::poll() — read unread messages from the messages table, optionally with a broadcast channel for real-time delivery. Wire it into the dashboard.session::manager::create_session().manager.rs, runtime.rs, and daemon.rs — the repo now has baseline tests here, but it still needs failure-path coverage around process crashes, timeouts, and cleanup edge cases.worktree/mod.rs and comms/mod.rs — these are still uncovered and back important orchestration features.claude --print via tokio::process::Command. Verify arguments are never shell-interpreted. Checklist: confirm Command arg usage, threat-model metacharacter injection, input validation/escaping strategy, logging of raw inputs, and automated tests. Re-audit if invocation code changes.dashboard.rs — extract SessionsPane, OutputPane, MetricsPane, LogPane into separate files under tui/panes/.agent_program() pluggable. Add codex, opencode, custom agent types.The codebase follows ratatui conventions well:
TableState for stateful selection (correct pattern)Widget trait implementation for TokenMeter (idiomatic)tick() method for periodic state sync (standard)broadcast::channel for real-time output events (appropriate)Minor deviations:
Dashboard struct directly holds StateStore (SQLite connection). Ratatui best practice is to keep the state store behind an Arc<Mutex<>> to allow background updates. Currently the TUI owns the DB exclusively, which blocks adding a background metrics refresh task.Clear widget usage when rendering the help overlay — could cause rendering artifacts on some terminals.| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Dashboard file exceeds 1500 lines (projected) | High | Medium | At 1,273 lines currently (Section 2); extract panes into modules before it grows further |
| SQLite lock contention | Low | High | DbWriter pattern already handles this |
| No agent diversity | Medium | Medium | Pluggable agent support |
| Task-string handling assumptions drift over time | Medium | Medium | Keep Command argument handling shell-free, document the threat model, and add regression tests for metacharacter-heavy task input |
Bottom line: ECC2 is a well-structured Rust project with clean error handling, good separation of concerns, and strong security features (risk scoring). The main gaps are incomplete features (comms, new-session dialog, single agent) rather than architectural problems. The codebase is ready for feature work on top of the solid foundation.