# Changelog All notable changes to clide are documented in this file. The format follows [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). This changelog tracks the Flutter rebuild at the repo root. The Python Textual implementation's changelog is preserved under [`legacy/CHANGELOG.md`](legacy/CHANGELOG.md). Versions are tracked in [`pubspec.yaml`](pubspec.yaml) under `version:`, which is the single source of truth. Cutting a release means (a) moving the entries below from `## [Unreleased]` under a new dated version heading, and (b) bumping `pubspec.yaml` `version:` in the same commit. ## [Unreleased] ### Removed - **`bin/clide.dart` + `DaemonServer`** — completing the D-56 dissolution. The separate daemon process was dissolved on 2026-04-23 but the entry point and socket server class were never actually deleted. Gone now, along with orphaned tests (`test/cli/`, `test/daemon/subprocess_test`, `test/daemon/in_process_test`), stale i18n strings, and "start `clide --daemon`" error messages. - **`ptyc/` source tree + `PtySession` + `scm_rights.dart`** — PTY spawning migrated to Dart FFI `forkpty()` (`NativePty`) but the old C helper and its Dart wiring were never cleaned up. Removed from toolchain resolution, `ToolCheck` gate, backend serialization, testmode harness, CI scripts, Makefile, and sandbox entitlements. D-5 amended to record the retirement. - CI golden images (`test/goldens/goldens/ci/`) — Skia anti-aliasing of geometric shapes differs between macOS and Linux even with the Ahem font, so a single set of CI goldens can't serve both platforms. Replaced with platform-keyed goldens (`goldens/linux/`, `goldens/macos/`), each only compared on its own OS. ### Added - Contrast gate split — baseline `canonicalPairs` every theme passes, strict `extendedPairs` (muted/status/syntax/focus-border) gated to `-hc` / `-cb` variants. Ships `clide-hc`, `midnight-hc`, `paper-hc`, `terminal-hc` siblings of the named themes (D-69, T-114, T-118). - Pre-push coverage gate — `make push-check` runs `ci/coverage_gate.sh`, which fails if total line coverage drops below `coverage_floor:` in `pubspec.yaml`. Floor ratchets up only; target 95% (D-66). - Pre-push changelog gate — `ci/changelog_gate.sh` fails on any `## [Unreleased]` bullet over 60 words; warns at 40. Enforces the Keep-a-Changelog conciseness rule in the git-commit skill. - Keymap layer (`KeymapService`) — typed Intents, YAML presets, VS-Code-style when-clauses, layered preset → user file → settings overlay. Default preset ships; vim/vscode/jetbrains unblocked (T-117, supersedes T-110). - Keyboard operability — `ClideTappable` is now Tab-focusable with a focus ring and Enter/Space activation; `ClidePalette` adds arrow nav, Escape dismiss, and selection highlight (T-100). - Panel-to-panel focus traversal — each `SlotHost` wraps in a `FocusScope` + `FocusTraversalGroup`; `F6` / `Shift+F6` cycle sidebar → workspace → context. `FocusTracker` integrates with Flutter focus rather than paralleling it (T-105). - Event-driven test waits — PTY + watcher tests await stream events instead of fixed sleeps; `onTimeout` callbacks now `fail()` loudly with diagnostic context. `RecordingEventSink` exposes a broadcast stream for the same pattern (T-108). - Code-quality cleanups — `TreeSitterLib` exposes a last-error diagnostic instead of swallowing dlopen failures; `ExtensionManager` surfaces a `failedExtensions` map for UI degradation; PTY constants consolidated in `libc.dart` + `PosixErrno`; `test_app.dart` gated behind `kDebugMode` (T-112). - Test sweep — `keybindings`, `toolchain_paths`, and several `widgets/src/` primitives (tooltip, palette, multitab, markdown). - `tree_sitter_service` sweep — fake-FFI + real-library smoke, 17% → 96%. Crosses the 95% global target (T-91). - Staged `dart doc` CI job — generates and uploads an HTML API reference for the public `lib/` surface. The step wraps `dart doc --validate-links` and grep-fails the build on any warning, so broken doc refs and dangling links can't accumulate. Inert with the rest of the workflow until Gitea Actions activates. - Mouse wheel scrolling in Claude pane — converts scroll events to PgUp/PgDown so Claude Code (and other TUI apps) scroll their history naturally. - Welcome screen Tips card — six common keybindings shown below the START / RECENT row when the viewport is tall enough. - `MultitabPane` widget + `MultitabController` for panes that host N runtime tab instances of the same kind. Generic over a payload type, supports pinned/non-closeable tabs (primary), drag-reorder, close × on hover, and an optional `+` add button. Used by the Claude pane to render primary + secondaries. - `MultitabPane.keepAlive` mode — when set, all entry bodies stay mounted via IndexedStack so switching tabs preserves their state (PTY connections, scroll position, etc.). ### Fixed - `TerminalView.onTapUp` now actually fires on primary tap — was wired to a dead code path (T-93). Dead `onTapUp` surface on `TerminalGestureHandler` / `TerminalGestureDetector` removed. - `BufferLine.eraseRange` no longer panics when called with `end == 0`. The right-side wide-char guard read `_data[-1]` via `getWidth(-1)`, which threw a `RangeError`. Real trigger path: `Terminal.eraseDisplayAbove` with the cursor at column 0 — common after `ESC[H\x1b[1J` (home + erase-above) sequences that many TUIs emit on redraw. - Terminal selections no longer vanish when resizing narrower — reflow's tail-anchor handler left anchors detached past the trimmed range. Common triggers: Ctrl+A then resize, drag past a partially-filled line (T-92). - `BufferLine.removeCells` / `insertCells` / `dispose` no longer skip anchors due to concurrent list modification during iteration — iteration now snapshots the list first (T-91). ### Changed - Changelog gate is binary — dropped the soft 40-word warning, kept the 60-word hard cap. Warnings that never blocked just normalised drift. - PTY spawning uses `posix_openpt` + `posix_spawn` instead of `forkpty` — closes a ~5% deadlock window in the multithreaded Dart VM (T-96, D-5 amended). Missing exe/cwd now throw `PtyException` at spawn time. Drops the `libutil.so.1` dependency. - Coverage floor ratcheted to 95% — D-66 target hit. - `TreeSitterService` and `TreeSitterLib` accept injectable FFI + asset loaders for fake-driven tests; production paths unchanged. - Tidied test imports flagged by `unnecessary_import`. - `README.md` rewritten to match current architecture; `docs/initial-plan.md` bannered as historical; new `docs/architecture.md` describes today's shape (T-101). - `SchedulerService._stopTicker` now awaits the in-flight isolate spawn before killing — closes the same race shape we fixed in PTY (T-106). - `make push-check-full` added — runs `push-check` plus integration + smoke for pre-release checks. Integration tests skip the hanging theme_picker case (T-116) until that's fixed (T-103). - Governance bookkeeping: D-66 amended (floor at `coverage_floor:` in `pubspec.yaml`); `licenses.yaml` reconciled with `pubspec.yaml`; Q-1/Q-2/Q-3/Q-25 triaged; `.claude/skills/README.md` inventory added; `--no-fatal-infos` dropped from `ci/test.sh` (T-113). - Terminal panes now render bold attributes with a real bold weight — bundled JetBrainsMono Bold + BoldItalic are registered with the `JetBrainsMono` family at `weight: 700`. The painter's bold suppression workaround (added when only Regular + Italic were wired and Flutter's synthetic bold drifted advance widths) is gone. - Claude pane uses `MultitabPane` for primary + secondaries — drops ~100 lines of bespoke tab-strip code, gains drag-to-reorder. - UI spacing constants live in `lib/widgets/src/spacing.dart` — `clideInset*` for paddings, `clideGap*` for sibling distances, `clideIcon*` / `clideControlHeight` for control sizes. Inline pixel literals replaced where they were repeated. ### Changed - Tagline reads "IDE for Claude Code CLI" everywhere (welcome subtitle, README, CLAUDE.md, pubspec, web manifest, CLI banner). ### Fixed - Closing a secondary Claude pane tab now kills its tmux session on the clide socket, honouring D-41's "closing a secondary kills that tmux session" lifecycle. Previously `pane.close` only killed the ptyc-spawned tmux client and the server-side session leaked. - Cold-start reap: every clide launch kills any leftover secondary tmux sessions for the current repo before spawning new ones, so D-41's "secondary numbering resets between runs" holds even after an abrupt previous exit (kill -9, crash, force-quit). - `claude.kill-all-sessions` command now actually kills the server-side tmux sessions for the repo, not just the panes. - Terminal cell grid no longer drifts on bold text — bold rendering is suppressed at the painter level since synthetic bold (with no Bold.ttf registered) shifts glyph advance widths. - PTY surfaces errno on `forkpty` / `write` / `ioctl` failures instead of swallowing. `execve` failures write a diagnostic to the slave before `_exit`. `NativePty.write` and `PtySession.write` loop on short writes; both throw `PtyException` on hard errors. - PTY teardown order fixed — kill child first so the master fd returns EOF, await reader isolate exit, then close the fd. Closing earlier could briefly target a reused fd. - Reader isolate spawn errors in `NativePty` / `PtySession` are now surfaced via the output stream instead of silently dropped. `_recvFdAsync` no longer leaks the `ReceivePort` on spawn throw; `PtySession.spawn` closes the master fd if any post-receive step fails. - IPC server hardening: per-request 60s timeout (configurable), broadcast/response write failures logged instead of swallowed, client dropped on response-write failure, and the stale-socket retry now probes for a live daemon before unlinking the socket (refusing to start if one answers). - `pane.spawn` and `editor.open` now map POSIX errno values to actionable IPC error kinds. ENOENT → `not_found`, EACCES/EPERM → `user_error` with a permissions hint, EISDIR/ENOTDIR/EEXIST → distinct user-error/conflict, EMFILE/ENFILE → `tool_error` with a "fd limit hit" hint. Previously every spawn/open failure was an indistinguishable `tool_error`. ### Security - Toolchain no longer resolves the dugite git binary against the open workspace — a malicious repo could plant `native/dugite/bin/git` and clide would run it on auto-fired `git.status`. Dugite now resolves against the install dir + `CLIDE_DUGITE_DIR` env override only (T-98). - `files.read` and `files.ls` now reject symlinks whose targets live outside the workspace — closes a path-safety bypass via in-repo symlinks (T-102). - `files.read` and `files.ls` now reject paths that resolve outside the workspace root. Previously a relative path containing `..` could read arbitrary files via path traversal. ### Changed - Inline terminal emulator based on xterm.dart v4.0.0 — replaces the pub.dev dependency with owned code under `lib/src/terminal/`. Drops three transitive dependencies (xterm, quiver, zmodem). - Bundle clide-specific tmux.conf for Claude pane sessions: no status bar, 50k scrollback, mouse on, zero escape delay, isolated socket. - Claude pane spawns `claude` directly inside tmux with `CLAUDE_CODE_NO_FLICKER=1` to enable Claude's fullscreen TUI mode (input box pinned at the bottom). - PTY read buffer increased from 4KB to 64KB. - Terminal view 2px padding on all sides. - Remove bold JetBrains Mono font registration to prevent glyph width mismatch in terminal rendering. ## [2.0.0] — 2026-05-03 ### Fixed - PTY FFI constants now platform-dispatched: `TIOCSWINSZ` (`0x80087467` macOS / `0x5414` Linux), `O_NONBLOCK` (`0x0004` / `0x0800`), and `MsghdrDarwin` struct with correct 4-byte field widths for macOS `recvmsg()`. - App settings directory uses `~/Library/Application Support/clide` on macOS instead of `~/.config/clide`. - Removed hardcoded `TERMINFO=/usr/share/terminfo` from pane spawn environment — let the system resolve terminfo per platform. - Panel `tabsFor()` now sorts by contribution priority when no user order is set. ### Changed - Canonical upstream moved from Gitea to GitHub (`github.com/postmeridiem/clide`). - README rewritten to reflect current single-process Flutter architecture, built-in extensions, and build commands. - Renamed desktop binary from `clide_app` to `clide` (Linux + macOS). - macOS app icon uses the black-circle variant matching the Linux desktop icon. - ClideTestApp expanded to three test categories (toolchain, ipc, extensions) with JSON summary, non-zero exit on failure, and `make run-testmode TESTMODE_CATEGORY=` for selective runs. ### Added - `make install` / `make uninstall` — builds the release bundle and installs it to `~/.local/` with XDG desktop entry, icon registration at seven sizes, and icon cache refresh. macOS installs to `~/Applications/clide.app`. - Backend isolate — all subprocess and file I/O runs in a dedicated isolate, keeping the merged UI/platform thread on macOS free for rendering. Communicates via SendPort using the existing IPC protocol. Two-phase boot: resolve toolchain on spawn, initialize services on project open. Scheduler ticker only runs while a project is active. - Toolchain — centralized binary resolution replacing five ad-hoc mechanisms. Resolves git, pql, tmux, ptyc, shell once at boot via background isolate. Status bar reads `toolchain.missing` directly. - GitClient — typed Dart API wrapping all git operations. Every subprocess call goes through `_run()` with toolchain-resolved path and environment. Replaces scattered `Process.run('git', ...)` calls. - Native directory picker — macOS NSOpenPanel via method channel in AppDelegate, GTK file chooser on Linux. Falls back to text-input dialog on web. Shows "No git repo found" dialog on invalid selection. - macOS desktop target — OS-detecting Makefile (`make run` works on macOS/Linux/Windows), 1280x720 default window, squared app icons, sandbox entitlements with SBPL exceptions, `_DARWIN_C_SOURCE` for ptyc compilation, native traffic dots skipped (macOS titlebar owns them), expanded PATH for Homebrew and `~/.local/bin` on GUI apps. - Shared ClideFilterBox widget with search icon, clear button, and debounced input. Applied consistently across all sidebar panes: Files (path filter with flat results), Git (filter staged/unstaged by path), Decisions (filter by ID/title/domain), Tickets (filter by ID/title/status), Problems (filter by source/message), and pql Query (replaces custom input). - Interaction model from Wireframe Flows v3: eight new D-records (D-47 through D-54) and five Q-records (Q-26 through Q-30) codifying layout invariants, chrome budget, editor mode, context auto-behavior, collapse spine, focus mode, state persistence, and the canonical keyboard map. - Panel collapse spine — collapsed side panels render as a 12px vertical spine with rotated label, hover highlight, and badge dot for pending context (D-51, T-30). - Focus mode — `Ctrl+.` takes the active panel full-window; `Escape` restores the prior layout with collapse states and divider positions intact (D-52, T-31). - Canonical keyboard shortcuts from the interaction model: collapse toggles (`Ctrl+Shift+1/3`), panel focus (`Ctrl+1/2/3`), sidebar section switching (`Alt+1–5`), focus mode, and `Escape` dismiss (D-54, T-33). - Right panel (context) icon rail — bottom section switcher matching the left sidebar rail pattern (D-47, T-34). - Editor-above-Claude mode — `Ctrl+E` opens the editor as a split above Claude in the middle column with a draggable divider; `Ctrl+W` or `Escape` closes it. Prompt bar Y stays fixed (D-49, T-35). - Layout state persists across sessions — collapse state, sidebar and context panel sizes, active sections, and editor split ratio saved to `.clide/settings.yaml` (D-53, T-32). - Phosphor Icons font (v2.0.8, MIT) — regular, bold, and fill weights. Replaces hand-painted CustomPaint icons in sidebar and context panel icon rails. - Decisions panel in sidebar — lists confirmed D-records from `pql decisions list` with ID and title (T-37). - Tickets panel in sidebar — lists tickets from `pql ticket list` with status dot color-coded by state (T-37). - Markdown viewer in context panel — shows raw content of the active .md file, auto-updating on buffer switch (T-38). - Clickable DQRT record links in markdown — `[D-56]`, `[T-43]` etc. rendered as tappable links that navigate to the decision or ticket detail pane via the message bus. - Ctrl+Plus / Ctrl+Minus / Ctrl+0 text zoom (5% steps, 60%–200% range) via `MediaQuery.textScaler`. Resets on app restart. - Sidebar focus indicators — selecting a ticket or decision highlights the active card in the sidebar list, auto-expands the accordion section if collapsed, and scrolls the card into view. - Typography scale constants `clideFontSmall` (12) and `clideFontBadge` (11) for sidebar metadata and status badges. `clideLineHeight` (1.25) applied app-wide via `DefaultTextStyle`. - `files.read` IPC command for reading file content by path. - pql sidebar restructured: Search tab is the default left tab with ranked text search (debounced, scored results with score bar) and a DSL toggle for raw PQL query mode; Markdown tab (filtered to `.md` files) on the right. Clicking a result opens it in the context panel markdown viewer with bidirectional focus highlighting. - Kernel scheduler service with tiered timers (1min, 10min, 15min, 1hr, midnight) running on a background isolate. Emits `SchedulerTick` events on the `DaemonBus`. Extensions subscribe by tier (T-61). - Auto-refresh for sidebar panels: decisions refresh on file changes to `decisions/*.md` and on 1-minute scheduler tick; tickets refresh on 1-minute tick and on status change events (T-62). - Manual refresh button (arrowClockwise icon) in decisions, tickets, and pql markdown panels (T-63). - `ClideAccordion` shared widget — extracted from tickets and decisions views. Supports optional `leading` widget (color dot). Pin/focus accordion logic: manually toggled sections are pinned; the focused item's section auto-opens; unpinned sections without focus auto-collapse. - Ticket status buttons wired to `pql ticket status` — clicking a status in the detail view transitions the ticket, refreshes the sidebar list, and scrolls the ticket into its new section. - `pql.tickets.status` IPC command accepting a list of IDs for batch status transitions. - Ticket sidebar sections split into individual statuses: IN PROGRESS, REVIEW, READY, BACKLOG, DONE, CANCELLED (was four coarse groups). - Phosphor Icons codepoint reference CSV at `assets/fonts/phosphor/codepoints.csv` — full mapping of all 1512 icon glyphs to kebab-case and PascalCase names. - Graph view in context panel — lists files with inbound/outbound link counts from `pql search --connections` (T-39). - Welcome screen redesigned as full-screen overlay with two-column layout: START actions (open folder, clone, Claude session) with keyboard shortcuts, and RECENT projects list showing path, branch, and relative timestamps. Status line shows version, daemon connection, and active theme. - Recent projects history persisted to user settings (up to 10 entries with path, branch, and last-opened timestamp). Last project auto-restored on boot; falls back to cwd, then welcome. ### Changed - Workspace renders Claude as the always-visible primary surface instead of showing a tab bar (D-47, D-48). The editor is a split overlay, not a tab. - Syntax highlighting via tree-sitter (dart:ffi to vendored libtree-sitter.so with embedded wasmtime). 48 grammar WASM files, 48 highlight queries. Colors map to theme syntax tokens. - ClideMarkdown renderer — inline grouping for tight list items, proper HTML entity unescaping after AST parse, Josefin Sans Light with 1.25 line height, 16px body text. Inline code sized to `clideFontMono` to match surrounding text weight. - Default sidebar and context panel widths widened to 400px and 420px respectively (previous maximums). Context panel max raised to 1000px. - Ticket detail loading uses `pql ticket show --with-context` for a single-call fetch of ancestors, decisions, and children. Replaces N+1 parent-chain walk. `--with-decision` and `--with-children` flags consolidated into `--with-context`. - Ticket descriptions render through ClideMarkdown instead of plain text, with clickable DQRT record links. - Decision detail view subscribes to the message bus for navigation (same pattern as tickets), enabling navigation from any source. - TreeSitterService is now a shared singleton — eliminates native double-free crashes from multiple WASM engine instances. - Code block syntax highlighting fixes overlapping tree-sitter spans that caused duplicated text (e.g. `makemake` instead of `make`). - Context panel drag resize fixed — dragging left now correctly grows the right panel instead of shrinking it. - POLICY.md — project-wide rules for runtime behavior, dependency vetting, vendored binary management, telemetry, and licensing. ### Changed - Line length set to 160 across .editorconfig and dart formatter. - Core frame vs shipped extension boundary defined (D-46). Builtins are frame infrastructure only; content extensions are bundled but architecturally removable. ### Removed - `builtin.jira` stub — Jira integration belongs as a third-party extension, not a frame builtin. - `wasm_run` and `wasm_run_flutter` dependencies — replaced by vendored libtree-sitter.so via dart:ffi. Eliminates runtime network download that violated POLICY.md. ### Added - pql skill installed via `pql init --with-skill=yes` (`.claude/skills/pql/SKILL.md`). Covers vault queries and the planning surface (decisions + tickets). - `Bash(pql)` and `Bash(pql *)` permissions in `.claude/settings.json`. - pql daemon subsystem (`lib/src/pql/`). `PqlClient` wraps the pql CLI per D-3. IPC verbs `pql.files | meta | backlinks | outlinks | tags | schema | query | doctor | decisions.sync | decisions.list | decisions.show | decisions.coverage | tickets.list | tickets.show | tickets.board | plan.status`. 15 new core tests. - `builtin.pql` — sidebar panel with four views: Files (pql-indexed file listing), Query (PQL DSL input + results), Decisions (synced D/Q/R records colour-coded by type), Tickets (kanban board columns). Context panel tab showing backlinks + outlinks for the active file, auto-refreshing on `editor.active-changed` events. - `builtin.problems` — sidebar panel aggregating diagnostics from `pql.doctor` and `pql.decisions.sync`. Surfaces missing index DB, stale skill installs, and broken decision cross-references with actionable hints. - Git subsystem in the daemon (`lib/src/git/`). Status parser (`git status --porcelain`), unified-diff parser, and operations (stage, unstage, stage-hunk, discard, commit, stash, log, pull, push). IPC verbs `git.status | diff | stage | stage-all | unstage | stage-hunk | unstage-hunk | discard | commit | stash | stash-pop | log | pull | push` with `git.changed` events on mutations. 42 new core tests cover parsing, operations, and dispatcher round-trips. - `clide git …` CLI shortcuts: `git status`, `git diff [--staged]`, `git stage `, `git stage-all`, `git unstage`, `git discard`, `git commit ""`, `git log [--count N]`, `git stash`, `git stash-pop`, `git pull`, `git push`. - `builtin.git` — sidebar panel showing staged, unstaged, untracked, and conflicted file groups. Per-file stage/unstage/discard on hover. Inline commit message input with Commit button. Branch + ahead/behind display with Pull/Push actions. Auto-refreshes on `git.changed` events. - `builtin.diff` — workspace tab rendering unified diffs with old/new line numbers, addition/removal colouring, and binary/rename metadata. Staged/Unstaged toggle toolbar. Auto-refreshes on `git.changed` events. - `builtin.editor` — Tier-2 editor tab wired up. Contributes a single `Editor` workspace tab that renders the daemon's active buffer via a new `EditorController`. Hydrates on mount (`editor.active` → `editor.read`), subscribes to `editor.opened | active-changed | edited | saved | closed`, and propagates user edits back through `editor.set-content`. Small echo-suppression guard avoids clobbering the caret when the daemon's authoritative edit echo comes back. Text surface is Flutter's `EditableText` primitive — no `TextField` / Material — so the D-7 "no Material root" stance carries into the editor; JetBrainsMono via the shared `clideMonoFamily` constants, cursor + selection colours bind to the theme. - File-tree click in `builtin.files` now opens the clicked file in the editor via `ipc.request('editor.open', {path})`. No local command hop — the dispatch goes straight to the daemon and the UI reconciles through the `editor.active-changed` event. - CLI shortcuts per CLAUDE.md's Tier-2 list: `clide open `, `clide active`, `clide insert `, `clide replace-selection `, `clide save`, `clide tail --events [--filter SUBSYSTEM[:ID]]`. A lone `-` on insert / replace-selection reads text from stdin (pipe-friendly). `tail` reads the event-broadcast stream and prints JSON lines until SIGINT; `--filter` narrows by subsystem or subsystem+id. 5 new end-to-end CLI tests spin up real daemon subprocesses via a per-test `CLIDE_SOCKET_PATH` override (new env knob on `defaultSocketPath`) so tests run in parallel without colliding. - Editor subsystem in the daemon (`lib/src/editor/`). `EditorBuffer` holds path + content + cursor/selection + dirty flag; `EditorRegistry` owns the open-buffer set, active-buffer tracking, and file I/O. IPC verbs land alongside (`editor.open | active | activate | list | read | insert | replace-selection | set-selection | set-content | save | close`) with matching events (`editor.opened | active-changed | selection-changed | edited | saved | closed`). Omitting `id` on mutating verbs targets the active buffer so the tier-2 CLI shortcuts (`clide insert "…"`, `clide replace-selection "…"`) read naturally. 16 new core tests cover the lifecycle + dispatcher round-trips. - `builtin.claude` — Tier-1 stub upgraded to the real Claude pane per D-41. Contributes a primary `Claude` tab in the workspace slot that spawns `tmux new-session -A -s clide-claude- -- claude` via IPC `pane.spawn`, with `` derived from the git root path so reopening the app re-attaches to the running conversation. Primary has no close affordance; closing the tab doesn't kill the session. Command `claude.new-secondary` is registered for the palette wiring that's coming next. If tmux isn't on PATH, falls back to spawning `claude` directly and surfaces "no-tmux · fresh every launch" in the header subtitle. Accompanied by D-41 in [`decisions/architecture.md`](decisions/architecture.md#d-41-claude-panes-one-primary-per-repo-tmux-backed). - `builtin.files` — workspace filesystem panel in the sidebar. Lazy tree rooted at the git root, expand/collapse, click-to-open plumbed to a future `editor.open` command. Backed by a new daemon-side `files.*` IPC subsystem (`files.root`, `files.ls`, `files.watch`) and a `FileWatcher` that wraps `Directory.watch(recursive: true)` with ignore-file filtering. Ignore set composes clide's built-in hide list (`.git/`, `.pql/`, `.clide/`, `.dart_tool/`, `build/`, `node_modules/`) with `.gitignore` / `.clideignore` at the root per D-4. `IgnoreSet` + `IgnorePattern` support line-per-pattern, `#` comments, anchored / directory-only / negated forms, and `**` across directories. 11 new unit tests on the matcher; 5 new dispatcher tests; 171 app tests still green. - `builtin.terminal` — general-purpose terminal pane, Tier-1 stub upgraded to a working implementation. Contributes a `Terminal` tab in the workspace slot that spawns `$SHELL -l` via IPC `pane.spawn`, streams `pane.output` events into `xterm.dart`, and routes user input through `pane.write`. Resize propagates via `pane.resize` on viewport change. `initState` → spawn; `dispose` → `pane.close`. Error-state surface for "daemon not connected" / "shell exited." No Claude-specific behaviour — that lives in `builtin.claude` + D-41. - Shared pane widgets under `app/lib/widgets/`: `ClidePtyView` wraps `xterm.dart` with clide-theme token bindings, JetBrains Mono as the face, and a Semantics live-region wrapper; `ClidePaneChrome` is the reusable title strip + optional close button. Consumers of the new widgets (`builtin.terminal`, `builtin.claude`) drive the xterm `Terminal` model and route bytes through IPC `pane.write` / `pane.output` events themselves — the widgets are rendering only, no IPC coupling. - `xterm: 4.0.0` Dart dependency on the Flutter app — MIT, listed in `licenses.yaml` per D-42. Hand-rolling a VT100 / xterm / truecolour parser + renderer would be weeks for no fidelity win. - `Q-23` — open question on SSH-remote development (run clide against a workspace on another host). Local-first stays the Tier-1 target; this records the constraint so the daemon / IPC / extension seams don't unknowingly accrete local-only assumptions. - IPC `pane` subsystem in the daemon (per D-6). Commands: `pane.spawn | list | focus | close | write | resize | tail`. Events: `pane.spawned`, `pane.output` (base64-framed), `pane.exit`, `pane.resized`, `pane.focused`, `pane.closed`. `PaneRegistry` owns per-pane `PtySession` lifecycles + id generation (`p_N`); a `DaemonEventSink` seam lets handlers emit events without depending on the IPC server package. `DaemonServer.broadcast()` fans events out to every connected client (a later pass adds per-client `--filter` scoping). Panes carry a `kind:` field — `terminal` today, `claude` ready for step 7. Covered by 14 new Dart core tests exercising the real registry + dispatcher against the `ptyc` helper. - `PtySession` in the Dart core (`lib/src/pty/`) — spawns a child under a PTY via the `ptyc` supporter tool, receives the master fd over `SCM_RIGHTS`, and exposes a byte stream, write, resize, and kill. A background isolate loops on blocking `read(fd)` and posts chunks to the main isolate. `close()` sends SIGTERM to the child so the PTY's EOF wakes the isolate cleanly, then falls through to SIGKILL + fd close + isolate kill as a safety net. Child env is built via `mergePtyEnv()` which stamps clide's true-colour defaults (`TERM=xterm-256color`, `COLORTERM=truecolor`, `CLICOLOR_FORCE=1`). Test coverage: echo round-trip, cat write/readback, env stamping verification, idempotent close. - `ffi: 2.1.3` as a runtime dependency on the Dart core — justified in `pubspec.yaml` + documented in `licenses.yaml` per D-42. Used by `lib/src/pty/ffi/` for `socketpair`, `recvmsg` with `SCM_RIGHTS`, `read`/`write` on raw fds, and `ioctl(TIOCSWINSZ)`. - `ci/test_core.sh` + `make test-core` — runs the Flutter-free core Dart tests (`test/`) under a 120s hard timeout with process-group cleanup. Wired into `push-check` ahead of the app test suite so a hung PTY test can't block the pre-push gate. - Josefin Sans bundled as `app/assets/fonts/josefin_sans/` as the application UI face — variable-font pair (upright + italic, weight range 100-700), OFL-licensed. Declared as the `JosefinSans` family in `app/pubspec.yaml`. `_AppRoot` installs it as the ambient `DefaultTextStyle` at weight `w300` (Light) per the project's aesthetic direction; callers can still pass an explicit `fontWeight` on `ClideText` to get bolder emphasis. - JetBrains Mono bundled as `app/assets/fonts/jetbrains_mono/` — Regular / Italic / Bold / BoldItalic weights (OFL-licensed, license file checked in alongside). Declared as the `JetBrainsMono` family in `app/pubspec.yaml`. `app/lib/widgets/src/typography.dart` exposes `clideUiFamily` + `clideMonoFamily` plus platform-ordered fallback chains for both faces, for web builds and harnesses that don't load asset fonts. - `app/assets/licenses.yaml` — canonical manifest of every bundled third-party artefact (fonts today; Dart packages + native tools as they land). Schema has name, kind, version, homepage, license, `license_file` pointer, and a one-line purpose. Bundled alongside the per-dep license texts. The About screen (Tier 6) will render this file verbatim. Accompanied by [`D-42`](decisions/tooling.md#d-42-bundled-dependencies-documented-in-licensesyaml): adding a dep is a two-step commit (artefact + `licenses.yaml` entry in the same changeset). ### Changed - CLAUDE.md "Dependencies & supply chain" section gains the "document every bundled dependency" rule, pointing at `app/assets/licenses.yaml` and `D-42`. - `ptyc/` — the C PTY-spawn helper, peer of `pql` per [`D-5`](decisions/architecture.md#d-5-dart-core-sidecar-dissolved-ptyc-as-pql-peer). One-shot, libc-only, ~400 LOC. Reads a JSON request on stdin (`argv`, optional `cwd`/`env`/`cols`/`rows`), does `posix_openpt` + `fork` + `execvp`, and hands the master fd back to the caller over a unix socket via `SCM_RIGHTS`. Socket fd defaults to 3; override via `PTYC_SOCK_FD` for language runtimes that shuffle pipe fds through the low numbers (Python's `subprocess` with `stdout=PIPE` does this). Exec-failure pipe (CLOEXEC) reports child-side errors to the parent without leaking zombies. Root Makefile gains `ptyc-test` target in addition to `ptyc-build` / `ptyc-clean`. - Migrated the `docs/ADRs/` content into `decisions/` as D/R records: ADR 0001 → `D-1`, ADR 0002 → `R-2` (superseded by `D-5`), ADR 0003 → `D-3`, ADR 0004 → `D-4`, ADR 0005 → `D-5`, ADR 0006 → `D-6`. Titles preserved; ADR 0006's trailing open questions moved to `questions-architecture.md` as `Q-1` / `Q-2` / `Q-3`. The originals are preserved in git history. - `decisions/` at the repo root — Q&D record system ported from settled-reach and adapted for clide's domains. Confirmed decisions (`D-NNN`) live under domain files (`architecture.md`, `extensions.md`, `accessibility.md`, `testing.md`, `tooling.md`, `process.md`); open questions (`Q-NNN`) live under parallel `questions-.md`; rejected alternatives (`R-NNN`) live in `rejected.md`. Record shape, claiming rules, and the eventual pql-side tooling plan are documented in `decisions/README.md`. Migration of the existing `docs/ADRs/` into these files lands in a follow-up commit. - `DECISIONS.md` one-line pointer at the repo root (matches settled-reach's convention). - `tools/scripts/plan` — Python stopgap entrypoint for `decisions` and `ticket` subcommands, writing to `.pql/pql.db` (gitignored). Supports `decisions sync | validate | claim | list | show | coverage` and `ticket new | list | show | status | assign | team | block | unblock | label | search | board`, plus `sqlite-query`. Verb shape and output format mirror the eventual `pql` subcommands so migration when pql ships feature parity is a call-site find-replace (`tools/scripts/plan ` → `pql `). Ported from settled-reach with the Scrum layer stripped; ticket IDs are `T-NNN` (TEXT PKs) and there's no `sprints` table. Time-limited per [`D-40`](decisions/process.md#d-40-python-stopgap-under-toolsscriptsplan) / [`R-11`](decisions/rejected.md#r-11-permanent-stopgap). - `make decisions-validate` — cheap parser dry-run wired into `push-check`. Catches malformed records before push. - Reserved extension slots — `builtin.decisions`, `builtin.tickets`, `builtin.claude-control`. Id-reserving stubs under `app/lib/builtin/` with no contributions yet. Implementations land once [`Q-21`](decisions/questions-architecture.md#q-21-pql-absorbs-planning-vs-keeps-separate) resolves (decisions + tickets) or when the claude-control tier arrives (`.claude/` first-class surface — distinct from the existing `builtin.claude` PTY-pane stub). - `CLAUDE.md` — new "Decision discipline" guardrail pointing at `decisions/`. ### Changed - `CLAUDE.md` — inline ADR links rewritten to point at the migrated `decisions/` records; bottom "Open questions" section collapsed to a pointer at `decisions/questions-*.md`; parent-project note updated to reference `decisions/architecture.md` instead of the deleted `docs/ADRs/`. - `make decisions-validate` rewired from `tools/scripts/plan` to `pql decisions validate`. - Decision discipline guardrail in CLAUDE.md now points at `pql decisions claim` instead of the Python stopgap. ### Removed - `tools/scripts/plan` — Python stopgap planning scripts, superseded by `pql` 1.0 native `decisions` and `ticket` subcommands. Sunset condition from [`D-40`](decisions/process.md#d-40-python-stopgap-under-toolsscriptsplan) met; deletion per [`R-11`](decisions/rejected.md#r-11-permanent-stopgap). ### Removed - `docs/ADRs/` directory — content lifted into `decisions/` as D/R records (see Added above). Originals preserved in git history. - Go sidecar skeleton under `sidecar/` — `cmd/clide/main.go`, `go.mod`, and the `internal/*` packages (`cli`, `daemon`, `diag`, `git`, `ipc`, `pql`, `proc`, `pty`, `version`). Deleted wholesale per [ADR 0005](docs/ADRs/0005-dart-core-ptyc-peer.md): the "sidecar language: Go" premise no longer holds once the core is Dart. All functionality listed for those packages will be reimplemented under `lib/` as part of Tier 0. - Go-specific Makefile targets (`lint`, `vuln`, `test-race`, `fmt`, `tidy`, `snapshot`, `tools`, `install` via Go), the `govulncheck`/`goimports`/`golangci-lint` version pins, and the pre-push hook's `GOBIN` PATH injection. Replaced with Dart/Flutter equivalents (`analyze`, `format`, `test`, `test-integration`, `build` via `dart compile exe`). - `module:` and `go_version:` from `pubspec.yaml` — single-language core means no Go module path to track. ### Changed - [ADR 0002](docs/ADRs/0002-sidecar-language-go.md) marked **superseded** by [ADR 0005](docs/ADRs/0005-dart-core-ptyc-peer.md). The "sidecar language: Go" guardrail is retired. CLAUDE.md's guardrails, dependency notes, and command reference are updated to reflect the Dart-core direction. - `.gitignore` retargeted: Flutter/Dart output at the repo root (`.dart_tool/`, `build/`, platform ephemeral dirs, `bin/clide`), plus a `ptyc/` section for the C helper's build artefacts. Go-specific rules removed. - `ci/lint.sh`, `ci/test.sh`, `ci/security.sh`, and `.githooks/pre-push` rewritten for the Dart toolchain — no Go shell-outs, no `GOBIN` PATH dance. ### Added - Testing docs under `docs/testing/`: `README.md` (what each layer covers, how to run, local vs CI flow), `a11y-manual.md` (15-minute Orca + VoiceOver checklist run at every tier cut), `claude-ui-workflow.md` (how Claude Code drives the app through the Playwright harness, including the `flt-semantics-placeholder` quirk). - Makefile targets for every test layer and the UI harness: `test`, `test-a11y`, `test-integration`, `test-e2e`, `test-all`, `coverage`, `smoke-bundle`, `ui-dev`, `ui-stop`, `ui-smoke`. `push-check` now runs `test + test-a11y` (fast pre-push gate, <90s). - Per-layer CI shell scripts under `ci/`: `test.sh` (analyze + format + unit + widget + golden, ~5s), `test_a11y.sh` (a11y contract), `test_integration.sh` (integration_test one file at a time — desktop can't batch them reliably), `test_e2e.sh` (daemon subprocess + browser WASM Playwright smoke), `smoke_bundle.sh` (xvfb-run the Linux release bundle for 5s; catches dynamic-linker / asset-bundle / plugin-init regressions that widget tests can't see), `coverage.sh` (flutter test --coverage + lcov summary). - `.gitea/workflows/test.yml` — four-job pipeline (`unit`, `integration`, `startup-bundle`, `e2e`) that shells out to the `ci/*.sh` scripts. **Not activated yet** — Gitea Actions has to be enabled in the instance settings first. GitHub-Actions-syntax-compatible, so copying to `.github/workflows/` is a one-file move when the repo migrates. - Web WASM harness under `tools/ui/` — Playwright driver so Claude Code (and humans) can drive the Flutter build in a real browser via the Semantics tree. `build.sh` / `serve.sh` / `stop.sh` manage a local `http.server` on `:4280` with port-based reclaim and kill (so orphaned listeners from earlier runs get swept). `driver.ts` exposes `ClideDriver` with `byLabel` / `click` / `type` / `readText` / `screenshot` / `dumpSemanticsTree` / `waitUntilReady` (auto-clicks the `flt-semantics-placeholder` to enable the semantics tree). First Playwright test `smoke.spec.ts` asserts welcome + disconnected labels render in the browser. - Integration tests under `app/integration_test/`, run with the `integration_test` package against the real built app (not an in-memory widget pump). The load-bearing startup gate lives here: `app_starts_test.dart` boots `ClideApp`, waits for the root shell to settle, and asserts the three-column layout + welcome tab + statusbar connection indicator all render. Also covers theme-picker modal open/select/dismiss (`theme_picker_test.dart`) and extension enable/disable lifecycle with contributions mounting/unmounting (`extension_lifecycle_test.dart`). - App-level test suite under `app/test/` — 168 tests across four layers: - **Unit** (`kernel/`, `extension/`) — events bus, settings (scope + YAML round-trip), log, i18n fallback chain matrix, theme resolver + loader + controller, panel registry + arrangement, command registry + keybinding parser + palette filter, extension-manager dep-order / cycle detection / enable-disable, manifest loader, extension scanner. - **Widget** (`widgets/`, `builtin/`) — every primitive's Semantics presence + token consumption + hover/press states; each Tier 0 built-in's contributions, view, and locale-switch re-render. - **Golden** (`goldens/`) — widget primitives only, Alchemist + Ahem font; PNG fixtures checked in under `_files/ci/` and `_files/linux/`. - **A11y** (`a11y/`) — `semantic_coverage_test.dart` (contract-level check that every built-in carries title + version + label-ready contributions), `contrast_test.dart` (WCAG-AA ratio gate on every bundled theme's canonical token pairs), `i18n_coverage_test.dart` (asserts every Tier-0-referenced key is present in its `en_US` catalog), `keyboard_traversal_test.dart` (focusability smoke). - Test helpers under `app/test/helpers/` — `KernelFixture` (boots a KernelServices with in-memory defaults + a fake daemon for widget-level tests), `FakeDaemonClient` (subclasses the real client, no socket, drivable connected-state), `golden_harness` (Alchemist config with Ahem font for cross-platform pixel stability), `widget_harness` (wraps a widget in Directionality + ClideKernel + ClideTheme + MediaQuery). - Flutter desktop app scaffold under `app/` with a bare `WidgetsApp` root (no Material, no Cupertino) and the Tier 0 three-column layout. - **Kernel** (`app/lib/kernel/`) — 18 services consumed by every extension: `settings` (scope-resolved get/set across `app.*`/`project.*`/`ext.*`), `project`, `extensions`, `theme`, `panels` (slot registry + arrangement), `events`, `ipc`, `commands` (+ palette + keybinding resolver), `clipboard`, `files`, `notify`, `dialog` (single-at-a-time modal router), `tray`, `secrets`, `os`, `net`, `focus`, and `log`. Unified in a `ClideKernel` `InheritedWidget`. - **i18n** (`app/lib/kernel/src/i18n/`) — text-driven lookup ported from [fframe](https://github.com/postmeridiem/fframe)'s `L10n`: namespaced JSON catalogs, `string()` / `interpolated()` calls with caller-supplied placeholders, and a proper locale fallback chain (exact → language → default-country → default-language → placeholder). Improves on fframe's design by adding the chain, which fframe lacks. - **A11y from Tier 0** — `Semantics(label:, hint:, button:)` on every interactive primitive; `SemanticsBinding.ensureSemantics()` at boot; a `theme/contrast.dart` helper exposes token pairs that the a11y suite walks for WCAG-AA compliance. - **Extension contract** (`app/lib/extension/`) — abstract `ClideExtension`, sealed `ContributionPoint` hierarchy (`TabContribution`, `StatusItemContribution`, `ToolbarButtonContribution`, `CommandContribution`, `TrayItemContribution`, `LayoutPresetContribution`). Each extension ships one manifest contributing N atoms into kernel slots. Priority-based ordering within a slot, dependency-aware activation, YAML manifest loader + scanner for `~/.clide/extensions/`. - **Three-tier theme pipeline** — palette (named colors) → semantic roles → ~60 VS-Code-style surface tokens, each layer with defaults so palette-only themes ship. Ported `summer-night` as the first bundled theme; muted value calibrated for WCAG-AA contrast. - **Widget primitives** (`app/lib/widgets/`) — `ClideSurface`, `ClideText`, `ClideButton`, `ClideTabBar`, `ClideDivider`, `ClideScrollbar`, `ClideTooltip`, `ClideIcon` + eight `CustomPainter`-rendered icons (folder, gear, x, chevron-left/right, dot, check, plug). All token-consuming, all Semantics-wrapped. - **Tier 0 built-in extensions** — `builtin.default-layout` (classic three-column preset + reset command), `builtin.welcome` (workspace placeholder), `builtin.ipc-status` (live-region statusbar indicator), `builtin.theme-picker` (command + modal, bound to `ctrl+k`). Plus 17 id-reserving stubs (`builtin.claude`, `builtin.terminal`, `builtin.files`, `builtin.editor`, `builtin.git`, ...) so later tiers can fill in without rename churn. - **Lua runtime boundary** — `app/lib/lua/` ships as typed stubs (`host`, `adapter`, `capability_api`, `render_intent`) so third-party Lua extensions can plug in at Tier 6 without retrofitting. - `.gitignore` extended to cover `app/` sub-package artefacts (`app/.dart_tool`, `app/build`, per-platform ephemeral dirs, `app/*.iml`) and the Playwright harness under `tools/ui/` (`node_modules`, `out`, test-results). - Dart core package at the repo root: `bin/clide.dart` (one binary, `--daemon` and one-shot subcommand modes), `lib/clide.dart` barrel exporting the shared IPC types, `lib/src/ipc/` (`envelope.dart`, `server.dart`, `paths.dart`, `schema_v1.dart`), and `lib/src/daemon/dispatcher.dart`. `clide --daemon` listens on a unix socket; `clide ping` / `clide version` round-trip through it with the ADR 0006 exit-code contract (`0/1/2/3/4`). Includes `test/ipc/` and `test/daemon/` suites covering envelope parsing, the in-process server, and a subprocess smoke that verifies signal-driven shutdown + socket unlink. - `scripts/bazzite-flutter-setup.sh` — one-shot installer for the Flutter SDK + desktop build deps on Bazzite / Fedora Silverblue. Drops the SDK under `~/opt/flutter`, wires PATH in the user's shell rc files, and layers the Linux desktop build deps via `rpm-ostree install`. - [ADR 0005](docs/ADRs/0005-dart-core-ptyc-peer.md) — Dart core; sidecar directory dissolved; `ptyc` as pql-peer. Establishes one Dart AOT binary for both CLI and daemon, `lib/` as the shared core, and promotes the C PTY helper to a standalone supporter tool on the same footing as pql. - [ADR 0006](docs/ADRs/0006-cli-and-event-surface.md) — CLI and event surface contract. Defines the subsystem list (`pane`, `tab`, `editor`, `panel`, `tree`, `git`, `pql`, `canvas`, `graph`, `theme`, `settings`, `project`), the command shape, the versioned JSON event schema, the pql-style exit-code contract, and the command↔event duality rule that operationalises user/Claude parity. - Architectural decision records carried forward from the short-lived `claudian` plugin project (discarded in favour of this Flutter rebuild): [ADR 0001](docs/ADRs/0001-cli-first-not-mcp.md) — CLI-first, not MCP. [ADR 0002](docs/ADRs/0002-sidecar-language-go.md) — Sidecar language: Go. [ADR 0003](docs/ADRs/0003-pql-as-supporter-tool.md) — pql as supporter tool; wrap, don't duplicate; pql is a Clide subsystem when present. [ADR 0004](docs/ADRs/0004-ignore-file-strategy.md) — Ignore file strategy (`ignore_files:` in `.pql/config.yaml`, layered). - Pre-push quality gate: `.githooks/pre-push` runs `make push-check` (lint + test + test-race + test-integration + vuln + app-analyze + app-test) so bad pushes are caught locally before they hit Gitea. The app-side targets gracefully noop until Flutter is scaffolded. Install with `make hooks` (sets `git config core.hooksPath .githooks`); the hook prepends `$GOBIN`/`$HOME/go/bin` to PATH so govulncheck resolves without the user touching their shell profile. - Go sidecar/CLI skeleton under `sidecar/` (module `git.schweitz.net/jpmschweitzer/clide/sidecar`): `cmd/clide/main.go`, `internal/cli` with a stdlib-flag dispatch, `internal/diag` mirroring pql's exit-code + stderr-JSON contract, `internal/version` with ldflag-stamped build info, and placeholder packages for `daemon`, `pty`, `proc`, `git`, `ipc`, `pql` awaiting their tier. `clide --version` emits JSON build-info today. - Root `Makefile` drives both the Go sidecar and the Flutter app under one toolchain. Version is read from `pubspec.yaml` via awk and stamped into the sidecar via `-ldflags -X`. Flutter targets gracefully noop before the app is scaffolded so the Makefile is usable from day one. Pinned Go tooling (govulncheck, goimports, golangci-lint) installs via `make tools`. - `ci/` entry scripts: `test.sh`, `lint.sh` (includes the supply-chain gate — no green lint without a green CVE scan), `security.sh`, `release.sh` (stub). - Project identity files for the Flutter rebuild at the repo root: `pubspec.yaml` (single source of truth for version + module path, version 2.0.0-dev), a fresh `README.md`, MIT `LICENSE`, and `.editorconfig`. The Python clide's manifest and README are preserved under `legacy/`. - [`docs/initial-plan.md`](docs/initial-plan.md) — the north-star design document for the Flutter rebuild. Captures what we kept from Python Clide (pane model, git skills, Claude-always-visible), what we took from Obsidian (canvas and graph — no vault, no bases, no plugin inheritance), what Claudian's short experiment contributed (Go sidecar, CLI-first, pql-as-subsystem, ignore-file strategy), and the tier roadmap (Tier 0 app+sidecar handshake → Tier 5 canvas+graph). - [`CLAUDE.md`](CLAUDE.md) orientation doc for future Claude Code instances: project identity, guardrails as one-liners, tier ordering, parent-project pointers, commands, dependencies & supply chain, open questions. Points at the design doc and ADRs rather than restating their content. - Claude Code configuration under `.claude/`: project-level allow/deny permissions and two skills — `skill-create` (generic skill authoring guidance) and `git-commit` (this repo's commit conventions: no Conventional Commits, Keep a Changelog discipline, `pubspec.yaml`-and-changelog-bumped-together rule, attribution trailer, safety reminders).