Skip to content

fix(repl): exit on Ctrl-C at an empty prompt, discard the continuation otherwise - #802

Merged
HuiJun merged 4 commits into
developfrom
fix/repl-ctrl-c-exits
Oct 2, 2026
Merged

HuiJun merged 4 commits into
developfrom
fix/repl-ctrl-c-exits

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

What and why

Ctrl-C in the sysml REPL never ended a session: the readline adapter turned readline.ErrInterrupt into an empty line. That also meant Ctrl-C at a ...> continuation prompt acted like a blank line and submitted the half-typed buffer.

Now the adapter reports the interrupt and repl.Loop decides what to do:

// cmd/sysml/prompt_readline.go
if err == readline.ErrInterrupt { return line, repl.ErrInterrupt }

// internal/frontend/repl/loop.go
if errors.Is(err, ErrInterrupt) {
    if buf.Len() == 0 && line == "" { return nil }   // empty sysml> prompt: exit, same as %quit / Ctrl-D
    buf.Reset(); prompt = primaryPrompt; continue    // otherwise discard line + continuation
}

So Ctrl-C with text on the line clears it, a second Ctrl-C quits (similar to the Node REPL). At a continuation prompt, Ctrl-C now drops the buffer without parsing it. A blank line still submits the buffer.

The wasm plainReader never returns an interrupt, so it isn't affected. Ctrl-C while a command is running is unchanged: readline is out of raw mode by then, and the default SIGINT already ended the process.

CI: timeout-minutes for the static-and-integrity job in .github/workflows/pr.yml goes from 20 to 30. make lint alone takes about 12 of the 20 minutes. The job was cancelled at the limit on this PR (twice) and on #800, both times after the REPL no-solver gate had passed and before the last corpus gates ran.

How it was verified

  • New TestLoopInterrupt in internal/frontend/repl covers each case: a typed line is discarded, a buffered continuation is discarded (never submitted), the next submission still works, and Ctrl-C at an empty primary prompt ends the loop before later lines are read.
  • Ran the real binary under a pty with expect, then again in a recorded GUI terminal (see the PR comment). Ctrl-C discarded both a typed line and a ...> continuation, and %eval 1 + 1 still gave = 2. Ctrl-C at the empty prompt exited with status 0, both in a plain session and after loading a file. Ctrl-D, %quit, a blank-line submit, and cancelling tab completion or reverse search are unchanged.
  • go test ./internal/frontend/repl ./cmd/sysml: passes, apart from the OPENSYSML_REQUIRE_*_CORPORA tests, which fail only because the corpora are missing on this machine. go vet, gofmt and staticcheck are clean on the changed packages, and scripts/changelog.py check passes.
  • Merged develop after fix(migrate): pass the uniqueness argument the read result pin's shaped multiplicity takes #800 landed, which fixes the shaped compile error that broke the first CI run.

Checklist

  • make test and make lint pass locally
  • Tests added or updated for the change
  • Documentation extended where it already covers the surface (see CONTRIBUTING.md): Ctrl-C row in docs/reference/repl-commands.md; the REPL testing skill's continuation note
  • Changelog entry added as changes/unreleased/<slug>.<section>.md, not as an edit to CHANGELOG.md
  • baselines regenerated and make docs-counts run if a gate count moved (no gate count moved)
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

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

…n otherwise

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

Copy link
Copy Markdown
Contributor Author

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

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Tested in a real terminal (Konsole) on 387663e83

  • Ctrl-C threw away a typed line and both an empty and a non-empty ...> continuation. Nothing was submitted and no diagnostics printed. Afterwards %eval 1 + 1 returned 2, and %list did not show the discarded packages.
  • Ctrl-C at an empty sysml> prompt exited with status 0, both in a plain session and after loading a model file.
  • Regressions: Ctrl-D and %quit still exit 0. A blank line at ...> still submits the buffer. Ctrl-C closes the tab-completion menu and cancels reverse search without leaving the REPL.
Discard and exit Exit after loading a file
Discarded inputs and Ctrl-C exit status 0 Loaded model and Ctrl-C exit status 0

The test binary included a local fix for the shaped compile error on develop (the one #800/#801 fix). That fix is not part of this PR.

devin-ai-integration Bot and others added 3 commits October 1, 2026 23:19
make lint alone takes about 12 of its 20 minutes, so the job was cancelled
before its last corpus gates ran.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review October 2, 2026 03:33

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

@HuiJun
HuiJun merged commit c5bff06 into develop Oct 2, 2026
24 checks passed
@HuiJun
HuiJun deleted the fix/repl-ctrl-c-exits branch October 2, 2026 04:53
devin-ai-integration Bot added a commit that referenced this pull request Oct 5, 2026
* fix(repl): exit on Ctrl-C at an empty prompt, discard the continuation otherwise

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

* ci: give the static and integrity job 30 minutes

make lint alone takes about 12 of its 20 minutes, so the job was cancelled
before its last corpus gates ran.

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>
(cherry picked from commit c5bff06)
@devin-ai-integration devin-ai-integration Bot mentioned this pull request Oct 5, 2026
6 tasks done
HuiJun added a commit that referenced this pull request Oct 5, 2026
* Merge pull request #746 from someshSandbox/feat/731-library-names

feat(export): name the standard library elements a converted model references (#731)

* Merge pull request #749 from someshSandbox/feat/732-convert-id-form

feat(grpc): Convert takes the id form, as sysml -id does (#732)

* Merge pull request #751 from Open-MBEE/fix/grpc-parser-warnings

* fix(grpc): report the parser's warnings as the workspace does

ParseFile and ParseSources handed the analysis passes an empty parse-diagnostic list and never read parser.Parser.Warnings, so a reserved keyword written as a name loaded with no diagnostic where sysml -validate reports the reserved-keyword-name error, and strict conformance never escalated a parser nonstandard-notation warning.

parser.AsDiagnostics is the one conversion of a parse's errors and warnings to pass diagnostics; the workspace, the REPL, the gRPC service and the edit validator all use it. The service reports each document's diagnostics once, through the passes, instead of the raw parse errors beside them.

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

* test(conformance): quote the reserved keyword the verification fixture uses as a name

The fixture named a part 'analysis' bare, which the service now reports as the reserved-keyword-name error the command line always reported, so every scenario over it errored.

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

* test(grpc): quote the reserved keyword the symbol_attributes fixture uses as a name

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>

* Merge pull request #747 from someshSandbox/feat/733-query-element-id

feat(query): report the elementId Convert writes (#733)

* Merge pull request #761 from someshSandbox/fix/760-succession-ends

fix(export): first a then b owns its connector ends (#760)

* Merge pull request #789 from someshSandbox/fix/760-requirement-constraint-refs

fix(export): a bare assume, require or assert names its constraint by reference (#760)

* Merge pull request #790 from someshSandbox/fix/760-variant-reference

fix: variant x is a VariantReference to x (#760)

* Merge pull request #748 from someshSandbox/fix/726-implicit-subsetting-uniqueness

fix(check): conformance of an implicit subsetting (#726)

* Merge pull request #794 from Open-MBEE/fix/733-element-id-by-declaration

* fix(query): report each scoped element's own elementId (#793)

ElementIDs recorded the ids a conversion writes by qualified name, so of two
elements one name in two identity scopes declares, a query reported the id
written last for both. Record them by declaration node too, as each encoder
wrote them, and look a symbol's id up by its declaration first.

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

* chore(ci): re-run checks after the static job timed out

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>

* Merge pull request #805 from someshSandbox/fix/transition-source-member

fix(export): a transition owns its source member and parameters; chained ends stay chains (#803)

* Merge pull request #846 from Open-MBEE/fix/default-built-against-release

* fix(clients): download the release a client was built against when none is named

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

* fix(clients): clarify built-against release fallbacks

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

* fix(clients): separate implicit binary release from service requirements

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

* fix(clients): isolate fallback test cache and format Rust

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

* fix(rust): satisfy clippy in binary resolution

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

* fix(python): resolve built-against prerelease tags

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

* fix(python): retry unpinned prerelease candidates

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>

* Merge pull request #849 from Open-MBEE/feature/rust-release-digest-stamp

* feat(rust): stamp each release's service digests into the published crate

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

* ci(rust): install python3 for the crate digest stamp when the image lacks it

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

* fix(release): sync client digest copies when stamping the shared table

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>

* Merge pull request #860 from Open-MBEE/feature/python-release-digest-stamp

* feat(python): stamp the release's sysml-grpc digests into the wheel

The committed release-digests.json pins only the releases that existed when
it was last back-filled, and the wheel for a tag was built before that tag's
service binaries, so every released opensysml had to verify its own release
through the signed manifest and the sigstore package at run time.

The release workflow now builds the binaries first (build-release-binaries),
and build-python-package hashes them with
pin_release_checksums.py --from-binaries dist/grpc into the table copy the
wheel and sdist package before python -m build, then fails unless both pin
all five assets for the tag. build-release assembles the release from the
two workspaces, writes SHA256SUMS.txt over the final distribution, fails
unless the wheel's pins are the manifest's service digests, and signs it as
before. Published bytes are the bytes those jobs verified.

A download refused because sigstore cannot be imported now raises
SigstoreUnavailableError naming the package, the install command, and that
this arises only for another release or an older client; the binary is
still never downloaded unverified.

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

* fix(release): persist only build-release's own files and name the missing verifier module

build-release attached build-release-binaries' dist/ layer and persisted the whole tree again. Workspace layers are additive and a path persisted by two upstream jobs fails the attach in every job downstream of both, which is every publish job; build-release now persists only the manifest, its signature and provenance bundles, the .sha256 sidecars and the Python distribution. A hygiene test holds the config to disjoint layers.

_load_sigstore reported "the sigstore package is not installed" for any ImportError, including one from cryptography, which _Sigstore imports first. The refusal now names the module that failed to import, as sigstore or a package sigstore depends on, with the same install command.

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>

* Merge pull request #873 from Open-MBEE/feature/python-metamodel-reader

* feat(ontology): generate the metamodel table from the pilot's SysML.ecore (20250201)

Read the pinned pilot implementation's SysML.ecore instead of the 202407 OWL rendering, record each property's ordering, derivation, redefinitions, subsettings and opposite and each class's abstractness, and fail CI when the table drifts from the pin. Write nonunique as isUnique false and own the flow, payload, end, terminate and instantiation metaclasses through a FeatureMembership, still reading graphs earlier releases wrote.

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

* fix(ontology): check a multiply-typed subject as its most specific rdf:type

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

* feat(ontology): record the metamodel's enumerations and their literals

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

* feat(python): generate metaclass classes and read metamodel JSON

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

* fix(ontology): treat an upper bound above one as multi-valued

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

* fix(python): refine metamodel reader contracts

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

* docs(python): add a metamodel classes and JSON guide page

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

* fix(python): fix API docs and CI type checking

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

* ci(python): install protobuf stubs for the metamodel type check

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

* fix(python): validate reference ranges and unwrap standalone envelopes

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

* feat(python): let read_json skip the reference range check

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

* fix(ontology): treat an ecore upper bound above one as multi-valued

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

* fix(ontology): read ecore upper bounds as EMF does, -2 unspecified included

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

* style(ontology): preserve generator test spacing

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

* fix(ontology): keep the upstream upper-bound rule after merging

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

* fix(build): restore the build-release-wasm recipe lost in a merge

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>

* docs(python): publish a short README to PyPI and keep the developer guide in DEVELOPING.md

The README PyPI renders as the opensysml project description was the full
developer document, with paragraphs that ran for forty lines. Keep the
install, quickstart and documentation links in README.md, and move the
complete walkthrough to DEVELOPING.md, where the comments and guides that
cited it now point.

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

* fix(python): import read_json where __all__ exports it

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

* chore(release): 0.9.2

Fold the changelog fragments into the 0.9.2 entry and set every version
surface — opensysml, @openmbee/opensysml and its platform packages, the
Java client and the editors — to 0.9.2 in lockstep with the core.

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

* chore(release): scrub Sonar findings carried by 0.9.2 and restore metamodel Make targets

Split the pymetamodel generator's generate, newModel and linearize into
focused helpers and factor the cached-binary decision out of
_ensure_binary_locked, so each stays under the cognitive-complexity
threshold without changing generated output or download behavior. Name
the repeated Ecore classifier kinds and the .sha256 sidecar suffix once.
Give each exception test a single raising call.

Exclude the generated Python metaclasses from analysis like the other
generated code, and record why built_against_releases returns one or
two candidate tags.

The release Makefile listed ontology-table(-check) and
python-metamodel(-check) as phony targets but lost their recipes, so
CI's make calls were silently no-ops; restore them from develop.

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

* fix(export,python): refuse an untargeted first-then end and a reference where a scalar is declared

initialEndsAgree skipped a connector end with no ReferenceSubsetting or
sysml:references target, so writing `first a then b` from the
succession's sourceFeature/targetFeature invented the end's target on
the way back. Refuse it as standardEndText does.

The metamodel reader's _convert followed any @id object, so a primitive
or enumeration property holding a reference returned the referenced
element instead of raising MalformedValue. Reject reference objects for
scalar ranges before resolving them.

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

* docs(project): performance record for 0.9.2 against 0.9.1

Every benchmark package on both revisions (six counts each, benchstat),
the rows left in doubt re-run interleaved, and whole-binary wall time and
RSS on the generated, example and Apollo 11 models. Parity: no row
regresses once interleaved, the model loader allocates +0.4-0.7% bytes
per element, the binary is 128 KiB larger.

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

* Merge pull request #756 from Open-MBEE/fix/dimension-one-identity

* fix(quantity): absorb MeasurementReferences::one as the identity of the unit product

A factor written in the library's dimensionless unit survived as a named
power of the composed unit product, so 800 [W] * 120 [s] * 0.7 [one] spelt
[SI::'kg⋅m²⋅s⁻²'*one] and never folded to SI::J. normalizeProduct now
absorbs the identity: it leaves any product it shares with another unit
and any power of it alone is itself. The identity is recognised by
structure (Model.IsIdentityUnit): a unit of DimensionOneUnit itself, of no
specialization such as AngularMeasureUnit, reducing to scale one — so rad,
sr and a percent scaling one by convention stay in the product.

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

* docs(skills): record how to test the dimension-one identity end to end

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

* fix(quantity): keep the absorbed identity for when the rest of the product cancels

An identity power (one) a product absorbs is remembered on UnitProduct.Identity
and is the unit again once every other power cancels, by symbol or by
reduction, so grouping does not change the result: (2 [one] * 3 [m]) / 3 [m] and
2 [one] * (3 [m] / 3 [m]) are both 2.0 [one]. Two identity declarations (one,
unity : DimensionOneUnit = one) are the same unit to normalisation, so
one * unity and unity * one agree and their measurement references compare equal.

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>
(cherry picked from commit bf0df66)

* Merge pull request #763 from Open-MBEE/fix/orthogonal-initial-entry

* fix(migrate): write an initial into an orthogonal region as that region's entry

A SysML v1 initial pseudostate whose transition enters a sibling orthogonal
region was refused and its own region written with no entry, so the runtime
stopped the machine at initialization. The entry is now settled per machine
before any region is written, and the writer, the owner's default-entry note
and the ledger read that one plan: a region's own initial is the one entering
it; a stray initial is written as the entry of the region owning its target,
coincides with that region's own entry into the same vertex, or is refused
when it conflicts with one into another.

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

* fix(migrate): donate initials only across orthogonal regions, and past a transition-less initial

An initial whose transition enters a region of another, non-orthogonal state
is refused instead of being written as that region's entry. An initial
pseudostate no transition leaves is no entry, so a stray initial of an
orthogonal region may enter its region, and the owner's default entry is
gated on written entries only.

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>
(cherry picked from commit a29c3e0)

* Merge pull request #795 from Open-MBEE/fix/758-nonunique-composites

* fix(examples): drop nonunique from the messages demo's channel messages

A message in a part implicitly subsets the unique Parts::Part::ownedActions,
so the two channel messages cannot be nonunique. The example validates clean,
leaves the known-failure list (which held only it), and the pilot-differential
baseline re-records the examples digest.

Fixes #758

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

* fix(migrate): write no nonunique where the v2 usage must be unique

A v1 property with isUnique=false, or a MagicDraw [] / [n] type modifier,
that becomes a usage implicitly subsetting a unique library feature, or that
redefines or subsets a feature written unique, is written without nonunique;
the report notes the dropped modifier and marks the entry approximated.

The decision reuses the checker's implicit-subsetting rules: semantics now
exposes ImplicitSubsettingCandidates over declaration kinds alone, and the
migrator reads the candidates' uniqueness from the bundled library.

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

* fix(migrate): judge a repeated slot value by the feature's written uniqueness

slotConflict read the v1 isUnique flag, so a repeated instance in a slot of a
nonunique composite property passed although the part is now written unique.
It asks featureWrittenUnique instead, which also admits a repeat on a feature
an array type modifier writes nonunique.

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>
(cherry picked from commit 341aa21)

* Merge pull request #802 from Open-MBEE/fix/repl-ctrl-c-exits

* fix(repl): exit on Ctrl-C at an empty prompt, discard the continuation otherwise

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

* ci: give the static and integrity job 30 minutes

make lint alone takes about 12 of its 20 minutes, so the job was cancelled
before its last corpus gates ran.

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>
(cherry picked from commit c5bff06)

* fix(export): load sysml-toolkit api-json exports

Co-Authored-By: jason.han <hanhuijun@gmail.com>
(cherry picked from commit addc9f8)

* fix(export): preserve toolkit API JSON round trips

Co-Authored-By: jason.han <hanhuijun@gmail.com>
(cherry picked from commit 7076103)

* fix(export): refuse named flow ends in a flow head

Co-Authored-By: jason.han <hanhuijun@gmail.com>
(cherry picked from commit 9bef894)

* fix(export): preserve kindless API JSON returns

Co-Authored-By: jason.han <hanhuijun@gmail.com>
(cherry picked from commit afaf845)

* chore(release): fold the 0.9.2 changelog fragments for the added fixes

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

---------

Co-authored-by: Jason Han <jason.han@jpl.nasa.gov>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>
HuiJun added a commit that referenced this pull request Oct 5, 2026
* Merge pull request #746 from someshSandbox/feat/731-library-names

feat(export): name the standard library elements a converted model references (#731)

* Merge pull request #749 from someshSandbox/feat/732-convert-id-form

feat(grpc): Convert takes the id form, as sysml -id does (#732)

* Merge pull request #751 from Open-MBEE/fix/grpc-parser-warnings

* fix(grpc): report the parser's warnings as the workspace does

ParseFile and ParseSources handed the analysis passes an empty parse-diagnostic list and never read parser.Parser.Warnings, so a reserved keyword written as a name loaded with no diagnostic where sysml -validate reports the reserved-keyword-name error, and strict conformance never escalated a parser nonstandard-notation warning.

parser.AsDiagnostics is the one conversion of a parse's errors and warnings to pass diagnostics; the workspace, the REPL, the gRPC service and the edit validator all use it. The service reports each document's diagnostics once, through the passes, instead of the raw parse errors beside them.

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

* test(conformance): quote the reserved keyword the verification fixture uses as a name

The fixture named a part 'analysis' bare, which the service now reports as the reserved-keyword-name error the command line always reported, so every scenario over it errored.

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

* test(grpc): quote the reserved keyword the symbol_attributes fixture uses as a name

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>

* Merge pull request #747 from someshSandbox/feat/733-query-element-id

feat(query): report the elementId Convert writes (#733)

* Merge pull request #761 from someshSandbox/fix/760-succession-ends

fix(export): first a then b owns its connector ends (#760)

* Merge pull request #789 from someshSandbox/fix/760-requirement-constraint-refs

fix(export): a bare assume, require or assert names its constraint by reference (#760)

* Merge pull request #790 from someshSandbox/fix/760-variant-reference

fix: variant x is a VariantReference to x (#760)

* Merge pull request #748 from someshSandbox/fix/726-implicit-subsetting-uniqueness

fix(check): conformance of an implicit subsetting (#726)

* Merge pull request #794 from Open-MBEE/fix/733-element-id-by-declaration

* fix(query): report each scoped element's own elementId (#793)

ElementIDs recorded the ids a conversion writes by qualified name, so of two
elements one name in two identity scopes declares, a query reported the id
written last for both. Record them by declaration node too, as each encoder
wrote them, and look a symbol's id up by its declaration first.

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

* chore(ci): re-run checks after the static job timed out

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>

* Merge pull request #805 from someshSandbox/fix/transition-source-member

fix(export): a transition owns its source member and parameters; chained ends stay chains (#803)

* Merge pull request #846 from Open-MBEE/fix/default-built-against-release

* fix(clients): download the release a client was built against when none is named

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

* fix(clients): clarify built-against release fallbacks

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

* fix(clients): separate implicit binary release from service requirements

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

* fix(clients): isolate fallback test cache and format Rust

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

* fix(rust): satisfy clippy in binary resolution

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

* fix(python): resolve built-against prerelease tags

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

* fix(python): retry unpinned prerelease candidates

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>

* Merge pull request #849 from Open-MBEE/feature/rust-release-digest-stamp

* feat(rust): stamp each release's service digests into the published crate

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

* ci(rust): install python3 for the crate digest stamp when the image lacks it

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

* fix(release): sync client digest copies when stamping the shared table

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>

* Merge pull request #860 from Open-MBEE/feature/python-release-digest-stamp

* feat(python): stamp the release's sysml-grpc digests into the wheel

The committed release-digests.json pins only the releases that existed when
it was last back-filled, and the wheel for a tag was built before that tag's
service binaries, so every released opensysml had to verify its own release
through the signed manifest and the sigstore package at run time.

The release workflow now builds the binaries first (build-release-binaries),
and build-python-package hashes them with
pin_release_checksums.py --from-binaries dist/grpc into the table copy the
wheel and sdist package before python -m build, then fails unless both pin
all five assets for the tag. build-release assembles the release from the
two workspaces, writes SHA256SUMS.txt over the final distribution, fails
unless the wheel's pins are the manifest's service digests, and signs it as
before. Published bytes are the bytes those jobs verified.

A download refused because sigstore cannot be imported now raises
SigstoreUnavailableError naming the package, the install command, and that
this arises only for another release or an older client; the binary is
still never downloaded unverified.

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

* fix(release): persist only build-release's own files and name the missing verifier module

build-release attached build-release-binaries' dist/ layer and persisted the whole tree again. Workspace layers are additive and a path persisted by two upstream jobs fails the attach in every job downstream of both, which is every publish job; build-release now persists only the manifest, its signature and provenance bundles, the .sha256 sidecars and the Python distribution. A hygiene test holds the config to disjoint layers.

_load_sigstore reported "the sigstore package is not installed" for any ImportError, including one from cryptography, which _Sigstore imports first. The refusal now names the module that failed to import, as sigstore or a package sigstore depends on, with the same install command.

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>

* Merge pull request #873 from Open-MBEE/feature/python-metamodel-reader

* feat(ontology): generate the metamodel table from the pilot's SysML.ecore (20250201)

Read the pinned pilot implementation's SysML.ecore instead of the 202407 OWL rendering, record each property's ordering, derivation, redefinitions, subsettings and opposite and each class's abstractness, and fail CI when the table drifts from the pin. Write nonunique as isUnique false and own the flow, payload, end, terminate and instantiation metaclasses through a FeatureMembership, still reading graphs earlier releases wrote.

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

* fix(ontology): check a multiply-typed subject as its most specific rdf:type

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

* feat(ontology): record the metamodel's enumerations and their literals

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

* feat(python): generate metaclass classes and read metamodel JSON

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

* fix(ontology): treat an upper bound above one as multi-valued

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

* fix(python): refine metamodel reader contracts

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

* docs(python): add a metamodel classes and JSON guide page

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

* fix(python): fix API docs and CI type checking

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

* ci(python): install protobuf stubs for the metamodel type check

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

* fix(python): validate reference ranges and unwrap standalone envelopes

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

* feat(python): let read_json skip the reference range check

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

* fix(ontology): treat an ecore upper bound above one as multi-valued

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

* fix(ontology): read ecore upper bounds as EMF does, -2 unspecified included

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

* style(ontology): preserve generator test spacing

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

* fix(ontology): keep the upstream upper-bound rule after merging

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

* fix(build): restore the build-release-wasm recipe lost in a merge

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>

* docs(python): publish a short README to PyPI and keep the developer guide in DEVELOPING.md

The README PyPI renders as the opensysml project description was the full
developer document, with paragraphs that ran for forty lines. Keep the
install, quickstart and documentation links in README.md, and move the
complete walkthrough to DEVELOPING.md, where the comments and guides that
cited it now point.

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

* fix(python): import read_json where __all__ exports it

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

* chore(release): 0.9.2

Fold the changelog fragments into the 0.9.2 entry and set every version
surface — opensysml, @openmbee/opensysml and its platform packages, the
Java client and the editors — to 0.9.2 in lockstep with the core.

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

* chore(release): scrub Sonar findings carried by 0.9.2 and restore metamodel Make targets

Split the pymetamodel generator's generate, newModel and linearize into
focused helpers and factor the cached-binary decision out of
_ensure_binary_locked, so each stays under the cognitive-complexity
threshold without changing generated output or download behavior. Name
the repeated Ecore classifier kinds and the .sha256 sidecar suffix once.
Give each exception test a single raising call.

Exclude the generated Python metaclasses from analysis like the other
generated code, and record why built_against_releases returns one or
two candidate tags.

The release Makefile listed ontology-table(-check) and
python-metamodel(-check) as phony targets but lost their recipes, so
CI's make calls were silently no-ops; restore them from develop.

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

* fix(export,python): refuse an untargeted first-then end and a reference where a scalar is declared

initialEndsAgree skipped a connector end with no ReferenceSubsetting or
sysml:references target, so writing `first a then b` from the
succession's sourceFeature/targetFeature invented the end's target on
the way back. Refuse it as standardEndText does.

The metamodel reader's _convert followed any @id object, so a primitive
or enumeration property holding a reference returned the referenced
element instead of raising MalformedValue. Reject reference objects for
scalar ranges before resolving them.

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

* docs(project): performance record for 0.9.2 against 0.9.1

Every benchmark package on both revisions (six counts each, benchstat),
the rows left in doubt re-run interleaved, and whole-binary wall time and
RSS on the generated, example and Apollo 11 models. Parity: no row
regresses once interleaved, the model loader allocates +0.4-0.7% bytes
per element, the binary is 128 KiB larger.

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

* Merge pull request #756 from Open-MBEE/fix/dimension-one-identity

* fix(quantity): absorb MeasurementReferences::one as the identity of the unit product

A factor written in the library's dimensionless unit survived as a named
power of the composed unit product, so 800 [W] * 120 [s] * 0.7 [one] spelt
[SI::'kg⋅m²⋅s⁻²'*one] and never folded to SI::J. normalizeProduct now
absorbs the identity: it leaves any product it shares with another unit
and any power of it alone is itself. The identity is recognised by
structure (Model.IsIdentityUnit): a unit of DimensionOneUnit itself, of no
specialization such as AngularMeasureUnit, reducing to scale one — so rad,
sr and a percent scaling one by convention stay in the product.

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

* docs(skills): record how to test the dimension-one identity end to end

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

* fix(quantity): keep the absorbed identity for when the rest of the product cancels

An identity power (one) a product absorbs is remembered on UnitProduct.Identity
and is the unit again once every other power cancels, by symbol or by
reduction, so grouping does not change the result: (2 [one] * 3 [m]) / 3 [m] and
2 [one] * (3 [m] / 3 [m]) are both 2.0 [one]. Two identity declarations (one,
unity : DimensionOneUnit = one) are the same unit to normalisation, so
one * unity and unity * one agree and their measurement references compare equal.

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>
(cherry picked from commit bf0df66)

* Merge pull request #763 from Open-MBEE/fix/orthogonal-initial-entry

* fix(migrate): write an initial into an orthogonal region as that region's entry

A SysML v1 initial pseudostate whose transition enters a sibling orthogonal
region was refused and its own region written with no entry, so the runtime
stopped the machine at initialization. The entry is now settled per machine
before any region is written, and the writer, the owner's default-entry note
and the ledger read that one plan: a region's own initial is the one entering
it; a stray initial is written as the entry of the region owning its target,
coincides with that region's own entry into the same vertex, or is refused
when it conflicts with one into another.

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

* fix(migrate): donate initials only across orthogonal regions, and past a transition-less initial

An initial whose transition enters a region of another, non-orthogonal state
is refused instead of being written as that region's entry. An initial
pseudostate no transition leaves is no entry, so a stray initial of an
orthogonal region may enter its region, and the owner's default entry is
gated on written entries only.

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>
(cherry picked from commit a29c3e0)

* Merge pull request #795 from Open-MBEE/fix/758-nonunique-composites

* fix(examples): drop nonunique from the messages demo's channel messages

A message in a part implicitly subsets the unique Parts::Part::ownedActions,
so the two channel messages cannot be nonunique. The example validates clean,
leaves the known-failure list (which held only it), and the pilot-differential
baseline re-records the examples digest.

Fixes #758

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

* fix(migrate): write no nonunique where the v2 usage must be unique

A v1 property with isUnique=false, or a MagicDraw [] / [n] type modifier,
that becomes a usage implicitly subsetting a unique library feature, or that
redefines or subsets a feature written unique, is written without nonunique;
the report notes the dropped modifier and marks the entry approximated.

The decision reuses the checker's implicit-subsetting rules: semantics now
exposes ImplicitSubsettingCandidates over declaration kinds alone, and the
migrator reads the candidates' uniqueness from the bundled library.

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

* fix(migrate): judge a repeated slot value by the feature's written uniqueness

slotConflict read the v1 isUnique flag, so a repeated instance in a slot of a
nonunique composite property passed although the part is now written unique.
It asks featureWrittenUnique instead, which also admits a repeat on a feature
an array type modifier writes nonunique.

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>
(cherry picked from commit 341aa21)

* Merge pull request #802 from Open-MBEE/fix/repl-ctrl-c-exits

* fix(repl): exit on Ctrl-C at an empty prompt, discard the continuation otherwise

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

* ci: give the static and integrity job 30 minutes

make lint alone takes about 12 of its 20 minutes, so the job was cancelled
before its last corpus gates ran.

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>
(cherry picked from commit c5bff06)

* fix(export): load sysml-toolkit api-json exports

Co-Authored-By: jason.han <hanhuijun@gmail.com>
(cherry picked from commit addc9f8)

* fix(export): preserve toolkit API JSON round trips

Co-Authored-By: jason.han <hanhuijun@gmail.com>
(cherry picked from commit 7076103)

* fix(export): refuse named flow ends in a flow head

Co-Authored-By: jason.han <hanhuijun@gmail.com>
(cherry picked from commit 9bef894)

* fix(export): preserve kindless API JSON returns

Co-Authored-By: jason.han <hanhuijun@gmail.com>
(cherry picked from commit afaf845)

* chore(release): fold the 0.9.2 changelog fragments for the added fixes

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

* ci(sonar): ignore the HTTP-URL rule on the migrator's profile namespaces and register the candidate-tag ignore

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

* ci(release): escape the publish-crates heredoc so the release workflow compiles

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

* chore(release): bump the version surfaces develop added since 0.9.1 to 0.9.2

The release line never had the @openmbee/opensysml-wasm peer dependency,
the Java API reference's Maven snippet or the Java agent skill, so the
back-merge left them at 0.9.1 while every other surface moved to 0.9.2;
the Node package test checks the peer matches the client version.
Regenerated package-lock.json against the published 0.9.2 platform
packages, which the release's lockfile had dropped.

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

---------

Co-authored-by: Jason Han <jason.han@jpl.nasa.gov>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant