Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ OpenMBEE View Editor brings source editing, named model views and reviewed graph
- **Edit highlighted SysML source.** CodeMirror provides line numbers, bracket matching/closing, indentation, find/replace and draft undo/redo. Apply or discard changes, undo/redo accepted source transactions, and keep the last valid model when a draft fails validation.
- **Query the draft model.** Request engine-backed completion, symbol information and project-file definitions against the complete current source set.
- **Inspect requirement graphs.** Browse requirement definitions/usages, declared identifiers and documentation, typing, hierarchy and positive/negated satisfaction links. Follow exact source declarations; diagram links do not report executed test results.
- **Open source-defined views.** Navigate named SysML views independently of the active source file. Inspect nested parts, typed inherited ports and resolved connection endpoints.
- **Open source-defined views.** Navigate named SysML views independently of the active source file. Inspect nested and inherited parts, typed inherited ports and resolved connection endpoints.
- **Navigate source issues.** Open an issue’s reported file and line in the source editor. Missing or stale locations are explained while drafts and the last valid diagram are retained.
- **Browse the model hierarchy.** Expand packages, definitions and declared members in the Model tree. Inspect type/kind labels; use arrow keys, type-ahead and Enter/Space to navigate and select.
- **Review graphical changes.** Rename parts, create native views, add typed part usages and connect compatible directed ports through an exact source-change preview.
Expand Down Expand Up @@ -184,7 +184,7 @@ npm run test:e2e # fresh build, real engines and visual compa
npm run preview -- --host 127.0.0.1
```

PR CI runs all fast checks plus fourteen critical browser workflows, adding visual comparisons for code changes. Full browser coverage runs weekly, manually and for release/main/hotfix PRs and pushes. Run `npm run test:e2e -- --project=functional --grep @critical` to reproduce the smaller browser gate; `npm run test:e2e` still runs everything. See [CI coverage](docs/testing.md#ci-coverage) for selection and deferred coverage.
PR CI runs all fast checks plus the critical browser workflows, adding visual comparisons for code changes. Full browser coverage runs weekly, manually and for release/main/hotfix PRs and pushes. Run `npm run test:e2e -- --project=functional --grep @critical` to reproduce the smaller browser gate; `npm run test:e2e` still runs everything. See [CI coverage](docs/testing.md#ci-coverage) for selection and deferred coverage.

Functional checks can use installed Chrome with `PLAYWRIGHT_BROWSER_CHANNEL=chrome npm run test:e2e -- --project=functional`. Visual comparisons always use bundled Chromium and reviewed platform-specific baselines. The browser suite starts its own fresh production server; port **4175** must be free. See [browser testing and baseline review](docs/testing.md).

Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/source-assistance-recovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Local recovery uses one IndexedDB record per browser origin: database `sysml-vie

After edits, the workbench debounces an immutable project snapshot for 750 ms. Dirty projects and unapplied drafts qualify, including drafts retained after a download. Recovery serialization does not mark the project downloaded or add history. Restored projects remain unsaved until downloaded, including when their drafts match applied source. While local recovery is enabled, when work returns to a clean state after discard, undo or download, an ordered atomic clear removes this session’s older recovery copy. Pending startup or foreign copies remain protected. The enabled preference is a separate boolean in localStorage under `sysml-view-recovery-enabled-v1`.

A startup record blocks replacement until an explicit restore/discard choice. Restore validates the project through the real workspace engine before replacing it, confirms replacement of dirty current work, and accepts the recovery snapshot synchronously before observing subsequent work. The active view is checked against the restored model. Failed restore retains the stored record and current project.
A startup record blocks replacement until an explicit restore/discard choice. Restore validates the project through the real workspace engine before replacing it, confirms replacement of dirty current work, and accepts the recovery snapshot synchronously before observing subsequent work. The active view is checked against the restored model. Failed restore retains the stored record and current project. After the project is accepted, a subsequent named-view or exploration refresh failure belongs to the workbench action/error channel, not the recovery action. It remains visible while current and is superseded by later workbench actions; a clean parse alone does not establish successful visualization. A validation failure before replacement remains recovery-owned and keeps the pending copy.

Writes and deletion use an atomic compare-and-swap transaction against the exact previously read string. A competing tab's change causes a visible conflict. Its message states that Retry recovery only reads the saved copy without replacing the current project. After Retry, the pending copy shows its project name and saved time, with expandable filenames, unapplied-draft status and applied/draft UTF-8 byte sizes; Restore recovery replaces the open project (with dirty-work confirmation), while Discard recovery deletes the saved copy and keeps the live project. Users are advised to download current work before restoring. The live copy has no invented save timestamp; pending edits are explicitly described as remaining in this tab until a choice permits recovery saving. Denied storage and write failures retain the editable project and permit retry. Malformed or future string envelopes remain protected until explicit discard. An unsupported non-string IndexedDB value is retained and reported unavailable; this version cannot discard that value through the app.

Expand Down
28 changes: 28 additions & 0 deletions docs/architecture/structural-views.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,34 @@ The retained bridge now adds `reference` and declared `direction` metadata by lo

The upstream pins, archives and licenses are unchanged. The bridge overlay, builder and shared fixture bytes contribute to prepared provenance. Preparation copies the same example sources into the Go test package, tests their ownership/directions/reference/endpoints/context/notices, and rebuilds the derived WASM. Local before/after raw captures and generated provenance are retained in ignored `sandbox/assembly-before-trace.json` and `sandbox/assembly-trace.json`; these are local evidence, not distributable fixtures or upstream conformance claims.

## Exposure and private imports

A named view’s exposure is not an owned-members-only query. SysML exposure ignores visibility: a private import can contribute members to `expose Package::*`, even though an ordinary name lookup outside that package cannot see them. A wildcard import of `StandardViewDefinitions::*` can therefore bring its publicly reexported SysML/KerML metamodel surface into a wildcard exposure. The native renderer may expand the selected elements further; selected subjects and rendered descendants are different sets.

Use explicit exposures to choose a small scope, and named imports to reduce the imported surface. For example:

```sysml
package Demo { part def Sat; }
package Probe {
private import Demo::Sat;
private import StandardViewDefinitions::GeneralView;
part p : Sat;
view focused : GeneralView { expose Probe::p; }
}
```

Changing the two imports to public imports would not make this explicit exposure broader. Conversely, changing `expose Probe::p` to `expose Probe::*` admits imported members as well as owned members; named imports reduce that set but do not change its meaning. Private imports remain private for ordinary external references. Each intermediate import still determines which source memberships it admits; an outer exposure does not recursively open every private dependency. The current pinned engine has a reproduced intermediate-import defect: a private source declaration can enter the selected subjects through an ordinary import, and another import layer can incorrectly admit a private owned declaration. That defect is separate from the valid immediate private-import behavior above; the [retained upstream investigation and executable fixture](../research/wp26-exposure-import-membership.md) record its failing cases and why an experimental fix was rejected. The editor does not silently remove imported/library subjects from authored exposure, and large graphs alone are not proof of a visibility failure.

The basis is [SysML 2.0 Part 1](https://www.omg.org/spec/SysML/2.0/Language/PDF), §8.3.26.2–4 (Expose and NamespaceExpose), together with [KerML 1.0](https://www.omg.org/spec/KerML/1.0/PDF), §8.3.2.4.5–6 (Namespace and NamespaceImport). These rules do not prescribe exact renderer node counts, guarantee large-view readability or establish complete editor conformance.

## Inherited structural occurrences

Authored interconnection views retain the native renderer’s effective structural members, including inherited parts under typed usages. Each occurrence binds the source declaration to its complete usage context: `Sat::c` under `sat1` and under `sat2` share a declaration but have different canvas identities. Native traversal still determines recursion and depth bounds. Attributes remain source-backed compartments; synthetic subjects and unsupported constraints remain excluded. Connector selection still requires the existing owner/exposure policy, so retaining inherited nodes does not automatically expose a type’s internal connections.

Temporary exploration and tree views retain their existing declaration-ownership scope. This change is specific to authored interconnection views. The bridge reserves the previous projection’s node and port IDs before assigning IDs to newly visible occurrences. This preserves compatible positions in unchanged version-1 projects without introducing a general persistent identity guarantee across source edits. The artifact remains parent-first even when reserved IDs are not numerically ordered. When a partial saved presentation puts an old child in a new child’s automatic slot, the projection keeps restored subtrees anchored and places automatic inherited siblings into free slots inside the canonical owner bounds. The native inherited flag is validated in the artifact contract; ordinary authored-node placement remains unchanged. Those bounds and drag extents remain unchanged because version 1 saves positions alone. If the saved geometry leaves no fitting slot, the editor keeps the saved positions and warns that automatic positions overlap; move saved nodes to make room. This does not provide general manual overlap prevention.

Keyboard Enter/Space selection now updates Properties for structural nodes as well as the other diagram families. Source reveal remains disabled while a draft is unapplied.

## Geometry and interaction

The structural projection lives under `workbench/diagrams/`; the workspace remains independent of React Flow and ELK. ELK calculates compound bounds from the engine's parent graph. Definitions/usages retain textual kind labels; references additionally have a `ref` label and dashed border. Typed inherited ports are selectable buttons with direction text and type labels, with handles on the appropriate boundary. The connector line itself does not assert a flow direction.
Expand Down
4 changes: 4 additions & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ The [visualization example library](../examples/README.md) records representativ
The read-only requirements table inspects all applied requirement declarations, identifiers, owned documentation and declared satisfaction/verification links. It supports search, name sorting and CSV export. The traceability matrix uses requirements as rows and linked satisfiers/verifiers as columns, retaining exact declaration identities and negation. It does not infer missing relationships, compute verification verdicts or edit model content. Unknown/incomplete extraction is unavailable rather than an empty result. Source reveal requires that file's draft to match applied source. [Requirements data contract](architecture/requirements-data.md).

The optional **Guide** in the project header introduces Explorer, layout, source application, model representations and project download for any open project. It can be dismissed and restarted, and does not alter project data.

Named-view wildcard exposure can include private imported memberships and public reexports from standard libraries. This follows exposure semantics rather than ordinary external name visibility. Use explicit element exposures and named imports to bound the selected scope; see [exposure and private imports](architecture/structural-views.md#exposure-and-private-imports). Large-view performance and readability remain separate limitations.

Authored interconnection views retain inherited structural members in their exact usage contexts. Compatible legacy node/port IDs remain reserved for unchanged saved projects; newly visible occurrences receive separate IDs. Temporary exploration and tree views retain declaration ownership. Connector selection and source-backed attribute compartments keep their existing scope. See [inherited structural occurrences](architecture/structural-views.md#inherited-structural-occurrences).
68 changes: 68 additions & 0 deletions docs/research/wp26-exposure-import-membership.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# WP26 exposure and intermediate import membership

Investigation recorded 2026-11-11 for [view-editor issue #20](https://github.com/Open-MBEE/sysml_view_editor/issues/20), on editor `890fe88d2bc31a137e156fa57d3783ca5ed7e807`, OpenSysML 0.12.0 / `760c4d1fc8edfa4e214423099065c0a7c1b24e2d`. Prepared WASM SHA-256: `9149b23d80b7fd0b6935b374f0e9102c270bc5f7d595b43a8186d01dece7b663`.

## Findings

The report's owned-only expectation is incorrect: a view exposure can admit private imported memberships. Wildcard standard-view imports bring public metamodel reexports into selection, then the renderer can expand the selected elements. Explicit element exposures and named imports are useful ways to bound that scope. They are not a repair for a visibility defect.

A separate intermediate-import defect is reproduced. The current resolver mixes flattened index reexports with namespace membership enumeration, bypassing the admission rules of intermediate imports. It also tests original declaration visibility where imported membership visibility depends on the import route. Unexpected declarations occur in the selected set before rendering.

Minimal shape:

```sysml
package ReviewSource {
public part def Visible;
private part def Hidden;
}
package Mid {
private import ReviewSource::*;
private part def OwnHidden;
}
package Probe {
private import Mid::*;
view focused : StandardViewDefinitions::GeneralView { expose Mid::*; }
}
```

The immediate exposure should select `Mid::OwnHidden` and `ReviewSource::Visible`. It also selects `ReviewSource::Hidden` at this pin. Changing the exposure to `Probe::*` should select only Probe's own view; the ordinary import of Mid cannot reexport Mid's private owned or privately imported memberships. The current pin incorrectly selects `Mid::OwnHidden`. The retained tests put some views outside the target namespaces so their expected sets do not include a view declaration accidentally.

The governing rules are [SysML 2.0 Part 1](https://www.omg.org/spec/SysML/2.0/Language/PDF), §8.3.26.2 and §8.3.26.4, and [KerML 1.0](https://www.omg.org/spec/KerML/1.0/PDF), §8.3.2.4.5–6. Exposure fixes its own `isImportAll` to true. Each selected intermediate import still invokes its own `importedMemberships` with its own `isImportAll`; an outer exposure does not recursively change every ordinary import to import-all. Imported membership visibility at the next boundary comes from its admitting import route.

## Retained upstream regression

[The executable Go fixture](../../tests/fixtures/opensysml-0.12-exposure-membership_test.go) targets the pinned upstream combined frontend package. It is a research reproduction, not a passing editor regression or a skipped test in the normal editor suite. At the current pin, seven of ten origin-selection cases fail. Ordinary-reference and native-traversal controls pass. The split-namespace control passes on the current pin and must remain passing in any proposed fix.

After normal engine preparation, from the repository root, run it without editing the prepared source:

```sh
mkdir -p sandbox/exposure-membership
python3 - <<'PY'
import json
from pathlib import Path
root = Path.cwd()
target = root / 'sandbox/opensysml-012/source/internal/frontend/combined/wp26_exposure_test.go'
fixture = root / 'tests/fixtures/opensysml-0.12-exposure-membership_test.go'
(root / 'sandbox/exposure-membership/overlay.json').write_text(
json.dumps({'Replace': {str(target): str(fixture)}})
)
PY
GOCACHE="$PWD/sandbox/exposure-membership/go-cache" \
GOMODCACHE="$PWD/sandbox/opensysml-012/go-modules" \
GOPROXY=off GOSUMDB=off GOTOOLCHAIN=local GOFLAGS=-mod=readonly \
sandbox/opensysml-012/go/bin/go -C sandbox/opensysml-012/source test \
-overlay "$PWD/sandbox/exposure-membership/overlay.json" \
./internal/frontend/combined -run TestIssue20 -count=1 -v
```

The expected failure is the exact selected-origin mismatch, not a dependency/build failure. Tests compare source declaration slices and origins, not ordinal IDs or diagram node counts. They cover ordinary/private/public/import-all routes, named and absent imports, private outside lookup, native traversal versus selected scope, and namespace declarations split across two source files.

## Rejected experiment and upstream request

An experimental resolver overlay made all ten simple selection cases pass, but dropped a valid import contributed by a second declaration of the same namespace in another document. It is not integrated. Resolver, semantics and symbols suites passed on that experiment; the final combined-package suite failed its split-namespace regression. Those partial passes are not sufficient acceptance evidence.

A safe change must enumerate memberships across every declaration and recorded library scope, preserve import-route visibility and each intermediate import's `isImportAll`, and retain valid alternate routes, filters, hiding and cycle behavior. The relevant upstream seams are `internal/semantic/resolve/filter.go` (`namespaceChildren`, `declaredChildren`, `appendNamespaceMembers`), `visibility.go`, and flattened reexport indexing in `internal/semantic/symbols/index.go`.

Request an upstream resolver/index fix with the retained tests. A corrected upstream pin is preferable; any temporary retained resolver overlay needs explicit source/patch identities, licenses, normal provenance regeneration, and full integration checks. Filtering private/library nodes in the renderer would hide the symptom while changing valid authored exposure semantics.

No safe production fix, hosted browser timing measurement, deployment or upstream acceptance is claimed. Raw diagnostic and experimental captures remain in the local session's ignored evidence directories; the fixture above retains the reproducible source-level contract independently of those directories. Issue #18's inherited-member projection is a separate mechanism and must not be used to compensate for import selection.
Loading
Loading