Skip to content

feat(wasm): serve RenderDocument from sysml-engine and sysml-wasm - #2158

Open
devin-ai-integration[bot] wants to merge 5 commits into
developfrom
feature/wasm-render-document
Open

devin-ai-integration[bot] wants to merge 5 commits into
developfrom
feature/wasm-render-document

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

What and why

sysml-wasm (and sysml-engine) now serve RenderDocument, so a browser consumer such as the view editor runs the whole document pipeline — the definition's queries, its document plan and tree, and the Markdown, HTML or AsciiDoc backend — from the embedded module, without a sysml-grpc service. Until now combined.Call answered it with CodeUnimplemented.

  • The request and reply are the protojson shapes of RenderDocumentRequest / RenderDocumentResponse (modelHash, documentId, form → markdown | html | asciidoc, fileName), so a typed client's renderDocument works over either transport unchanged.
  • The pipeline the service rendered through in internal/frontend/grpc/docquery.go is extracted to a new package internal/doc/docset (Definitions, Names, SiblingPlans, Files, Evaluate, Render, Forms, UnknownFormError, NotDocumentError, FaultOf, OriginOf, QueryOutcomeOf). The gRPC service, the engine and modeldoc.RenderDocumentMarkdown (workspace/LSP) all render through it; the gRPC status mapping (queryPlanCode/queryExecCode/docPlanCode/docIRCode) collapses to faultCode(docset.FaultOf(err)), and the engine maps the same faults to its JSON-RPC codes. model.DocumentNames/SiblingDocumentPlans/DeclaredDocumentDefinitions and modeldoc.DocumentFiles move there; their scope walk becomes symbols.Scope.Declarations/WalkDeclarations, which workspace/model now uses too.
  • Forms: markdown (default), html and asciidoc — the same three the service renders. The brief proposed refusing asciidoc under wasm, but docrender.AsciiDoc is pure Go; Asciidoctor.js is only needed for rep language "asciidoc" prose bodies, which fail the same way in every form, and docpdf is the PDF form, which the service already refuses. pdf (or any other spelling) is refused with InvalidArgument: form "pdf" is not one a document is rendered in: markdown, html or asciidoc.
  • Diagrams: the engine renders with WithoutGraphviz, so a positioned view falls back to Mermaid with the renderer's notice (drawn as Mermaid, not at its stated positions: Graphviz (dot) is not installed); the service's output is unchanged (it still writes DOT).
  • GetServerInfo advertises render_document, render_document_html, render_document_asciidoc, render_document_file_name (all four are capabilities sysml-grpc reports; tests/wasm checks that).
  • Docs: docs/reference/wasm.md, client/node/README.md (the module's RPC surface), docs/internals/architecture.md (new package), changelog fragment. The Node/Python clients have no served-by-wasm method table to update, and .agents/skills/testing-engine-browser has no method table.

Wasm size

GOOS=js GOARCH=wasm go build ./cmd/sysml-wasm succeeds. Only internal/doc/docir, internal/doc/docrender, internal/doc/docset and internal/translate/filename join the js build (no os/exec, no cgo, no third-party packages; docpdf stays forbidden by tests/wasm). Measured with the library snapshot embedded, go1.27.4, gzip -9:

build before (develop d60fa14) after delta
sysml-wasm, go build 43,151,675 raw / 10,483,603 gz 47,331,289 raw / 11,456,064 gz +4.18 MB raw / +0.97 MB gz (+9.3 %)
sysml-wasm, -trimpath -ldflags="-s -w" 42,059,975 / 10,332,553 46,146,329 / 11,289,949 +4.09 MB / +0.96 MB
sysml-engine, -trimpath -ldflags="-s -w" 37,384,228 / 9,377,493 43,461,495 / 10,712,298 +6.08 MB / +1.33 MB

The gate's own gzip (TestWasmRuns/js/fits_the_*_size_budget) reads 11,396,106 (sysml-wasm, was 10,413,317) and 10,816,237 (sysml-engine, was 9,451,750); the second commit raises those two budgets to 11,500,000 and 11,000,000 and records the sizes in their comments. If the budgets should move in their own PR instead, that commit can be dropped from this one.

Browser harness

Per .agents/skills/testing-engine-browser: a page outside the checkout loaded build/sysml-wasm.wasm (this tree, 47,331,296 bytes, served as application/wasm, uncompressed localhost) through wasm_exec.js; globalThis.sysmlWasm appeared 365–1320 ms after navigation. ParseSources of docs/manual/examples/prose.sysml returned modelHash 5f06c856…2900cc; GetServerInfo listed the four new capabilities last. RenderDocument answers, first lines:

form omitted (markdown), 66.8 ms, fileName: "Prose-MassReport.md":

# Telescope Mass Report

This report is *generated* by `sysml -render-document` ([OpenSysML](<https://opensysml.org/>)); see [Subsystem Masses](#breakdown) for the masses, and the mount at mount. Mass scales as $m \propto D^{2.5}$.

- Masses are rolled up from the parts.
- Margins are computed against the allocation.

<a id="breakdown"></a>

## Subsystem Masses

form: "html", 4.0 ms, fileName: "Prose-MassReport.html":

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Telescope Mass Report</title>
<style>
/* The default OpenSysML document stylesheet. Every value comes from a

…

<article class="sysml-document" data-document="Prose::MassReport">
<h1 class="sysml-title">Telescope Mass Report</h1>
<p class="sysml-paragraph" data-content="paragraph">This report is <em>generated</em> by <code>sysml -render-document</code> …

form: "asciidoc", 4.9 ms: = Telescope Mass Report / :stem: latexmath / This report is _generated_ by …, fileName: "Prose-MassReport.adoc". Error probes, each followed by a working call: form: "pdf" → code 3 naming the three forms; Prose::Missing → 5 symbol not found: Prose::Missing; modelHash: "not-a-hash" → 5 model not found: not-a-hash; Prose::Telescope → 3 … is not a document: one is a part def specializing DocumentQueries::Document. No console errors, rejections or Go exits.

Known limitation observed, not introduced here (identical on develop): the module's ParseSources/ParseFile diagnostics for prose.sysml include document-plan-invalid-context ("document planning requires an index, resolver, and semantic model") on the rep language "markdown" body — the core's document check pass runs without the resolver/semantic model the planner needs. Rendering is unaffected.

How it was verified

Gates on this head: go build ./... ok · go vet ./... ok · gofmt -l . empty · GOOS=js GOARCH=wasm go build ./cmd/sysml-wasm ok · go test ./... ok (the only failures of the first full run — TestPackageLayering wanting internal/doc/docset assigned to the doc layer, the self-model's surfaces.sysml listing the served methods, and a stale local stdlib.snapshot — are fixed in this head or were environment) · make lint ok (staticcheck + gosec) · go test -count=1 ./tests/hygiene ok · python3 scripts/check-doc-ids.py ok · python3 scripts/changelog.py check ok · the two-line comment gate prints nothing.

New tests: TestRenderDocumentRoutesThroughEngine (combined: markdown default, html, asciidoc, fileName, refused pdf, unknown document, non-document, missing documentId, missing model); tests/wasm TestEngineRenderDocumentMatchesTheRendererGoldens (engine output equals docrender's telescope goldens in all three forms), TestEngineRenderDocumentFallsBackToMermaid (positioned views of graph_views.sysml → two Mermaid blocks with the notice in Markdown and HTML, no DOT), TestEngineRenderDocumentRefusals; TestCombinedWireParity now compares RenderDocument on the combined module against sysml-grpc protojson in every form; docset unit tests for the form refusal and FaultOf/OriginOf/QueryOutcomeOf; the wasm dependency and capability gates updated. The gRPC RenderDocument tests and tests/grpc are unchanged and green.

Checklist

  • make test and make lint pass locally
  • Tests added or updated for the change
  • Documentation extended where it already covers the surface (see CONTRIBUTING.md)
  • Changelog entry added as changes/unreleased/<slug>.<section>.md, not as an edit to CHANGELOG.md
  • baselines regenerated and make docs-counts run if a gate count moved (none moved; the two wasm size budgets are raised in their own commit)
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

Link to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/6fb986d1fe074cfc89d598daed8a896a
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/6fb986d1fe074cfc89d598daed8a896a?variant=devin
Requested by: @HuiJun


🟢 Devin Review found nothing to fix · View findings · Follow Devin's work

Devin Review

devin-ai-integration Bot and others added 2 commits November 11, 2026 22:36
The combined module and the engine answer RenderDocument with the protojson RenderDocumentResponse the service answers, in the forms markdown, html and asciidoc, refusing pdf by naming the three. The pipeline the service rendered through is extracted to internal/doc/docset — document definitions, sibling plans, the files a set writes, evaluation, the backends and the fault classification each transport maps to its status codes — so the service, the engine and the workspace render through one implementation. Positioned diagrams fall back to Mermaid with the renderer's notice under the engine, which runs no Graphviz. Scope declaration walking moves to symbols.Scope.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ment

The document pipeline (docir, docrender, docset, translate/filename) the engine now links grows the gate's gzipped js builds to 10,816,237 bytes (sysml-engine, was 9,451,750) and 11,396,106 bytes (sysml-wasm, was 10,413,317); the budgets move to 11,000,000 and 11,500,000, the measured sizes recorded in their comments.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

… converter

The engine's query context now supplies ElementID as the service's does, through queryexec.OpenElementIDs over the model's documents, so a document query projecting elementId answers ids where the binary links the converter; the js build links none and leaves the column absent, which docs/reference/wasm.md now states.

Co-Authored-By: jason.han <hanhuijun@gmail.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 finding. Devin automatically fixed it.

Fixed by Devin (1)
  • Native document IDs remain absent — The engine's query context now supplies ElementID through queryexec.OpenElementIDs over the model's documents, as the service does; the js build links no converter and leaves elementId absent, which docs/reference/wasm.md states.

View findings in Devin Review · Follow Devin's work

Devin Review

devin-ai-integration Bot and others added 2 commits November 11, 2026 22:47
sysml-engine and sysml-wasm link no converter, so the elementId column RenderDocument answers is empty on every target, not only the js build; the comment and docs/reference/wasm.md say so.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
The module's GetServerInfo now ends with render_document, render_document_html, render_document_asciidoc and render_document_file_name; the Node client's wasm connection test lists the capabilities it expects and so lists them too.

Co-Authored-By: jason.han <hanhuijun@gmail.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant