clean-house sweep over governance/ (validate green, 0 broken refs before and after): - RULE-ANCHOR-DRIFT (37): rewrote inline cross-reference links left over from before the decisions/questions/rejected subdir split — flat naming (questions-architecture.md, questions-process.md, rejected.md) and bare same-dir paths that were actually cross-type — to canonical subdir-relative form with current slugs. pql resolved these by ID so they were never broken to the tooling, only to GitHub anchor navigation. - RULE-RECORD-SORT (1): reordered decisions/architecture.md D-records to strictly ascending (the D-1..D-6 block had been appended after D-41); pure block move, line count unchanged, content identical. - RULE-FILE-OVER-THRESHOLD: deferred (architecture.md 435 > 350; splitting would re-churn the anchors just fixed). - EOF/whitespace and Q↔D backlinks clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.6 KiB
Extension Decisions
Extension contract, Lua runtime, grain, contribution points.
D-15: Extension grain — container-level, multi-contribution
- Date: 2026-04-21
- Decision: An extension is a shipping unit that contributes one or more named contributions (panel, command, theme, keybinding, language, layout, provider, view). Grain is container-sized — a single
builtin.gitextension contributes panel + commands + keybindings + status-bar items; we do not ship one extension per contribution. - Rationale: Finer grain (one extension per contribution) multiplies manifest files with no win and fragments ownership. Coarser grain (one mega-extension per domain) hides which parts a user might reasonably disable.
- Cost: Extension authors make a taste call about grouping; disagreements go to review.
- Raised by: 2026-04-21 planning.
D-16: Built-ins in Dart, third-party in sandboxed Lua
- Date: 2026-04-21
- Decision: Bundled extensions (every
lib/builtin/<name>) are Dart — they link into the app binary. Third-party extensions (Tier 6) run in sandboxed Lua via theptyc-peer Lua runtime (see D-19). The contribution contract is language-agnostic — same contribution shapes, same manifest schema. - Rationale: Dart built-ins get full SDK power (custom painters, isolates, FFI); third-party Lua gets a narrow capability API, no arbitrary syscalls, no deps on pub.dev. VS Code's Node-runs-with-full-power model is a supply-chain nightmare we're explicitly rejecting.
- Cost: Two implementation paths for the same contract; we pay in API design to keep them equivalent at the seams.
- Raised by: 2026-04-21 planning.
D-17: Panels are extension-shaped from day one
- Date: 2026-04-21
- Decision: Every bundled panel (file tree, git, problems, pql query, terminal, etc.) is a contribution on the extension contract, not a hardcoded widget tree in the panel manager. First party contributes via Dart; third party contributes via Lua; same contract.
- Rationale: Forces the contract to be real on day one. The alternative ("extensions can contribute panels later") always becomes "the contract doesn't quite fit our bundled panels, so built-ins get a shortcut" — and the shortcut becomes permanent.
- Cost: Every panel goes through the manifest/registration path even when it's trivial. Price paid once.
- Raised by: 2026-04-21 planning.
D-18: YAML for themes + manifests; JSON for i18n catalogs
- Date: 2026-04-21
- Decision: Themes and extension manifests are YAML; i18n catalogues are JSON (fframe parity — see D-21).
- Rationale: YAML for human-edited config files (themes, manifests) — comments, multi-line strings, less noise. JSON for machine-written / machine-read files (i18n catalogs get generated by translation tooling eventually). Mixing is fine; each format is where it's best.
- Cost: Two parsers in the tree.
yaml: 3.1.3is exact-pinned. - Raised by: 2026-04-21 planning.
D-19: Lua runtime as ptyc-peer supporter tool
- Date: 2026-04-21
- Decision: Third-party extensions run Lua inside a sandboxed runtime (working name TBD; same peer-status as
ptycandpql). The runtime vendors liblua, links from Dart viadart:ffi, exposes a narrow capability API, and takes contributions as a declarative render-intent DSL (widget shapes, not arbitrary widget code). - Rationale: Lua is small, embeddable, battle-tested (Neovim, World of Warcraft, Redis). Native sandboxing at the VM level is cheap. A declarative render-intent DSL keeps "run arbitrary Flutter widgets from Lua" off the table — the runtime interprets the DSL into Dart widgets, which keeps Tier 6 supply-chain risk bounded.
- Cost: Runtime is a separate supporter tool to build; ffi is tricky. Deferred to Tier 6; only the slot is reserved now.
- Raised by: 2026-04-21 planning.
D-46: Core frame builtins vs shipped extensions boundary
-
Date: 2026-04-22
-
Decision: The
lib/builtin/directory is reserved for core frame infrastructure — components the shell cannot function without. Everything that renders content (editor surfaces, tool panels, integrations) is a shipped extension: still Dart, still bundled in the binary, but architecturally an extension that registers through the contribution contract and could in principle be disabled by the user.Core frame builtins (cannot be disabled; the frame breaks without them):
default-layout,welcome,ipc-status,theme-picker,terminal,files,grammars-core,settings-ui,extensions-ui,keybindings-ui.Split components (core process in the frame, UI surfaces as shipped extensions):
git— the daemon-side git process (branch, status, stage, commit, diff computation) is frame infrastructure that the status bar, file tree dirty markers, and other extensions depend on. The git panel, conflict UI, and diff tab are shipped extensions that consume it.Shipped extensions (bundled but removable; contribute content, not infrastructure):
editor,claude,claude-control,markdown,diff,git-ui,pql,canvas,graph,decisions,tickets,todos,problems. -
Rationale: The previous session bled several content extensions (jira, todos, decisions, tickets, canvas, graph) into
builtin/as stubs, treating "shipped with the app" as "part of the frame." This conflates two concerns: the frame's structural integrity and the bundled feature set. A user who disables the canvas extension should get a working IDE with no canvas panel; a user who disables the layout extension gets a broken window. The boundary is: can the frame render and function without it? If yes, it's a shipped extension, not a frame builtin. -
Cost: Shipped extensions need a separate registration path (e.g.
lib/extensions/or equivalent) distinct fromlib/builtin/. The extension contract must support "bundled Dart extension" as a first-class category alongside "builtin" and "third-party Lua." Migration is incremental — move one at a time, each behind a working build. -
Supersedes: Removes
builtin.jira(already deleted; should never have been a builtin — Jira integration is a third-party extension, not a shipped one). -
Raised by: 2026-04-22 session review.
See also the existing builtin.grammars_core stub for tree-sitter
questions (Q-15,
Q-16).