SEMAPRAX saved-source editor adapter
September 13, 2026 · View on GitHub
Experimental. A focused local Visual Studio Code Extension Host run passed for
exact subject 2888f84f123b7caa44aa6807388d98f851d4beaf; the standalone
50-case controller suite remains separate. The additive 97-case controller and
real-host candidate-task scenario passed for exact subject
3fccd30b861d48c9d404eb2698fa2eff510569af in Visual Studio Code 1.136.1 on
Darwin arm64. This zero-build CommonJS extension uses only
VS Code APIs and Node built-ins. No npm dependencies, bundling, telemetry,
webviews, language server, automatic process startup, or publication command.
It is not a packaged or marketplace release. The exact local execution claim is bounded by the evidence contract below.
Syntax highlighting
Opening a .spx file gives SEMAPRAX highlighting, // comment toggling,
bracket matching, and auto-closing pairs without starting a session. This is
the declarative languages and grammars contribution in package.json
backed by syntaxes/semaprax.tmLanguage.json and
language-configuration.json; it runs no code and needs no compiler path.
The repository's documentation gate checks that the grammar names every
keyword the parser recognises, so the highlighting cannot silently lag the
language. The grammar itself provides no completion or navigation.
This is also the one grammar this repository would reuse for GitHub-hosted
.spx highlighting; see GitHub Linguist submission v1
for the interim .gitattributes override and what native GitHub recognition
still requires.
Check on save
Saving a .spx file or semaprax.toml runs the compiler named by the
user/machine setting semaprax.compilerPath as
check <subject> --json, where the subject is the nearest semaprax.toml
walking up from the saved file, or the file alone when no manifest exists. Each
JSON diagnostic line becomes an editor diagnostic with the message
code: message and the compiler's help on a following line; entries for files
the re-check no longer reports are cleared. The compiler reports UTF-8 byte
offsets and a Unicode-scalar column, and VS Code positions are UTF-16 code
units, so the span is translated against the exact saved bytes of the file it
names: editors/vscode/positions.js is the one mapper diagnostics,
declaration navigation, and code lenses share. It handles CRLF, supplementary
characters, combining sequences, tabs, and the end of file, and rejects an
offset that is not on a UTF-8 boundary, runs backwards, or lies past the saved
source. When the saved bytes are unreadable or the offsets do not fit them, the
range falls back to the compiler's one-based line and column with the span's
byte width, which is exact on ASCII. A bare position is one character wide. A
file the editor changed while the compiler ran is never given the older run's
positions: the run is reported as failed and the previous diagnostics stay. SEMAPRAX: Check Project runs the
same check explicitly for the active file's project or the first workspace
folder, and names the setting to fill when semaprax.compilerPath is empty.
semaprax.checkOnSave (default true, machine scope) turns the save trigger
off. With an empty semaprax.compilerPath nothing runs. The binary is invoked
directly, never through a shell, never discovered, and never taken from
workspace settings; the child's combined output is capped at 4 MiB and its run
at 30 seconds, after which it is killed and the failure is written to the
SEMAPRAX Check output channel. check is read-only: this feature builds
nothing, publishes nothing, writes no file, and starts no saved-source session.
A run is published only when the adapter can classify it. check exits 0
after printing exactly one {"status":"verified", …} record and no error, and
exits 1 after printing at least one error diagnostic and no verified record.
Any other combination — a killed or unstartable child, a foreign exit status,
a line that is neither a diagnostic nor the verified record, an error with
status 0, or a verified record with status 1 — is a check failure: the
previously published diagnostics stay exactly as they were, the reason is
written to the SEMAPRAX Check output channel, and SEMAPRAX: Check Project
reports the failure and that the visible diagnostics may be stale. A check that
a newer check of the same subject superseded publishes nothing either. The
adapter never reports a clean project from output it could not read.
test/diagnostics.test.js covers manifest discovery, malformed-line skipping,
severity/range mapping, appended help, stale clearing, the exit-status and
output classification matrix, retention of the previous ledger across every
failure, and the byte and time bounds against a scripted child.
Navigate by meaning
With semaprax.compilerPath set, three commands and one code-lens provider
read the saved active .spx file through the compiler's read-only query and
doc routes; nothing runs on a dirty buffer. The query subject is resolved
exactly as check-on-save resolves one: the nearest semaprax.toml walking up
from the file, or the file alone. A module with use imports has no standalone
meaning — the compiler answers SPX-G172 — so a project-owned file is read
through query <manifest> --json and its semaprax.project-query.v1 result,
whose matches carry their own path and source_revision under the project's
project_revision and graph_revision. A match naming an absolute path or one
that escapes the project root is dropped, never opened. Declarations and
callers therefore span the whole project, and a selection opens the
authenticated file the match was found in.
SEMAPRAX: Go to Declaration by Stable ID lists every declaration of the
module (name, kind, @id, canonical header) and moves the cursor to the chosen
declaration's name token, translating the compiler's byte span against the
saved source through the same mapper check-on-save uses. A query whose document
changed while the compiler ran is refused rather than applied to positions that
no longer describe it, and code lenses for such a document are withheld until
the next request. SEMAPRAX: Show Callers of a Declaration asks for a function or
method, then lists the declarations whose bodies call it, from the compiler's
persistent call index rather than a text search, and jumps to the chosen
caller. SEMAPRAX: Show Module Documentation opens the Markdown page
semaprax doc renders beside the source. Code lenses above each declaration
show its @id (or that the identity is automatic), its uses { … } effects
when it declares any, and its requires/ensures counts when it declares
contracts; semaprax.codeLens (default true, machine scope) turns them off.
SEMAPRAX: Show Ownership, Contracts, and Effects asks for a function or
method and opens the compiler's bounded context document for it, which for a
project-owned file is the project's own context <manifest> <id> route (the
compiler admits no --filters there, so the whole bounded projection is
shown; a standalone file keeps depth one, the contracts, ownership, and
effects facets, and an 8 KiB budget) beside the source, so parameter and
result ownership modes, contract clauses, and effect sets are read from the
checked graph rather than inferred from text.
SEMAPRAX: Inspect Agent Definition runs agent inspect on the saved active
AgentDefinition v1 .json file and opens its AgentGraph v1 beside it.
SEMAPRAX: Safe Rename by Stable ID asks for a function or method and a new
lowercase name, authors the one-line semantic patch base <revision> /
rename <id> to <name> in a temporary file, shows the compiler's impact
analysis (how many declarations change and which consumers), and only on
confirmation lets the compiler's replay-checked patch route rewrite the
saved file; the stable identity never changes and the temporary patch is
removed afterwards. A standalone patch rewrites one file, so a project-owned
file is not renamed here at all: that command reports the boundary and points
at the saved-source session's replay-checked rename_declaration typed intent,
which is the project semantic workflow. SEMAPRAX: Show Cleanup Plan opens the
canonical cleanup plan the module graph records for a chosen function, exactly
as graph emits it, so cleanup order is read rather than inferred.
doc and graph are module routes over one standalone executable module.
A module with use imports, and a library module without fn main, has
neither, and there is no project route for either today, so
SEMAPRAX: Show Module Documentation and SEMAPRAX: Show Cleanup Plan name
that boundary alongside the compiler's own diagnostic instead of leaving it
unexplained. Everything else — declarations, callers, code lenses, ownership
and contracts — resolves through the project. SEMAPRAX: Run Agent Transcript (Trace/Evidence) takes the saved active AgentDefinition v1 file,
asks for a task and a transcript document, and opens the scripted run's
trace, evidence, or receipt from agent run; the run has no provider, tool,
or network authority.
Every run is bounded exactly like check-on-save (4 MiB, 30 seconds, direct
spawn without a shell, never workspace settings) and its failure is written to
the SEMAPRAX Check output channel. test/navigation.test.js covers the
argument vectors, result validation, source-ordered items, zero-based ranges,
lens titles, and the byte and time bounds against a scripted child.
Saved-source session
Load this directory as a development extension using VS Code's extension development host. Configure these user/machine settings, all absolute paths:
{
"semaprax.compilerPath": "/absolute/path/to/semaprax",
"semaprax.manifestPath": "/absolute/project/semaprax.toml",
"semaprax.hostPolicyPath": "/absolute/path/to/host-policy.json"
}
Workspace and folder overrides are rejected even if supplied manually. A trusted
local filesystem workspace and saved source/manifest buffers are required.
The explicit Start command invokes the selected binary directly, without a shell:
serve-workspace-mcp <manifest> <host-policy>. Nothing downloads or builds it.
The existing host-policy v1–v7 parser remains authoritative. Prefer a policy with
candidate preparation enabled and builds and Git commit disabled. Candidate
interpreter tests are unavailable unless startup policy selects fixed test limits. Enable
diagnostics in the host policy only if you want the optional attempt workflow
below. The adapter cannot widen policy and its own fixed allowlist excludes
builds, direct synchronous tests, commit approval, source publication and archive
restoration even if the supplied policy grants them.
Use the command palette in this order:
- Start Saved-Source Session negotiates MCP 2025-11-25, reads the paginated host-selected tool catalog and opens the held workspace image.
- Open Candidate, then Select Stable Target ID. IDs are explicit inputs; the compiler's target catalog checks them. No display-name guessing occurs.
- Show Target Change Catalog, or New Typed Intent Scratch. The latter
shows the selected constructor descriptor and opens an untitled JSON buffer
containing
kindandtarget. Fill its required fields using the catalog. - Apply Active Typed Intent submits only that tracked scratch buffer to the exact selected candidate/target. The compiler independently checks and replays the complete intention; the extension is not a semantic verifier. Integers beyond JavaScript's safe range reject instead of being silently rounded.
- Preview Candidate Source Diff reconstructs the bounded source-review
report, verifies its canonical report digest and each source/diff digest,
then displays the selected base/candidate text through read-only virtual
documents. It performs no
WorkspaceEdit, filesystem write or arbitrary path read. Source paths are validated labels, not filesystem access instructions.
Run Candidate Interpreter Tests uses Semaprax's explicit
candidate/test-task-* methods when all four are in the startup-selected tool
catalogue. These are Semaprax tools, not MCP standard task augmentation. The
start response is queued behind a one-shot gate; polling releases the bounded
interpreter worker. Cancel Candidate Interpreter Tests and the cancellable
VS Code progress notification request cooperative cancellation. Cancellation is
sticky, but completion may win if it was already terminal. Source drift, refresh,
finish or stop invalidates the editor handle, requests cancellation and discards
late results. A completed report is accepted only after exact revision, authority,
blind-spot, schema, pagination and digest checks. It claims no native or Wasm
runtime, deployment, generated-artifact, external API, runtime-environment or
external-consumer coverage. Builds and commits remain non-cancellable and absent.
For diagnostic recovery, Try Active Typed Intent with Diagnostics preserves a rejected attempt separately from the valid candidate. Show Rejected Attempt Summary and Show Retained Attempt Diagnostics inspect it; the latter verifies the bounded report's exact bytes and displays diagnostic locations as descriptions, never source navigation. Show Compiler-Admitted Repair Catalog displays available proposals. Select and Apply Exact Diagnostic Repair asks you to select a proposal, then sends only its exact repair ID and attempt revision. Displayed intentions and potentially rounded numbers are never resubmitted as repairs. The accepted candidate can use the existing source diff. Discard Diagnostic Attempt releases the attempt without changing source. These commands require the existing host policy to expose diagnostics; ordinary Apply remains fail-fast. There is no automatic repair, policy change or retry.
Source or manifest edits invalidate candidate selection and visible previews. Unsaved buffers must first be saved or reverted. Preview and Explicitly Refresh Saved Source asks the server to authenticate a new snapshot, then requires an explicit confirmation before replacing the held image. Open a new candidate afterward. The adapter does not silently rebase old candidates. Watcher events are only invalidation hints; every semantic call still uses exact server-bound image/candidate revisions and server source authentication. Manifest configuration changes that the host refuses require a new explicitly configured session.
For incomplete work, Open Typed Hole plans a body, body-expression or contract-expression replacement. Choose expression identities from the compiler catalogue. Once a draft exists, choices come from that draft's current last-valid state, so more holes can be opened after earlier fills. Select Pending Hole, Show Descriptive Hole Summary, Show Hole Facet Page or Next Page and Show Full Hole Context (Unbundled) inspect the current draft without exposing it as valid source. Facet pages are expanded explicitly and remain bound to that exact hole context. Show Typed Hole Constructor Schemas displays the compiler's recursive expression grammar without fetching schema references or claiming semantic admission.
New Hole Fill Scratch creates a typed-expression JSON document bound to the
selected draft revision and hole. Choose Checked Hole Fill for Scratch can
instead request bounded compiler suggestions and copy one explicitly selected
expression into the same kind of scratch. The command requires the host's
hole/fill-suggestions method. It shows accepted/considered counts and whether
the finite search was exhausted; empty results do not prove no valid fill exists.
Suggestions passed source replay, not tests or proof of the desired behavior.
Selecting one never adopts its preview digest or fills the hole automatically.
You can inspect or edit it before Fill Selected Hole from Active Scratch
submits it through ordinary compiler admission. Rejected fills preserve the draft; successful
changes invalidate older fill scratches and navigation references. Select the
next pending hole and create a fresh scratch. Only Complete Ready Draft as
Candidate releases a candidate for source review after every hole is filled. Discard
Typed-Hole Draft returns to the original candidate without source writes.
An active draft, even one ready to complete, blocks ordinary candidate changes and source-diff preview. Stop, source drift and refresh clear its editor state. Superseded in-memory draft handles are released after successful transitions; a failed release terminates the session without pretending that the preceding operation was rolled back. There is no automatic retry or publication.
Only one protocol request can be pending. The Cancel command can mark the active test controller while its current request is pending; the controller sends the explicit cancellation request sequentially. Requests are capped at 128 KiB outer MCP and 64 KiB inner v5, responses at 8 MiB, source reviews at 16 MiB and 16 files, and virtual-document references at 64 and 32 MiB total per session. Requests time out after 30 seconds. A response is assembled from the chunks it arrives in: the retained fragments are counted rather than concatenated for the cap check, each chunk is scanned for the newline delimiter once, and each byte is copied at most once into the frame it completes, so a legal fragmented response costs work proportional to its length rather than to the square of its fragment count. Framing, identity, protocol or digest failures terminate the session; there is no automatic restart or mutation retry. Ordinary rejected intentions preserve the last valid candidate for correction. Source authentication or stale image errors invalidate candidate UI and require explicit refresh. Stop remains available while a request is pending. Session process errors never imply that a source transaction was approved or published. This adapter never calls those transactions.
The generated virtual diff is a review of an immutable in-memory candidate. It does not save that candidate into canonical source. Use separately authorized SEMAPRAX tools for publication; this extension intentionally has no such route.
Authored Node tests live in test/. They use the built-in node:test runner and
mock processes to cover protocol bounds, exact inner envelopes, rejected tool
authority, timeouts, duplicate keys, canonical source-review digests and hostile
chunk/path inputs. Additional authored cases cover typed-hole lifecycle,
context/reference binding, failed fills and explicit completion. Suggestion
controller cases cover exact summary/report bindings, bounded expression
grammar, stale and asynchronous failures, and no implicit preview adoption. Repair cases
use schema-shaped mock responses to cover exact selectors, bound raw diagnostic
reports, malformed responses and failed handle retirement. Task-controller cases
cover exact queued and terminal states, sticky cancellation, bounded report
chunks, digest binding, authority and blind spots. Verification can use
node --test test/*.test.js; no VS Code or compiler process is started by those tests.
The separate scripts/graph-operational-vscode-host-evidence.py v2 runner
provisions an actual Extension Host plus compiler task-cancellation scenario and
must be reported only for the exact clean subject it succeeded on.
Implementation references: VS Code workspace trust, virtual documents, and configuration contributions.