test / unit + widget + golden + a11y (push) Failing after 41s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m22s
Wraps the `dart doc --validate-links` step so any warning fails the
job, not just hard errors. The previous step exited 0 even with
broken doc refs and dangling README links — exactly the
informational-mode drift that lets a clean board rot.
Updates the CHANGELOG entry to describe the gate accurately (the
earlier wording overstated `--validate-links`, which only prints).
Co-Authored-By: Claude <noreply@anthropic.com>
Eight unresolved doc references and broken README-rewritten links
that surfaced under `dart doc --validate-links`:
- Library-scope refs `[spawn]`, `[openProject]` qualified to
`[Backend.spawn]` / `[Backend.openProject]`; same treatment for
`[resolvePaths]` / `[applyResolved]` on Toolchain.
- `[D-41]` was a decision ID, not a Dart symbol — drop the brackets.
- `[from]` from I18n.interpolated qualified to `[I18nReplacer.from]`.
- `[DefaultSurfaceMap]` was a stale name (private `_defaultSurfaceMap`
in resolver.dart); switch to backticked path reference since
dartdoc can't link private members.
- `[icons/]` was a directory, not a symbol; backticked path.
- README links to `legacy/`, `docs/initial-plan.md`, `decisions/`,
`LICENSE` rewritten as absolute github.com/postmeridiem/clide URLs
so dartdoc stops re-rooting them into the doc tree.
Co-Authored-By: Claude <noreply@anthropic.com>
`dart doc` writes the rendered API site to `doc/api/`. The CI step
uploads it as an artefact; locally it's regenerated on every run and
should never land in the tree.
Co-Authored-By: Claude <noreply@anthropic.com>
Walks the pql initiative/epic tree, filters to unblocked tickets,
optionally refines context via parallel agents, and transitions a
confirmed batch to in_progress. Mirrors the existing pql skill's
place in the planning flow so /whats-next is the natural counterpart
to "what's the plan status".
Co-Authored-By: Claude <noreply@anthropic.com>
Adds a docs job to .gitea/workflows/test.yml that runs
`dart doc --validate-links` and uploads doc/api/ as an artefact.
Runs in parallel with unit; documents the public lib/ surface and
fails the build on broken references. Stays inert with the rest of
the workflow until Gitea Actions activates per D-32.
Co-Authored-By: Claude <noreply@anthropic.com>
The pql plan auto-export (pre-commit) and auto-import (post-merge)
hooks were previously gitignored as part of `.pql/*`. Allowing the
hooks directory to be committed means a fresh clone gets the
planning-state sync without needing to run `pql init` first — pql
plan snapshots stay current on push and absorb changes on pull
the same way for every contributor.
Co-Authored-By: Claude <noreply@anthropic.com>
Three remaining acceptance criteria for T-87:
1. Cold-start reap. The Claude extension's activate() now kills
every leftover secondary tmux session for the current repo
before any new spawn. activate runs before any UI mounts, so
_nextSecondary's starting value of 1 is correct even when a
previous run died abruptly (kill -9, OOM, force-quit). The
deactivate() hook also calls reapSecondaries as a courtesy on
explicit extension teardown — but Flutter's deactivate doesn't
fire on app quit, so activate is the load-bearing path.
2. claude.kill-all-sessions actually kills server-side. The
command previously called pane.close on every claude pane,
which only kills the tmux client. It now also calls
tmux.killAllForRepo to kill the sessions on the clide socket.
3. Tests. test/builtin/claude/tmux_session_test.dart covers
killSession, listClideSessions, reapSecondaries, and
killAllForRepo via the TmuxRunner override — no real shell-out
in tests.
Co-Authored-By: Claude <noreply@anthropic.com>
Adds lib/builtin/claude/src/tmux_session.dart with helpers for the
clide-socket tmux server: killSession, listClideSessions,
reapSecondaries, killAllForRepo. The runner is overrideable via a
TmuxRunner typedef so tests don't shell out for real.
Wires ClaudePane.dispose() to call killSession(sessionName) for
secondary panes. Primary panes are left alone — D-41 keeps the
primary's tmux session alive across clide restarts so the next
launch re-attaches via `tmux new-session -A`.
Imports the helpers in the Claude extension as groundwork for the
app-shutdown reap and the existing claude.kill-all-sessions
command — wiring those uses lands separately.
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes 3 substring-truncated cross-reference anchors so they match
the full heading slug:
- D-3 link in architecture.md
- D-40 link in process.md (heading gained the [SUPERSEDED] tag)
- Q-15 link in questions-process.md
Strips the legacy `app/` prefix from path references in 5 files —
the dirs were flattened to repo root in the Flutter rebuild
(D-56). Three "was `app/...`" historical references in D-5 and
D-56 are deliberately preserved as record of the dissolution.
Adds an inline (tracked in T-88) note to D-59 so the
"must track dugite-native releases for security updates" intent
is wired to a backlog item — RULE-SUNSET-WITHOUT-TICKET would
otherwise keep flagging it on every sweep.
Co-Authored-By: Claude <noreply@anthropic.com>
Adds lib/widgets/src/spacing.dart with three categories of named
constants — insets (clideInsetHairline / Tight / Icon / Standard /
Text), gaps (clideGapTight / Standard / Section / SectionLarge /
Major / Column), and sizes (clideIconMicro / Caption / Standard /
HitTarget / Emphatic, clideControlHeight).
Migrates MultitabPane to consume the constants and updates the
ui-design geometry reference to point at them. Inline pixel
literals in widget code were drifting (12 here, 6 there, 28
elsewhere) — pulling them through named symbols makes the
"uniform inner spacing" rule enforceable instead of eyeballed.
Co-Authored-By: Claude <noreply@anthropic.com>
Replaces the single-file theme-ui skill with a routed ui-design skill
backed by four references:
- theme.md — token system, identity rule, palette layers, type
- surface.md — token selection per surface (chrome, panels, tabs,
buttons, status, overlays)
- geometry.md — control spacing/alignment principles distilled from
the MultitabPane work: uniform inner spacing rule,
no double-edge padding, two-column control pattern,
perceived mass over measured pixels
- icons.md — Phosphor icons + clide-owned painters
SKILL.md routes to the right reference and holds the universal rules.
The trigger description widens to cover spacing/alignment questions
in addition to token selection.
geometry.md references T-86 (codify spacing constants); the doc uses
literal pixel values until those constants land.
Co-Authored-By: Claude <noreply@anthropic.com>
ClaudeSessionHost replaces its bespoke tab strip / add button /
close handler with a MultitabPane<_Session> in keepAlive mode.
The primary tab is seeded as non-closeable and non-reorderable
per D-41; secondaries spawn via the existing addSecondary()
entry point and gain drag-to-reorder for free.
Drops ~100 lines of custom _TabRow / _Tab / _AddButton code in
favour of the shared widget. Behaviour is preserved: primary
persists across clide restarts, secondaries can be closed, and
PTY state survives tab switches because IndexedStack keeps every
ClaudePane mounted.
Co-Authored-By: Claude <noreply@anthropic.com>
Adds keepAlive: when true, all entry bodies stay mounted via
IndexedStack so switching tabs doesn't tear down their state.
Hosts that own PTY-backed sessions or any long-lived widget
state opt in; callers that want fresh state on each switch use
the default single-body mode.
Polishes the tab strip itself for production use:
- bottom divider so the strip visually anchors to the body below
- Column.crossAxisAlignment.stretch so the strip fills the pane
width instead of sizing to its content
- close button: replace the text × glyph with the CloseIcon
painter (clean cross strokes, font-independent)
- two-column tab layout — Expanded title on the left, fixed
16x16 close button on the right; uniform 12px left padding,
6px right padding to match the 6px top/bottom breathing room
around the close button
Two new widget tests cover keepAlive (state preserved across
switches) and default mode (inactive bodies disposed).
Co-Authored-By: Claude <noreply@anthropic.com>
Each tab is wrapped in a Draggable (when allowReorder is true and the
entry itself is reorderable) and a DragTarget (always — the controller's
barrier logic decides whether the move actually happens). Drops insert
the dragged entry at the target tab's index. A 2px leading insertion
indicator highlights the active drop target.
The widget harness now wraps children in an Overlay so Draggable's
feedback can mount without each test re-wrapping. Sized by the test
view's bounds to avoid disturbing existing tests that query
find.byType(SizedBox).first.
Four widget tests cover the gesture path: drop reorders, pinned
barrier blocks, pinned tabs aren't draggable, and allowReorder=false
disables drag entirely.
Co-Authored-By: Claude <noreply@anthropic.com>
MultitabController<T> is a Flutter-free ChangeNotifier owning the
tab list, active selection, and reorder/close invariants:
- pinned (non-reorderable) entries form barriers that other tabs
cannot cross
- non-closeable entries silently no-op on remove() so hosts don't
need to gate the call site
- closing the active tab falls right, then left, then to null
- duplicate ids are rejected
MultitabPane<T> is the widget shell: a horizontal tab strip
followed by the active entry's body. Active tab gets the
panelHeader background and a panelActiveBorder top accent;
inactive tabs blend into the tab bar. Close × is hidden until
hover. Add button only renders when onAddRequested is wired.
Hosts route the user's add/close intent through callbacks so the
widget stays domain-free — for the Claude pane, add will spawn a
new tmux session and close will kill one. Drag-to-reorder is
controller-side only for now (the gesture wiring lands with T-24).
19 controller tests + 9 widget tests.
Co-Authored-By: Claude <noreply@anthropic.com>
Reusable widget for panes that need N runtime tab instances of the
same kind. First consumer is the Claude pane (primary + 0..N
secondaries per D-41); the generic shape lets other panes adopt it
later without reinventing tab strips.
Includes:
- Design doc with API sketch, rendering, interaction, persistence
boundary, integration sketch for the Claude pane, and three open
questions (keyboard scoping for nested cases, overflow, density).
- Wireframe of the Claude pane with primary (pinned) + 2 secondaries
+ add button, accent border on the active tab.
- Architecture diagram (sketch mode) showing the host / widget /
controller / IPC boundary that keeps the widget domain-free.
Co-Authored-By: Claude <noreply@anthropic.com>
Per the frame0-wireframe skill: the per-machine ID mapping file
(*.idmap.json) is local state that lets pull/push reconcile with
Frame0. The committed JSON wireframes are the source of truth.
Co-Authored-By: Claude <noreply@anthropic.com>
Five wireframes generated via the frame0-wireframe skill, sourced
from JSON and rendered to PNG. Cover the welcome screen and four
main-view states: default, editor-above-Claude (D-49), focus mode
(D-52), sidebar-collapsed (D-51), and ticket detail in the context
panel.
The hi-fi mockups under docs/claude-design/ are now reference-only;
README marks the bundle as superseded and points at docs/wireframes/
as the canonical source. The token files there still feed the
runtime themes per D-43 / D-44, so the bundle is kept rather than
removed.
Co-Authored-By: Claude <noreply@anthropic.com>
Two general-purpose skills for visual design work:
- frame0-wireframe drives Frame0 (local wireframing app) from
JSON source files, with push/pull/export and a batch script.
- d2-diagram wraps the d2 text-to-diagram CLI for architecture
and flow diagrams.
Sourced from settled-reach/main where they were already in use.
Co-Authored-By: Claude <noreply@anthropic.com>
Six common keybindings (Quick open, Command palette, Toggle
sidebar, Toggle context, Switch theme, New Claude session) shown
as a 3x2 grid card spanning the same 850px content column as the
two action columns above. LayoutBuilder gates the card on viewport
height (>640px) so on shorter windows the centered START / RECENT
columns stay the focus and the tips drop out cleanly.
Co-Authored-By: Claude <noreply@anthropic.com>
clide is an IDE for the Claude Code CLI. The previous tagline
"Flutter desktop IDE for Claude Code" overemphasized the host
toolkit (Flutter is implementation detail, immediately obvious to
contributors) and was ambiguous about whether the integration
target is the CLI specifically.
Updates the welcome subtitle (i18n catalog + widget test + view),
the project description in pubspec.yaml, README and CLAUDE.md, the
CLI banner, and the web manifest/index.
Co-Authored-By: Claude <noreply@anthropic.com>
pane.spawn (via PtyException.errno) and editor.open (via
FileSystemException.osError.errorCode) now route ENOENT to
not_found, EACCES/EPERM to user_error with a permissions hint,
EISDIR/ENOTDIR/EEXIST to distinct user-error/conflict, and
EMFILE/ENFILE to tool_error with a "fd limit hit" hint. The
mapping lives in lib/src/ipc/errno_mapping.dart so other handlers
can adopt the same surface as they pick up errno-bearing failures.
Co-Authored-By: Claude <noreply@anthropic.com>
Three hardening fixes:
- 60s per-request timeout (configurable via DaemonServer constructor)
prevents a misbehaving handler from blocking the connection's
read pipeline indefinitely. On timeout the client gets a clean
tool_error response.
- broadcast() and the per-request response writeln are wrapped in
try/catch with stderr logging. Previously write failures silently
dropped clients with no diagnostic; events going missing was
invisible.
- start() probes for a live daemon before unlinking a stale socket.
If something answers within 200ms, refuse to start. Previously
two daemons racing to bind would let the second rip the first's
live socket out.
Co-Authored-By: Claude <noreply@anthropic.com>
NativePty.close() now awaits the reader-isolate spawn, kills the
child first to drive EOF on the master fd, awaits the isolate's
EOF acknowledgement, and only then closes the fd. Previously the
fd-close racing with the polling isolate left a window where the
fd number could be reused and the isolate would briefly target the
wrong file.
Both NativePty and PtySession now surface reader-isolate spawn
errors via the output stream's addError instead of silently
swallowing them.
PtySession.spawn closes the master fd on any post-receive failure,
closes parentSock in finally (was leaking on every spawn), and
kills the ptyc process if recvFd fails.
PtySession._recvFdAsync uses try/finally to close the ReceivePort
and kill the spawn isolate even when Isolate.spawn itself throws.
Co-Authored-By: Claude <noreply@anthropic.com>
forkpty failures throw PtyException with the captured errno
(previously a generic StateError). The spawned child's chdir/execve
failures write a diagnostic line to its slave PTY before _exit, so
the parent's reader sees "exec failed: <path>" instead of an
indistinguishable EOF.
NativePty.write and PtySession.write loop on short writes and throw
PtyException on hard errors (with errno). NativePty.resize sets
_dead on EBADF so subsequent calls short-circuit cleanly.
Co-Authored-By: Claude <noreply@anthropic.com>
Both handlers concatenated the request path onto the workspace root
without validating containment, letting `path: "../../../etc/passwd"`
escape the workspace. resolveUnderRoot normalizes the path and
checks containment under root.absolute.path before any filesystem
access.
Co-Authored-By: Claude <noreply@anthropic.com>
29 issues across PTY (lib/src/pty/), IPC (lib/src/ipc/), and
daemon command handlers (lib/src/daemon/). 14 critical (silent
failures, resource leaks, races), 8 high (degraded UX/debug),
7 medium (cleanliness). Each item references the follow-up
ticket where the fix lands (T-75 through T-81).
Co-Authored-By: Claude <noreply@anthropic.com>
Flutter falls back to synthetic bold when JetBrainsMono-Bold isn't
registered, and synthetic bold drifts glyph advance widths enough
to break the monospace cell grid (cursor block lands between
characters, prompts wrap mid-word). Color is enough to convey
emphasis in TUIs; semantic italic and underline still render.
Also drop the temporary `tmux -L clide kill-server` from the
install target — the rapid-iteration loop is no longer needed.
Co-Authored-By: Claude <noreply@anthropic.com>
Spawn `claude` directly as the tmux command with
CLAUDE_CODE_NO_FLICKER=1 so Claude Code runs in its fullscreen TUI
mode (input box pinned at bottom, owns its own scrollback). Mouse
wheel events are converted to PgUp/PgDown key input — universal
scroll signal that Claude, less, vim normal mode all respect, and
sidesteps the mouse-mode-but-no-scroll dead end where TUI apps
capture mouse without binding the wheel.
Drops the 1000-row tmux canvas + SingleChildScrollView experiment
in favor of viewport-sized tmux and Claude's native bottom-pinning.
Makefile install target now kills the clide tmux server so the
new config takes effect immediately. Marked TEMP — to be removed
once we no longer need the rapid-iteration loop.
Co-Authored-By: Claude <noreply@anthropic.com>
licenses.yaml: xterm entry changed from dart-package to
inlined-source with derivative-work description. JetBrains Mono
weights updated (Bold/BoldItalic dropped). Terminal LICENSE
clarifies this is a derivative work based on xterm.dart v4.0.0.
Co-Authored-By: Claude <noreply@anthropic.com>
Extract bundled tmux.conf to ~/.config/clide/tmux.conf on first
spawn and pass via -f. Use -L clide for a dedicated tmux server
so clide sessions don't inherit the user's tmux settings.
Terminal maxLines bumped from 5k to 50k.
Co-Authored-By: Claude <noreply@anthropic.com>
Replace the xterm pub.dev package with owned code under
lib/src/terminal/. Based on xterm.dart v4.0.0 by xuty (MIT).
Quiver LRU replaced with hand-rolled LinkedHashMap cache.
Scrollable removed from TerminalView — scroll events are forwarded
via Listener.onPointerSignal instead. zmodem, debugger, and
suggestion modules stripped as unused.
Also: bundle clide.tmux.conf (no status bar, 50k scrollback,
mouse on, zero escape delay, isolated -L clide socket), bump PTY
read buffer to 64KB, add 2px terminal padding, drop bold
JetBrains Mono registration.
Co-Authored-By: Claude <noreply@anthropic.com>
Six new D-records codify rules that lived only in POLICY.md:
D-60 no network on default launch path, D-61 dependency vetting
checklist, D-62 dependency removal process, D-63 vendored binary
rebuild process, D-64 no telemetry (architecture), D-65 license
compatibility matrix. Closes T-28.
Co-Authored-By: Claude <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 27s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
The daemon dispatcher (pql, git, files, editor) was built once at
boot against cwd and never updated. Opening a project from the
welcome screen or directory picker had no effect on the daemon
services — pql couldn't find its index, git operated on the wrong
repo.
Now onProjectOpen rebuilds the full dispatcher against the new
workspace root and swaps it into the InProcessClient. The IPC
client's dispatcher field is no longer final.
Co-Authored-By: Claude <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 29s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
Revert priority sort in tabsFor() — registration order in
main.dart is the intended sidebar order, not priority. The
priority fields on extensions were dead code.
Remove ptyc from toolchain missing list since NativePty
replaced it.
Co-Authored-By: Claude <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 28s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
StartupWMClass now matches the GTK application ID
(net.schweitz.clide) so the running window groups with the
launcher icon. Desktop file installs as
net.schweitz.clide.desktop per freedesktop convention.
Renamed my_application.{cc,h} to clide_app.{cc,h} with
matching GObject type (ClideApp / CLIDE_APP).
Co-Authored-By: Claude <noreply@anthropic.com>
tabsFor() sorts by contribution priority when no user order is
set. Test expectations updated for sidebar defaultSize 400 and
decision ID D-1 (no zero-padding). PTY tests tagged forkpty and
run via dart test (forkpty output unreliable inside flutter test
runner). CI script adds --no-fatal-infos and --exclude-tags.
Co-Authored-By: Claude <noreply@anthropic.com>
Unused _resolvePtycPath (bin/clide.dart), _TrafficDot (app.dart
and clide_column_hat.dart), stale @override on _spawned field
(claude_pane.dart), duplicate import and unused local in
test_app.dart, unnecessary non-null assertions in project.dart.
Co-Authored-By: Claude <noreply@anthropic.com>
Platform-dispatched constants where Linux and macOS diverge:
TIOCSWINSZ, O_NONBLOCK, MsghdrDarwin struct (4-byte msg_iovlen
and msg_controllen vs Linux's 8-byte size_t fields), and split
recvmsg into Linux/Darwin typed variants so scm_rights uses the
correct struct per platform.
Also: app settings dir uses ~/Library/Application Support on
macOS, removed hardcoded TERMINFO from pane spawn env, removed
debug print from native_pty resize.
Co-Authored-By: Claude <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 40s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
macos-pty-problem.md: diagnosis of the SCM_RIGHTS fd issue (resolved
by switching to forkpty via NativePty).
pty-proposition.md: Gemini's diagnostic plan (cmsg_len analysis,
ptyc instrumentation, async recvFd).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 28s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
tmux determines window size from its client, not the PTY winsize.
TIOCSWINSZ + SIGWINCH on the master fd has no effect on tmux's
internal dimensions. Use tmux resize-window -t <session> -x -y
to explicitly set the window size on resize.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 27s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
tmux doesn't re-read the PTY winsize on SIGWINCH. After the ioctl
resize, call tmux refresh-client -C cols,rows to update tmux's
internal window dimensions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 27s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
tmux running inside a PTY without a traditional terminal client
defaults to a huge window size (2000+ cols, 10000 rows). Pass
explicit -x and -y flags matching the TerminalView dimensions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 34s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
Reverts the bare-shell diagnostic. Spawns tmux new-session -A -s
<sessionName> for persistence. Claude command will be added back
after tmux + resize are stable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 28s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
Missed in the workspace → project rename. Consistent with the
project.open / project.validate / ProjectOpened naming.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 28s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
top fails with "Error opening terminal: xterm-256color" because the
PTY child can't find terminfo. Set TERMINFO=/usr/share/terminfo
explicitly in the pane environment.
Output flush changed from 16ms Timer to Duration.zero (next event
loop turn) for tighter batching of escape sequences.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>