Skip to content

Roadmap ​

Pending work for Kratos MDPA Preview, prioritizing full meshio++ integration, a clearer UI shared with CAD-Preview, and practical mesh preparation and results-analysis workflows. Existing foundations include MMG remeshing, meshio++ WASM, replayable edit histories, Python problemtypes, field visualization, time-series playback, tracked Kratos runs, and a headless MCP server.

meshio++ is adopted at the locked 18.3.0 baseline. Historical binding failures, missing side channels, format defects, and build omissions are no longer exclusions from this roadmap — @meshioplusplus/wasm is pinned at ^18.3.0, the locked baseline for this integration, and the extension's packaged runtime declares and uses it (both artifact pairs: the lean build for every ordinary format, the full one for the CAD reads, the fitted STEP export and the codec fallbacks — see Tier 0's standing note). What remains is connecting its capabilities to the extension end to end, preserving Kratos semantics, and verifying the resulting workflows — the items below, not the dependency adoption itself. Keeping that pin current as upstream releases is its own standing item, Tier 0.

This page is aspirational, not a release commitment. All numbered items are pending except the completed work recorded in CHANGELOG.md. Completed item 4 (user layers) was removed 2026-10-09 without renumbering the remaining items, item 5 (reusable recipes and batch processing) was delivered and removed the same day without renumbering, item 7 (large-mesh rendering and regression coverage) was delivered and removed 2026-10-27 without renumbering (its remainder moved to item 2), and item 16 (coupled problemtypes) was added the same day without renumbering, and item 14 (graceful solver stop) was delivered and removed 2026-10-10 without renumbering, item 8 (solver convergence and saved monitors) was delivered and removed 2026-10-11 without renumbering, with its unverifiable remainder moved to new item 17, and item 3 (saved view state and independent result comparison) was delivered and removed 2026-10-13 without renumbering, item 18 (verifying the ROM and Structural Optimization problemtypes against a Kratos run) was added 2026-10-14, item 9 (scientific plotting) was delivered and removed 2026-10-15 without renumbering, with its unmet remainder moved to new item 19, items 20 (verifying the fluid problemtypes and the virtual wind tunnel against a Kratos run) and 21 (fluid follow-ups) were added 2026-10-17 and delivered 2026-10-17 and 2026-10-20, and items 6 (case preflight and isolated run workspaces) and 13 (in-place save for directory-based cases) were delivered 2026-10-19, all four removed 2026-10-22 without renumbering, and item 22 (unused vtk.js capabilities) was added and delivered 2026-10-22 and removed without renumbering (its record is in CLAUDE.md and doc/vtk-js-evaluation.md), so numbering now runs 0–2, 7, 10–12, 15–19 with gaps at 3–6, 8, 9, 13–14 and 20–22; item 23 (hanging-node octree meshes and MPCs) was added 2026-10-27, and items 24–35 were added 2026-10-31 from the ElmerStudio review and from Non-goals re-read against the current kernel (Tier 3 below, plus the new Non-goals section), items 36–43 the same day from a second, deeper pass over its results and solver-setup code, and items 44–46 from a third pass over its integration numerics and moving-mesh machinery, so numbering now runs 0–2, 7, 10–12, 15–19 and 23–46; historical citations remain unchanged. Effort is approximate: S = a day or two, M = roughly a week, L = multi-week. Completed features belong in CHANGELOG.md and implementation details in CLAUDE.md; remove completed items here. No tracker issues have been assigned to the items below yet.

Renumbered 2026-10-08 to add user layers as item 4 and future multilanguage support as item 15 — items are now 0–15 contiguous. Old→new for this change: 4→5, 5→6, 6→7, 7→8, 8→9, 9→10, 10→11, 11→12, 12→13, 13→14 (0–3 unchanged). Pending-item citations in src/, tests, docs and CLAUDE.md were swept in the same change. former roadmap item N always names the number an item had when it was delivered and is never renumbered. The 2026-10-05 renumbering (7→6, 8→7, 14→8, 16→9, 17→10, 18→11, 19→12, 21→13, 22→14) still applies to references made before that date, including CHANGELOG.md history.

Research baseline ​

Reviewed /home/vicente/src/meshioplusplus and the published WASM artifacts on 2026-09-29, including releases 16.23.0–16.25.0 (re-probed 2026-09-30 against 16.27.0, 2026-10-09 against 16.32.0, 2026-10-19 against 17.8.0 and 2026-10-22 against 17.11.0: same inventory, see Tier 0), bindings/wasm/js_bindings.cpp, and src/wasm/index.d.ts. The extension now declares @meshioplusplus/wasm: ^17.13.0; sequential and OpenMP builds were loaded and probed alongside the previous packaged runtime.

16.25.0 keeps the WASM format inventory stable and adds operation exports. The live probe found 76 readers, 68 writers, and 37 options-aware readers in both 16.22.0 and 16.25.0; no format key was added or removed, and hasCgnslib remains true. New bindings cover feature edges, Hausdorff distance, region editing, periodic-node matching, quality gates, agglomeration options, and sequence blending/resampling. Operation results are integrated through fidelity-checked adapters; agglomeration stays unavailable for editable Kratos models because polyhedral cells exceed the model representation. The 16.25.0 declarations are additive for the functions this extension uses. The host now decodes bzip2 libMesh inputs because the published WASM artifact does not link bzip2.

The three boundary behaviour changes are all no-ops here, and each for a different reason worth recording. (1) val_to_mesh now feeds polygon/polyhedron blocks to the stored CSR directly, which is internal and semantically identical for what modelToMeshio emits. (2) writeMesh/convert/convert_surface/convert_surface_ops switched from resolve_format to resolve_write_format, so a write no longer lets the file it replaces choose its format — the fix for upstream's .dat→marc misresolution. It cannot reach us because writeMeshioBytes always passes an explicit key from MESHIO_WRITE_FORMAT and throws when it has none, so upstream never resolves by name on any path the extension owns. (3) encoding: 'raw_appended' for vtu works at runtime but is absent from the shipped index.d.ts, and .vtu is written by our own vtkXmlWriter anyway, so it is a new option rather than a missing capability. Separately, ensight's fortran: true write option exists in the C++ and Python APIs but is not exposed in the WASM binding (unknown key 'fortran' in the write options), so there is no decision to make.

The performance work landed largely where this extension does not call. 16.15.0–16.21.0 are a large parallelisation and I/O campaign that claims byte-identical operation output. Timed here on a 48,000-tet mesh (16 threads, OpenMP backend, median of 3) across the routed oracles: reorder 1.61×, estimateError 1.13×, hessian 1.10×, smooth 1.06×, but curvature 0.83×, surfaceWatertightCheck 0.87× and partitionLabels 0.95× — mixed, and no reason to claim a speedup in the release notes. The reason is legible in the changelog: 16.16.0's shared facet/edge tables and 16.19.0's per-operation parallel phases target extract_surface, refine, convert_cells(elevate), optimize_volume and decimate, and this extension replaced every one of those with a native implementation (extractSkin.ts, refineMesh.ts, simplexify.ts, meshing.ts) precisely because the meshio++ path was lossy. The large text-reader gains (Tecplot 2.1×, SU2 3.5×, the C++ mdpa reader 3.2×) are real but unmeasurable here: the committed .tec/.dat/.msh fixtures are a few kilobytes, so a 384 KB Exodus read is ~1 ms either way. Whether they matter is a question about a user's file size, not about this bump.

One fix does reach a routed path silently. 16.17.0 corrected Exodus hexahedron20/hexahedron27 node ordering (SEACAS lists the vertical mid-edges before the top ring, and the Hex27 body centre before the face centres) and added wedge15/pyramid13. .e/.exo/.ex2 are routed, so a file written by another Exodus writer now reads correctly where meshio++ previously scrambled those cells. The same release fixed the same class of bug in Kratos .mdpa and taught its reader Prism3D15/Pyramid3D13; that half is not reachable — mdpa is parsed natively here — and geometryMap.ts already carried 3:13, 3:15, 3:18, 3:27 plus the prism:15/pyramid:13 family fallbacks, so no mapping work was owed.

The following paragraphs retain the prior-release audits as historical evidence.

Reviewed /home/vicente/src/meshioplusplus on 2026-09-25, including its changelog through 16.14.0, bindings/wasm/js_bindings.cpp, and capability documentation. The extension declares @meshioplusplus/wasm: ^16.14.0. This is the first bump to cross a major version — 16 releases from the 15.4.0 baseline, and the measurement below is against the published artifact (downloaded and loaded side by side with the installed 15.4.0), not against the changelog's prose.

The WASM boundary is unchanged and that is the load-bearing finding: git diff v15.4.0..HEAD over bindings/wasm/js_bindings.cpp and index.d.ts is additive only — one new export (tensorInvariants), zero removals, zero signature changes. The v16.0.0 C++ ABI break (CellType::Custom moving 76→77 with Triangle7 appended) is irrelevant to a prebuilt-WASM consumer, so this is a routing exercise, not a compatibility one. The one boundary behaviour change is read_with_info/readMeshSelective for unv now taking ReadOptions.

Live capability probe, 15.4.0 → 16.14.0: 54 → 76 readers, 57 → 66 writers, 21 → 37 options-aware readers, no key removed, hasCgnslib still true. 16.13.0's vtx/szplt are absent from the WASM build entirely (they need optional ADIOS2/TecIO, which this build does not link), so they need no routing decision. The bundled dist/meshio/ grows 17.42 MB → 22.47 MB, +5.05 MB across both variants.

22 new reader keys at the 16.14.0 audit. Eleven are solver results: abaqus_fil, ansys_rst, ansys_rst_cyclic, lsdyna_binout, lsdyna_d3plot, marc_t19, nastran_h5, nastran_op2, radioss_anim, radioss_th, xplt. Seven carry a real extension and are routed (.fil, .rst/.rth, .d3plot, .t19, .h5, .op2, .xplt); the other four are found upstream by file name, not extension, so no extension this extension offers can reach them and they stay inputFormat-only. None is admitted to the in-file timelines: every one reports fellBackToFullRead: true, the frd bar. The other eleven were structural CAE formats (code_aster, elmer, febio, femap, libmesh, marc, mfem, mphbin, patran, radioss, z88). Those were deferred at that audit and are now integrated under the former task 15, completed 2026-09-29; writer formats without a unique extension remain discoverable through MCP's named-writer inventory.

Behaviour changes touching code this extension already runs, all re-measured rather than assumed: the Fluent ansys writer was rewritten (16.9.0, now emits Fluent's own face-based layout its own reader accepts — the round trip survives, the row-for-row connectivity guarantee does not, and the test now says which); conservative_interpolate lost up to 0.7% of mass between aligned meshes until 16.14.0 fixed it, which is now pinned positively; UNV's group entity types were swapped and its hexahedron20/wedge15 mid-edge order corrected (15.6.0); the Nastran bulk deck is now read natively (16.2.0), so .bdf/.fem/.nas open at all where they previously needed a Python twin wasm does not have; MED gained HEXA27/PENTA18 and triangle7 became a real cell type (16.0.0, wedge18 and triangle7 are now mapped to drawable VTK types); COMSOL's quadratic node order was corrected (16.1.0); and a non-transient multi-zone Tecplot file now reads all its zones instead of silently discarding all but the first (15.5.0).

The transient audit was re-run against 16.14.0 (src/test/fixtures/transient/README.md): no existing classification changed. Two already-routed readers flipped options-awareness: unv (genuinely, and still not admitted) and ansysinp (which registers a ReadOptions overload for a format with no time axis — the sharpest case of the flag not being capability). mcpTools.test.ts now refuses any unrouted key left on the UNEXAMINED_REASON fallback, so the next release's new formats fail a test until each is routed or its reason is written down. .github/workflows/meshio-watch.yml reports a major-version lag on a schedule, because a caret range never crosses a major and Dependabot's minor/patch group structurally cannot propose it.

Evidence in the local checkoutRoadmap opportunity
WASM bindings and JavaScript API expose repair, computeCurvature, shrinkwrap, sobolevDeform, remesh, remeshVolume, optimizeVolume, grid, voxelize, and computeSdfAdd preparation, analysis, and mesh-generation workflows beyond the operations already integrated.
The same binding exposes diff, meshesEqual, interpolate, slice, isosurface, split, partition, field-management operations, and provenance functionsMesh comparison, sampled results, reusable derived meshes, partition exports, and traceable conversions.
WASM pipeline documentation describes runPipeline and convertSurfaceOps, which keep intermediate meshes inside C++Reduce repeated whole-mesh transfers and enable bounded batch processing.
Partition documentation covers weights, recorded source indices, and ghost layers; bindings expose ghostLayersExtend the existing partition-label operation into actual domain exports.
Changelog records FLAC3D group fixes in 10.36.0, curvature in 10.37.0, and repair/shrinkwrap/Sobolev deformation in 10.38.0Refresh format fidelity and integrate concrete new operations rather than treating every unused export as a feature request.
The changelog explicitly identifies curved tessellation (10.39.0), point budgets, proximity graphs, PhysicsNeMo workflows, and PMSH/Zarr/CAE/USD format additions as Python-only workTrack these separately as optional companion or future-binding work; resolving WASM defects does not automatically port Python code.
The 16.x changelog records optional-dependency readers vtx (16.13.0, ADIOS2) and szplt (16.13.0, TecIO), which the published WASM build does not link — hasAdios2/hasTecio are absent and neither key appears in availableFormats()Nothing to do now, and the reason is worth recording: a format gated on a vendor library this build cannot ship is a different decision from one that merely lacks a wrapper. Revisit only if a dependency-free or freely-redistributable path appears.
The 16.x changelog added eleven structural CAE readers while the extension lacked directory and fixed-basename dispatchCompleted 2026-09-29 under former item 15: routed the formats with explicit retry order, marker and companion collection, rank handling, named writers, round-trip fixtures, and documented fidelity limits.

The historical audits in src/test/fixtures/transient/README.md and CLAUDE.md remain evidence about the versions tested. During integration, replace obsolete expectations with positive capability tests and retain relevant regression fixtures. The 16.14.0 audit was a live-WASM one (both artifacts loaded and probed side by side, upstream's own result fixtures staged read-only); earlier entries in this section were source inspection only, and their version claims are evidence about those versions, not about the current pin. A VTK-wasm rendering-runtime evaluation (standalone-session boot, bulk data transfer, and a minimal end-to-end render, testing the shipped webview CSP and required changes) was run and closed on 2026-09-18 with a drop verdict — see doc/vtk-wasm-spike.md for the full findings, measurements, and reproduction steps. That verdict remains evidence about the tested artifact. Item 12 then resolved its blockers for a newer build and shipped VTK-wasm as an experimental, opt-in renderer; by decision (2026-09-26) vtk.js remains the default and is not being replaced.

Magnusim review — 2026-09-21 ​

Implementation review at af8d059: read the result exporters, transient-control calculation, function-object writer, pressure conversions and material library, plus test_transient.py, test_result_filter_registry.py and the relevant local integration paths. This is source/test inspection, not a run of Magnusim or OpenFOAM.

Source evidenceConcrete local gap / decision
estimate_delta_t / resolve_transient_control and transient testsOur fluid problemtype writes a fixed time step; add transparent time-step and output-budget guidance.
surface averages and summed phi monitorsExisting field integrals are measure-weighted totals, not signed boundary flux or a conservation report.
pressure conversionThe native OpenFOAM parsed-field interface does not retain dimensions; raw p cannot safely be labelled Pa.
material libraryProblemtypes define constitutive laws and numeric defaults but lack a reusable physical-material catalog with provenance and unit conversion.
volume threshold exporterthresholdCells.ts already provides a view-only threshold. Extend it to quantitative extraction/export; do not add a duplicate filter.
streamline exporterAdd seeded trajectories from actual volume velocity; its PyVista implementation is not available automatically in vtk.js.

ElmerStudio review — 2026-10-31 ​

Source review of ElmerStudio at b1be253 (2026-10-30, GPL-2.0-or-later), a COMSOL-style Qt model builder for Elmer FEM: read its unit evaluator, parameter and selection handling, study runner, probes, datasets, derived values, material status table, meshing presets and its known-limitations page. This is source inspection, not a run of Elmer; only ideas transfer, no code. Geometry and meshing-size findings are scoped in CAD-Preview's roadmap and cross-mode workflow findings (dependency reports, multi-parameter and geometry sweeps, recovery, the wizard) in KKSS's; this table keeps what lands in this extension.

Source evidenceConcrete local gap / decision
core/units.py: 10[mm]/20[degC] bound to the operand before the bracket, a seven-exponent Dim deduced per expression, unit_warnings that warn and still apply the SI valueProblemtype fields already declare unit (src/problemtype/types.ts) and presets convert into it, but typed values are bare numbers and there are no case-level parameters. Item 27.
core/datasets.py: chainable Mirror, Revolution 2D (start angle, sweep, layers), Parametric Extrusion and Cut Plane views that delegate every evaluation to the base solutionmesh_derive mirror/revolve write a static file; nothing expands a symmetric or axisymmetric RESULT on screen while the timeline and field picker keep working. Item 24.
ui/plots.py: click_values (Evaluation 2D/3D: one row per click, every plot of the group in its unit) and evaluate_derived over a sweep (one row per run, swept parameter first)Inspect shows one pick at a time and Plot over time covers one entity; there is no accumulating evaluation table and no "quantity per run" view. Item 25.
core/probes.py + main_window.update_probes(live=True): probe columns re-read from the frames written so far, at most every 2 s, skipping the newest (possibly half-written) fileSaved monitors are evaluated post-run only; the RunManager already watches vtk_output/. Item 26 (distinct from item 17's solver-written monitors).
ui/settings.py: _matprops + required_matprops: required rows first with a status icon, a missing value is a node error naming the propertiesThe material rulebook validates assignments in the dry run, but there is no per-law "required / provided / from preset / missing" table before Generate. Item 28.
core/study_runner.py: initial values from another study (refused with a reason when the mesh differs) and "reuse solution from previous sweep step" through restart filesNo problemtype writes or reads a Kratos restart; KKSS's sweep warm start needs this half. Item 29.
ui/plots.py: Polar plot group, Radiation Pattern and Directivity; Global plots against the swept parameterPLOT_FAMILIES has no polar family, and a plot cannot take a case parameter as its x axis across runs. Item 30.
core/meshing.py: SIZE_PRESETS, flow halving, acoustics.acoustic_hmax = c_min/(5·f_max)Problemtypes know the physics and timeStepEstimate.ts already estimates from mesh and case; nothing hands a recommended element size to the CAD mesher. Item 33 (CAD-Preview owns the presets).
Solution._operator_funcs: aveop/intop/maxop usable inside any result expressionfieldCalc formulas and formula seeds see x, y, z and fields but no regional scalar; region.ts already computes those reductions for plots and monitors. Delivered (item 34, 2026-11-01).
MessagesPane.add(node=): every message about a node is a link to itPreflight findings carry conditionId, lawId, propertyId and entityIds targets (src/problemtype/preflight.ts), but the webview only acts on smpPath (webview/problemtype.ts). Item 35.
docs/.../known-limitations.adoc: one explicit page of what is not available or only partly supportedThe equivalent statements are spread over guide pages and CLAUDE.md; the new Non-goals section below collects this file's standing exclusions, and KKSS tracks the user-facing page.

Second pass (same day, same commit): a deeper read of elmerstudio/ui/plots.py, ui/graphics.py, core/results.py, core/sif.py and core/physics.py added the rows below; each gap was checked against this repository's code before it was listed.

Source evidenceConcrete local gap / decision
plots.py: Clim carries the data's true extremes beside the mapping range, so the legend's ▲/▼ markers stay honest under a manual or symmetric range; CONSTANT_RTOL = 1e-9 draws a near-constant field in one colour instead of stretching round-off over the whole table; a symmetric range is offered and implied by diverging tablesfieldScalars.ts's effectiveRange returns the data range or the override with no degenerate-range rule, diverging colormaps exist (colormaps.ts) but nothing centres them on zero, and no marker shows where the extremes are. Item 36.
results.py: auto_scale_deformation: the largest displacement drawn at about 10 % of the bounding-box diagonal, rounded down to 1/2/5×10ⁿThe deformation scale starts at 1 (paneView.ts) and the user hunts for a factor (the KKSS cantilever tutorial needed 1000×). Item 37.
plots.py: quadratic cells are tessellated before contouring so isosurfaces follow the curved interpolantisoSurface.ts reads only the corner nodes of a quadratic cell (decompositionFor(...).corners), so mid-side values are ignored. Item 38.
graphics.py: candidates_at, cycle_candidate: Up/Down steps through every face the view ray crosses ("k of n under the cursor"); hover is red where a click adds, green where it removesA pick always takes the front-most entity; reaching one behind it means hiding or clipping. Item 39 (CAD-Preview tracks the same interaction).
plots.annotation_text: eval(expr, unit) inside an annotation is evaluated at its point and printed in the unitScreenshot titles and captions are fixed text (capturePlan.ts), apart from the step label. Item 40.
sif.py: _merge records the owner of every keyword per entity and fails when two features set it differently, naming bothPreflight reports the same condition twice on one part (assignment-duplicate) but not two different conditions fixing the same variable on shared nodes. Item 41.
physics.py: _solid_bload: a Total force is divided by the boundary's measured area at generation timeStructural surface and line loads take N/m² and N/m only. Delivered (item 42, 2026-11-01).
sif.py: solver_settings: direct below a DOF threshold, iterative above, always direct for eigen, with the reason recordedlinearSolverSettings (builtins/common.ts) omits linear_solver_settings for automatic, so Kratos picks its own default and nothing tells the user what that is for a large 3D case. Item 43.

Not carried over from the second pass: its reaction-load flux reconstruction (regional sum and reactionMoment already ship in plot/region.ts), its random-node streamline seeding (seeded traces already ship) and its camera persistence (keepCamera already ships).

Third pass (same day, same commit): core/results.py's integration and flux-recovery code and core/deforming.py/mesh_motion.py/remesh.py.

Source evidenceConcrete local gap / decision
results.py: _integrate_values, _axi_line_hrz: integrals over a 2D axisymmetric result revolve by 2πr (on by default, labelled), with an r-weighted lumping that stays positive on the axis nodeThe fluid and structural problemtypes generate axisymmetric formulations (builtins/fluid.ts, structural axisym), but plot/region.ts and partMeasures.ts integrate in the r-z plane and flowBalance.ts refuses an axisymmetric boundary outright. Item 44.
results.py: boundary_projection, _hrz_fractions, integrate: a boundary flux field recovered from nodal reaction loads by a consistent boundary-mass projection (lumped HRZ where that does not apply), integrated with the same weights so subset integrals sum exactly to the totalplot/region.ts sums nodal reactions (sum, reactionMoment) but nothing shows where on a face they act; dividing nodal loads by a lumped area leaves quadratic-element corners at zero. Item 45.
deforming.py: _quality: quality measured relative to the reference mesh (signed area ratio and shape ratio), so an intentionally graded mesh is not flagged and only degradation since the last remesh triggers actionFSI and CoSimulation results carry MESH_DISPLACEMENT, but nothing reports whether the ALE mesh stayed valid over the run. Item 46.

Not carried over from the third pass: per-frame topology switching (filename series already re-parse each frame), remeshing that keeps boundary numbers (MMG remeshing keeps SubModelParts and remaps fields), and the acoustics exterior field and electrochemistry kinetics (no counterpart problemtype).

Limits found during review: ElmerStudio's convergence plot parses solver log lines (elmer.RunState.feed); that contradicts this extension's rule that progress and convergence come from solver-published files, not stdout (see Non-goals), so it is deliberately not carried over. Its "Form Assembly identity pairs" (mortar continuity) are covered differently here by conformal meshes and hanging-node constraints (item 23). Its OpenFOAM and NASTRAN-95 backends have no counterpart: this extension generates Kratos cases only.

Delivery rules ​

  • Keep the kernel current first. Tier 0 precedes everything else: each tier's scope is measured against a specific meshio++ release, so a stale pin makes later estimates and acceptance checks describe the wrong artifact.
  • Prioritize integration and usability before breadth. Tier 1 establishes a reliable shared foundation; later tiers add workflows on top. Independent UI work can proceed alongside kernel integration.
  • Full integration means access to the useful kernel surface through a consistent adapter. It does not require deleting working native implementations or exposing duplicate buttons for equivalent algorithms. Choose a backend per operation on fidelity, performance, and maintenance cost.
  • Preserve the extension's data contract. Original node and entity IDs, independent Elements/Conditions/Geometries ID spaces, Properties, constraints, SubModelParts, field components, and source-cell correspondence must survive wherever the operation permits. For topology-changing operations, define generated IDs, field transfer, and metadata handling explicitly.
  • MCP parity ships with every headless capability. Update src/mcp/tools.ts, src/mcp/register.ts, src/test/mcpTools.test.ts, and the tool documentation in the same implementation change. UI-only work is exempt; a UI wrapper around a new analysis or edit is not.
  • Acceptance checks are part of each estimate. Use real format fixtures, numerical invariants, cancellation and undo/redo tests, and packaged-extension checks where appropriate. Retest the target package as normal integration work rather than leaving features indefinitely labelled “needs live-WASM verification.”

Tier 0 — Keep meshio++ up to date ​

Admission criterion: upkeep that every other tier depends on, because each tier below is scoped against a specific meshio++ release. Unlike the numbered items further down, this tier is standing work: it is never removed when a bump lands, only its status line is refreshed.

0. Track upstream meshio++ releases — S per release, recurring ​

Standing work — last bumped 2026-10-26, 17.13.0 → 17.21.0 (^17.21.0), integrated in the same change. The binding diff was meshCadSurface only (eighteen new options, ten new report fields); every other function and every reader/writer key is unchanged (lean 81 readers/82 writers, full 83/82), transientAudit.test.ts passes unedited, packaged .vsix 54.75 MB. Per-bump findings, measured behaviour and decisions for every earlier bump (16.14.0 through 17.13.0: audits, routing, licence statement, performance) live in CLAUDE.md (the 17.21.0 bump and the sections after it) and CHANGELOG.md. Procedure per release: probe the published tarball (index.d.ts diff, exported functions, reader/writer keys, options-awareness), adopt additive capabilities with a measured test, record unrouted ones with a reason in meshCapabilities.ts's operations.unavailable, keep transientAudit.test.ts unedited unless a classification really changed, and record the packaged .vsix size. Still unrouted by decision: agglomerate, retagRegions, fitPrimitives, refinePatches without a segment:patch field, the remaining fitCad options (freeformGeneral, snapJunctions, extendSurfaces, sewTolerance), eleven structural CAE readers (former item 15) and the read-only result readers whose steps cannot be enumerated without a full read.

The earlier bump (2026-10-09, 16.27.0 → 16.32.0) added medMeshNames/readMedNamed/writeMedMulti, writeGltf, the PCD lzf codec, ExodusTimeSeriesWriter, region adjacency/contact helpers and surfaceBuffersOps, none routed; its packaged .vsix was 31.44 MB.

The previous bump (2026-09-25, 15.4.0 → 16.14.0) was the first to cross a major version and the largest this tier has recorded; its classification is below. Its four buckets are backed by measurement, not by the changelog's prose: the binding diff is additive-only, two already-routed readers flipped options-awareness, one kernel fix (16.14.0's conservative_interpolate mass loss) removes a defect this extension would otherwise have inherited silently, and all 22 new reader keys are either routed or carry a recorded reason — with mcpTools.test.ts refusing the unexamined fallback so the next bump fails a test rather than a user.

The detection is now automated. .github/workflows/meshio-watch.yml runs weekly, compares the declared range against npm view @meshioplusplus/wasm version, and files or updates one issue when the latest release falls outside it. It reports rather than acts, because the work a major bump needs (classify, route, re-audit) is exactly what a version comparison and a Dependabot PR cannot do.

For each release, read the upstream changelog entry and classify what it touches for the WASM build before bumping:

  • Formats: a new or changed reader/writer key (14.0.0 adds vtkhdf, with partition selection and transient Steps). Decide explicitly whether to route it, and extend MESHIO_READER_KEYS/MESHIO_WRITER_KEYS, MESHIO_READ_CANDIDATES, EXPORT_MENU_GROUPS, SUPPORTED_MESH_EXTENSIONS and the timeline lists. A key that the live artifact reports but nothing routes is listed as deliberately absent with a reason, never left unexamined.
  • Boundary and ABI: option structs or dtypes crossing the wasm boundary (14.0.0 grows ReadOptions with piece, 13 → 14). Re-check readMeshSelective/readMetadata options handling, integer array types (BigInt64Array), and the *_components maps.
  • Behavior changes that alter what the extension already relies on: 13.0.0 changes fallback semantics in the Python shims and pins C++ streams to the classic locale; whether any of that reaches the WASM build is to be measured, not assumed.
  • Fixes that retire a workaround: upstream fixes (for example 12.1.0's MED and Gmsh higher-order node-ordering permutations, VTU polyhedron mixed-node-count cell_data) may make a local compensation redundant or wrong. Remove the workaround and its stale note in the same change.
  • Timeline admission: re-test the readers still outside IN_FILE_TIMELINE_EXTENSIONS (Gmsh, UNV, the solver-result readers, and anything new) against the admission rule, which follows capability rather than reader options: an in-file timeline needs distinct time values that readMetadata can enumerate without a full read (fellBackToFullRead: false). A reader that merely accepts a timeStep option stays static, as ansysinp shows. Admit a reader only with a committed fixture that proves distinct step selection, and record each decision in src/test/fixtures/transient/README.md.

Then, per bump: update package.json/package-lock.json, confirm both dist/meshio/ variants (sequential and _mt) still load and that locateFile stays name-aware, re-run the transient audit and the per-format options-awareness pins so a changed capability fails a test rather than a user, refresh mesh_capabilities expectations, and record the .vsix size change. Update the "Research baseline" date and the pinned version in the introduction, and add a CHANGELOG.md entry naming the version and the notable capability changes.

KKSS embedding follow-up (S per upgrade):

  • Evidence: KKSS copies this extension's dist/meshio tree beside its application and MCP bundles. In the 2026-09-25 audit, mesh locked 16.14.0 and CAD independently locked 16.7.0; KKSS's bundled mesh/CAD MCP and MMG worker checks passed with the shared tree. Those exercised paths establish a tested baseline, not compatibility with every API or future version.
  • First useful increment: mesh owns a documented staged-runtime contract and repeatable artifact checks for MCP, meshio++ loaders/WASM variants, and the MMG worker/WASM pair. KKSS owns copying those artifacts into its layout and testing its shared CAD/mesh runtime on every dependency update. Keep runtime separation an explicit response to a demonstrated conflict.
  • Verification: load and execute from staged artifacts with development package resolution unavailable; check companion lookup, binary/version identity, and actionable missing-artifact errors. Repeat both engines' real operations in KKSS after either lockfile changes; the MCP half of the workflow matrix is delivered (former item 7: npm run check:staged) and its provider and Electron half is tracked in item 2.
  • Done when: an upgrade cannot pass packaging checks with a missing worker/kernel or an incompatible shared runtime, and the checked versions and layout are recorded with the result.

Automate the detection so it does not depend on remembering: shipped as .github/workflows/meshio-watch.yml, a scheduled job comparing the declared range with npm view @meshioplusplus/wasm version and reporting when the latest release is outside it, since that is the one case Dependabot's grouping misses.

Acceptance: the pinned version equals the latest published release (or a documented reason for staying behind is recorded here), the full test suite passes against it, every reader/writer key the live artifact reports is either routed or listed as deliberately absent, and no roadmap item cites a superseded version as its baseline. MCP: no new tool by itself; any newly routed format or operation must appear through mesh_capabilities and the existing info/convert tools in the same change.

Tier 1 — Full integration and a unified user experience ​

Admission criterion: work that enables multiple subsequent features or improves the everyday workflow across the extension.

1. Improve the UI and unify it with CAD-Preview — L ​

Pending — explicit cross-extension priority. Audit both extensions against the existing shared UI design system and converge on a common interaction vocabulary and component set. Align File/View/Advanced menus, toolbar hierarchy, navigation controls, orientation cube, layer-tree actions, selection feedback, form layouts, icons, spacing, typography, progress/cancel states, and empty/error states. Update the design-system document's stale “pending” descriptions against what actually ships before using it as the implementation checklist.

Reduce the density of mesh-operation forms with searchable actions, clear categories, progressive disclosure of advanced settings, and consistent inline validation. Establish predictable panel docking and overflow at narrow widths. Include keyboard navigation, visible focus, accessible names, high-contrast themes, and reduced-motion behavior. Share tokens and reusable components through a versioned source or synchronized copies with a drift check; keep Kratos-specific problem setup distinct within the common shell.

KKSS integration increment — panel docking and overflow (M):

  • Evidence: the Mesh 4.6.0 Electron acceptance run reproduced a VTK probe chart overlapping Selection's Box control and intercepting clicks. The affected layout belongs to the extension webview; KKSS supplies the surrounding viewport.
  • First useful increment: mesh owns predictable placement and scrolling for simultaneous Inspect, Probe and Selection panels. Preserve active form values and focus when panels resize or move; coordinate available viewport bounds with the embedding host rather than relying on a VS Code editor's dimensions.
  • Verification: in the extension and KKSS, open a probe chart and Selection together at the default and narrow viewport sizes, resize, traverse controls by keyboard, and perform selection and probe export without forced clicks or closing another panel. Include high-contrast themes and visible focus.
  • Done when: all visible panel actions remain reachable by pointer and keyboard, no chart intercepts another panel's controls, and resizing preserves the user's draft input. KKSS retains the embedded interaction regression; mesh owns the layout fix and extension coverage.

Panel docking delivered (2026-10-20): the bottom-left cluster is now laid out by one authority. The Selection card and the two wide strips (the Probe chart and the Plot-over-time chart) used to be positioned independently — left/right for the strips, left/width for the card — and each was bottom-anchored to a different line, so a chart painted over the card's lower half and swallowed its clicks. Measured in the browser before the fix at 1680x1000, the Probe strip covered Selection's Delete entities and Clear buttons, the whole seed builder and its Add set button; the two strips overlapped each other the same way. src/parser/panelLayout.ts (pure, Node-tested) now decides the geometry: the card keeps the left edge, the strips sit beside it, they move below it when the viewport is too narrow for both, the panels in a column stack upwards, and the cluster is bounded so a panel that does not fit is shortened and scrolls instead of covering the canvas. webview/panelDock.ts applies it, and it only moves the panels — it never re-renders their contents, so a form keeps the text the user typed into it. Two supporting fixes came out of the measurement: the card's action row now wraps (its five buttons were wider than the 300px card, and the last two hung over the edge where the strip beside them could paint over them), and the Probe strip's stale inline bottom is cleared, since an inline value beats the stylesheet's var(--panel-bottom). Tests: src/test/panelLayout.test.ts (nine cases: beside, stacked, closed, strip-versus-strip, a tall card, five viewport sizes, the floor, the minimum strip width, a narrow viewport) and the Chromium regression scripts/screenshots/check-panel-dock.mjs, which fails on the old code with the exact overlap and passes now at 1680x1000, 1280x700 and 1000x650, and checks that a draft survives a resize. Still pending in this item: the rest of the cross-extension convergence (menus, toolbar hierarchy, icons, the shared vocabulary) and the KKSS-side embedded interaction regression, which KKSS owns.

Command-line increment delivered (2026-10-14): the bottom command row CAD-Preview has now exists here (#commandline, src/parser/commandSpec.ts/commandParse.ts, webview/commandLine.ts): every mesh operation and UI action is typeable (translate dx=1, view top, export vtu, a ; b sequences) with Tab completion, history, usage hints and host-worded rejection reasons (commandResult). Guide: doc/guide/command-line.md. Path arguments for open/export/exportPart/saveProblem delivered (to=/overwrite=, not driven in VS Code). Real VS Code 1.143.1 (code-server, 2026-10-27, scripts/vscode-host/commands.mjs/autosave.mjs): an applied edit marks the custom-editor tab dirty, Kratos MDPA: Save Mesh shows the one-time overwrite modal and then writes the edited mesh and clears the dirty marker, files.autoSave: afterDelay neither prompts nor writes (tab stays dirty, file untouched), and the palette finds the Kratos commands; the Kratos activity-bar entry is present. Plain Ctrl+S, hot exit and the command-line row typed inside VS Code were not driven. Pending: desktop-GUI and Remote-SSH coverage.

Acceptance: compare the same open → inspect → clip → edit → export workflow in both extensions, with visual and interaction checks in dark, light, and high-contrast themes and at small viewport sizes. MCP: UI-only exemption; any new underlying operation discovered during this work still needs parity.

2. Run expensive meshio++ work in cancellable workers — L ​

Pending. Delivered (2026-10-11 to 2026-10-25; detail in CLAUDE.md, the src/meshAnalysis.ts bullet and the worker bullets): every wasm-backed or long synchronous analysis (probe, watertight, integrate, lod, qualityGate, hausdorff, periodicNodes, featureEdges, flowBalance) runs in dist/analysisWorker.js with one superseding runner per kind (a newer request, panel close or document disposal terminates the worker and its reply is never posted; replies echo seq); streamlines keep their own worker and post the partial result on Cancel; the multi-file series scans (Plot over time, Flow balance All steps) run in dist/seriesScanWorker.js with cooperative cancel; oracle/adopting ops, derived meshes and partitions, meshio++ reads and writes each have their own worker; MCP mesh_transform/mesh_batch_transform/mesh_derive take a cancellable execution. npm run bench:heartbeat (Node-level) measures event-loop stall in-process versus worker, for example slice 462 ms to 10 ms, KaHIP partition 1,056 ms to 9 ms, read .msh 176.6 ms to 6.4 ms. Real-host check (2026-10-27, code-server 4.143.1 / VS Code 1.143.1, packaged .vsix, scripts/vscode-host/responsiveness.mjs) on a 64,000-hexahedron .mdpa: a cheap host round trip stayed at 12–36 ms (idle 14–29 ms) while a 1,001-sample probe and a quality gate ran; a superseded probe's reply never arrived and a cancelled quality gate posted no result. Open finding: a 100,000-sample probe (the panel's own maximum) stalled the host for about 1.6–2.1 s once, when its result was delivered — the sample cap or the reply size still needs bounding. Still pending (including the remainder of former item 7, large-mesh rendering and regression coverage, delivered and removed 2026-10-27): latency and memory budgets for very large meshes in Remote-SSH and desktop-GUI sessions (the Node-level npm run bench:large-mesh and the 2026-10-27 real-host load of a 1,000,000-hexahedron .mdpa, about 35 s with all fields staged as metadata, are the baselines; timeline frame-cache and LOD auto-on were not driven in the host); a true level-of-detail surface sent before the full model (a new host path, not verifiable without a real host); the staged-artifact checks for the MDPA and VTK providers and KKSS's Electron assertions against the same fixtures (the MCP half is npm run check:staged); a maintained packaged-extension harness covering save/revert/hot-exit, timelines, cancellation and sidebar/palette parity; runPipeline batching; Remote-SSH, desktop-GUI and KKSS measurements; transferable buffers where ownership permits; end-to-end memory including filesystem staging and webview delivery.

Evaluate runPipeline for compatible batches to avoid repeated JS/WASM copies. Preserve the operation queue's per-step history and partial-completion semantics; a faster backend must still return enough results or checkpoints to honor undo and cancellation.

KKSS integration increment — execution and reply lifecycle (L):

  • Evidence: KKSS embeds the MDPA/VTK providers in Electron's main process. Their meshAnalysis handlers await probeAlongPath, but its WASM interpolation is synchronous; await does not move that computation off the host thread. In src/meshAnalysis.ts, successful probe replies echo seq, while validation/error replies omit it; applyProbeResult in webview/main.ts only rejects a mismatching sequence when one is present. Successful stale-reply rejection was exercised in KKSS; delayed failures and cancellation still need coverage.
  • First useful increment: mesh owns a worker execution boundary for expensive analyses, starting with line probing and reusing the MMG worker pattern. Carry request, document and frame identity through progress, success and failure responses. Cancel superseded requests and work owned by a closed document, release worker/WASM resources, and make late replies harmless. Preserve sequential edit-history and partial-completion semantics as additional operations migrate.
  • Verification: use a large probe fixture while measuring host heartbeat latency and exercising navigation/cancel in VS Code and KKSS. Inject delayed successes and failures after a newer frame/request, panel close/reopen and document disposal. Check worker termination, resource release, unchanged geometry/history after cancellation, and numerical agreement with the existing synchronous core.
  • Done when: the host continues servicing UI events during computation, cancellation stops owned work, and no stale success or error can replace current state. Mesh provides the execution/cancellation contract to both providers and MCP; KKSS validates it in its main-process embedding.

Acceptance: large reads and edits leave the extension host responsive, cancellation releases resources, and batching agrees with sequential execution. MCP: use the shared execution layer where applicable and expose progress/cancellation through the MCP request lifecycle without writing logs to the stdio transport.

Tier 2 — Extension workflows and maintainability ​

Admission criterion: useful extension-level capabilities that build on the integrated kernel and existing document/run infrastructure.

10. Curved high-order and native polyhedral fidelity — L ​

Pending — curved display increment delivered 2026-10-25. src/parser/render/curvedDisplay.ts (pure) subdivides quadratic edges, triangles (6 nodes), quads (8/9) and the boundary faces of tet10/hex20/hex27/wedge15/pyramid13 with their own shape functions; each generated point is a weighted mix of the cell's nodes, so nodal scalars interpolate exactly, picks keep the owning entity id and neighbouring cells weld by (id, weight) key. buildDisplayGeometry(..., {curved:{level}}) is off by default; View ▾ ▸ Curved quadratic cells turns it on (4 segments per edge). Display only: stored cells and writers are untouched. Unit-tested (curvedDisplay.test.ts); not browser-verified, detail 2/4/8 segments in the Lighting panel and remembered in the view sidecar (view.curvedLevel); the VTK node-numbering tables are checked by tessellating straight-sided cells (curvedOrdering.test.ts), while the per-reader audit was delivered 2026-10-25 (highOrderOrdering.test.ts: straight-sided tet10, hex20, wedge15, pyramid13, tri6 and quad8 written through VTU, Gmsh, MED, Abaqus, Exodus, CGNS, Nastran and UNV and read back with real meshio++ 17.13.0; every mid node stays on its VTK-ordered edge, and the only gap is the Abaqus writer, which rejects pyramid13). Second increment delivered 2026-10-27. (1) Explicit tessellation export: meshio++'s tessellate is bound in the WASM build (the earlier note that it was Python-only was stale), so mesh_derive gained kind: "tessellate" (levels 0–5) and Advanced ▸ Tessellate curved cells… writes a LINEAR copy of the quadratic cells while the open mesh keeps its curved ones; each child carries SOURCE_ENTITY_ID (Elemental over the Elements, Conditional over the Conditions). It shares tessellateModel with the in-place op, which now also returns its parent→child table. Tests pin that two adjacent quadratic tetrahedra weld into a watertight display surface and that curvedDisplay.ts is imported by nothing but displayGeometry.ts, so display subdivision cannot reach a writer. (2) Polyhedral side channel: a polyhedron read from a .vtu (VTK_POLYHEDRON) or a meshio++ polyhedron block is still decomposed into tetrahedra for display and editing, but MdpaModel.polyhedra now keeps its faces, the Element ids of its tetrahedra and the nodes the decomposition invented (polyhedraTopology.ts, captured in vtkXmlParser.ts and meshioConvert.ts). The native .vtu writer folds the children back into one VTK_POLYHEDRON cell and drops the invented nodes (writers/polyhedralLayout.ts); Inspect names the polyhedron a picked tetrahedron came from; mesh_info reports counts. Any edit that can change a node or cell id drops the channel (applyOpAsync; only unscoped translate/scale/rotate and field-only operations keep it). A skipped polyhedron no longer shifts the cell data after it in the VTU reader. Remaining scope: polyhedral output through meshio++ writers (OpenFOAM, CGNS NFACE_n, MED POE, EnSight nfaced), which needs a polyhedron path in modelToMeshio, the cell order volumeCellIdsInWriteOrder assumes and the export-fidelity matrix; the OpenFOAM reader's cell reordering (volume types first, polyhedron buckets after) which needs a per-cell source index from upstream before nonuniform fields on mixed meshes can be transferred; field transfer back from tetrahedra to a polyhedron when a child was edited (today the representative child's value is written); the interleaving of polyhedra with ordinary cells on .vtu export (they are emitted in place of the first child, but the original file order is not kept); and a browser-verified Inspect line.

Later increment — polyhedral subdivision and agglomeration: meshio++'s subdivide and agglomerate produce polyhedral cells. Today they are deliberately not offered, because the extension decomposes polyhedra into tetrahedra on read and no topology-conversion workflow would be completed. Evaluate them as adopting operations only after native polyhedral rendering and export land in this item.

Acceptance: curved shared faces remain watertight, high-order node ordering is checked per format, and display subdivision never silently replaces original cells on save. MCP: faithful read/write and explicit tessellation/export capabilities (mesh_derive tessellate, mesh_info polyhedra); viewport tessellation alone is UI-only.

11. Optional Python companion for datasets and surrogate results — L ​

Pending. Explore an explicitly configured external Python companion for meshio++ capabilities outside the WASM package: point budgets, proximity graphs, PMSH (.pmsh), Zarr/CAE dataset export, USD time-series export, and PhysicsNeMo inference. Start with exporting existing runs and loading predicted fields with model metadata and comparison metrics; training orchestration is a later scope decision. Do not assume the problemtype Pyodide runtime can host native Python, Torch, or CUDA dependencies.

Increment — edge-cut-quality partitioning: delivered 2026-10-21 without a companion. meshio++ 17.9.0 links KaHIP into the full WebAssembly artifact (hasKahip()), so method: "kahip" now runs for partition, Export partitions… and mesh_split through loadMeshioFull (loaded lazily, only for that call); mesh_capabilities.partitioning.backends names the artifact per method. Measured on 17.10.0: 40–47% fewer cut edges than the curve on a 40×40 triangle grid at 3 and 7 parts, equal at 4 parts of a square, imbalance up to ~2.4%. The default stays the curve and auto is resolved here to KaHIP when the full artifact is installed (upstream's auto is KaHIP on the full build although its typings say otherwise). A Python companion is no longer needed for this; the rest of this item is unchanged.

Acceptance: discover companion capabilities and versions, report missing dependencies clearly, stream large datasets, and label predictions separately from solver output. Python-only and optional-backend features are never advertised as bundled WASM capabilities: mesh_capabilities names the backend that provides each one, and a feature is unavailable, with a reason, when that backend is absent. Track the binding or runtime each feature needs so it stays eligible for later integration. MCP: mirror every enabled dataset/export/inference capability through the same optional backend.

12. VTK-wasm as an experimental alternative renderer — decided ​

Decided (2026-09-26): vtk.js stays the renderer; VTK-wasm ships as an experimental, opt-in alternative and will not replace it. The item began as "replace vtk.js with VTK-wasm" and was worked as a gated migration (the migration record holds every gate, measurement and evidence file). Phases 0–4 passed: the three blockers of the 2026-09-18 spike were resolved for the pinned VTK 9.7.20260920 build (they were largely loading artifacts, plus a deterministic glue rewrite that removes Embind's dynamic code), the webview reaches its renderer only through a backend boundary (pixel-identical on vtk.js), the runtime ships verified in the .vsix with its licences (+13 MB), and the packaged extension boots VTK-wasm in real VS Code 1.138/1.139 under a CSP that adds only 'wasm-unsafe-eval' and a scoped connect-src, falling back to vtk.js with an explanation when it cannot start. Measured on a software rasterizer, VTK-wasm meets every proposed performance and memory budget. The remaining differences are rendering style rather than defects — thin-line rasterization on dense edge overlays, VTK's own annotation actors, and a coplanar translucent wireframe the order-independent pass drops — and none justifies switching a working default. So the default switch and the vtk.js removal are not pursued, and hosts without JSPI keep vtk.js by construction.

What remains supported: kratos.preview.renderer: "vtkwasm" (tagged experimental) selects the backend for newly opened previews; the boundary (webview/render/backend.ts), the asset pipeline (npm run vtkwasm:prepare, verified at package time), the upstream watch workflow and the parity/smoke tooling (scripts/render-parity/, scripts/vtk-wasm/g4-*.mjs) stay, so the alternative keeps working and a future re-evaluation starts from measurements rather than from scratch. Revisit only with a concrete reason vtk.js cannot serve — a feature it lacks, or a real-GPU result (the checklist is in the migration record) showing VTK-wasm materially better. Rendering remains a separate runtime concern from the meshio++ kernel, and several limits belong to the webview runtime rather than to either backend: translucency under a software WebGL rasterizer, recording from a drawing buffer that is not preserved, browser codec availability, and the webview CSP. A kernel upgrade does not fix them, and neither does a backend swap. Address them where they surface (item 2 for large-mesh rendering budgets, the recorder for capture) rather than as a reason to switch renderers.

MCP: none. The renderer is UI-only; the headless numerical tools are renderer-independent and keep their contracts.

15. Future multilanguage support — L ​

Pending, future. Localise the human-facing text of the extension: the VS Code manifest (command titles, menus and settings descriptions), the webview chrome and panels, host messages and diagnostics, and the documentation site. No locale is committed and no translation work is scheduled. Start after item 1 settles the shared vocabulary, so translators do not translate labels that item 1 is still renaming.

Current boundary: user-facing strings are English literals in src/webviewChrome.ts, the webview/ modules, the host modules and package.json, with no catalogue and no fallback path. Locale-sensitive formatting has already broken a test: a grid estimate formatted with the host locale printed 1.000.000.000 under a Spanish locale, so its counts are now pinned to en-US, and localisation must not reopen that failure in exported data.

Decisions first: machine-facing contracts stay English and identical in every locale. That covers MCP tool, parameter and result names, enum values and diagnostic codes; recipe, sidecar, manifest and problem-archive formats; .mdpa keywords; and CSV and XLSX headers and values. Agents and scripts depend on one vocabulary, so MCP descriptions and messages stay English unless a separate decision changes that. Display numbers may follow the display language, but written files never do: decimal points in exported data stay ..

First useful increment (manifest half delivered): package.nls.json now holds every manifest string and manifestNls.test.ts guards it; the webview-chrome half is delivered 2026-10-23 as src/parser/i18n/messages.ts: localizeHtml rewrites the finished chrome markup (text nodes and title/aria-label/placeholder/alt) through a catalogue keyed by the English text, per-key English fallback, a bracketed/lengthened qps-ploc pseudo-locale, locale from vscode.env.language as data-locale (webviewChrome.test: webviewMessages.test.ts); scripts/screenshots/check-pseudo-locale.mjs is written but not yet run (no Playwright here). Runtime strings, first increment delivered 2026-10-25: src/parser/i18n/runtime.ts (setRuntimeLocale, t(english, vars?): per-key English fallback, {name} placeholders filled after translation, pseudo-locale aware) is booted from body[data-locale] in webview/main.ts and adopted by the Quality, Inspect and Field panels' static literals; runtimeI18n.test.ts and webviewMessages.test.ts fail on an unused catalogue key or a placeholder mismatch. Not browser-verified. Second increment delivered 2026-10-25: every webview/*.ts panel wraps its static literals in t() (a test refuses a template literal inside t()), a Spanish seed catalogue grows, and doc/guide/languages.md states the docs' translation status. Translations delivered 2026-10-25: Spanish, French, German and Brazilian Portuguese tables (src/parser/i18n/locales/, about 640 strings each: chrome, panels, interpolated messages with {name} placeholders) and package.nls.{es,fr,de,pt-br}.json for command titles and view names; a test requires every language to translate the same strings. Host-side notifications, first increment (2026-10-26): the extension host now boots the same t() from vscode.env.language at activation (extension.ts) and monitorSave.ts is the first host module using it (13 strings, four languages); the two catalogue tests scan it. The other ~200 show*Message call sites are still English. Not translated: long explanatory tooltips, the remaining host-side notifications, the configuration descriptions, and documentation pages. Not reviewed by native speakers. Pending: the Chromium pseudo-locale check and further languages. Original scope: a catalogue for the manifest, using VS Code's own package.nls.json mechanism, and one for the webview chrome, loaded by both runtimes, with English as the fallback for each missing key. A test fails when a key is referenced but not defined, or defined but never used. A pseudo-locale build that lengthens and brackets every string must show no clipped control at the narrowest sidebar width (176 px).

Acceptance: VS Code's display language selects the catalogue, a missing translation falls back per key rather than per file, and the pseudo-locale check passes in dark, light and high-contrast themes. CSV, XLSX, recipes, sidecars and MCP output are byte-identical across locales for the same input. Each documentation page states its translation status and links to its English source, and the documentation build still fails on dead links. MCP: unchanged, because localisation is presentation; keeping MCP English is part of this item's acceptance rather than an omission.

16. Coupled problemtypes (FSIApplication, CoSimulationApplication) — M ​

Delivered except the solver run and per-solver output attribution (2026-10-15). What can be built and checked without a Kratos installation is in: the fsi builtin (partitioned Dirichlet-Neumann over FsiAnalysis, relaxation or quasi-Newton acceleration, ALE mesh solver, mapper settings per interface pair) and the cosim builtin (ProjectParametersCoSim.json plus ProjectParametersCFD.json / ProjectParametersCSM.json and MainKratosCoSim.py; gauss_seidel_weak or gauss_seidel_strong with num_coupling_iterations, a relative_norm_previous_residual criterion and an MVQN, IQN-ILS or Aitken accelerator per pair; average-value or linear predictors; nearest-element, nearest-neighbor or barycentric kratos_mapping; optional ALE, which turns the fluid participant into an ale_fluid solver with processes.ale_boundary_conditions; any number of interface pairs). Both compose the Fluid and Structural problemtypes' own hooks over the domains mechanism, and the CoSimulation shape follows the CoSimulationApplication's Mok benchmark at master (the structure pulls the fluid REACTION with swap_sign and pushes its DISPLACEMENT; the fluid's exchange lists stay empty so an accelerated displacement is not overwritten). Shared with every problemtype: declared couplingInterfaces are checked against the mesh before anything is written (one-to-one pairing by assignment order, line cells in 2D or face cells in 3D, spatial overlap), and required Kratos applications are worked out from the stage, the written documents and the declaration, recorded in kkss-preparation-v1.json, checked by every generated driver (exit code 3 naming what is missing) and probed by Run and MCP case_run before launch (case_validate takes checkInstall). Both drivers (driverTemplate: "fsi" / "cosim") record the coupling iterations of each step in kkss-convergence-v2.jsonl (adapters kkss.fsi-convergence / kkss.cosim-convergence, with per-iteration couplingResiduals; a weak coupling reports no criterion and so never reads as converged) and keep the cooperative stop. Python ports of both ship in example/problemtypes/coupled.py, parity-tested byte for byte including the participant documents. See doc/guide/coupled-simulations.md.

What remains:

  • The acceptance run. A small 2D wall-style FSI case, and the same case through CoSimulation (weak, then strong with ALE), run to vtk_output through the existing Run path where FSIApplication, CoSimulationApplication, MappingApplication and MeshMovingApplication are installed; its convergence records captured as fixtures (with item 17's captured runs). Every document key was read from Kratos master on 2026-10-15 and from its tests, not from a run, so the first run is a check of the generated files.
  • Per-solver output attribution. A coupled case writes one fluid and one structure vtk_output process into the same case folder; telling their outputs apart in an isolated run folder belongs to the isolated run workspaces delivered with former roadmap item 6 (delivered 2026-10-19).

Out of scope: CoSimIO and external (non-Kratos) solvers, MPI/Trilinos variants, embedded FSI, and the FSIApplication's IBQN and randomized-SVD accelerators, until a fixture proves them. MCP: problemtype_list/problemtype_describe, case_validate/case_generate (interface checks, extra documents, per-domain materials) and case_run/case_status/case_convergence share the builders, the refusal rules and the coupling records.

17. Solver-live monitors and solver-captured convergence fixtures — M ​

Pending — the unverified remainder of former item 8 (solver convergence history and saved monitors, delivered 2026-10-11 and removed without renumbering; see CHANGELOG.md and CLAUDE.md). What shipped is everything that can be checked without a Kratos installation: the convergence history read side, its plot and CSV, and saved post-run monitors (point at an entity, Nodal field at fixed coordinates, regional quantities) evaluated over the saved result frames, with a post-run stride, optional binding to a verified isolated run, authoring from the Inspect panel and from MCP. This item holds what was left out because it needs a real solver, and each part must be verified against one rather than hand-written:

  • Solver-live monitors: point and surface monitors written by the solver itself at their own interval, independent of the VTK output cadence, through a supported Kratos output process or a generated hook in the MainKratos.py template (the cooperative-stop sentinel contract already shows the pattern for a script that must cooperate). Record the adapter version, keep unsupported output unavailable, and read it back through the same case_monitors tables so live and post-run values are one contract. Never infer a live value from a log line.
  • A divergent and a truncated run captured from a real solver: src/test/fixtures/convergence/ is hand-written to the v2 record shape. Capture at least one converged, one divergent and one killed run from Kratos and replace or complement the hand-written files, so the verdict rules are checked against what a solver actually emits.
  • Adapters beyond the built-ins: the convergence writer is in the generated scripts of the five single-physics built-ins and of the FSI and CoSimulation drivers (coupling iterations, item 16); user-defined problemtypes and the other coupled ones (Buoyancy, Conjugate Heat Transfer) write nothing until they gain an adapter, and the coupling records are as hand-checked as the rest until item 16's acceptance run captures them. The Structural Optimization and ROM problemtypes (item 18) report through their own files instead — the optimizer's summary and the ROM driver's progress record — and gain no convergence adapter until their primal or full-order runs are checked against a solver.
  • Authoring monitors in the viewer (delivered 2026-10-26): the Inspect panel now saves entity monitors, coordinate monitors (At point, the node's coordinates) and regional monitors (Monitor region on a SubModelPart row; the host asks for field, quantity and, for moments, the reference point). The QuickPick/InputBox flow is not driven under VS Code. Host flow verified 2026-10-27 (scripts/vscode-host/monitors.mjs, code-server/VS Code 1.143.1): with two cases beside a result the "which case" QuickPick offers both, the name InputBox is prefilled, the sidecar is written for the chosen case only; region/coordinate variants share the same code but were not each driven.

Acceptance: a live monitor of a running case matches the same quantity evaluated from its saved frames at the frames both have; the captured fixtures reproduce the documented verdicts; and an unsupported adapter reports unavailable with the reason, never a value. MCP: case_monitors and case_convergence read the live tables through the same adapters; the server still never owns a run and reports orphaned/detached rather than inventing an exit code or a verdict.

18. Verify the ROM and Structural Optimization problemtypes against a Kratos run — M ​

Pending — the unverified remainder of the ROM and optimization problemtypes (delivered 2026-10-14 without a Kratos installation; see doc/guide/optimization.md, doc/guide/reduced-order-models.md and CLAUDE.md). What shipped generates the cases, reads their results and shows them: the optimization builtin over the OptimizationApplication (responses, material/thickness/shape/SIMP controls, constraints, six algorithms) with the Optimization progress panel, Kratos Runs rows, design fields and MCP case_optimization; the four rom* builtins over RomApplication's RomManager with sampling, parameter location by perturbation, the singular-value plot, the HROM selection set and MCP case_rom. Every document shape was read from Kratos master on 2026-10-14 and from its tests, not from a run. What remains needs a solver:

  • The cooperative stop routes. Optimization: the kkss_optimization_stop process registered under processes.kratos_processes raises at the next iteration's initialize step and the driver finalizes the analysis, so the summary closes; confirm the process factory imports a module given without kratos_module and that Finalize after the exception leaves a well-formed summary. ROM: the stop check in the customized KeepAdvancingSolutionLoop and the partial-database summary.
  • Design-field naming. The VTU output is given <model_part_full_name>_0 so the files land as <prefix>_<rank>_<step>.vtm and group into a timeline; confirm against real output, including where Optimization_Results/ is written.
  • SIMP and thickness projection keys, and the shape control's filter and damping settings, against a run of each control.
  • The ROM driver's document updates. The parameter leaves found by perturbation are written by UpdateProjectParameters and UpdateMaterialParametersFile; confirm the manager calls the latter before each analysis reads the materials, that the materials file is restored, and that RomParameters.json, the singular values and the HROM ids/weights are where case_rom reads them (the HROM ids were read from hrom_training_utility.py as Kratos entity ids, not 0-based indices).
  • Fixtures captured from a solver for src/test/fixtures/optimization/ (hand-written to the documented summary format today) and for a ROM rom_data/ folder.

Out of scope until asked: the shape control's mesh_moving_analysis, the ANN-enhanced ROM decoder (TensorFlow), the older ShapeOptimizationApplication, and optimization primals other than Structural Mechanics.

Acceptance: one optimization case per control type and one ROM case per physics run to completion through the existing Run path where the applications are installed; a Stop of each finalizes cleanly and reads stopped/partial; the progress panel, the Kratos Runs rows and case_optimization/case_rom report the captured files without change, or the readers are corrected with the captured files committed as fixtures. MCP: no new tool; the same readers serve both sides.

19. Plotting: unmet budgets and packaged acceptance — M–L ​

Pending — the remainder of former item 9 (scientific plotting, delivered and removed 2026-10-15 without renumbering; see CHANGELOG.md, the guide and the verification record). What shipped is everything checkable at the Node level; this item holds only what failed a budget or was never run in a packaged or remote session.

  • Node-level budgets (met 2026-10-16): npm run bench:plot now passes all four budgets on the 1M-row table: peak RSS 10.6x the file (was 23x), worker host stall 34 ms (was about 2 s). Table import streams each record into numbers as it is lexed and shares original with points until a transform replaces them; the worker sends large arrays in bounded chunks with a per-chunk acknowledgement (src/plotChunks.ts), because Node drains all queued messages of a port in one task. Progressive partial results use the same chunked, ordered delivery (no benchmark or large-partial test yet: a partial above one chunk needs thousands of history steps). Not yet measured: the same budgets inside VS Code, Remote-SSH and the webview.
  • VS Code, Remote-SSH and webview delivery (unverified 2026-10-21): reproducible end-to-end memory, latency, responsiveness and cancellation measurements for million-row tables and large mesh/history inputs in a real and a Remote-SSH-style session, including filesystem staging, worker/WASM lifetime, cache retention and webview delivery, coordinated with item 2 and former item 7. The packaged runs below use an isolated code-server profile as a host stand-in; they prove behaviour, not extension-host or remote-filesystem budgets. No real desktop VS Code, Remote-SSH or KKSS session was available, so those budgets remain unmeasured rather than accepted. 2026-10-27: the real-host runs that were possible here are recorded under items 2 and 7 (code-server, VS Code 1.143.1); million-row plot budgets inside the host were not run and remain unmeasured, as do Remote-SSH and KKSS.
  • Packaged-VSIX checks (delivered 2026-10-21): the installed-VSIX harnesses now cover ownership resolution (plot_run_resolve: verified, unverified with no receipt, unverified through a latest-run sidecar, unresolved outside a recorded result, changed companion, and changed or missing PVD→PVTU pieces), the Field panel's Plot distribution (20 bins over the fixture's four nodal values), Plots Show on mesh, the Presets selector (volumeTotal over the elemental field; sectionComparison over Wall/Inlet), the exterior-boundary convention (whole mesh, boundaryIntegral, current scope: 4 owned faces, integral 12*(1.5+√3/2) = 28.39230484541326, diagnostics say no Conditions are read) and the opt-in persistent cache (a refresh reports the cache hit). A million-row table is cancelled with no chart data and no complete count; an in-flight title edit publishes only the superseding title; closing the plot during collection publishes nothing. The run harness also found and fixed a real race: .foam's {*,*/*} watcher treated the extension's own *.kratosview.json sidecar as a mesh change, and its debounced reload could supersede an in-flight Show in mesh; view sidecars are now excluded from rediscovery. Detailed fixtures, commands, thresholds and observed values are in the verification record, where the Node suite for the 2026-10-21 tree is recorded as 2,965 tests across 262 files with 0 failures; the receipt fixtures are synthetic contracts, not Kratos solves.
  • Caches: caching for run-bound and table sources (deliberately uncached today), a cheaper validity check than the stat shortcut for companions, resampling, and large-data measurements. Retained fixed snapshots used by profile following must never silently change or reread disk.
  • Legacy-evidence import: a user-supplied evidence file format for importing legacy runs, coordinated with the run-ownership work delivered with former roadmap item 6 (delivered 2026-10-19). Never infer past ownership by hashing current bytes or matching paths, frame indices or entity IDs.
  • Native polyhedral faces for exterior-boundary extraction, with item 10.

Acceptance: the failing budgets pass and the local and Remote-SSH-style measurements are published with fixtures, commands, thresholds and results; the packaged checks pass without regressing existing plotting behaviour; legacy-run import distinguishes verified ownership from unverified or unresolved data. MCP: extend the existing plotting/run tools through the same host implementation; presentation-only changes remain exempt.

23. Hexahedral octree meshes with hanging-node constraints, and MPC support — remainder ​

Delivered 2026-10-27 (first increments). src/parser/hangingNodes.ts recovers the hanging nodes of any hexahedral or quadrilateral mesh from the geometry (the kernel's octreeMesh reports only a count, numHanging) and ties each to the end masters of the coarse edge (0.5/0.5, or the exact barycentric mix) or face (bilinear) it sits on, resolving chains to end masters and refusing a cycle; addHangingNodeConstraints (sync op, sidebar Tie hanging nodes, command line, MCP mesh_transform) writes one LinearMasterSlaveConstraint per node and variable (default DISPLACEMENT_X/Y/Z) into a HangingNodes SubModelPart that a re-run replaces; createConstraint/deleteConstraint (constraintOps.ts) edit single constraints; mesh_derive octreeMesh takes hangingConstraints. Verified analytically (a linear field is reproduced exactly at every hanging node, including on a real castellated sphere where the geometric count equals the kernel's numHanging) and through write, re-parse, renumber and orphan removal (hangingNodes.test.ts, mcpKernelMeshes.test.ts).

Also delivered the same day: View ▸ Constraint links (constraintLinks.ts, one overlay layer), Inspect's constraint lines for the picked node, the Data table Constraints kind (also mesh_export_table), the preflight findings constraint-unused-node and constraint-slave-fixed, and tests for crop, merge and undo/redo of generated constraints. Verified 2026-10-27 in Chromium (scripts/screenshots/check-constraint-view.mjs, fixture src/test/fixtures/mdpa/hanging_two_level.mdpa): the Constraint links overlay (36 segments), the Data table Constraints kind (15 rows, row selection) and Inspect's constraint lines. Inspecting a constrained node now also highlights the nodes it is tied to (inspect:constraint). Pending. (1) VS Code-hosted behaviour (no integration harness) and picking a constraint by clicking its link line in the overlay. (2) A Kratos structural run on a constrained octree result (needs the solver; verify with item 18's setup). MCP: the ops and the octreeMesh option are mirrored; the overlay and panels are UI-only.

Tier 3 — Case authoring and result evaluation ​

Admission criterion: closes a gap between a generated case and how its results are read, found in the ElmerStudio review (2026-10-31) or recovered from a standing exclusion that the current kernel no longer justifies. Each item names its MCP half; UI-only work says so.

24. Symmetry and axisymmetry result expansion — M ​

First increment delivered 2026-11-04: Mirror (parser/render/mirrorDisplay.ts pure mirrorExpand, View ▾ ▸ Mirror view, command mirror, view.mirror in the sidecar; the webview keeps the host model as hostModel and draws an expanded copy, so Clip/Slice/Inspect/fields/timeline act on it; copies are re-wound for linear and (2026-11-05) quadratic cell types, the mid-edge and face-centre nodes following their edge or face (tests check each mid node against its re-wound corners); vectors and 3x3 tensors reflect, other widths are copied and reported; SubModelParts gain <name>_mirror copies (2026-11-05: node ids keep on-plane ids and offset the rest, entity ids offset, children re-pathed, constraint ids not copied); a pick on a copy toasts the source id; tests mirrorDisplay.test.ts, viewState.test.ts, Chromium check-mirror-view.mjs, not VS Code verified). Revolve delivered 2026-11-04 (parser/render/revolveDisplay.ts revolveExpand, View ▾ ▸ Revolve view, command revolve, view.revolve; planar xy meshes only, axis x or y, tri→wedge, quad→hex with axis-touching cells degenerating to wedge/tetra/pyramid, vectors/tensors rotated, line/quadratic cells skipped with a diagnostic; tests revolveDisplay.test.ts, viewState.test.ts, Chromium check-revolve-view.mjs, not VS Code verified). Sector repeat delivered 2026-11-04 (parser/render/sectorDisplay.ts sectorExpand, View ▾ ▸ Sector repeat, command sector axis= copies= angle=, view.sector; rotated copies, axis nodes shared, seam nodes not welded, vectors/tensors rotated; exclusive with the other symmetry views; tests sectorDisplay.test.ts, Chromium check-sector-view.mjs). Delivered 2026-11-05: a plane picker (eight extra View ▾ rows for the y and z planes and the x-max and centre planes, parser/render/mirrorPlanes.ts; the centre resolves to a number so view.mirror is unchanged). Original text: Kratos models are routinely solved on a half, a quarter, a sector or a 2D axisymmetric section, and the result is then read as if it were the whole part. Add view-only result datasets: Mirror about a plane (with a per-component sign rule for vectors — the normal component flips — and tensors), Revolve of a 2D axisymmetric result about an axis (start angle, sweep, number of layers; nodal fields rotated as vectors in the r-z plane) and Sector repeat (n copies about an axis). They follow the timeline and the field picker, picks resolve to the source entity id, and Clip/Slice/Inspect act on the expanded view. This is a display path like the curved-cell subdivision (item 10): nothing is written, the history is untouched, and mesh_derive's mirror/revolve (17.23.0, kernel ops that write a new static mesh) stay the export route.

Acceptance: a quarter-plate result mirrored twice matches a full-plate solve of the same case at shared nodes (displacement sign rule checked component by component); a revolved axisymmetric pipe shows hoop-constant fields and a pick on any copy names the source node; turning the view off restores the original scene and nothing is written. MCP: none for the view; mesh_derive remains the headless expansion.

25. Evaluation table and per-run quantities — S–M ​

Per-run half (2) delivered 2026-11-01 as MCP case_run_quantity (problemtype/runQuantity.ts: a monitor definition evaluated once per terminal isolated run through readRunEvidence + evaluateMonitors, varied case settings as columns, CSV; the case_compare argument form was delivered 2026-11-04 and the viewer table 2026-11-06 as the Kratos Runs command Quantity Across Runs…: runQuantityReport.ts renders the Markdown, runQuantityPlot.ts supplies the optional plot; the tree and QuickPick flow is not driven under VS Code). Inspect Evaluation table delivered 2026-11-03 (parser/evaluationTable.ts pure rows/CSV/TSV, webview/inspectPanel.ts; each pick is a frame-labelled snapshot in the field's display unit, capped at 500 rows, Copy / Export CSV via menuExportAnalysis / Clear; UI-only; scripts/screenshots/check-evaluation-table.mjs written but not run, its timeline-scrub step only toggles the panel; locale entries for its labels not added; the frame label is the step label, not a unit-bearing time). case_compare quantity argument delivered 2026-11-04 (quantity takes the same monitor JSON as case_run_quantity and adds a quantity table with one row per side; test in runQuantity.test.ts). Still pending: nothing of the original scope; only VS Code-hosted verification of the new command. Original text: (1) An Evaluation table in the Inspect panel: each pick (node or cell, or a coordinate probe point) appends one row with its coordinates and every displayed field in its display unit (fieldDimensions.ts), with copy and CSV export through the existing exportSeriesCsv path; rows keep their frame label so a timeline scrub does not silently change them. (2) A per-run quantity view: one saved regional quantity (the region.ts reductions monitors already use) evaluated once per isolated run of the same case, one row per run with its recorded case parameters as columns, built on runCompare.ts's run discovery rather than a second one.

Acceptance: two picks on a two-frame result produce two rows that do not change when the frame changes; the CSV matches mesh_probe at the same points; a three-run sweep produces one row per run whose parameter columns match the runs' recorded settings. MCP: the per-run view as an extension of case_compare (a quantity argument) or a new read tool; the evaluation table itself is UI-only (mesh_probe is its headless equivalent).

26. Saved monitors follow a running case — S ​

Delivered 2026-11-03. RunManager refreshes a live run's saved monitors from the frames written so far: the existing vtk_output/ watcher feeds a pure MonitorRefreshThrottle (problemtype/monitorRefresh.ts, at most one evaluateMonitors per 2 s with one trailing run, nothing after stop or exit), evaluateMonitors leaves out the newest possibly-truncated frame while the run is not finished, the result is kept in RunProgress.monitors, and the Kratos Runs row says "evaluated from N written frames" until the run ends (a final evaluation then replaces it; stop/exit/dispose cancel the refresh). MCP case_monitors already reported partial. Tests: monitorRefresh.test.ts (throttle with a fake clock, row wording) and the live-growth case in monitors.test.ts (frames added one by one under a running sidecar; the final table equals the post-run one). The tree row and the RunManager glue are compile-checked only (no VS Code harness). Not done: a lone first frame is kept while live (nothing else to show), so the first row can still be an in-progress frame.

27. Units and case parameters in problemtype forms — M ​

Core delivered 2026-11-01 (problemtype/caseExpressions.ts): unit-suffixed values (10[mm], 20[degC], 2[bar]) and a case parameters table (name = expression, kept in the case file) resolve to plain numbers in the field's declared unit inside generateCase; a quantity mismatch is a unit-mismatch warning (so case_validate/preflight report it as a generate warning) and the number is kept unconverted; a bad expression refuses the generation by name. The expression is stored in the field value itself, so a reader unaware of expressions sees a string rather than a number. Delivered 2026-11-04: vector3 components resolve per component (["F * 2", 0, "1[m]"], a bad component names its axis) and preflight reports a mismatch as its own unit-mismatch finding (caseExpressions.test.ts). Sidebar delivered the same day: numeric fields and vector components are text inputs that keep a plain number as a number and anything else as expression text (parseNumericText), and a Parameters card edits the name = expression table with a live value preview (previewParameters; Chromium check-case-expressions.mjs; the number spinner is gone, a recorded cost). problemtype_describe fieldUnits delivered (declared unit, bracket in the label or a known Kratos variable). Nothing of the original scope is pending except a documented sidebar polish. Original text: Fields already carry a unit and material presets already convert into it. Accept unit-suffixed input (10[mm], 20[degC], 2[bar]) in numeric fields, converted to the field's declared unit on commit, and a case-level Parameters table (name = expression) whose names may be used in any numeric field and in boundary-condition values. A dimension mismatch is reported as a preflight warning and is never silently converted (the material catalog's "exact match or refusal" rule applies to presets; typed values warn, as in ElmerStudio). The case file keeps both the expression and the resolved number so a reader that does not understand expressions still sees a value; recipes and generated documents only ever contain resolved numbers. Locale rule from item 15: a bracket never contains a decimal comma, and exported numbers keep ..

Acceptance: 20[degC] in a Kelvin field generates 293.15; L = 2[m] used by two fields regenerates both when L changes; 5[s] in a length field produces a unit-mismatch preflight warning and case_validate reports the same; a case file written before this item opens unchanged. MCP: case_generate/case_validate accept and report the same expressions; problemtype_describe lists the declared unit per field.

29. Restart and initial state from another run — M ​

Pending — added 2026-10-31 (ElmerStudio review); needs a solver to verify. Let a case start from a previous run's state: the generators of the structural, fluid and convection-diffusion problemtypes write a restart output (Kratos save_restart_process / RestartUtility) when asked, and a new case can name a previous run's restart as its initial state. Refuse with a reason when the meshes differ (node count or coordinates), when the previous run is not terminal, or when the problemtype has no adapter. This is the upstream half of KKSS's sweep warm start (a stationary sweep row starting from the previous row) and of continuing a stopped run.

Acceptance: a structural case continued from a stopped run reaches the same final displacement as an uninterrupted run within the solver tolerance; a mesh mismatch is refused by name; the run receipt records which restart it started from. MCP: case_generate takes the initial-state reference; case_status reports it.

30. Polar plots and plots against a case parameter — S ​

Polar family delivered 2026-11-01 (scatterpolar is in the bundled plotly build; presentation.angleUnit; Plots family picker; no revolved-probe-circle helper yet). Parameter axis delivered 2026-11-04 (problemtype/runQuantityPlot.ts runQuantityPlotRecipe turns a case_run_quantity result into an inline recipe of the value against a numeric case parameter, sorted by it, runs without a value as gaps; MCP case_run_quantity plotParameter; test in runQuantity.test.ts; no viewer button). Original text: Two plotting gaps: a polar family in PLOT_FAMILIES (angle and radius from table columns or from a revolved probe circle; confirm first that the bundled plotly.js-strict-dist-min build carries scatterpolar, otherwise record the gap and stop), and an x axis bound to a recorded case parameter across isolated runs, so item 25's per-run quantity can be drawn as a curve. Both reuse the recipe format with an additive key and the existing worker; no new numerical backend.

Acceptance: a polar recipe round-trips through save/load and renders the analytic cos θ of a field linear in x within display precision; a three-run sweep draws one point per run against its parameter. MCP: plot_dataset accepts the new family and axis binding.

32. MPI/Trilinos and CoSimIO generator variants — L ​

Pending — added 2026-10-31 (recovered from item 16's out-of-scope list). Item 16 excluded MPI/Trilinos variants and CoSimIO "until a fixture proves them". KKSS now has a single-node MPI runner that rewrites parallel_type and refuses serial-only linear solvers, so the generator half is the bottleneck: per-problemtype Trilinos linear-solver settings, distributed output processes and a partitioned-input path (the per-rank MDPA export already exists in CAD-Preview), behind an explicit parallel case option. CoSimIO (an external participant through CoSimIO) follows only after a fixture with a non-Kratos participant exists. Embedded FSI and the IBQN/randomized-SVD accelerators stay out of scope.

Acceptance: one structural and one fluid tutorial case generate an MPI variant that runs on 2 and 4 ranks on an MPI-enabled Kratos and matches the threaded baseline within solver tolerance (coordinated with KKSS's MPI verification item); a serial-only solver choice is refused by name. MCP: case_generate takes the option; case_validate reports the missing MPI applications through the existing checkInstall probe.

Wall rule delivered 2026-11-01 as case_validate sizeAdvice (problemtype/sizeAdvice.ts: fluid problemtypes, first-cell height for a target y+ from refVelocity, the fluid material and the mesh's longest side, inputs listed; nothing for other problemtypes). Wavelength rule delivered 2026-11-05 as the pure adviseWavelengthSize (h ≤ λ/n, n = 5 by default, from an explicit wavelength or a frequency with a wave speed, nothing inferred; sizeAdvice.test.ts; published by case_validate through explicit wavelength / frequency + waveSpeed / pointsPerWavelength arguments as wavelengthAdvice, since no problemtype has a wave field to read). Still pending: publishing into the handoff manifest, the RANS tutorial y+ ≈ 33 check. Pending — added 2026-10-31 (ElmerStudio review); the source half of CAD-Preview's physics-calibrated size presets. Each problemtype that can say so publishes a recommended element size and the rule it came from into the handoff manifest it already reads: a wavelength rule for transient wave problems (h ≤ λ/5 from the case's frequency and material speed), a wall rule for fluid problemtypes (first-cell height for a target y⁺ from the inlet velocity and viscosity, the same inputs timeStepEstimate.ts reads), and nothing for problemtypes without a defensible rule. The value is advice with its inputs shown; the mesher decides.

Acceptance: the RANS tutorial's recorded y⁺ ≈ 33 wall row is reproduced from the case's own inputs within 20 %; a problemtype with no rule publishes nothing rather than a guess. MCP: case_validate reports the recommendation and its inputs.

38. Contours and isosurfaces that follow quadratic cells — M ​

Delivered 2026-11-01 in the viewer: tet10, hex27 isosurfaces and tri6, quad9 iso-lines (quadraticDecomposition in isoSurface.ts, an in-memory level-1 subdivision over all ten/six nodes; linear meshes take the unchanged path). Delivered 2026-11-03: hex20, quad8 and wedge15 through virtual points (the serendipity face/body centres the cell lacks, a weighted mix of its own nodes, welded across neighbours by the sorted ids of their contributors so a shared face shares its centre; the sub-cell tables are the hex27/quad9 ones, wedge15 gets 8 sub-wedges over three quad-face centres). Delivered 2026-11-04: pyramid13 (viewer: base-centre virtual point, six sub-pyramids and four tetrahedra whose volumes tile the cell, pinned by a test) and mesh_derive isosurface on quadratic input (deriveMesh.ts tessellates once at level 1 first — tet10/tri6/quad/hex27 — contours the linear result, and mapSourceToOriginal points SOURCE_ENTITY_ID back at the open mesh's cells; hex20/wedge15/pyramid13 are not tessellated by the kernel path and keep corner-only contours there). Contour level labels delivered 2026-11-05 (isoLabelAnchor in isoSurface.ts picks one on-level anchor per iso value; webview/main.ts draws a DOM label per level, re-projected on every render through its pane's camera; test in isoSurface.test.ts; the webview half was not compiled or run in this environment, so it is not browser-verified). Delivered 2026-11-05: linear hexahedra and wedges are split about face and body centres (symmetricDecomposition in isoSurface.ts, 24 and 14 tetrahedra), so a shared quad face triangulates identically from both neighbours and the hairline cracks are gone; the quadratic sub-cell tables still use the fixed diagonal. Still pending: a browser check. Original text: isoSurface.ts decomposes each volume cell into tetrahedra over its corner nodes only, so on tet10/hex20/hex27 results the mid-side nodal values, which the solver computed, play no part in where an isosurface or contour lies. Run marching tetrahedra over a level-1 subdivision of quadratic cells, built in memory with the same parent→child rules as the shipped tessellate derive (item 10) and never written, and the surface iso-lines over curvedDisplay.ts's subdivided faces; linear meshes keep today's path byte for byte. A second increment adds contour level labels spaced along each line (ElmerStudio prints one label per level at one vertex, which is the part not worth copying).

Acceptance: on a quadratic tetrahedral mesh carrying a field quadratic in x, the isosurface at a level lies within 1 % of the analytic plane where the corner-only surface does not; a linear mesh produces an identical surface to today's. MCP: mesh_derive isosurface takes the same quadratic path, so the exported surface and the drawn one agree.

44. Revolved integrals and measures for axisymmetric results — S–M ​

First increment delivered 2026-11-01: revolved part measures (computePartMeasures(model, {revolveAbout: "x" | "y"}), Pappus per triangle/segment, planar xy meshes only, MCP mesh_info revolveAbout with measure.revolvedAbout in the reply; a 3D mesh or any other axis is refused). Second increment (2026-11-01): the plot library's measureIntegral region source takes revolveAbout (so plot_dataset, monitors and the regional int()/avg() scalars of item 34 can revolve; validated in recipes). Third increment (2026-11-01): flowBalance and mesh_flow_balance take revolveAbout (exact 2-point Gauss over r·u on each line; an axisymmetric Poiseuille inlet reads πR²Ū within 0.2 %), and the region sources' boundaryMean/boundaryIntegral/flux do too (not pressure loads, exterior boundaries or a thickness). Fourth increment (2026-11-03): mesh_field_integrate takes revolveAbout (parser/revolvedIntegrate.ts: 2π∫r dA per cell and 2π·L·mean r per line Condition, piecewise-constant cell fields, per block and SubModelPart; 3D, other axes, volume cells, wrong-side nodes and Nodal fields refused by name; reply frame says revolved or plane; corner nodes only for quadratic cells; it duplicates part of elementMeasures' revolve weighting and could be folded into it). UI control delivered 2026-11-04: the Field integrals panel has an Integrate selector (in the plane / revolved about y / about x) whose choice rides meshAnalysis integrate (revolveAbout, through the analysis worker) and is never inferred (revolvedIntegrate.test.ts, Chromium check-integral-revolve.mjs). Nodes-on-axis lumped weights delivered 2026-11-05 (nodalRevolvedWeights in revolvedIntegrate.ts: exact for a field linear over the cell, 2π·A/12·(Σr + r_j) per triangle corner and 2π·L/6·(2r_j + r_other) per line end, so an axis node keeps a positive weight; mesh_field_integrate and the Field integrals panel now accept a Nodal field with revolveAbout, a node without a value is a gap excluded from numerator and measure; tests in revolvedIntegrate.test.ts, the MCP case written but not run in this environment because seek-bzip is not installed). Still pending: the case formulation naming the axis. Original wording of what remained: flowBalance (its refusal becomes the revolved path), plot_dataset region sources, nodes-on-axis lumped weights and a UI control. Original text: Integrals and measures of a 2D axisymmetric result are integrals in the r-z plane today, which is the wrong number for every quantity a user of an axisymmetric case asks for (a flow rate, a heat flow, a total force, a volume). Add an explicit revolved option to region reductions, part measures and flow balance: the user (or the case's own formulation, when the problemtype says it is axisymmetric) names the symmetry axis, nodal weights gain the 2πr factor, nodes on the axis keep a positive lumped weight, and every result says "revolved about y" beside its unit. It is never inferred from the geometry alone. flowBalance.ts's refusal becomes the revolved path; a tilted axis stays refused.

Acceptance: a revolved rectangle of radius R and height H measures πR²H; the flow rate through the inlet of an axisymmetric Poiseuille pipe equals the analytic πR²·Ū; the r-z integral is still available and labelled as such. MCP: mesh_field_integrate, mesh_flow_balance, plot_dataset region sources and mesh_info's measure take the option.

45. Boundary flux field recovered from nodal reactions — M ​

Headless half delivered 2026-11-01: boundaryFlux.ts + MCP mesh_recover_flux (3/6-node triangles consistent, 4-node quads lumped; whole-part integral equals the reaction sum; a uniform pressure on quadratic faces recovers a uniform traction). 8/9-node quadrilateral faces delivered 2026-11-04 (consistent, 3×3 Gauss on the bilinear map of the corner nodes; a uniform traction on the unit square is recovered exactly from the textbook consistent loads; SurfaceCondition3D9N now decodes to a biquadratic quad). Viewer half delivered 2026-11-06: Advanced ▸ Recovered flux… (kratos.mdpa.recoverFlux, command recoverFlux) runs recoverBoundaryFlux in the analysis worker (meshAnalysis kind recoverFlux) and shows the result in the Field panel as a field labelled recovered; it is held only in the webview (never in model/hostModel, so never saved, exported or edited), refuses an output name that already exists as a Nodal field, and is re-asked on a frame change while the panel is open and dropped otherwise. Still pending: the half-part exactness claim as worded below (it holds only where the cut does not split a node's load). Original text: Kratos reports reactions (REACTION, REACTION_FLUX) as nodal loads, so a user can sum them (plot/region.ts sum, reactionMoment) but cannot see where on a support or wall they act. Recover a per-area field — traction or heat flux — on the faces of a SubModelPart by a consistent boundary-mass projection on triangle faces (Jacobi-preconditioned CG, solved once per frame) and lumped HRZ weights elsewhere, and integrate it with the SAME weights, so that the integral over any subset of the part's faces sums exactly to that subset's reactions. A naive division by a lumped area is the trap this avoids: on a six-node triangle under uniform load the corner loads are zero, so the field would show spots. The result is a derived field, never written back into the source data, and it is labelled as recovered.

Acceptance: a uniform pressure on a quadratic tetrahedral face recovers a uniform traction within 1 %; the recovered field's integral over the whole part equals the reaction sum to 1e-9 relative, and over half of it equals that half's reactions; a linear mesh gives the same answer through either path. MCP: a mesh_derive-style or region operation returning the field and its integral.

46. ALE mesh quality over time — S–M ​

Headless half delivered 2026-11-01: aleQuality.ts + MCP mesh_ale_quality (per-frame signed measure and shape ratios against frame 0, inverted ids, worst frame; synthetic fold test; the Mok tutorial result was not available here). Viewer panel delivered 2026-11-04 (Advanced ▸ ALE mesh quality…, webview/alePanel.ts: worst-shape-ratio curve, frame table, Go to worst frame, Select worst elements as an ALE worst selection set, Export CSV; the scan is the new ale kind of the series-scan worker via aleSeries/aleSeriesProgress/aleSeriesResult/aleSeriesCancel, all three providers; MENU_ACTION_COMMANDS.aleQuality → kratos.mdpa.aleQuality; tests in seriesScanWorker.test.ts and analysisExport.test.ts, Chromium check-ale-panel.mjs with a simulated reply; the Mok FSI result is still unavailable). Still pending: only a run against a real FSI result. Original text: FSI and CoSimulation results carry MESH_DISPLACEMENT, and the question after a run is whether the mesh solver kept the fluid mesh valid. Add a series analysis over the timeline: for each frame, element quality on the deformed coordinates relative to the same element at frame 0 (signed measure ratio and shape ratio, so an intentionally graded mesh is not flagged), the count of inverted elements, and the worst frame, plotted with the existing series machinery and with a "go to worst frame" action that also selects the worst elements. It runs in the series-scan worker like the other multi-file scans and is read-only.

Acceptance: on the Mok FSI tutorial result the analysis reports no inverted element and a worst relative quality above zero, and a synthetic series that folds one element at frame k reports exactly that element and frame. MCP: mesh_field_series (or a sibling) returns the per-frame values.

Non-goals / known constraints ​

Standing exclusions, gathered here on 2026-10-31 from CLAUDE.md, the guides and the out-of-scope lines of the items above (this file had no such section, while CLAUDE.md cited one). Each says what would change our mind; where a narrower version is now worth doing, it names the item that recovers it.

  • Run progress or convergence inferred from solver stdout. Progress comes from vtk_output/ and convergence from solver-published records (kkss-convergence-v2.jsonl), never from log lines; stdout is shown verbatim and used only as a "last output" tooltip. A log format is not a contract, and a parsed line that changes between Kratos versions would turn into a wrong verdict silently. Would change our mind: nothing for verdicts. Narrower versions that respect the rule: item 26 (monitors refreshed from written frames) and item 17 (solver-written monitors).
  • Adopting meshio++'s returned mesh for operations whose round trip is lossy. modelToMeshio/meshioToModel lose entity kinds, Properties ids and original entity ids, so operations use meshio++ as an oracle (Group A) or are native (Group B); only explicitly adopting operations (ADOPTING_OPS) take the returned mesh and rebuild what it drops. Would change our mind: a meshio++ release that carries per-cell source indices and kinds through every operation, measured by the export-fidelity matrix.
  • In-place surface decimation. It rewrites topology with no back-map, drops side regions and blends integer tags as floats. The lossy-by-intent version shipped as the mesh_derive decimate copy (deriveMesh.ts), which is why this stays excluded only as an edit. Would change our mind: a decimator that reports a parent map.
  • Python-only meshio++ capabilities advertised as bundled. Point budgets, proximity graphs, PMSH/Zarr/CAE/USD time-series exports and PhysicsNeMo inference are not in the WASM build. They are reachable only through item 11's optional companion, and mesh_capabilities names the backend that provides each one. Would change our mind: a WASM binding for one of them.
  • Readers gated on vendor libraries the WASM build cannot link (vtx needs ADIOS2, szplt needs TecIO). Would change our mind: a dependency-free or freely redistributable path.
  • Routing medit_sol (17.23.0) as a mesh. A SolAtVertices file carries values with no geometry, so it would open as nothing and a write would drop the mesh (UNROUTED_READER_REASONS). Would change our mind: a companion rule that pairs it with its .mesh deterministically, read as a field of that mesh.
  • agglomerate, subdivide and other polyhedral-output operations. The editable model decomposes polyhedra on read, so their output cannot be kept; item 10 owns the native polyhedral path that would admit them. retagRegions stays unrouted because SubModelParts keep no native region tags. fitPrimitives is exposed as the mesh_fit_primitives report (2026-11-01); it is not an editing operation.
  • Solvers and coupling outside Kratos's own applications. CoSimIO and non-Kratos participants, embedded FSI, and the IBQN/randomized-SVD accelerators stay out until a fixture proves them; MPI/Trilinos generation is recovered by item 32. The shape control's mesh_moving_analysis, the ANN-enhanced ROM decoder, the older ShapeOptimizationApplication and non-structural optimization primals stay out until asked (item 18).
  • Translating machine-facing contracts. MCP tool, parameter and result names, recipes, sidecars, .mdpa keywords and exported numbers stay English and .-decimal in every locale (item 15).
  • Switching the default renderer to VTK-wasm. Decided in item 12: VTK-wasm stays experimental and opt-in, revisited only for a feature vtk.js lacks or a real-GPU measurement.
  • MCP tools for embedded editors and launchers. The Flowgraph node editor, the Kratos sidebar, What's New and the command line are ways of reaching capabilities MCP already exposes by path, so they have no headless mirror.

Released under the GNU AGPL-3.0-or-later License.