From b7f373139481747b4fc67d5c0b098d87ecccc663 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Sat, 6 Jun 2026 12:25:24 +0200 Subject: [PATCH] decide D-85 (event bus delivery) + D-86 (MCP tool surface); resolve Q-2/3/32/33 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit D-85 — event bus delivery semantics, resolving Q-2 + Q-3: bounded per-subscriber ring buffer with drop-oldest back-pressure (producer never blocks, subscribers never killed) and a gap marker so a pull reader detects loss; in-memory cursor retention serves `clide events --since`; if persistence is ever needed it is bus-owned, not a subscriber-subsystem — reversing ADR 0006's unreasoned open-questions footer. D-86 — MCP tool surface, resolving Q-32: expose the full mcp__clide__* namespace but generate tools/list from the co-registered command registry (D-74) that already feeds CLI + palette, so breadth costs no second maintained surface; per-command MCP opt-out for poor-fit verbs. Q-33 drift-fixed: was already closed by D-73 (SSE only) but the index still listed it open; re-confirmed SSE-only, stdio/WS not added. Refines T-223 (cursor pull events) and T-225 (MCP reachable) — both now unblocked and scoped. Index regenerated by pql decisions sync. Co-Authored-By: Claude Opus 4.8 (1M context) --- .pql/changelog/ticket_history/2026-06.sql | 6 ++++++ .pql/changelog/tickets/2026-06.sql | 6 ++++++ governance/README.md | 10 ++++++---- governance/decisions/architecture.md | 22 ++++++++++++++++++++++ governance/questions/architecture.md | 12 ++++++++---- 5 files changed, 48 insertions(+), 8 deletions(-) diff --git a/.pql/changelog/ticket_history/2026-06.sql b/.pql/changelog/ticket_history/2026-06.sql index e01dd7a0..f8694319 100644 --- a/.pql/changelog/ticket_history/2026-06.sql +++ b/.pql/changelog/ticket_history/2026-06.sql @@ -462,3 +462,9 @@ list, ship our own curated, on-brand list instead. Document the decision. - Add a widget/golden test for the animated states (bounded pumps — no real timers).', NULL, '2026-06-06 09:57:12', '2026-06-06 09:57:12', '2026-06-06 09:57:12', NULL, '586f397ed5d0f14a0e7d510237d54ea2', 1) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-212', 'status', 'in_progress', 'done', NULL, '2026-06-06 10:02:03', '2026-06-06 10:02:03', '2026-06-06 10:02:03', NULL, 'c0174026e93366bda336a58bfdaec999', 1) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-209', 'status', 'in_progress', 'done', NULL, '2026-06-06 10:02:06', '2026-06-06 10:02:06', '2026-06-06 10:02:06', NULL, 'b66cdca1e3acedc775ec2e25e9509cd6', 1) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-223', 'description', 'Return all events since a cursor plus a new cursor, alongside the existing stream (T-129). Acceptance: `clide events --since ` returns events + next-cursor with exit 0; repeated calls neither drop nor duplicate. Coordinate the cursor/persistence design with open questions Q-2 (event back-pressure) and Q-3 (event persistence + audit) — this dovetails with both.', 'Return all events since a cursor plus a new cursor, alongside the existing stream (T-129). Acceptance: `clide events --since ` returns events + next-cursor with exit 0; repeated calls neither drop nor duplicate. Coordinate the cursor/persistence design with open questions Q-2 (event back-pressure) and Q-3 (event persistence + audit) — this dovetails with both. + +Refinement (2026-06-06): blockers resolved. Q-2 + Q-3 closed by D-85 (event bus delivery). Design is now fixed: serve --since from a bounded in-memory ring keyed by a monotonic cursor; return events-after-cursor + next-cursor, and a gap marker (per-subscriber dropped-count) when the requested cursor has aged out so callers detect loss rather than silently miss events. Back-pressure is drop-oldest (producer never blocks, subscribers never killed). No on-disk persistence in v1. Ready to implement.', NULL, '2026-06-06 10:24:57', '2026-06-06 10:24:57', '2026-06-06 10:24:57', NULL, 'a343da4fb69f6cfa6dd6c01193a94a8c', 1) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-225', 'description', 'Gap 6 from self-analysis.md. The MCP SSE server (T-130, D-68) is live but mcp__ide__* tools are not exposed to an external agent out of the box, and getDiagnostics/executeCode were noted as stubs. The CLI path is the priority (Epics A-C); MCP follows. Decide + wire the minimal reachable MCP surface, or explicitly defer with a note. Relates to open question Q-32 (minimum MCP tool surface).', 'Gap 6 from self-analysis.md. The MCP SSE server (T-130, D-68) is live but mcp__ide__* tools are not exposed to an external agent out of the box, and getDiagnostics/executeCode were noted as stubs. The CLI path is the priority (Epics A-C); MCP follows. Decide + wire the minimal reachable MCP surface, or explicitly defer with a note. Relates to open question Q-32 (minimum MCP tool surface). + +Refinement (2026-06-06): tool-surface question resolved. Q-32 closed by D-86 — expose the full mcp__clide__* namespace, but GENERATE tools/list from the co-registered command registry (D-74) that already feeds the CLI + palette, so there is no hand-maintained second surface; add a per-command MCP opt-out for poor-fit verbs (long-lived streams, UI-side-effecting). Transport stays SSE-only per D-73 (Q-33 re-confirmed, not reopened). Remaining stubs to make real: getDiagnostics + executeCode. Scope is now: registry->MCP tool-definition adapter (arg-schema -> JSON-Schema), served over the existing SSE transport.', NULL, '2026-06-06 10:25:02', '2026-06-06 10:25:02', '2026-06-06 10:25:02', NULL, '00d54d4febb98792806e520c9921c790', 1) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-06.sql b/.pql/changelog/tickets/2026-06.sql index dd4c95f1..92c20245 100644 --- a/.pql/changelog/tickets/2026-06.sql +++ b/.pql/changelog/tickets/2026-06.sql @@ -994,3 +994,9 @@ INSERT INTO tickets (id, type, parent_id, title, description, status, priority, Net: `which clide` -> not found, `clide ping` -> exit 127. The CLI-first contract (D-1) has no delivery mechanism. Related to the broader installer epic T-46, but that is the GUI/runtime installer; this epic is specifically C-client-on-PATH delivery.', 'done', 'high', NULL, NULL, 'D-1', '2026-06-02 18:13:52', '2026-06-06 10:02:06', NULL, '7a173eae965eedcfaf742707873651aa', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-256', 'task', 'T-208', 'Install affordance: distinguish a dev-tree clide from a production-installed build', 'Flagged during T-212 (2026-06-06). The Install-clide-command affordance copies whatever CliInstaller resolves as the bundled C client into ~/.local/bin, and ''make run'' points CLIDE_CLI_BIN at the dev-tree client (native//clide). On a normal user machine that is correct: the bundled client beside the GUI runner IS the production build. But on a clide DEV system the clide on PATH should point at the properly-installed production build, not a dev-tree binary or stale build artefact. CliInstaller.inspect only classifies missing / staleGui (points into the Flutter bundle) / installed, and treats any non-GUI binary as installed without distinguishing dev-tree vs a real install. Good enough for now per the user; the affordance works for end users and fixes the stale-GUI-symlink footgun. Decide desired dev-machine behaviour (e.g. detect dev tree via CLIDE_PROJECT / repo-relative path and warn instead of silently installing, or prefer an already-correct production build) and refine inspect() accordingly.', 'backlog', 'low', NULL, NULL, NULL, '2026-06-06 10:07:35', '2026-06-06 10:07:35', NULL, '979892310810e63cd436dcbccf9bd3a1', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-223', 'task', 'T-222', 'Add cursor-based pull events: clide events --since ', 'Return all events since a cursor plus a new cursor, alongside the existing stream (T-129). Acceptance: `clide events --since ` returns events + next-cursor with exit 0; repeated calls neither drop nor duplicate. Coordinate the cursor/persistence design with open questions Q-2 (event back-pressure) and Q-3 (event persistence + audit) — this dovetails with both. + +Refinement (2026-06-06): blockers resolved. Q-2 + Q-3 closed by D-85 (event bus delivery). Design is now fixed: serve --since from a bounded in-memory ring keyed by a monotonic cursor; return events-after-cursor + next-cursor, and a gap marker (per-subscriber dropped-count) when the requested cursor has aged out so callers detect loss rather than silently miss events. Back-pressure is drop-oldest (producer never blocks, subscribers never killed). No on-disk persistence in v1. Ready to implement.', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-02 18:13:52', '2026-06-06 10:24:57', NULL, 'b3ab2d01b025d2577a12f04def93797f', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-225', 'task', 'T-208', 'MCP transport reachable by an external agent', 'Gap 6 from self-analysis.md. The MCP SSE server (T-130, D-68) is live but mcp__ide__* tools are not exposed to an external agent out of the box, and getDiagnostics/executeCode were noted as stubs. The CLI path is the priority (Epics A-C); MCP follows. Decide + wire the minimal reachable MCP surface, or explicitly defer with a note. Relates to open question Q-32 (minimum MCP tool surface). + +Refinement (2026-06-06): tool-surface question resolved. Q-32 closed by D-86 — expose the full mcp__clide__* namespace, but GENERATE tools/list from the co-registered command registry (D-74) that already feeds the CLI + palette, so there is no hand-maintained second surface; add a per-command MCP opt-out for poor-fit verbs (long-lived streams, UI-side-effecting). Transport stays SSE-only per D-73 (Q-33 re-confirmed, not reopened). Remaining stubs to make real: getDiagnostics + executeCode. Scope is now: registry->MCP tool-definition adapter (arg-schema -> JSON-Schema), served over the existing SSE transport.', 'backlog', 'low', NULL, NULL, 'D-68', '2026-06-02 18:13:52', '2026-06-06 10:25:02', NULL, '041f27c551ea57bd86a3b3d5caf678c1', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); diff --git a/governance/README.md b/governance/README.md index 9dc85300..6116c9b9 100644 --- a/governance/README.md +++ b/governance/README.md @@ -126,12 +126,12 @@ You might also want, project-permitting: - [D-82: Keymap sequences are space-separated; matching is a reusable matcher consumed at the interception point](decisions/architecture.md#d-82-keymap-sequences-are-space-separated-matching-is-a-reusable-matcher-consumed-at-the-interception-point) — _architecture_ - [D-83: Dogfood agent model — hosted stream-json session primary, external CLI driver secondary](decisions/architecture.md#d-83-dogfood-agent-model--hosted-stream-json-session-primary-external-cli-driver-secondary) — _architecture_ - [D-84: Diff view placement — editor-mode inline above Claude, spawned from the git sidebar](decisions/architecture.md#d-84-diff-view-placement--editor-mode-inline-above-claude-spawned-from-the-git-sidebar) — _architecture_ +- [D-85: Event bus delivery — bounded ring-buffer back-pressure; in-memory cursor retention, bus-owned if persisted](decisions/architecture.md#d-85-event-bus-delivery--bounded-ring-buffer-back-pressure-in-memory-cursor-retention-bus-owned-if-persisted) — _architecture_ +- [D-86: MCP tool surface — full clide namespace generated from the co-registered command registry](decisions/architecture.md#d-86-mcp-tool-surface--full-clide-namespace-generated-from-the-co-registered-command-registry) — _architecture_ ## Open questions - [Q-1: Authorisation granularity on the IPC socket](questions/architecture.md#q-1-authorisation-granularity-on-the-ipc-socket) — _architecture_ -- [Q-2: Back-pressure on event streams](questions/architecture.md#q-2-back-pressure-on-event-streams) — _architecture_ -- [Q-3: Event persistence + audit/undo](questions/architecture.md#q-3-event-persistence--auditundo) — _architecture_ - [Q-4: `.canvas` schema compatibility with Obsidian](questions/architecture.md#q-4-canvas-schema-compatibility-with-obsidian) — _architecture_ - [Q-5: IPC wire-format stability + `schema_version:`](questions/architecture.md#q-5-ipc-wire-format-stability--schema-version) — _architecture_ - [Q-7: macOS app bundle signing / notarisation](questions/architecture.md#q-7-macos-app-bundle-signing--notarisation) — _architecture_ @@ -155,15 +155,17 @@ You might also want, project-permitting: - [Q-29: Branch picker location](questions/architecture.md#q-29-branch-picker-location) — _architecture_ - [Q-30: Focus behavior when editor is dirty and viewer is peeked](questions/architecture.md#q-30-focus-behavior-when-editor-is-dirty-and-viewer-is-peeked) — _architecture_ - [Q-31: XWayland fallback for frameless — proper Wayland protocol needed](questions/architecture.md#q-31-xwayland-fallback-for-frameless--proper-wayland-protocol-needed) — _architecture_ -- [Q-32: MCP tool surface — minimum slash-ide or extended clide tools?](questions/architecture.md#q-32-mcp-tool-surface--minimum-slash-ide-or-extended-clide-tools) — _architecture_ -- [Q-33: MCP transport — SSE, WebSocket, stdio, or all?](questions/architecture.md#q-33-mcp-transport--sse-websocket-stdio-or-all) — _architecture_ ## Resolved questions +- [Q-2: Back-pressure on event streams](questions/architecture.md#q-2-back-pressure-on-event-streams) — _architecture_ +- [Q-3: Event persistence + audit/undo](questions/architecture.md#q-3-event-persistence--auditundo) — _architecture_ - [Q-6: Window chrome — native frame vs frameless custom](questions/architecture.md#q-6-window-chrome--native-frame-vs-frameless-custom) — _architecture_ - [Q-19: (withdrawn)](questions/process.md#q-19-withdrawn) — _process_ - [Q-21: Pql absorbs planning vs keeps separate](questions/architecture.md#q-21-pql-absorbs-planning-vs-keeps-separate) — _architecture_ - [Q-22: Ticket persistence strategy](questions/architecture.md#q-22-ticket-persistence-strategy) — _architecture_ +- [Q-32: MCP tool surface — minimum slash-ide or extended clide tools?](questions/architecture.md#q-32-mcp-tool-surface--minimum-slash-ide-or-extended-clide-tools) — _architecture_ +- [Q-33: MCP transport — SSE, WebSocket, stdio, or all?](questions/architecture.md#q-33-mcp-transport--sse-websocket-stdio-or-all) — _architecture_ ## Rejected diff --git a/governance/decisions/architecture.md b/governance/decisions/architecture.md index 07aa09e8..5814404b 100644 --- a/governance/decisions/architecture.md +++ b/governance/decisions/architecture.md @@ -411,3 +411,25 @@ Core, rendering, IPC, kernel, panel manager. - **Raised by:** 2026-06-05 — T-42 (decide diff view placement). User chose editor-mode over the context-panel viewer: a diff needs space for resolution widgets and spawns from the git sidebar, so it's too cramped to compress into the right panel. --- + +### D-85: Event bus delivery — bounded ring-buffer back-pressure; in-memory cursor retention, bus-owned if persisted +- **Date:** 2026-06-06 +- **Status:** accepted +- **Decision:** The in-memory event bus ([D-6](#d-6-cli-and-event-surface-contract)) gets explicit delivery + retention semantics, resolving [Q-2](../questions/architecture.md#q-2-back-pressure-on-event-streams) (back-pressure) and [Q-3](../questions/architecture.md#q-3-event-persistence--auditundo) (persistence). **(1) Back-pressure — bounded per-subscriber ring buffer, drop-oldest.** Each subscriber gets a bounded ring; when a slow or absent reader fills it, the oldest events are dropped and a per-subscriber dropped-count is incremented. The producer never blocks and subscribers are never force-disconnected. **(2) Cursor retention — in-memory, monotonic cursor.** The pull API (`clide events --since `, T-223) is served from a bounded in-memory ring keyed by a monotonically increasing cursor; `--since` returns the events after the cursor plus a next-cursor, and reports a **gap marker** (the dropped-count) when the requested cursor has already aged out of the ring — so a caller detects loss instead of silently missing events. No on-disk persistence in v1. **(3) If persistence is ever needed** (audit log, undo history) it is **owned by the bus itself**, not a separate subscriber-subsystem. ADR 0006 carried the opposite as a bare one-line position in its *open-questions* footer ("a subsystem that subscribes and writes — not a property of the bus") with **no rationale** — which is exactly why it migrated to Q-3 rather than a decision; this resolves that open question the other way, on the reasoning below. +- **Rationale:** drop-oldest plus a visible gap marker is the only policy that keeps an interactive IDE responsive — no producer stall from a wedged reader (the main-isolate dispatch of [D-72](#d-72-ipc-server-is-multi-connection-with-serial-dispatch-on-the-main-isolate) must never block on a subscriber) — while still letting a request/response agent loop *know* it fell behind. Blocking the producer risks janking the PTY/UI; killing subscribers pushes resync onto every client. The ring doubles as the cursor store, so back-pressure and pull-retention are one mechanism, not two. Bus-owned persistence (if it ever lands) keeps a single authoritative ordering + cursor space; a side-subscriber writer would have to reconstruct ordering and could itself fall behind under the very back-pressure policy this decides. +- **Cost / risk:** ring size is a tuning knob — too small and slow agents see frequent gaps, too large and memory grows under a firehose; sized per channel and revisited if a real consumer hits gaps (the Q-2 triage trigger). A caller that ignores the gap marker silently misses events — mitigated by making next-cursor + gap explicit in the `--since` response shape. Naming bus-owned persistence as the eventual shape pre-commits against the subscriber-writer pattern; acceptable because nothing persists yet and the call is revisitable when the first real audit/undo requirement lands. +- **Cross-reference:** [D-6](#d-6-cli-and-event-surface-contract) (the event surface), [D-72](#d-72-ipc-server-is-multi-connection-with-serial-dispatch-on-the-main-isolate) (serial main-isolate dispatch the producer must not block), [Q-2](../questions/architecture.md#q-2-back-pressure-on-event-streams) + [Q-3](../questions/architecture.md#q-3-event-persistence--auditundo) (resolved here). Unblocks T-223 (cursor pull events); amends ADR 0006 (persistence lean). +- **Raised by:** 2026-06-06 — walking Q-2/Q-3 during the T-208 "give Claude hands" wind-down; user chose the drop-oldest ring buffer, in-memory retention now, and bus-owned persistence if it ever becomes durable. + +--- + +### D-86: MCP tool surface — full clide namespace generated from the co-registered command registry +- **Date:** 2026-06-06 +- **Status:** accepted +- **Decision:** Resolves [Q-32](../questions/architecture.md#q-32-mcp-tool-surface--minimum-slash-ide-or-extended-clide-tools). clide exposes the **full `mcp__clide__*` tool namespace** — not just the `/ide` minimum (`getDiagnostics` + `executeCode`) — but **every surface (CLI argv, MCP `tools/list`, command palette) is generated from the one co-registered command registry** established by [D-74](#d-74-ipc-command-schema-is-co-registered-with-the-handler-validated-at-dispatch), never hand-authored per-surface. A command registers once (handler + typed arg schema); the MCP adapter derives its tool definition (name, description, JSON-Schema input) from that same registry entry, exactly as the CLI argv grammar and palette entry already do. There is no separately-maintained MCP tool list. A registry entry may carry an **MCP opt-out flag** so a command that is a poor tool (long-lived streams, UI-side-effecting verbs) can register without exposing a twin. This extends [D-68](#d-68-dual-integration-surface--bash-cli-primary-mcp-secondary) (which deferred the `mcp__clide__*` namespace to Q-32) and consumes the registry whose MCP generation D-74 had marked deferred to the T-130 track. +- **Rationale:** the maintenance objection to a broad MCP surface (D-68's "every CLI verb invites an MCP twin; surface bloat") only bites if the twin is authored by hand. Generated from the registry, breadth is nearly free: the same source of truth that already feeds CLI + palette feeds MCP, so adding a command lights up all three surfaces at once and they cannot drift. The extensions-first model ([D-46](extensions.md#d-46-core-frame-builtins-vs-shipped-extensions-boundary)) requires registry-driven surfacing anyway — extension-contributed commands must appear in every surface without the core editing a central list. Full breadth makes clide a real backend for non-Claude-Code MCP clients (Cursor, Windsurf, Copilot), the growing `/ide` ecosystem D-68 already says clide should be a peer in. Claude-in-a-clide-pane still uses the CLI; the MCP breadth is for *external* clients. +- **Cost / risk:** the adapter must map D-74's arg-schema vocabulary to MCP's JSON-Schema tool-input shape — one adapter, written once, not per command. Exposing the full action surface to any connected MCP client widens what a client can drive — gated by the same transport boundary as today (MCP reaches the dispatcher over the SSE transport of [D-73](#d-73-mcp-transport-for-ide-is-sse-over-http); no new auth surface, same dispatcher as the socket). More tools in `tools/list` is more for a client to reason about; the opt-out flag + good per-command descriptions keep it sane. +- **Cross-reference:** [D-68](#d-68-dual-integration-surface--bash-cli-primary-mcp-secondary) (dual surface; deferred `mcp__clide__*` to Q-32 — resolved here), [D-74](#d-74-ipc-command-schema-is-co-registered-with-the-handler-validated-at-dispatch) (the co-registered command+schema registry this generates from), [D-73](#d-73-mcp-transport-for-ide-is-sse-over-http) (SSE transport it serves over — unchanged; Q-33 stays closed by D-73), [D-46](extensions.md#d-46-core-frame-builtins-vs-shipped-extensions-boundary) (extensions-first, which already requires registry-driven surfacing), [D-6](#d-6-cli-and-event-surface-contract) (CLI/parity the registry enforces). Defines the tool surface for T-225. Resolves [Q-32](../questions/architecture.md#q-32-mcp-tool-surface--minimum-slash-ide-or-extended-clide-tools). +- **Raised by:** 2026-06-06 — walking Q-32 during the T-208 wind-down; user chose the full namespace but constrained it to single-registry generation to avoid a second maintenance front, noting the extensions-first pattern needs that anyway. + +--- diff --git a/governance/questions/architecture.md b/governance/questions/architecture.md index d1a5d89d..d8b352cb 100644 --- a/governance/questions/architecture.md +++ b/governance/questions/architecture.md @@ -13,14 +13,16 @@ ticket persistence. - **Source:** ADR 0006 (migrated to [D-6](architecture.md)). ### Q-2: Back-pressure on event streams -- **Status:** Open +- **Status:** Resolved → [D-85](../decisions/architecture.md#d-85-event-bus-delivery--bounded-ring-buffer-back-pressure-in-memory-cursor-retention-bus-owned-if-persisted) +- **Resolved (2026-06-06):** Bounded per-subscriber ring buffer, drop-oldest, with a per-subscriber dropped-count surfaced as a gap marker — producer never blocks, subscribers never killed. The ring also serves the [Q-3](#q-3-event-persistence--auditundo) cursor retention. - **Question:** A subscriber that falls behind on `pane.output` (a firehose) needs a policy: drop oldest, block producer, coalesce, or kill subscriber. Which? - **Context:** The event bus is in-memory; back-pressure policy is undefined. Defer until Tier 1 is in real use and we have a real firehose to measure against. - **Triage (2026-05-17):** Still open. PTY panes ship and produce real firehoses, but no subscriber has fallen behind in observed use. Re-evaluate when a multi-client IPC scenario (T-99) makes this measurable. - **Source:** ADR 0006 (migrated to [D-6](architecture.md)). ### Q-3: Event persistence + audit/undo -- **Status:** Open +- **Status:** Resolved → [D-85](../decisions/architecture.md#d-85-event-bus-delivery--bounded-ring-buffer-back-pressure-in-memory-cursor-retention-bus-owned-if-persisted) +- **Resolved (2026-06-06):** In-memory cursor ring only in v1 (no disk); serves `clide events --since`. If persistence is ever needed it is **owned by the bus**, not a subscriber-subsystem — reversing ADR 0006's unreasoned open-questions footer (it asserted "a subsystem that subscribes and writes — not a property of the bus" with no rationale, which is why it became this question). - **Question:** Events are in-memory only in v1. If a future need (audit log, undo history) wants persistence, is it a property of the bus or a subsystem that subscribes and writes? - **Context:** ADR 0006 leaned "subsystem that subscribes and writes" but didn't commit. - **Triage (2026-05-17):** Still open; no concrete trigger yet. Revisit when the first persistence requirement lands (likely Tier-6 audit/undo). @@ -130,13 +132,15 @@ ticket persistence. - **Source:** 2026-04-23 D-57 implementation. ### Q-32: MCP tool surface — minimum slash-ide or extended clide tools? -- **Status:** Open +- **Status:** Resolved → [D-86](../decisions/architecture.md#d-86-mcp-tool-surface--full-clide-namespace-generated-from-the-co-registered-command-registry) +- **Resolved (2026-06-06):** Full `mcp__clide__*` namespace, but **generated from the co-registered command registry** ([D-74](../decisions/architecture.md#d-74-ipc-command-schema-is-co-registered-with-the-handler-validated-at-dispatch)) that already feeds the CLI + palette — so breadth costs no hand-maintained second surface, and the extensions-first model needs registry-driven surfacing anyway. Per-command MCP opt-out for poor-fit verbs. - **Question:** [D-68](../decisions/architecture.md#d-68-dual-integration-surface-bash-cli-primary-mcp-secondary) commits clide to an `/ide`-compatible MCP server. The minimum surface is the two tools Claude Code's `/ide` integration currently expects: `mcp__ide__getDiagnostics` (lint/diagnostics for a file) and `mcp__ide__executeCode` (run code in a Jupyter kernel). Do we stop there, or also expose a `mcp__clide__*` namespace with higher-leverage tools (`open_file`, `goto_symbol`, `pql_query`, `pane_spawn`, `git_status`, …) so MCP clients other than Claude Code (Cursor, Windsurf, VS Code Copilot) can drive clide as a real backend? - **Context:** The minimum surface keeps clide a good citizen in the `/ide` ecosystem and avoids duplicating the CLI in MCP form. The extended surface would let non-Claude-Code MCP clients integrate richly, but invites surface bloat (every CLI verb tempted to gain an MCP twin) and a maintenance second front. Note that for *Claude-Code-in-a-clide-pane*, the CLI surface already covers this — extended MCP tools serve external MCP clients only. - **Source:** [D-68](../decisions/architecture.md#d-68-dual-integration-surface-bash-cli-primary-mcp-secondary). ### Q-33: MCP transport — SSE, WebSocket, stdio, or all? -- **Status:** Open +- **Status:** Resolved → [D-73](../decisions/architecture.md#d-73-mcp-transport-for-ide-is-sse-over-http) +- **Resolved (2026-05-19, drift-fixed 2026-06-06):** SSE over HTTP only. Closed by D-73 (and confirmed in D-68's amendment) at the time, but this index was never flipped from Open. Re-confirmed 2026-06-06: stdio/WebSocket not added — D-73's reasoning (the always-running GUI is *connected to*, not spawned) still holds and no external process-spawn MCP client is a concrete need yet. - **Question:** Claude Code's `/ide` integration connects via SSE-IDE or WS-IDE (URL passed at startup). MCP also supports stdio for process-spawn clients. Which transport(s) should clide's MCP server expose — SSE only (the most common `/ide` server pattern), SSE + WS (broader compatibility), or all three including stdio? - **Context:** Transport choice affects discovery and lifecycle. SSE/WS need a port and a published URL, which collides with the `XDG_RUNTIME_DIR` Unix-socket model used for the CLI; we'd likely publish the URL alongside the socket path (env var or `XDG_RUNTIME_DIR` discovery file). stdio is process-per-client and works for clients that prefer process-spawn over network. Decision interacts with [Q-32](#q-32-mcp-tool-surface-minimum-slash-ide-or-extended-clide-tools) — if the surface stays at the `/ide` minimum, SSE alone is sufficient. - **Source:** [D-68](../decisions/architecture.md#d-68-dual-integration-surface-bash-cli-primary-mcp-secondary).