architecture
Editor Extension Adapter Contract
Goal
Define the boundary between Forma Core and editor-specific extensions so the first VS Code implementation can move quickly without making VS Code the owner of workspace semantics.
Boundary
repository Markdown and .forma.md
-> Forma Core operations
-> JSON-compatible result contracts
-> editor adapter
-> native editor features and themed previews
Forma Core owns:
- configuration loading and validation;
- workspace-relative path safety;
- Markdown, wikilink, embed, and view-mount recognition;
- schema and semantic reference interpretation;
- reference resolution and ambiguity diagnostics;
- workspace health and entry diagnostics;
- view source, query, sort, and projection evaluation;
- stable operation result shapes.
An editor adapter owns:
- activation and editor lifecycle;
- finding candidate
.forma.mdentrypoints inside opened workspace folders or current-file ancestors; - selecting the applicable workspace in multi-root sessions;
- locating and invoking a compatible Forma binary;
- optionally acquiring and managing the release-aligned Forma binary inside editor extension storage after explicit user confirmation;
- cancellation, timeout, process output, and user-visible status;
- translating Forma diagnostics, links, definitions, commands, and locations into editor APIs;
- opening source Markdown documents;
- extending editor-native Markdown Preview and mapping host theme tokens into projection styles;
- refreshing derived state when source files are saved or configuration changes.
The adapter must not deserialize .forma.md into a second product model, rescan Markdown to build a reference graph, evaluate view queries, or silently write workspace files.
Host-Native Enhancement Contract
Forma editor integrations enhance the host editor's Markdown experience. They do not attempt to replace the editor's Markdown language, source editor, preview pipeline, navigation model, theme, or accessibility behavior.
The default ownership model is:
| Area | Primary owner | Allowed Forma enhancement | Boundary |
|---|---|---|---|
| Standard Markdown links, images, headings, and code regions | Host editor | A narrow compatibility fallback when a documented host gap prevents required navigation | Do not duplicate or suppress working native behavior. |
| Wikilinks, embeds, and schema-declared references | Forma Core | Map Core resolution into native Definition, DocumentLink, and diagnostic APIs | Do not reimplement reference semantics in the adapter or emit semantic tokens to style source syntax. |
| Inline code and fenced code blocks | Host editor, with bounded lexical projections | Forma returns no indexed references, diagnostics, or Definition results for link-like literals. An adapter may project explicit link syntax from inline code into DocumentLink when the host already exposes partial path navigation, and from explicitly md/markdown fenced blocks into DocumentLink when injected Markdown navigation is incomplete or inconsistent. |
The projection is lexical editor data only: it must not enter the reference graph, restyle source text, apply to other fence languages, replace the grammar, or intercept native commands. |
| Markdown Preview | Host editor | Extend the native pipeline with Forma projections and resolved-reference behavior | Do not add a competing preview surface when the host provides an extensible native preview. |
| Theme and accessibility | Host editor | Preserve host-native source rendering and interaction feedback | Do not ship source-highlight overlays, fixed colors, theme-name branches, or inaccessible custom interaction conventions. |
| Workspace language intelligence | Forma-managed document predicate | Provide Forma features only to configured Pages and Views | Do not analyze every Markdown document merely because the extension is attached to the host's Markdown language. |
Adapters should follow these engineering rules:
- Prefer native editor APIs, commands, previews, themes, and Markdown parsing behavior before adding a parallel Forma surface.
- Add only the semantics the host does not already provide, or the minimum compatibility fallback needed to make an accepted Forma feature work.
- Keep every fallback narrow, named, documented, tested, and removable. A host-specific workaround must not silently become a product-wide semantic rule.
- Preserve benign host differences across editors. Cross-editor consistency applies to Forma-owned resolution and operation results, not to every native gesture or visual detail.
- Degrade gracefully when an editor lacks a non-invasive extension point. Missing enhancement is preferable to replacing a grammar, intercepting native commands, or forking a preview pipeline without a separately accepted decision.
- Treat a required grammar replacement, native-provider suppression, editor command interception, or parallel preview as an architecture escalation. Record the user value, compatibility cost, maintenance burden, and rejected native alternatives before implementation.
- Test ownership at three layers: Core semantic behavior, adapter or protocol mapping, and real-editor behavior. A real-editor result must not be attributed to Forma until the responsible provider has been identified.
- Keep source highlighting entirely host-owned. Forma navigation adapters must not advertise semantic-token capabilities or emit styling tokens for Markdown links, wikilinks, embeds, or schema-declared references. Pursue syntax-style improvements in the host editor or its Markdown grammar instead.
The current Zed boundary follows this contract. Core semantic analysis ignores valid link syntax inside every inline and fenced code region. Separately, Core can project ordinary Markdown links, wikilinks, and embeds from inline code and md/markdown fences with full-source spans. The LSP maps both projections to DocumentLink without advertising semantic-token support. Zed can therefore navigate explicit examples consistently without turning them into workspace relationships or changing their source rendering. Forma does not replace Zed's Markdown grammar or intercept Command-click.
Positionless document opening is a host adapter concern. Zed currently converts an unfragmented file:// DocumentLink into an LSP location at (0,0), while its zed://file open path skips position navigation when no row is present. Forma LSP therefore selects zed://file only for a Zed client and only for resolved document targets without a fragment. Standard clients retain file://, and fragment-bearing links retain positioned LSP navigation. This client-specific transport must remain outside Core and must be revalidated before claiming Zed remote-workspace compatibility.
Workspace Discovery
The presence of .forma.md is the explicit discovery signal. .forma/ alone is not sufficient.
For each opened editor workspace folder, the adapter should:
- Check the folder root for
.forma.md. - When a Markdown file is active, walk its ancestors only until the editor workspace boundary and select the nearest
.forma.md. - Keep each discovered root as a separate Forma workspace in multi-root sessions.
- Call
config.inspectbefore treating a candidate as ready. - Expose ready, invalid-config, binary-missing, incompatible-version, and no-workspace states.
Discovery must not scan arbitrary parent directories outside the opened editor workspace or the whole machine.
Transport Baseline
The initial extension should prefer short-lived forma ... --json subprocess calls for operations that already have CLI surfaces. This avoids requiring a background server and port lifecycle before product behavior is proven.
An operation needed by editor integrations should still be defined in the shared Rust operation and RPC model first. A CLI command may then expose it for the first extension. The adapter must not treat CLI output text intended for humans as an API; only structured JSON results are valid inputs.
Long-lived HTTP RPC, a future stdio RPC adapter, or a language server can be introduced after repeated invocation, unsaved-buffer analysis, latency evidence, or an editor API requirement justifies the lifecycle cost.
Zed provides editor-native Definition and DocumentLink behavior through LSP rather than a general extension API. A narrowly scoped forma lsp process is therefore accepted as the editor-neutral language-intelligence transport for Zed navigation. This exception does not replace structured CLI and RPC operations for workspace health, Explorer projections, view rendering, or other saved-workspace interactions.
The implementation boundary is:
Forma Core transient document analysis and workspace snapshot
-> forma-lsp protocol adapter
-> editor LSP client
forma-lsp must be a separate Rust crate. It may depend on forma-core, while forma-core must not depend on LSP protocol types, URI rules, UTF-16 positions, or editor lifecycle concepts. The single published forma binary exposes the server through forma lsp; Forma does not publish a separate LSP executable.
The accepted navigation-intelligence expansion keeps that dependency direction. Core owns document diagnostics, reference Hover metadata, cursor-aware completion candidates, schema-declared entryRef filtering, exact resolved reference occurrences, and open-document semantic overlays. The LSP advertises and maps native Hover, push Diagnostics, Completion, and References capabilities, converts byte spans to UTF-16 ranges, and manages notification lifecycles. The editor adapter remains a launcher; it does not duplicate semantic parsing, add custom UI, or acquire process-execution capability. Ordinary Markdown links and source styling remain host-owned, and the first References contract returns no declaration until Forma defines a stable entry declaration identity and range.
Operation Requirements
Existing operations cover much of the MVP:
| Interaction | Operation |
|---|---|
| Validate a discovered root | config.inspect |
| Report workspace health | workspace.health and check |
| Inspect a saved entry | inspect |
| Read outgoing and incoming relationships | file.references |
| Render a configured view | view.render |
The editor navigation loop needs one additional read-only operation:
reference.resolve
Suggested input:
{
"sourcePath": "knowledge/tasks/example.md",
"target": "members/tiscs",
"intent": "reference",
"fragment": null
}
The result should contain the canonical target path when resolved, an optional fragment location, display metadata, ambiguity candidates, and diagnostics. Resolution must use the same workspace index, path rules, schema types, and case behavior as normal Forma checks.
The first VS Code implementation may refresh saved documents only. Cross-editor LSP navigation uses a Core-owned transient document-analysis boundary that accepts source text without persisting it. The result must provide exact reference ranges and schema-aware frontmatter interpretation without introducing an editor-side Markdown or YAML semantic parser.
Core reference ranges remain editor-neutral byte or source locations. forma-lsp owns conversion to LSP UTF-16 positions and must test non-ASCII source text, CRLF input, quoted YAML scalars, and repeated frontmatter values.
View Preview Contract
A view remains a Markdown document. Opening a view path uses the ordinary text editor, and the editor's native Markdown Preview remains the only preview surface. Forma contributes a Markdown-it enhancement and Preview stylesheet instead of registering a second preview button or hosting a parallel WebView.
Core metadata kind: view determines whether a document receives a View projection. The content mount controls placement only: the projection replaces the mount when present and is appended to the document when the mount is absent. For example:
# Task Board
Current delivery work grouped by status.
<!-- forma:content -->
The board is generated from repository metadata.
The backend remains responsible for metadata, reference semantics, View evaluation, and mount validation. Because Core operations are asynchronous while Markdown-it rendering is synchronous, the adapter may pre-render and cache structured projection HTML by document URI, then refresh the native Preview. The adapter must not reinterpret Forma directives or query configuration.
Native Preview frontmatter links and the Forma Explorer navigation model are specified in design/vscode-preview-links-and-navigation.
Preview refresh is save-driven in the first version. A later transient render operation can support unsaved view source after the editor contract proves the need.
Theme Contract
Projection components use VS Code theme variables already available in native Markdown Preview:
VS Code Preview tokens
-> narrowly scoped --forma-* projection tokens
-> list, table, kanban, graph renderers
The VS Code adapter derives these values from --vscode-* variables, including editor colors, focus and contrast borders, chart colors, and editor font settings. Renderers must support light, dark, high-contrast, and reduced-motion modes without theme-name-specific rules.
The WebApp and other editor adapters provide the same semantic Forma roles from their own theme APIs. Shared renderer code must not import a VS Code API, React, React Router, or the WebApp theme context. Geometry, opacity hierarchy, label policy, selected and neighbor emphasis, and edge semantics remain identical across Hosts while concrete colors, fonts, surfaces, borders, and focus treatment adapt to the environment.
Graph Renderer Boundary
view.render graph nodes and edges are the stable input boundary. The current WebApp renderer's fixed circular placement, simple space-color hash, and hover-only focus behavior are not part of the adapter contract.
Sigma.js plus Graphology is the accepted 2D direction. packages/graph-view is the required implementation boundary for both WebApp and editor extensions. The VS Code adapter contributes the Preview browser bundle, active-document input, native source links, theme mapping, and reload lifecycle; it must not duplicate graph construction, layout, Sigma reducers, or interaction state.
The shared implementation and each Host adapter should be validated against the same fixtures and requirements:
- meaningful force-directed or hierarchical layout;
- deterministic initial placement and stable refresh behavior;
- persistent node selection and one-hop focus;
- node search and filters for configured taxonomy/term membership, document kind, and edge type;
- source navigation from nodes;
- readable light, dark, and high-contrast themes;
- reduced-motion behavior;
- usable empty, invalid, and moderately dense graph states.
User-adjusted coordinates may be stored in editor workspace state, but must not be written into Markdown or Forma configuration in the MVP.
Security And Trust
- Do not execute a workspace-provided binary automatically in an untrusted editor workspace.
- Do not download or execute a managed binary in Restricted Mode.
- A managed binary must come from the exact release tag aligned with the extension version, use a platform-specific release asset, and pass its published checksum before becoming executable.
- Managed installation must stay inside editor extension storage. It must not modify
PATH, overwrite a user-managed executable, or derive an executable path from workspace content. - An explicit host-level LSP binary override remains authoritative, but it bypasses the editor adapter's command construction, compatibility checks, and managed lifecycle. Treat it as a user-owned escape hatch rather than a managed Forma installation. Without that override, a release-aligned managed binary may be preferred over extension-host
PATHdiscovery. - Do not expose absolute host paths in structured public results.
- Do not export cookies, credentials, editor storage, or repository content to external services.
- Prefer native editor preview and navigation APIs so the adapter does not introduce another script-enabled WebView security boundary.
- Treat preview interactions as read-only. Opening a source file is allowed; mutations require a separately accepted operation contract.
Version Compatibility
The extension should declare the operation schema versions it supports. On activation, it should detect incompatible Forma output and present an actionable upgrade or downgrade message rather than attempting best-effort interpretation of unknown result shapes.
During the coordinated Alpha release line, each editor extension expects the Forma CLI from the same release. Broad acceptance of every 0.1.0 prerelease is unsafe because CLI operations may be added without changing the operation schema version. The extension manifest or Cargo package version is the expected CLI version and must not be duplicated as a separately maintained constant.
VS Code and Zed pass their aligned extension version through Forma LSP initializationOptions. A compatible running Forma LSP is the single owner of package-version comparison and publishes one actionable workspace diagnostic with the installation-documentation link when the versions differ. The warning is advisory rather than a startup gate. The VS Code adapter does not run a separate forma --version probe: its first structured config inspect operation verifies that the CLI can execute, and every structured CLI response is validated against its operation schema. Zed still reports language-server launch failures through its host status and logs. A CLI that predates the initialization option, rejects the option, or fails before LSP initialization cannot publish the advisory and remains a startup or protocol error instead.
The Zed Dev Extension resolves the CLI from the worktree PATH and starts forma --workspace <root> lsp without executing a separate pre-launch version command or acquiring process:exec. Zed's native lsp.forma.binary setting remains a user-owned command override. This boundary does not download, replace, or update a binary.
When the configured or discovered CLI is missing or has a different version, the extension may offer a user-initiated installation of the matching release. Downloads use the exact v<extension-version> tag rather than latest, install into a versioned directory under extension global storage, and preserve external forma.path and PATH installations. In a remote workspace, acquisition and execution occur in the remote workspace Extension Host; environments without outbound release access retain the explicit-path and manual-install fallbacks.
The bounded compatibility-window design records a workspace-independent forma compatibility --json probe, protocol ranges, capability identifiers, a two-release bridge, and a safe exact-version fallback in decisions/define-cli-editor-compatibility-window. Its negotiation command and adapter fixtures remain unimplemented and are tracked by tasks/design-cli-editor-compatibility-window.
The initial managed lifecycle implementation and release validation are tracked in tasks/manage-vscode-forma-cli-lifecycle.
Validation Boundary
Core operation behavior should be tested in Rust. JSON result compatibility belongs in forma-rpc and packages/shared. LSP protocol mapping, transient buffer overlays, UTF-16 conversion, cancellation, and path boundaries belong in forma-lsp. Adapter tests should cover workspace selection, subprocess cancellation and errors, result-to-editor mapping, theme token mapping, preview refresh, and navigation commands without duplicating Core semantic fixtures.
External References
- VS Code Markdown extension contributions, including Markdown-it plugins and Preview styles.
- VS Code Theme Color Reference, including editor, focus, contrast, selection, and chart color tokens available to themed extension surfaces.