Skip to content

feat(model): a workspace copy rooted at a library's packages is the library - #301

Merged
HuiJun merged 51 commits into
developfrom
feature/workspace-library-version
Sep 16, 2026
Merged

HuiJun merged 51 commits into
developfrom
feature/workspace-library-version

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

What and why

The language server treated a workspace copy of a bundled library file as the user's document — derived ids, a minting code action on every declaration — where sysml -convert recognised the same bytes as the library. The workspace now applies the one recognition, so a copy rooted at a library's packages is the library in the editor as it is on the CLI.

  • Shared recognition. The roots-based recogniser moves from internal/core/export to internal/core/identity (LibraryRoot, Catalog.DocumentRootedAt, Catalog.NamesEveryRoot, LibraryVersion); export/library_names.go delegates to it and RDF behaviour is unchanged. A document qualifies when every root is a top-level package of one bundled library file that either states that package's normative id or is declared as the library declares it (library/standard keywords). Nested library elements, a foreign root id, or roots from different bundled files do not qualify.
  • Workspace stand-in lifecycle (model/library_version.go, workspace.go): a qualifying document displaces the bundled file from the overlay index, is marked with the bundled tier and its own digest, and its declarations are what the library's names resolve to. IdentityOf answers normative identity for it (and, via resolverOver(w.libBase), still for a displaced bundled file). Editing a root so it stops qualifying, closing a version whose on-disk text is the user's, or removing it restores the bundled file; several copies may coexist, and the sibling index used by ApplyEdit mirrors the arrangement.
  • A file opened under a bundled file's own name now restores that file on close. Before, Close removed the document and put nothing back, so every later document was checked against a library missing it. TestExprTypeCheckNoStdlibFalsePositives passed on develop only because of this: with the whole library present, the checker reports nine dimension defects in the published SI.sysml and USCustomaryUnits.sysml (e.g. 'Sv/s' : DoseEquivalentUnit, J*s*eV*s : TotalAngularMomentumUnit, 229835/900 [K]). Each is verified against the ISQ definitions and recorded in docs/project/omg-issues.md ("Defects in the vendored quantity libraries"); the gate pins them as an exact set — an extra finding fails as a false positive, a missing one as a regression — and TestExprTypeCheckStdlibFindingsNeedTheLibrary proves the library survives the close. Library bytes are untouched.

internal/interop/reposync/diff.go was inspected and needs no change: it does not consume the workspace-vs-library classification.

Specification basis

No specification clause moves; this is editor/CLI consistency over the project's own normative-identity rules (docs/reference/rdf-mapping.md, now naming the shared recognition for both surfaces). The nine library findings rest on SysML v2.0 §9.8.9.1 (dimension of a bound value) and, for 229835/900 [K], KerML 1.0 §8.2.5.8.1–8.2.5.8.2 (the bracket construction is a primary expression), the same reading already adjudicated for the geometry example.

How it was verified

  • New: internal/core/model/library_version_test.go (qualifying copy stands in, normative identity, no library-package or duplicate-name diagnostics, multiple copies, root rename / keyword drop / foreign id stop qualification, normative explicit id qualifies without keywords, close follows on-disk text, delete releases, nested-only / mixed / cross-file roots do not qualify, ApplyEdit through a standing copy, the exact-bundled-name case); identity/pilot_library_xmi_test.go covers DocumentRootedAt; internal/lsp/identity_test.go and hover_test.go expect (normative, KerML) hover and no mint action on a qualifying copy, and mint on a non-qualifying user file.
  • go build ./..., go vet ./..., gofmt -l . (empty), go test -count=1 ./..., make lint — all green.
  • OPENSYSML_REQUIRE_TRAINING_CORPUS=1 OPENSYSML_REQUIRE_PILOT_CORPORA=1 go test -count=1 ./internal/core/model -run 'TestTrainingExamples|TestPilotCorpora' and ./internal/core/export -run TestCorpusRoundTrip — green, no ratchet movement.
  • python3 scripts/changelog.py check, scripts/check-doc-ids.py, scripts/check-doc-links.py, make docs-counts — green (test counts regenerated).

Checklist

  • make test and make lint pass locally
  • Tests added or updated for the change
  • Documentation extended where it already covers the surface (see CONTRIBUTING.md)
  • Changelog entry added as changes/unreleased/<slug>.<section>.md, not as an edit to CHANGELOG.md
  • baselines regenerated and make docs-counts run if a gate count moved (compliance rows need nothing: the census is counted at docs build)
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

…ibrary

The language server treated a copy of a bundled library file as the user's
document (derived ids, a minting action on every declaration) where the RDF
mapping recognised the same bytes as the library. The roots-based recognition
moves from internal/core/export to internal/core/identity
(LibraryRoot, Catalog.DocumentRootedAt, LibraryVersion) and the workspace
applies it: a qualifying document displaces the bundled file from the index,
carries its tier and the copy's digest, keeps normative identity and offers
no mint action; editing a root so it no longer qualifies, or closing a version
whose on-disk text is the user's, restores the bundled file.

Closing a file opened under a bundled file's own name now restores that file
too; before, nothing was put back and later documents were checked against a
library missing it. With the whole library present the standard-library
expression gate reports nine dimension defects in the published SI.sysml and
USCustomaryUnits.sysml, pinned as an exact set and recorded in
docs/project/omg-issues.md; the library bytes are unchanged.

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

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

…library-version

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review September 15, 2026 05:08
devin-ai-integration[bot]

This comment was marked as resolved.

An edit judged in a stand-in's own document was validated over an index that
still held the bundled file the stand-in displaces: siblingIndexLocked only
removed the bundled file for other stand-ins, and the edit engine then added
the edited copy beside it, so the package and its members were declared twice
and valid edits were rejected or resolved against the wrong declaration.

edit.Model gains an Indexed hook, called after each document the edit adds to
its index. The workspace supplies standInOverLocked, which applies the live
stand-in rule to that index from the edited roots: a copy that is still a
version of a bundled file displaces it and carries its tier; a copy that
stopped qualifying gets the bundled file back.

Tests: renaming a datatype inside a standing copy is accepted and the
reference beside it follows; the validator's index holds exactly the edited
copy while it qualifies and the bundled file once a root is renamed or its
library keywords are dropped.

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

This comment was marked as resolved.

…ame leaves it indexed

A stand-in for a bundled library file removed that file's index key without
checking whether a workspace document held the name, so opening a version
beside a buffer opened under the bundled path dropped the buffer from name
resolution and diagnostics, and closing the version did not bring it back.
The workspace and the edit validator's index now leave a document holding
the bundled name where it is; the version stands beside it, and the bundled
file comes back only once neither is open.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration Bot and others added 3 commits September 15, 2026 06:44
…rts naming it

Removing the last version of a bundled file re-expands wildcard imports while the
file is still absent, and re-adding it does not expand them again, so a package
importing it lost the names until the next edit. The restore, in the workspace
and in the index an edit is judged in, now expands imports once the file is back.

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

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 3 commits September 15, 2026 14:02
…dex holds

An edit with several operations is judged in one index, re-fed each rewrite
in turn. The rule that displaces a bundled library file for a version of it,
and restores the file when the version moves off the library's roots, read
what the workspace's documents stand in for; an earlier rewrite in the same
edit may already have moved the index's own. The index now carries its own
stand-ins from the workspace's and moves them with each rewrite, so a copy
that an intermediate rewrite rooted at the library's package and a later one
moved off again is judged with the bundled file back in place.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…text, not their names

An unchanged version of a bundled file standing in under a workspace name
changed LibraryIdentity, so the runtime dropped every library-backed shape
it could have carried across a re-analysis. The identity now digests each
library document's tier and text digest only.

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

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

# Conflicts:
#	README.md
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 2 commits September 15, 2026 15:21
…fecycle

A workspace over NewWorkspaceWithIndex skipped displaceLocked, restoreLocked,
libraryVersionLocked and the edit validator's stand-in tracking whenever it
had no frozen library base, so a document opened under a marked library
file's name removed that file for good when closed, and a copy rooted at its
package was never recognised as a version. The workspace now snapshots every
marked library file (root, language, mark) at construction, restores from
that snapshot, and judges versions against a catalog of a library-only index
built once on demand; IdentityOf reads a displaced file from the same index.

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

This comment was marked as resolved.

…library files

An edit's temporary index overlays the frozen base and then re-indexes only
what the base does not hold, so documents a caller added to its overlay,
marked or not, survive ApplyEdit's validation. The library catalog records
every library document's top-level packages apart from its normative
elements, so a file MarkLibrary marks at the generic tier, which has no
normative language, is still matched and restored as a version.

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

This comment was marked as resolved.

…ary is what the index shows

A document is a version of a library file only in that file's language: text of a
.kerml file under a .sysml name was parsed as SysML, so it stays the user's file
and the bundled file stays. The library-alone catalog reuses the frozen base only
when it holds exactly the library files the index shows, so a file an overlay
shadows under a base name is recognised by its own roots.

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

This comment was marked as resolved.

…entity digests the language

The edit validator's temporary index overlays the frozen base, so a document the caller's overlay replaced under the base's name was taken from the base: an edit beside it resolved OldTanks where the workspace shows Tanks. The base is now trusted for a name only when it holds the document as the workspace's index shows it (root, language, library record).

LibraryIdentity digested each library document's tier and text only; the generic tier states no language and AddDocumentWithKind allows any, so the same bytes parsed as KerML and as SysML agreed on an identity. The language now joins the digest; the document's name still does not.

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

This comment was marked as resolved.

devin-ai-integration Bot and others added 2 commits September 15, 2026 16:36
… neither file

Catalog.roots kept whichever file's top-level package was catalogued last, so
a copy rooted at a package two marked library files share displaced one of
them arbitrarily. RootNamed and DocumentRootedAt now find no document for such
a name; the copy is the user's file and both library files stay.

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

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
devin-ai-integration[bot]

This comment was marked as resolved.

…shows

LibraryCatalog shares a frozen base's catalog only with an overlay that
holds the base's library documents as the base does; one that shadows,
removes or adds a library document gets a catalog of what it shows, so
LibraryVersion recognises a copy of the shown package and not one of the
shadowed. Workspace.LibraryDocument classifies a name before consulting
its cache.

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

This comment was marked as resolved.

devin-ai-integration Bot and others added 8 commits September 15, 2026 21:56
Open may take a bundled name between LibraryDocument's read and write
locks; the name is classified again before the bundled file is loaded so
a name the workspace now holds answers nil.

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

LibraryVersion reads the library its index holds apart from the judged
document: over a base, the base's file of that name stands where the copy
does, so a copy of a library file indexed under the file's name is its
version whether or not its bytes are the file's.

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

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
…library-version

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
…library-version

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
…library-version

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
…library-version

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 6 commits September 16, 2026 03:36
…ked files

baseHoldsLibrary reused the frozen base whenever its marked files matched the
library, so an unmarked base file the overlay removed — one annotating a library
element — still reached a displaced library file's identity. Any unmarked base
file now sends libraryAlone to build the index from the library files.

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

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
…library-version

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 9 commits September 16, 2026 13:50
…library-version

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
…library-version

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
#	internal/core/model/edit.go
#	internal/core/model/workspace.go
…ce does

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

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

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
…library-version

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

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
…library-version

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

# Conflicts:
#	internal/core/model/edit.go
@HuiJun
HuiJun merged commit 2872f7d into develop Sep 16, 2026
12 checks passed
@HuiJun
HuiJun deleted the feature/workspace-library-version branch September 16, 2026 21:56
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