Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
cc61215
feat(model): persistent resolver and semantic model per workspace, in…
devin-ai-integration[bot] Sep 15, 2026
ff8beab
feat(semantics): document-owned memo lifecycle helpers
devin-ai-integration[bot] Sep 15, 2026
13cc5c3
perf(passes): cache the workspace-wide audits' gathers per document
devin-ai-integration[bot] Sep 15, 2026
6cfa158
perf(resolve): ledger memo entries per table and remember recently en…
devin-ai-integration[bot] Sep 15, 2026
fef175c
test(stressmodel): generate the network split by plane, and benchmark…
devin-ai-integration[bot] Sep 15, 2026
94d3700
docs: record what the persistent semantic model changes and costs
devin-ai-integration[bot] Sep 15, 2026
28016af
refactor: shorten the doc comments of the persistent model's entry po…
devin-ai-integration[bot] Sep 15, 2026
be0cff8
fix(semantics): effectiveEnds answers nil for no connector before jou…
devin-ai-integration[bot] Sep 15, 2026
7901432
fix(semantics): own the body-application index and scalar table by do…
devin-ai-integration[bot] Sep 15, 2026
52656be
docs: regenerate test-function counts
devin-ai-integration[bot] Sep 15, 2026
601e155
perf(resolve): keep whole-index readers across judgment-only changes
devin-ai-integration[bot] Sep 15, 2026
bebab0d
Merge remote-tracking branch 'origin/develop' into feature/persistent…
devin-ai-integration[bot] Sep 15, 2026
2164bab
fix(semantics): own MemberSources and lookupSources memo hits by the …
devin-ai-integration[bot] Sep 15, 2026
5eb3fd4
Merge remote-tracking branch 'origin/develop' into feature/persistent…
devin-ai-integration[bot] Sep 15, 2026
de32c54
Merge remote-tracking branch 'origin/develop' into feature/persistent…
devin-ai-integration[bot] Sep 15, 2026
a2e9434
Merge remote-tracking branch 'origin/develop' into feature/persistent…
devin-ai-integration[bot] Sep 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ What these numbers cannot show: the OMG corpora are demonstrations rather than a
<!-- doc-counts:end refereed-figures -->

**Current commit:** All tests pass (`go test -race ./...`), builds clean (`go build ./...`).
**Test coverage:** <!-- doc-counts:begin test-suite -->8,393 top-level `Test` functions (counted from the `_test.go` files, as `go test ./...` runs them) covering parsers, semantics, runtime (actions, states, instances, operators, validation). Behavioral robustness: 206 golden ASTs, 252 negatives, 940 conformance cases, 257 golden traces, 465 runtime robustness cases, 21 gRPC conformance cases and 8 gRPC robustness cases.<!-- doc-counts:end test-suite --> These figures are generated by `make docs-counts` from the tree and gated. A test skips only for want of something the run did not provide, and says what: the held-image round trip declines a conformance case that creates no instance, a few gate on a PDF or Mermaid toolchain, a pinned pilot artifact, the PSSM suite, a locale, a case-insensitive filesystem or a live Flexo stack, and the OMG corpus gates skip until the corpora are downloaded unless asked to fail.
**Test coverage:** <!-- doc-counts:begin test-suite -->8,419 top-level `Test` functions (counted from the `_test.go` files, as `go test ./...` runs them) covering parsers, semantics, runtime (actions, states, instances, operators, validation). Behavioral robustness: 206 golden ASTs, 252 negatives, 940 conformance cases, 257 golden traces, 465 runtime robustness cases, 21 gRPC conformance cases and 8 gRPC robustness cases.<!-- doc-counts:end test-suite --> These figures are generated by `make docs-counts` from the tree and gated. A test skips only for want of something the run did not provide, and says what: the held-image round trip declines a conformance case that creates no instance, a few gate on a PDF or Mermaid toolchain, a pinned pilot artifact, the PSSM suite, a locale, a case-insensitive filesystem or a live Flexo stack, and the OMG corpus gates skip until the corpora are downloaded unless asked to fail.
**Parser coverage:** 101/101 bundled library files parse cleanly — the 94 official SysML v2 standard library files and the non-normative `OpenSysML Libraries/OpenSysMLMathFunctions.kerml`, `OpenSysML Libraries/DocumentQueries.sysml`, `OpenSysML Libraries/IdentityMetadata.sysml`, `OpenSysML Libraries/DiagramLayout.sysml`, `OpenSysML Libraries/OOSEM.sysml`, `OpenSysML Libraries/MOSA.sysml` and `OpenSysML Libraries/StateSpaceIntegration.sysml` extensions. Conformance verified by [stdlib_conformance_test.go](internal/core/libs/stdlib_conformance_test.go). Grammar reference: [OMG Xtext grammar](https://github.com/Systems-Modeling/SysML-v2-Pilot-Implementation/tree/master/org.omg.kerml.xtext/src/org/omg/kerml/xtext).
**Behavioral execution:** Calc/constraint/requirement/satisfy functional. Action/state executors handle nested invocation, control flow keywords, loop and conditional statements and the send statement (<!-- doc-counts:begin conformance-passing -->940/940 conformance cases passing<!-- doc-counts:end conformance-passing -->). Coverage is self-assessed against the specification text and the normative library: the pinned OMG pilot implementation evaluates expressions but does not execute actions or state machines headlessly, so no external implementation currently adjudicates these rows. See [spec compliance](docs/project/spec-compliance.md).
**Reference differential:** 377 files compared diagnostic-by-diagnostic against the pinned OMG pilot implementation (`2026-08`), 346 in full agreement; every divergence is enumerated and adjudicated in [the differential](docs/project/pilot-differential.md), reproducible with `go run ./cmd/pilot-diff`.
Expand Down
22 changes: 22 additions & 0 deletions changes/unreleased/persistent-semantic-model.performance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
- **A workspace keeps its semantic model between edits and invalidates it per document.**
`model.Workspace` owns one `resolve.Resolver` and one `semantics.Model` for its lifetime and
hands them to every analysis it runs; the resolver keeps a frame per document owning what was
memoized while that document was analyzed and records which documents it read (a namespace it
imports that another contributes to, a namespace both contribute to, a symbol of another that a
resolution returned). Replacing a document drops its frame and, transitively, its dependents' —
their memo entries, cached diagnostics and reverse references — and nothing else, where every
edit used to clear the whole workspace. The OOSEM, MOSA and identity-metadata audits and the
coherent-quantity ranking gather each document's facts once into the workspace and judge each
analyzed document over the union, where they gathered every document once per document
analyzed. `TestIncrementalEqualsFresh` replays scripted and random edit sequences over the
fixtures and the OMG corpora and compares diagnostics, resolutions and references with a fresh
workspace after every step. On the satellite-network stress test, editing a two-line file beside
512 satellites goes from 861 ms and 327 MiB per edit to 8.7 ms and 2.0 MiB; editing the library
every file of the split network imports costs one analysis of the model (8.8 s to 5.3 s at 512
satellites), and loading the 1 600-satellite network split into 34 files through one workspace
goes from 126 s to 18 s. A loaded workspace holds about twice the heap (254 MiB to 478 MiB at
512 satellites), the memo tables that were allocated and discarded on every analysis, and a
thousand edits grow it by 4.5%. A one-shot `sysml -validate` pays the dependency recording it
never uses: about a sixth more wall time (1.9 s to 2.2 s at 200 satellites) and 4% more
allocation. Figures and the machine they were taken on are in `docs/internals/performance.md`
and `docs/project/satellite-network-stress-test.md`.
69 changes: 69 additions & 0 deletions docs/internals/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,75 @@ What this says about a real workload is that the collector, not the run, is what
grows: a long-lived session over a large model tunes better with `GOGC` than with
a faster executor.

## What the persistent semantic model changes

A `model.Workspace` keeps one `resolve.Resolver` and one `semantics.Model`
for its lifetime, beside its index, and hands them to every `passes.Context`
it builds; a context built outside a workspace still gets fresh ones. The
resolver keeps a frame per document that owns what was memoized while that
document was analyzed, and records which documents each frame read: a
document depends on another when it imports a namespace the other contributes
to, when both contribute to one namespace, or when a resolution from its scope
returned the other's symbol. Replacing a document drops its frame and,
transitively, its dependents' frames — their memo entries, cached diagnostics
and reverse references — and nothing else. The three workspace-wide audits
(OOSEM, MOSA, identity metadata) and the coherent-quantity ranking gather each
document's facts once into the workspace, regather a document when it changes,
and judge each analyzed document over the union.

`TestIncrementalEqualsFresh` replays scripted and seeded random edit sequences
— edits, reverts to earlier versions, closes and opens — over the fixtures and
the four OMG corpora and, after every step, compares diagnostics, resolutions
and reverse references with a workspace built fresh from the same documents.

Measured on the satellite-network generator (`docs/project/satellite-network-stress-test.md`,
"Editing"; Intel Xeon Platinum 8559C, 8 CPUs, 31 GiB, Go 1.25, `-benchtime=5x
-count=3` medians), rebuilt on every edit → kept:

| measurement | rebuilt | kept |
| ----------- | ------- | ---- |
| `BenchmarkEditBeside`, 512 satellites beside a two-line file, per edit | 861 ms, 327 MiB | 8.7 ms, 2.0 MiB |
| `BenchmarkEditBeside`, 128 satellites | 189 ms, 83 MiB | 2.5 ms, 0.64 MiB |
| `BenchmarkEditBeside`, 32 satellites | 48 ms, 22 MiB | 0.82 ms, 0.31 MiB |
| `BenchmarkEditImported`, 512 satellites in 6 files, edit the library then every file's diagnostics | 8.78 s, 2.77 GiB | 5.26 s, 1.17 GiB |
| `BenchmarkLoadFiles`, 512 satellites in 6 files through one workspace | 9.20 s, 3.05 GiB | 5.26 s, 1.56 GiB |
| 1 600 satellites in 34 files through one workspace, open and analyze all | 126.5 s | 18.3 s |
| `BenchmarkLoad`, 512 satellites in one file | 5.13 s, 254 MiB held | 5.99 s, 478 MiB held |
| live heap after 1 000 edits beside 32 satellites, against after the first | — | 67.2 MB → 70.2 MB |

Editing the library every file imports costs one analysis of the whole model,
what loading it costs; the audits no longer gather every document once per
document analyzed, which is the whole of the 34-file difference. What the
model holds between edits nearly doubles — the memo tables that were allocated
and discarded during every analysis now stay — and a thousand edits grow it by
4.5%.

### What the bookkeeping costs a one-shot validation

Every memoized read records that the current document depends on the owner of
the entry it read. That is what makes invalidation sound: an entry keyed by two
symbols of two documents (`composed[(S, T)]`) must go when either changes, and
the reader of a cached answer must be re-analyzed when the answer's owner is
replaced, so the dependency has to be recorded on a hit as well as on a miss.
A validation that will never edit records about 25 million such reads at
roughly 8 ns each for nothing. `sysml -validate -memstats` on the 200-satellite
constellation, one file, three runs each:

| | rebuilt | kept |
| --- | ------- | ---- |
| wall | 1.85–1.96 s | 2.19–2.25 s |
| allocated | 738 MiB in 10.97 M allocations | 767 MiB in 10.98 M allocations |
| peak RSS (`/usr/bin/time`) | 421 MiB | 428 MiB |

At 1 600 satellites: 17.7 s and 5.5 GiB allocated became 20.5 s and 5.8 GiB.
The cost falls on whatever analyzes through the workspace's own context: the
LSP server, a REPL session, and `sysml -validate`, which loads through a REPL
session. A batch that analyzes each document in a private `passes.Context` —
its own resolver and model over the read-only index, as a pool of workers
must — has no frames to record into and pays none of it; the workspace's
gathered facts are what such a batch should hand its workers, so that they do
not gather per worker what the workspace gathered once.

## Notes for further work

- The `about`-metadata index walks the bundled library's documents once per
Expand Down
Loading
Loading