Skip to content

ADR 0020: record the declarative-first extension model (and its relationship to ADR 0011/0012) #1943

Description

@phmatray

Problem / motivation

Persona: the maintainer or contributor who, six months from now, asks "why isn't the Koine extension system just wasm like Zed's?"

The extension system (#1936) rests on a decision that is genuinely non-obvious and, without a record, looks like an omission rather than a choice: Koine adopts Zed's packaging but not its runtime, and ships a declarative-first extension model with no executable tier.

Read cold, that decision invites exactly the wrong correction. Someone will propose "just use the Component Model like Zed" — a reasonable-sounding suggestion that costs weeks to re-disprove, because the disproof depends on facts that are neither in the codebase nor intuitive:

CLAUDE.md is unambiguous that this needs an ADR: significant architecture decisions — "a new emitter target, a change to the compiler pipeline layering, a new cross-cutting validator, ... a dependency the whole repo will lean on"must be captured under /adr/ in MADR 4.0 format, before or alongside the implementing PR. An extension system is all of those at once.

There is a second reason this ADR is not optional: it interacts with decisions already on record. ADR 0011 chose an out-of-process sandbox for scenario execution and explicitly rejected a collectible AssemblyLoadContext as isolation; ADR 0012 built the OS confinement that goes with it. Stage 1 (#1939) introduces a collectible ALC for extensions — for dependency isolation and unload, not isolation — and that distinction has to be recorded, or it reads as silent drift against ADR 0011.

Proposed solution

Write adr/0020-declarative-first-extension-model.md in MADR 4.0 format from adr/template.md, and add it to adr/README.md's index.

It records one decision — declarative-first, executable tier deferred behind a spike — with the evidence that forced it, the options rejected and why, and the consequences accepted. It carries links entries relating it to ADR 0011, ADR 0012 and ADR 0004, and it is the document #1940's spike updates with its outcome.

Alternatives considered

Area

Docs / website


Related: #1936 (epic), #1937, #1938, #1939, #1940 (the spike that updates this ADR), #1941, #1942 — and ADR 0011 / ADR 0012 / ADR 0004, which this ADR must link to explicitly

🧠 Brainstorm

Problem / context

Three research passes (~13.5M tokens, adversarially vote-verified against live primary sources on 2026-08-04) produced a decision that inverts the obvious approach. The obvious approach — copy Zed, which is what was originally asked for — is blocked on facts that are external, time-sensitive and invisible from inside this repository.

That combination is exactly what an ADR exists for. The code will show a JSON manifest and no wasm runtime; nothing in the code explains that this was chosen over wasm rather than as a stepping stone toward it, nor that the wasm path was investigated to the level of specific unmerged pull requests.

The time-sensitivity matters too, and cuts both ways. The blocking facts could change — wasmtime-dotnet #324 could land; componentize-dotnet could gain macOS support. An ADR that records why the door is closed also records what would reopen it, which a code comment never does.

Approaches

A — One ADR for the extension model, updated by the spike. ✅ Recommended.

B — No ADR. Violates CLAUDE.md and leaves the ADR 0011 interaction as silent drift.

C — Defer until after the spike. Inverts the dependency.

D — Per-stage ADRs. Over-records a single decision.

Recommendation

Approach A. Three things it must do that a routine ADR would not:

Status starts proposed and flips to accepted when the implementing PR merges, per the README's own workflow.

📋 Spec

Goal

A MADR 4.0 record of the declarative-first extension decision, its evidence, its relationship to ADRs 0011/0012/0004, and the conditions under which it should be revisited.

Deliverable

adr/0020-declarative-first-extension-model.md plus an index row in adr/README.md.

Frontmatter

---
id: 0020
title: Declarative-first extension model for Koine Studio
status: proposed
date: 2026-08-04
deciders: [phmatray]
tags: [studio, extensions, security, plugins]
links:
  - { type: relates-to, target: 0011 }
  - { type: relates-to, target: 0012 }
  - { type: relates-to, target: 0004 }
---

Required content

Context and Problem Statement — Studio has no extension system; #69 shipped compiler seams that are invisible to users and unisolated. State the four verified constraints as facts understood at the time, not as the conclusion: .NET has no component hosting; browsers cannot run components natively and jco is AOT-only; no IDE ecosystem ships a hard runtime sandbox; Zed's capability model is default-allow, global, and its real boundary produced two CVEs.

Considered Options

  1. Faithful Zed copy (Component Model wasm)
  2. Core-module wasm hosted in .NET
  3. Component-model wasm in the Rust Tauri host
  4. Out-of-process .NET plugins on the ADR 0012 harness
  5. Declarative-first, executable tier deferred to a spike

Decision Outcome — option 5, stated as a decision ("We will …"): ship a declarative tier that hosts no untrusted code and works identically in the Tauri webview, the browser tab and the CLI; adopt Zed's packaging (two tiers, a versioned contract with a never-raised floor, precompiled artifacts, a pointer-index registry); reject Zed's capability model in favour of default-deny, per-extension capabilities modelled on VS Code's capabilities.untrustedWorkspaces; and defer the executable tier to #1940.

Consequences — accepted trade-offs stated honestly, not just benefits:

Revisit triggers — named concretely: wasmtime-dotnet ships component hosting (#324); componentize-dotnet gains macOS/osx-arm64 support; the Component Model reaches meaningful browser engine support; or #1940 returns a viable substrate.

Non-goals

Assumptions

🛠️ Implementation plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Record the declarative-first extension decision as ADR 0020, linked to ADRs 0011/0012/0004.

Architecture: A single MADR 4.0 document from adr/template.md, plus an index row.

Tech Stack: Markdown, MADR 4.0 (ADR 0010).

Global Constraints

  • MADR 4.0 format, copied from adr/template.md — required sections are Context and Problem Statement, Considered Options, Decision Outcome, Consequences (ADR 0010).
  • status: proposed at creation; flip to accepted when the PR merges.
  • Do not edit ADR 0011's or 0012's Decision Outcome — this is a relationship, not a supersession.
  • Commit identity: git -c user.email=phmatray@gmail.com -c user.name="Philippe Matray".
  • Conventional Commits: docs(adr): ….

Task 1: Write ADR 0020

Files:

  • Create: adr/0020-declarative-first-extension-model.md

Interfaces: none (decision record).

  • Step 1: Copy adr/template.md to adr/0020-declarative-first-extension-model.md and fill the frontmatter exactly as the spec gives it, including the three links entries.
  • Step 2: Write Context and Problem Statement — the four verified constraints as facts, each with its concrete evidence (wasmtime-dotnet Explicit-id create factories: allow a create factory on a non-Guid (natural/sequence) identity via an explicit id parameter #324 open since 2024-07-09 and PR Harden the Studio main-thread boot fallback (CSP-safe loader) + sharpen the boot smoke-test diagnostics (#359) #360 closed unmerged 2026-06-12; Component Model at Phase 1 with zero engine support; VS Code's "the extension host has the same permissions as VS Code itself"; Zed's wildcard default-allow grants and CVE-2026-27800 / CVE-2026-27976).
  • Step 3: Write Considered Options — all five, each with its genuine appeal stated before its disqualifier, so the record reads as a decision rather than a justification.
  • Step 4: Write Decision Outcome in the "We will …" form, naming what is adopted from Zed and what is explicitly rejected.
  • Step 5: Write Consequences, covering every accepted trade-off in the spec — including the ADR 0011 relationship on the collectible ALC and the ADR 0004 constraint on themes.
  • Step 6: Add a Revisit triggers subsection naming the four concrete conditions.
  • Step 7: Commit: docs(adr): record the declarative-first extension model for Koine Studio

Task 2: Index it and cross-link the related ADRs

Files:

  • Modify: adr/README.md (index table)
  • Modify: adr/0011-scenario-execution-sandbox.md, adr/0012-scenario-sandbox-os-confinement.md, adr/0004-concept-colors-single-palette-and-lsp-kind-modifiers.md (links frontmatter only)

Interfaces: none.

  • Step 1: Add the ADR 0020 row to adr/README.md's index table, matching the existing rows' format exactly.
  • Step 2: Add a links: [{ type: relates-to, target: 0020 }] entry to each of ADR 0011, 0012 and 0004 — frontmatter only; do not touch their Decision Outcome or Consequences prose.
  • Step 3: Verify every links target resolves to a real ADR id and that the relationships are reciprocal.
  • Step 4: Commit: docs(adr): index ADR 0020 and cross-link the related decisions

Task 3: Point the epic and stage issues at the ADR

Files: none in-repo (issue bodies).

Interfaces: none.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: docsDocs site & website (website/, README)effort: SSmall — ~hours to ~1 dayenhancementNew feature or requestpriority: mediumTier 2 — expected capability, partial or absent

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions