Files
clide/CHANGELOG.md
T
jpmschweitzerandClaude 2888efbe5f add per-agent permission-mode badge to the cockpit roster
Each roster row shows a D/A/P mode badge reflecting the session's live
permission mode. Click cycles the safe trio default -> acceptEdits ->
plan and sends a set_permission_mode control_request to that session
(mirrors interrupt(); fire-and-forget). bypassPermissions is a footgun,
so it is reachable only on Shift-click and behind an inline confirm. A
clide.agent.set-permission-mode command gives the CLI parity.

T-181.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-30 23:25:07 +02:00

68 KiB
Raw Blame History

Changelog

All notable changes to clide are documented in this file.

The format follows Keep a Changelog 1.1.0, and this project adheres to Semantic Versioning.

This changelog tracks the Flutter rebuild at the repo root. The Python Textual implementation's changelog is preserved under legacy/CHANGELOG.md.

Versions are tracked in 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]

Fixed

  • Claude conversation card actions (copy + custom) are now keyboard-focusable and always reachable — revealed on hover or focus, activatable by Tab + Enter/Space, with Semantics labels for assistive tech (T-174).
  • Status bar no longer overflows when the focused-pane context line is long — the in-pane slot now takes a flexible share of the bar and marquee-scrolls within it instead of pushing the row past its width.
  • Write/Edit permission cards no longer print the file path twice — the description line is suppressed when it just repeats file_path.
  • Resumed Claude session no longer starts with an empty pane — claude --resume carries Claude's prior context but emits no past turns over stream-json, so the orchestrator now seeds the conversation by reading the tail (up to 256 KB) of the transcript JSONL on disk.

Changed

  • Claude conversation tool cards are now typed — Edit/Write render a diff, Bash shows the command and its output, Read/Grep show the file/query, and each tool result pairs back to its call to render the diff or error in place instead of an indented JSON dump (T-168).
  • Claude status line reflects live session state from stream-json events — model, permission mode, context size, and turn cost come straight off the init/result events rather than a separate config probe (T-168).
  • Claude session persistence now rides on claude --resume instead of tmux (T-167, amends D-41). A restart resumes the primary session, /clear starts a fresh one, and /resume reopens a picked session — all without tmux.
  • Permission prompt cards render the tool input in the shape that fits the tool — Bash shows the command as a shell code block (with a footer for run_in_background / timeout), Write shows the path plus the content highlighted from its extension, Edit shows the path plus before/after blocks. Unknown tools fall back to the indented-JSON dump.
  • Claude meta sidebar is now tabbed — Activity / Team / Config (T-182). Activity shows usage stats plus the primary session's live runtime; Team holds the roster and auto-fronts when a team spawns; Config shows the environment settings table. Activity and Config share one table geometry so switching doesn't jump.

Removed

  • tmux is no longer used for Claude sessions (T-167) — the tmux session lifecycle and the tmux-polling team observer are gone, replaced by the managed-session orchestrator. tmux is still used for the general-purpose terminal pane.

Added

  • Claude team cockpit — the meta sidebar's Team tab gains live controls for clide-managed agents: show/hide, mute, close, and inject-a-message per roster row, plus a live shared task list with reassign. Each action has a matching clide command (D-6 parity). (T-171, D-77)
  • Per-agent permission-mode badge in the cockpit roster — click cycles the safe trio default → acceptEdits → plan and sends set_permission_mode to that session; Shift-click reaches bypassPermissions behind a confirm. The badge reflects the live mode. (T-181, D-77)
  • Team coordination broker (T-170, D-77) — clide hosts an in-process MCP server (clide-team) for managed sessions over the stream-json control channel, giving agents tools to message each other, broadcast, see the roster, read an inbox, and share a task list. Each agent's role and the roster are injected into its system prompt.
  • Interrupt a running Claude turn (D-78) — Escape in the composer (when no typeahead is open) or a Stop button shown while busy cancels the current turn over the stream-json control channel. The escape hatch from a runaway turn.
  • Native permission & AskUserQuestion prompts (T-166, T-175, T-176, T-179, D-78) — the composer becomes a prompt: Allow / Allow-and-don't-ask-again / Deny showing the command, or an AskUserQuestion option picker (single or stepped, with "Other" free-text and per-choice notes). Closes the tmux prompt gap.
  • Conversation message cards (T-173) — every turn in the Claude pane now renders through one card template with a copy button on hover and a collapse/expand caret for tool calls, results, and thinking.
  • Collapsed-by-default tool cards (T-177) — multi-line tool calls and results start collapsed behind a one-line summary; one-line output stays inline so a caret never hides a single line.
  • Prompted tool calls are quieter in the log (T-179) — a permission request shows the command in the prompt, not as a raw tool-use card; once decided it collapses to a one-line summary with a green (approved) or red (denied) border, and the result is kept. AskUserQuestion's tool-use + result are replaced by the logged answer.
  • Harness-injected messages are de-emphasized (T-178) — skill loads, slash-command expansions, and system reminders (Claude's isSynthetic messages) render as a muted, collapsed "context" card instead of a blue "you" message, since they weren't typed by the user.
  • Claude meta sidebar (T-141, T-157) — an always-pickable left-panel tab showing Claude activity (the latest day's messages/sessions/tool-calls plus lifetime totals, from stats-cache.json) and, when a tmux team is running, a roster of its members (colour · name · agent type · model) with each member's live permission-mode and context once it's active.
  • Claude session storage view (T-148) — the claude.session-storage command opens a modal listing the workspace's session transcripts with their on-disk sizes and a total, each removable with a two-click confirm. User-driven only; clide never deletes transcripts on its own.
  • Slash-command typeahead in the Claude composer (T-152) — typing / (anywhere in the message, not just at the start) pops a list of matching commands and skills sourced from the Claude environment; arrow keys move, Enter/Tab completes, Escape dismisses.
  • Claude environment service (T-151) — clide reads skills, commands, settings, and permissions from ~/.claude and the repo's .claude (layered), and caches the slash-command list per claude version. Backs the typeahead and command-aware send.
  • Per-session status in the bottom status bar (T-145, T-150, T-154) — the active Claude pane shows its model · permission mode (accept-edits / plan / …) · context-token count · configured skills count, swapping to the focused pane on tab switch and clearing on blur. Long status text marquee-scrolls within the slot.
  • tmux agent teams surface as native teammate tiles (T-139, T-140) — when a Claude team is running, each teammate shows as a live conversation tile beside the lead in a grid that wraps 1→2→3 columns, with a resizable split. Identity and lifecycle come from the team config; per-teammate content streams over the MessageBus.
  • Native composer in the Claude pane (T-138) — type below the conversation and press Enter to send (Shift+Enter for a newline). Submits via the tmux server (bracketed paste + Enter), so input reaches Claude even when no tmux client is attached; multi-line goes as one message.
  • File and image paste in the composer (T-138, T-142) — Ctrl/Cmd+V of a copied file or clipboard image adds a removable chip (image thumbnail or file icon) above the input; on send its @path is appended to the message. Plain text pastes inline. Backed by a native clide/clipboard channel (GTK + macOS).
  • Claude pane renders natively from the transcript (T-137, D-75) — the conversation shows as native cards (user / assistant markdown / thinking / tool-use / result) instead of a terminal, with text selection + copy across cards. Claude still runs in tmux; the terminal builtin stays for general use.
  • Multi-file editor tabs — the editor pane now shows one tab per open buffer (filename + a dot when unsaved) via the shared tab strip; opening a second file no longer replaces the first. Click a tab to switch, × to close. Backed by the daemon's existing multi-buffer model.
  • Typed IPC command-schema framework (T-119/T-120, D-74) — commands register an argument schema beside their handler; the dispatcher normalises argv into named args, coerces types, and validates (charset, leading-dash, ranges, caps) before the handler runs.
  • clide panel resize <slot> CLI verb (T-119) — set an absolute size with --to or nudge with --by; editor targets the split ratio. Completes user/Claude parity (D-6) with T-111's keyboard resize.
  • Unix-domain IPC socket server in the Flutter app (T-99 / T-124). Per-workspace path (D-70: $XDG_RUNTIME_DIR/clide/<hash>.sock on Linux, ~/Library/Caches/clide/<hash>.sock on macOS). 0600 socket
    • 0700 parent (D-71). Multi-connection accept loop with serial dispatch through DaemonDispatcher (D-72). Foundation for the C clide client (T-126) and MCP (T-130). No client yet — testable via socat - UNIX-CONNECT:$SOCK.
  • argv→IpcRequest translator (lib/src/cli/argv_to_request.dart) — parses clide SUBSYSTEM VERB [pos...] [--flag] [-- passthrough] and the umbrella commands (status, tail, version, ping) per D-6 into the wire envelope. Pure Dart; lets the C client (T-126) stay a dumb pipe (T-99 / T-125).
  • DaemonClient.reconnectAt(newPath) — swap an active client onto a different socket without restart (project switch in T-127).
  • Event streaming over the IPC socket (T-99 / T-129) — clide tail --events [--filter X] opens a long-lived subscription, replays up to 16 recent matching events per subsystem (D-6), and streams new ones as JSON lines. C client loops on data.streaming ack. Slow / broken subscribers drop themselves without blocking the bus.
  • MCP server over HTTP+SSE (T-99 / T-130, per D-68 / D-73). Localhost HTTP listener advertises via $HOME/.claude/ide/<pid>.lock so Claude Code's /ide discovers it. JSON-RPC 2.0 with the two minimum /ide tools shipped as stubs (mcp__ide__getDiagnostics, mcp__ide__executeCode); real implementations follow.
  • C clide shell client at native/clide-cli/clide.c. Walks CWD up to the git root, hashes to the per-workspace socket (D-70), ships argv. make clide-cli builds it; on PATH, clide status works from any clide-workspace directory once the app is up (T-99, T-126).
  • Startup project picker — clide now opens to the welcome screen by default instead of auto-opening the last project. A per-row "always open this project on launch" checkbox in welcome's RECENT list sets a sticky-startup flag; if exactly one project has it, that one opens directly. Two or more, or none ⇒ picker (T-115).
  • CONTRIBUTING.md "Running clide from the shell" section — documents the shell verbs, exit-code contract per D-68, and Claude Code /ide MCP discovery via ~/.claude/ide/<pid>.lock (T-131).

Changed

  • The empty Claude pane now shows a native startup banner — clide logo, session role, workspace, and tmux status — instead of a bare "Waiting for Claude…" (T-149).
  • User and Claude turns in the conversation now render as distinct accent-striped cards over a filled background — your prompts in the theme focus colour, Claude's replies in Claude's brand orange (T-143, T-144).
  • Claude conversation content now flows through the kernel MessageBus — a reader tails the transcript and publishes items; the pane subscribes. Decouples reading from rendering so the upcoming team panels can show one lead plus a tile per teammate (T-137).
  • In-process IPC dispatch swapped for socket loopback (T-127). The Flutter UI's DaemonClient now talks to its own IpcServer over the same per-workspace Unix socket the C clide client uses — one transport, one contract.
  • D-56 / D-68 amended with implementation notes — both decisions now link out to T-99's eight slices (T-124T-131) and the D-70/71/72/73 records they spawned (T-131).

Deprecated

Removed

  • lib/kernel/src/ipc/in_process.dart (InProcessClient) — replaced by the socket-loopback DaemonClient (T-127).
  • lib/kernel/src/backend.dart, lib/kernel/src/backend_entry.dart, lib/kernel/src/ipc/isolate_client.dart — the third unused IPC path (a backend-isolate model that was never wired through). Only the socket model survives now (T-128).

Fixed

  • Claude pane is no longer dead-on-arrival when resuming a session (T-161). The primary pane (and /resume) relaunched Claude with --session-id <existing-id>, which Claude rejects as "already in use" — so the pane had no live backend and typed input vanished. clide now uses --resume for an existing session and --session-id only for a brand-new one.
  • Claude pane no longer floods the console with "markNeedsBuild called during build" (T-159) — a focused pane surfacing its status-bar widget now defers the notification out of the build phase instead of rebuilding the status item mid-build.
  • The git / tickets / decisions / pql / problems tabs no longer log i18n: namespace not registered on boot (T-155). An extension's localized tab title is now loaded automatically on activation, and the five missing catalogs were added.
  • Slash commands sent from the Claude composer now actually run (T-153). Recognised commands are delivered as typed input so Claude's TUI parses them; other input (and stray leading slashes like a /tmp path) stays bracketed-pasted as literal text.
  • /clear is now handled by clide — it resets the Claude pane to a fresh, empty session — instead of being forwarded to Claude Code, whose /clear forked to a new session clide couldn't follow and left the pane unresponsive (T-156).
  • /resume is now handled by clide too (T-156): it opens a picker of the workspace's past sessions — each labelled by its first … last user message and when it was last active — and re-binds the pane to the chosen one, instead of forwarding Claude Code's session-forking /resume.
  • Claude secondary panes no longer flash a false "session exited" while the session is alive (a transient tmux client exit is now verified against the live session), and the tab and banner agree on the label ("session N") (T-149).
  • Claude pane no longer gets stuck on "Waiting for Claude…" when its session can't be bound (T-147) — e.g. a session left over from before session-id binding, or a fresh machine. It now retires that stale clide session and starts a clean one. Only clide's own -L clide sessions are touched (never a terminal Claude, never transcript files).
  • Secondary Claude tabs showed the primary's conversation instead of their own (T-146). Each pane now binds to its own session via claude --session-id, so concurrent sessions in one workspace stop colliding on the newest transcript — the primary keeps a stable id (resumes), secondaries get a fresh one (clean session).
  • Claude pane no longer freezes the app on open — the transcript reader caps its initial read to the recent tail, parses off the UI isolate, and coalesces view notifications into one rebuild per burst (T-137).
  • Daemon-not-connected on startup — panels and the Claude pane raced the socket loopback. Requests now wait briefly for an in-flight connection, the server isn't restarted for the same workspace, and the client connects once instead of twice.

Security

[2.1.0] — 2026-05-18

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. 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. Inert with the rest of the workflow until Gitea Actions activates.
  • Mouse wheel scrolling in Claude pane — converts scroll events to PgUp/PgDown so 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, drag-reorder, close × on hover, and an optional + add button.
  • MultitabPane.keepAlive mode — entry bodies stay mounted via IndexedStack so switching tabs preserves their state (PTY connections, scroll position, etc.).
  • CONTRIBUTING.md — human-addressed contributor guide covering clone / build / test / DQR / tickets / commit conventions. The [Unreleased] section is reorganised to one subsection per kind per Keep a Changelog 1.1.0 (T-109).
  • make verify — no-tests sweep (analyze + format + decisions + changelog gate). For mid-edit checks; make push-check stays the full pre-push pipeline.

Changed

  • Terminal mouse wheel forwards as proper xterm wheel-button escapes when the inner program declares a mouse mode (?1000h / ?1002h / ?1003h, optionally +?1006h SGR). Falls back to PgUp/PgDown only when no mouse mode is active. vim mouse=a / htop / less mouse modes now react to the wheel (T-74).
  • lib/src/terminal/ cleaned to the project bar — commented-out print() debugging stubs stripped from custom_text_edit.dart, stale TODOs in parser.dart + keytab.dart replaced with clear "not implemented" notes (G2/G3 charsets, VT52 records), and the one // ignore: invalid_use_of_protected_member in terminal_view.dart gets an inline reason explaining why TerminalView owns its own ShortcutManager. Parser split deferred to T-123 (T-107).
  • Window-control close-button red, white close glyph, and palette ambient shadow are now tokens (windowControl.closeHover*, shadow.ambient) instead of hard-coded hex. Light themes get a softer ink-tinted shadow (T-114).
  • Text-zoom (Ctrl +/-/0) is now a kernel TextZoom service and shows up in the palette as View: Zoom In/Out/Reset Zoom (T-114).
  • make gen-build-info bakes lib/src/build_info.g.dart (name, tagline, version, repository, commit, date) and rewrites assets/licenses.yaml self.version: from pubspec.yaml on every build/run/test target. Welcome banner / status line / window title / project switcher labels all read from those constants — one source of truth, no manual sync, no --dart-define plumbing. New tagline: field in pubspec for the short user-facing line (welcome subtitle, future web meta).
  • Panel splitters (sidebar / context / editor-split) are tab-focusable; arrow keys nudge by 10 px, Shift+arrow by 50 px (2% / 10% for the editor split). Exposed as slider Semantics nodes so screen readers announce the current size. CLI verb deferred to T-99 (T-111).
  • 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 until that's fixed (T-103, T-116).
  • 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 render bold attributes with a real bold weight — bundled JetBrainsMono Bold + BoldItalic registered with the JetBrainsMono family at weight: 700. The painter's bold suppression workaround 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.dartclideInset* for paddings, clideGap* for sibling distances, clideIcon* / clideControlHeight for control sizes.
  • Tagline reads "IDE for Claude Code CLI" everywhere (welcome subtitle, README, CLAUDE.md, pubspec, web manifest, CLI banner).
  • 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.
  • PTY read buffer increased from 4 KB to 64 KB.
  • Terminal view 2 px padding on all sides.

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 deleted. Gone now, along with orphaned tests, 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. Replaced with platform-keyed goldens (goldens/linux/, goldens/macos/).
  • Bold JetBrains Mono font registration that prevented glyph-width mismatch in terminal rendering — superseded by the Bold/BoldItalic re-registration above.

Fixed

  • Welcome status line no longer overflows on narrow viewports — whole-row FittedBox(scaleDown) instead of fixed sibling widths. Inline fontSize: literals replaced with the typography constants. Version label reads clideVersion so the status line stays in sync with pubspec.yaml (T-116).
  • Integration test theme_picker_test.dart no longer deadlocks — was awaiting services.commands.execute('theme.pick') whose Future doesn't complete until the dialog is dismissed. Now fire-and-forget around pumpAndSettle (T-116).
  • 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. Real trigger: Terminal.eraseDisplayAbove with the cursor at column 0 — common after ESC[H\x1b[1J (home + erase-above).
  • 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).
  • Closing a secondary Claude pane tab now kills its tmux session on the clide socket, honouring D-41's lifecycle. Previously pane.close only killed the ptyc-spawned tmux client.
  • 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.
  • 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.
  • 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.
  • 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.
  • pane.spawn and editor.open map POSIX errno values to actionable IPC error kinds (ENOENT → not_found, EACCES/EPERM → user_error, EISDIR/ENOTDIR/EEXIST → distinct kinds, EMFILE/ENFILE → tool_error with an fd-limit hint).

Security

  • IPC: git.checkout, git.push reject branch/remote args starting with - (closes the --upload-pack=... argv-injection vector). files.read rejects files over 10 MB. git.log caps count at 1000; git.diff / git.stage cap paths at 256 (T-104).
  • 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 reject symlinks whose targets live outside the workspace — closes a path-safety bypass via in-repo symlinks (T-102).
  • files.read and files.ls reject paths that resolve outside the workspace root. Previously a relative path containing .. could read arbitrary files via path traversal.

[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=<cat> 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+15), 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 <paths>, git stage-all, git unstage, git discard, git commit "<msg>", 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.activeeditor.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 <path>, clide active, clide insert <text | ->, clide replace-selection <text | ->, 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-<hash> -- claude via IPC pane.spawn, with <hash> 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.

  • 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; disposepane.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: 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. 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-<domain>.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 / R-11.

  • 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 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 met; deletion per R-11.

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: 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 marked superseded by ADR 0005. 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'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 0Semantics(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 extensionsbuiltin.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 boundaryapp/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 — 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 — 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 — CLI-first, not MCP. ADR 0002 — Sidecar language: Go. ADR 0003 — pql as supporter tool; wrap, don't duplicate; pql is a Clide subsystem when present. ADR 0004 — 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 — 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 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).