update CLAUDE.md and reserve decision/ticket/claude-control slots
test / unit + widget + golden + a11y (push) Successful in 4m34s
test / integration_test (xvfb) (push) Failing after 1m4s
test / bundle smoke (xvfb 5s) (push) Failing after 58s
test / daemon subprocess + web WASM smoke (push) Successful in 2m27s

CLAUDE.md gains a "Decision discipline" guardrail pointing at
decisions/<domain>.md, rewrites every inline ADR link to the new
anchor-link style (ADR 0001 → D-001, etc.), collapses the bottom
"Open questions" section to a one-line pointer, and updates the
parent-project note to reference decisions/architecture.md instead
of the deleted docs/ADRs/.

Three extension slots are reserved with id-only stubs, registered
alongside the existing 21 built-ins in main.dart:
  - builtin.decisions — future sidebar for D/Q/R records
  - builtin.tickets — future sidebar + kanban workspace
  - builtin.claude-control — future .claude/ first-class surface
    (Settings / Skills / Agents / Hooks / MCP). Distinct from the
    existing builtin.claude PTY-pane stub.

`flutter analyze` clean; builtin + a11y test suites still pass.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-04-21 17:32:06 +02:00
co-authored by Claude
parent 4dd9a74be7
commit e2b3f7cff1
9 changed files with 99 additions and 12 deletions
+19
View File
@@ -53,6 +53,25 @@ heading, and (b) bumping `project.yaml` `version:` in the same commit.
- `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-021`](decisions/questions-architecture.md#q-021-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/`.
### Removed
- `docs/ADRs/` directory — content lifted into `decisions/` as D/R
+9 -11
View File
@@ -13,7 +13,7 @@ A Flutter desktop IDE for Claude Code. One language (Dart) across the stack, plu
App ↔ daemon ↔ CLI speak a single JSON-lines unix socket protocol. The daemon outlives app restarts so Claude sessions survive reopens. Native rendering — markdown, canvas, graph — is Dart/Flutter (`CustomPaint` + widgets), not third-party packages.
Design doc: [`docs/initial-plan.md`](docs/initial-plan.md). Decisions: [`docs/ADRs/`](docs/ADRs/). Python Textual predecessor under [`legacy/`](legacy/).
Design doc: [`docs/initial-plan.md`](docs/initial-plan.md). Decisions: [`decisions/`](decisions/) (`D-NNN` confirmed, `Q-NNN` open, `R-NNN` rejected — see [`decisions/README.md`](decisions/README.md)). Python Textual predecessor under [`legacy/`](legacy/).
## Guardrails
@@ -21,13 +21,14 @@ These are load-bearing. Violating any means the design is wrong, not the rule.
- **Flutter desktop is the host. No Electron, ever.** Web target may work as a happy accident — don't compromise desktop fidelity for it. If we ship a web build at all, prefer Flutter's **WebAssembly (CanvasKit/Skwasm) compile** over the JS/HTML renderer: it matches the desktop rendering pipeline, keeps our custom `CustomPaint` components pixel-identical, and avoids the DOM-renderer quirks around input handling and terminal-style content. `xterm.dart` is the terminal renderer (Tier 1); markdown, canvas, graph are custom `CustomPaint`/widget components (Tiers 2+, 5).
- **No heavy lifting in the UI layer.** The app renders and handles input; process/PTY/IO lifecycles live in the daemon. The daemon is Dart too — the split is *process boundary*, not language boundary.
- **CLI-first, not MCP.** Claude talks via Bash (`clide ...`), matching pql's contract. See [ADR 0001](docs/ADRs/0001-cli-first-not-mcp.md).
- **Dart is the core; native supporter tools fill specific gaps.** One Dart AOT binary for CLI + daemon. `ptyc` (C) for PTY spawning. `pql` (Go) for queries. No second "core language." See [ADR 0005](docs/ADRs/0005-dart-core-ptyc-peer.md) (supersedes [ADR 0002](docs/ADRs/0002-sidecar-language-go.md)).
- **CLI-first, not MCP.** Claude talks via Bash (`clide ...`), matching pql's contract. See [`D-001`](decisions/architecture.md#d-001-cli-first-not-mcp).
- **Dart is the core; native supporter tools fill specific gaps.** One Dart AOT binary for CLI + daemon. `ptyc` (C) for PTY spawning. `pql` (Go) for queries. No second "core language." See [`D-005`](decisions/architecture.md#d-005-dart-core-sidecar-dissolved-ptyc-as-pql-peer) (supersedes [`R-002`](decisions/rejected.md#r-002-go-sidecar)).
- **Own the rendering stack.** PTY (via `ptyc`), markdown renderer, graph, canvas — all clide-owned, not pulled from opinionated packages. Third-party rendering is where we'd hit ceilings first; we'd rather pay the cost up front.
- **User/Claude parity.** Every CLI subcommand has a UI affordance in the app, and every UI action has a CLI. Events are symmetric: every UI state change is a subscribable event. See [ADR 0006](docs/ADRs/0006-cli-and-event-surface.md).
- **pql: wrap, don't duplicate, and treat it as a clide subsystem when present.** Pql logic only lives in `lib/src/pql/` (pure shell-outs). Clide owns pql's `ignore_files:` config key; it never touches pql's `.pql/` index/cache data. See [ADR 0003](docs/ADRs/0003-pql-as-supporter-tool.md).
- **User/Claude parity.** Every CLI subcommand has a UI affordance in the app, and every UI action has a CLI. Events are symmetric: every UI state change is a subscribable event. See [`D-006`](decisions/architecture.md#d-006-cli-and-event-surface-contract).
- **pql: wrap, don't duplicate, and treat it as a clide subsystem when present.** Pql logic only lives in `lib/src/pql/` (pure shell-outs). Clide owns pql's `ignore_files:` config key; it never touches pql's `.pql/` index/cache data. See [`D-003`](decisions/architecture.md#d-003-pql-as-supporter-tool-clide-wraps-never-duplicates).
- **Repo-is-the-workspace.** The git repo root is the workspace — no parallel "vault" concept. Clide dogfoods against its own repo.
- **Ignore discipline.** Single knob: `ignore_files:` in `.pql/config.yaml`, ordered layering. Default `[.gitignore]`; clide writes `[.gitignore, .clideignore]` when `.clideignore` exists. See [ADR 0004](docs/ADRs/0004-ignore-file-strategy.md).
- **Ignore discipline.** Single knob: `ignore_files:` in `.pql/config.yaml`, ordered layering. Default `[.gitignore]`; clide writes `[.gitignore, .clideignore]` when `.clideignore` exists. See [`D-004`](decisions/architecture.md#d-004-ignore-file-strategy).
- **Decision discipline.** All architectural choices live in `decisions/<domain>.md` as `D-NNN` records. Open questions live in `decisions/questions-<domain>.md` as `Q-NNN`. Rejected alternatives live in `decisions/rejected.md` as `R-NNN`. Before an architectural change, read the relevant domain file; before disagreeing with a guardrail, propose an amendment to the underlying `D-NNN` rather than a one-off. Claim new IDs via `tools/scripts/plan decisions claim D <domain> "title"` (until pql ships planning subcommands — see [`Q-021`](decisions/questions-architecture.md#q-021-pql-absorbs-planning-vs-keeps-separate)). See [`decisions/README.md`](decisions/README.md).
## Tier ordering (don't skip ahead)
@@ -44,7 +45,7 @@ See `docs/initial-plan.md` for the full tier definitions and acceptance criteria
## Parent projects
- **`legacy/`** — Python Textual clide v1.2.0. Feature-frozen. Reference for the pane model, panel set, git skills (`/commit`, `/stash`, `/pull`, `/push` — rewire to `clide git …`), TODO.md parsing format.
- **`projects/claudian`** (April 2026, discarded) — 2-day experiment with an Obsidian-plugin approach. Its architectural patterns (CLI-first, pql-as-subsystem, ignore-file strategy, supply-chain gate, changelog discipline, commit conventions) are the ADRs and skills you see here. It first proposed a Go sidecar; ADR 0005 reversed that in favour of a Dart core.
- **`projects/claudian`** (April 2026, discarded) — 2-day experiment with an Obsidian-plugin approach. Its architectural patterns (CLI-first, pql-as-subsystem, ignore-file strategy, supply-chain gate, changelog discipline, commit conventions) are the records you see under `decisions/` and the skills under `.claude/skills/`. It first proposed a Go sidecar; [`D-005`](decisions/architecture.md#d-005-dart-core-sidecar-dissolved-ptyc-as-pql-peer) reversed that in favour of a Dart core.
- **[`projects/pql`](https://github.com/postmeridiem/pql)** — active supporter tool. Clide depends on it; never duplicates it.
## Dependencies & supply chain
@@ -81,7 +82,4 @@ One-time setup on a fresh clone: `make hooks && flutter pub get` once Flutter is
## Open questions
- `.canvas` schema compatibility with Obsidian — decide during Tier 5 spike.
- Extension API shape (widgets, subcommands, both) — decide during Tier 6.
- IPC wire-format stability + `schema_version:` in `project.yaml` — decide when the first real subcommand lands.
- Editor tab: full LSP integration via sidecar vs tree-sitter-only highlight — decide during Tier 2.
Open questions live under [`decisions/questions-*.md`](decisions/questions.md).
@@ -0,0 +1 @@
export 'src/extension.dart';
@@ -0,0 +1,23 @@
import 'package:clide_app/extension/extension.dart';
/// Tier-reserved stub. Will surface a sidebar tab with sub-tabs
/// Settings / Skills / Agents / Hooks / MCP — `.claude/` as a
/// first-class IDE surface. Orthogonal to pql; purely clide-internal.
/// Commands: `claude.settings.open`, `claude.skills.new`,
/// `claude.skills.edit`, `claude.agents.new`, `claude.agents.edit`,
/// `claude.hooks.log`, `claude.mcp.status`.
///
/// Distinct from `builtin.claude`, which is reserved for Tier 1's
/// "run Claude Code in a PTY pane."
class ClaudeControlExtension extends ClideExtension {
@override
String get id => 'builtin.claude-control';
@override
String get title => 'Claude control';
@override
String get version => '0.0.0-stub';
@override
List<String> get dependsOn => const [];
@override
List<ContributionPoint> get contributions => const [];
}
+1
View File
@@ -0,0 +1 @@
export 'src/extension.dart';
@@ -0,0 +1,19 @@
import 'package:clide_app/extension/extension.dart';
/// Tier-reserved stub. Will surface a sidebar tab (filter by domain /
/// status, backlinks from current file) + commands (`decisions.open`,
/// `decisions.claim`, `decisions.amend`). Data source: `pql decisions
/// …` once `Q-021` resolves; `tools/scripts/plan decisions …` until
/// then.
class DecisionsExtension extends ClideExtension {
@override
String get id => 'builtin.decisions';
@override
String get title => 'Decisions';
@override
String get version => '0.0.0-stub';
@override
List<String> get dependsOn => const [];
@override
List<ContributionPoint> get contributions => const [];
}
@@ -0,0 +1,19 @@
import 'package:clide_app/extension/extension.dart';
/// Tier-reserved stub. Will surface a sidebar tab (filtered list) + a
/// workspace tab (kanban board) + commands (`tickets.open`,
/// `tickets.new`, `tickets.move`, `tickets.block`). Data source: `pql
/// ticket …` once `Q-021` resolves; `tools/scripts/plan ticket …` until
/// then. Ticket persistence strategy open at `Q-022`.
class TicketsExtension extends ClideExtension {
@override
String get id => 'builtin.tickets';
@override
String get title => 'Tickets';
@override
String get version => '0.0.0-stub';
@override
List<String> get dependsOn => const [];
@override
List<ContributionPoint> get contributions => const [];
}
+1
View File
@@ -0,0 +1 @@
export 'src/extension.dart';
+7 -1
View File
@@ -1,6 +1,8 @@
import 'package:clide_app/app.dart';
import 'package:clide_app/builtin/canvas/canvas.dart';
import 'package:clide_app/builtin/claude/claude.dart';
import 'package:clide_app/builtin/claude_control/claude_control.dart';
import 'package:clide_app/builtin/decisions/decisions.dart';
import 'package:clide_app/builtin/default_layout/default_layout.dart';
import 'package:clide_app/builtin/diff/diff.dart';
import 'package:clide_app/builtin/editor/editor.dart';
@@ -18,6 +20,7 @@ import 'package:clide_app/builtin/problems/problems.dart';
import 'package:clide_app/builtin/settings_ui/settings_ui.dart';
import 'package:clide_app/builtin/terminal/terminal.dart';
import 'package:clide_app/builtin/theme_picker/theme_picker.dart';
import 'package:clide_app/builtin/tickets/tickets.dart';
import 'package:clide_app/builtin/todos/todos.dart';
import 'package:clide_app/builtin/welcome/welcome.dart';
import 'dart:io' show Directory, Platform;
@@ -75,7 +78,10 @@ Future<void> main() async {
..register(GraphExtension())
..register(SettingsUiExtension())
..register(ExtensionsUiExtension())
..register(KeybindingsUiExtension());
..register(KeybindingsUiExtension())
..register(DecisionsExtension())
..register(TicketsExtension())
..register(ClaudeControlExtension());
await services.extensions.activateAll();