Skip to content

fix(doc): render every migrated document — multi-valued cells, positioned figures in every form, partial sets, same-named documents - #568

Merged
HuiJun merged 7 commits into
developfrom
fix/document-rendering-residuals
Sep 25, 2026
Merged

HuiJun merged 7 commits into
developfrom
fix/document-rendering-residuals

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

What and why

A Cameo/SysML v1 model migrated with -convert sysml -layout carries its DocGen documents as DocumentQueries::Document definitions. Four generic defects in the document, query and view layers kept some of them from rendering; each is fixed at its root.

1. A table column over a multi-valued feature failed the whole document. queryexec demanded exactly one value per cell, so a column reading attribute tCalibNB : Real[0..*] bound as (69.0, 98.0) stopped the document with "produced 2 values, expected one". The query planner (internal/ir/queryplan) now carries each column expression's declared multiplicity (Multiplicity on RowProperty/Parameter operations, inferred through semantics.Model.GoverningMultiplicityOf), and evaluateColumnCell admits as many values as that multiplicity allows. docir cells were already []Value; the Markdown and HTML backends (and PDF through HTML) write them in order, , -joined, and an optional feature with no value is an empty cell. A column declared [1] still refuses zero or several values (the error now names the bound: expected [1]), and every scalar place — captions, Refs, comparison operands — still takes one value.

2. A positioned view drawn in Mermaid or PlantUML ignored its Layout and drew the whole exposed tree. Omitting unplaced nodes lived in the DOT writer alone, so the other forms expanded exposed packages into thousands of nodes and tripped the Mermaid workload bound. internal/ir/view/placement.go now makes one placement decision for every graph form (Rendering.settleUnplaced): the placed members and the edges between them by default, every member under -render-unplaced strip, with the accounting comment in each form's syntax (// not represented: …, %% not represented: …, ' not represented: …). Since this happens on the Rendering before any writer runs, -render, -render-all, document figures, the LSP and gRPC share it. The Mermaid bound and its message are unchanged. DOT output is byte-identical to develop (verified on every figure of the TMT How-to document).

3. -render-documents <dir> aborted the set on one failing document. docir.EvaluateSet now returns one Evaluated outcome per document and RenderDocumentSet* compiles, evaluates and renders each on its own. Pages that render are written; each document that does not gets a stub page stating This document could not be rendered. and the error, so links to it from other pages resolve to an explanation rather than dangle (chosen over omitting the link, because docrender already resolves a cross-document Ref to a file and the reader should learn why the target is missing). Each failure is reported on stderr as document <qualified name> could not be rendered: <reason>, and the run exits with the new status 3 (exitPartial): 0 every document written, 2 nothing written, 3 written in part.

4. Same-named documents. -render-document <short name> when ambiguous was already refused with every candidate's qualified name (AmbiguousNameError in internal/frontend/repl/lookup.go, verified on develop with the TMT model); a CLI test now pins that. Set file names were already qualified, but a case-only collision stopped the run and device stems / over-long names were not handled. The collision planner -render-all used is extracted into internal/translate/filename (Fit, DeviceStem, CaseFolded, Plan) and both -render-all and -render-documents plan their destinations with it: names participating in a case-folded collision are tagged ~<hash> until every destination is distinct, device stems are escaped, and over-long names are cut at their own encoding's escape boundary. Cross-document links are rendered from the planned (final) file names, in a set and for a single document rendered on its own. -render-all file names are unchanged.

Migration output is untouched: the migrator is not modified and the three migration artifacts of the TMT model are byte-identical between the develop and branch builds.

How it was verified

Tests beside the code: queryexec/computed_test.go (collection, [1], [0..1], scalar places), docrender/{markdown,html}_test.go over testdata/collection_report.sysml (a multi-valued and an empty cell), view/placement_test.go (mermaid and plantuml omitting/stripping unplaced nodes with the accounting comment, DOT unchanged), cmd/sysml/render_documents_test.go (TestRenderDocumentsPartialSet in Markdown and HTML, TestRenderDocumentsSameShortName, TestRenderDocumentsCaseCollidingNames, TestRenderDocumentAmbiguousName), docir/crossdoc_test.go and docrender/crossdoc_test.go (links to tagged names), translate/filename/filename_test.go. Gates: go build ./... && go vet ./... && gofmt -l . && go test ./..., the training/pilot corpus gates with their require variables, make man-check, make docs-check, python3 scripts/changelog.py check, and the PDF tests with the doc toolchain present.

Acceptance on the migrated TMT model (48,721 elements, 0 validation errors, 103 Document definitions)

develop this branch
migration artifacts (.sysml, report, results) — byte-identical to develop
-validate 0 errors 0 errors
-compare-results … -seed 1 41 configurations compared output byte-identical to develop
-render-documents (Markdown, default Mermaid) aborted at the M1CS document, nothing written 103/103 pages, exit 0
-render-documents -doc-form html aborted 103/103 pages + stylesheet, exit 0
-render-document M1CS results document "column tCalibNB produced 2 values, expected one" renders; tCalibNB cell 69, 98
How-to document, default Mermaid "diagram 2 is 4814322 characters and 63348 edges of mermaid, past the 1000000 characters and 10000 edges …" renders; figure 2 is 60 nodes / 28 edges
How-to document, -diagram-form dot 60 nodes / 28 edges, // not represented: 3358 node(s) without a position, left undrawn byte-identical to develop
-render-all (2,641 .mmd + 100 .md) 2,741 files, exit 0 same 2,741 file names, exit 0; the 256 positioned views now write their placed picture (e.g. TMT Overview.mmd 15,867,176 → 9,037 bytes)
L1 TMT to L2 APS, L3 APT to L2 APS render render under the default relationship budget
TMT-APS DDD, -html-theme nasa -doc-title-page -doc-toc -doc-number-sections renders renders as HTML and PDF (WeasyPrint and mmdc found under build/doc-pdf)

No document fails on the branch, so no stub page and no exit 3 occur on TMT; that path is covered by TestRenderDocumentsPartialSet. The 103 pages are 103 distinct files (38 of them tagged ~<hash> because the qualified name exceeds a 255-byte component). The migrated TMT documents carry no cross-document references, so the intra-set link check passes vacuously; link resolution to tagged names is covered by the cross-document tests.

M1CS results table, Markdown (the tCalibNB column is the multi-valued one; Real values are written as the scalar backends already wrote them, so 69.0 appears as 69):

| name | … | tNB | tCalibRBSF | tCalibBB1 | tCalibNB | tAcquisition |
| m1CS Sensor Calibration Duration Scenario.aPS Mission Logical5.aps operational blackbox.peas | … | 69 |  |  | 69, 98 |  |

How-to document, figure 2 (Template Observatory Workflow 2, render Views::asTreeDiagram) — the same 60 node ids and 28 edges in both forms:

Mermaid (this branch; develop refused it):

How-to figure 2 in Mermaid

DOT (unchanged):

How-to figure 2 in DOT

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 (compliance rows need nothing: the census is counted at docs build)
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

devin-ai-integration Bot and others added 5 commits September 24, 2026 20:54
A computed column reading a feature or parameter now keeps every value the
row binds, bounded by the declared multiplicity, so a [0..*] attribute renders
as its ordered values and an unbound optional one as an empty cell; a scalar
declaration still fails the column, naming the declared bound.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
A view some DiagramLayout::Layout positions was thinned to its placed
members only by the DOT writer; the Mermaid and PlantUML forms drew the
whole exposed tree, so a figure exposing an unplaced package expanded
into the package's contents and tripped the Mermaid workload bound.
One placement classification now serves every graph form: the placed
nodes and the edges between them by default, every node under
UnplacedStrip, each accounted for in the form's comment syntax.

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

-render-documents stopped at the first document whose queries failed and
wrote nothing; it now compiles and evaluates each document on its own,
writes the ones that rendered, stands a page carrying the error in for
each that did not so links to it resolve, reports every failure with the
document's qualified name and exits 3. File names are planned by the
collision tagger -render-all uses, moved to internal/translate/filename,
so names meeting under case folding, device stems and over-long names
get the tagged form and cross-document links point at it.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
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

@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review September 24, 2026 23:28
devin-ai-integration[bot]

This comment was marked as resolved.

…ent null as an empty cell

A set's page for a document that could not be rendered now carries a
paragraph under each anchor the other documents link into it by, so a
link into one of its blocks lands on the page rather than on a fragment
that does not exist. A column expression of null, alone or as the
default of ??, declares an empty cell; the query cookbook and the
changelog fragment say so and a test pins it.

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

This comment was marked as resolved.

…mes in every frontend

The LSP's opensysml/renderDocument and the gRPC RenderDocument rendered a
document without the set's file plan, so a cross-document link pointed at
the untagged name where -render-documents writes a tagged one. The planner
now lives in workspace/model as DocumentFiles, and the REPL, LSP and gRPC
renderers all link through it.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@HuiJun
HuiJun merged commit bacb7ae into develop Sep 25, 2026
15 checks passed
@HuiJun
HuiJun deleted the fix/document-rendering-residuals branch September 25, 2026 00:27
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