You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have verified that I am running the latest version of the SysML2.NET
I have searched open and closed issues to ensure it has not already been reported
Description
There is no layer that composes REST → Dal → Semantics → Serializer.TextualNotation. SysML2.NET.Dal and SysML2.NET.Semantics each reference only SysML2.NET.Extensions, SysML2.NET.REST references only SysML2.NET.Serializer.Json, and ISession is a stub (Open/Close). Every consumer therefore wires the pipeline itself, and two contracts end up with no owner.
1. Identity scope is per loader, so the same element exists as several POCO instances.
DeSerializer creates its XmiDataCache in its constructor and ModelLibraryLoader news up a private DeSerializer, so a library load and a model load produce disjoint object graphs. Elements are deduplicated within a load (keyed on IData.Id, which is parsed from xmi:id and therefore stable) but not across loads.
POCOs use reference equality, and NameResolutionCache relies on ReferenceEquals in 15+ places, so a foreign instance can never match a resolution target — it answers Shadowed and stops the outward walk. This is currently patched inside NameResolutionCache.TranslateToResolutionGraph, which maps implied generals back into the writer's graph by Id. That fix is private to the writer, is untyped (nothing marks an instance as foreign), and every future consumer of ILibraryTypeIndex or the implied layer will hit the same trap.
2. Nothing owns "the library index is complete".
ILibraryTypeIndex must be populated eagerly, before any resolution runs, and must not fault libraries in on a miss — doing so re-enters the bootstrap cycle the implied layer exists to break. No component enforces this. It becomes acute for REST clients, where libraries live in a separate project/commit and RequestElements can be paged: rebuilding the index per response yields an index that looks valid but is incomplete, producing silently missing implied specializations instead of a loud UnresolvedLibraryTypeException. Completeness is a state transition, not a per-response side effect.
Proposed approach
Add a transport-agnostic composition layer (working name SysML2.NET.Workspace, referencing Dal + Semantics) fed by either REST or Serializer.Xmi:
Owns a single IAssembler, making its ConcurrentDictionary<Guid, Lazy<IElement>> the one identity scope. One instance per Id removes the duplication by construction for both transports — the XMI path currently has no shared store at all.
Exposes an explicit libraries complete transition. Implied computation and textual-notation writing are unavailable before it; after it the index is built once from the assembled graph, with explicit invalidation when a later Synchronize touches library elements.
Builds ILibraryTypeIndex via the existing OwnershipTreeLibraryTypeIndex.Build(roots) — whose parameter doc already says "typically the referenced Namespaces reported by a deserializer", i.e. the consumer's own graph. Library roots can be selected from the graph itself via Element::isLibraryElement (implemented as LibraryNamespace() != null), avoiding threading project/commit provenance through the DAL.
Deliberately not doing:
Populating the index from Assembler. That would make Dal depend on Semantics, and hands an incremental component (Synchronize is additive and handles deletes) an invariant it cannot check, since it does not know when resolution runs.
Extending SysML2.NET.REST.Session to do this, which would couple transport to semantics and leave the file path unable to reuse it. Session should feed the new layer instead.
Changing ILibraryTypeIndex. TryGetType(qualifiedName, out IType) is the right seam, and AddSysML2Semantics already declines to register one — the new layer simply becomes that caller.
Follow-ups once the invariant holds: NameResolutionCache.TranslateToResolutionGraph / BuildResolutionGraphIndex / IsInResolutionGraph become dead and should be deleted rather than kept as a silent safety net, and DeSerializer may want a constructor accepting an externally supplied IXmiDataCache.
Steps to Reproduce
System Configuration
SysML2.NET version: 0.22.0
Environment (Operating system, version and so on): Windows 11
What type of issue is this?
Prerequisites
Description
There is no layer that composes
REST→Dal→Semantics→Serializer.TextualNotation.SysML2.NET.DalandSysML2.NET.Semanticseach reference onlySysML2.NET.Extensions,SysML2.NET.RESTreferences onlySysML2.NET.Serializer.Json, andISessionis a stub (Open/Close). Every consumer therefore wires the pipeline itself, and two contracts end up with no owner.1. Identity scope is per loader, so the same element exists as several POCO instances.
DeSerializercreates itsXmiDataCachein its constructor andModelLibraryLoadernews up a privateDeSerializer, so a library load and a model load produce disjoint object graphs. Elements are deduplicated within a load (keyed onIData.Id, which is parsed fromxmi:idand therefore stable) but not across loads.POCOs use reference equality, and
NameResolutionCacherelies onReferenceEqualsin 15+ places, so a foreign instance can never match a resolution target — it answersShadowedand stops the outward walk. This is currently patched insideNameResolutionCache.TranslateToResolutionGraph, which maps implied generals back into the writer's graph byId. That fix is private to the writer, is untyped (nothing marks an instance as foreign), and every future consumer ofILibraryTypeIndexor the implied layer will hit the same trap.2. Nothing owns "the library index is complete".
ILibraryTypeIndexmust be populated eagerly, before any resolution runs, and must not fault libraries in on a miss — doing so re-enters the bootstrap cycle the implied layer exists to break. No component enforces this. It becomes acute for REST clients, where libraries live in a separate project/commit andRequestElementscan be paged: rebuilding the index per response yields an index that looks valid but is incomplete, producing silently missing implied specializations instead of a loudUnresolvedLibraryTypeException. Completeness is a state transition, not a per-response side effect.Proposed approach
Add a transport-agnostic composition layer (working name
SysML2.NET.Workspace, referencingDal+Semantics) fed by eitherRESTorSerializer.Xmi:IAssembler, making itsConcurrentDictionary<Guid, Lazy<IElement>>the one identity scope. One instance perIdremoves the duplication by construction for both transports — the XMI path currently has no shared store at all.Synchronizetouches library elements.ILibraryTypeIndexvia the existingOwnershipTreeLibraryTypeIndex.Build(roots)— whose parameter doc already says "typically the referenced Namespaces reported by a deserializer", i.e. the consumer's own graph. Library roots can be selected from the graph itself viaElement::isLibraryElement(implemented asLibraryNamespace() != null), avoiding threading project/commit provenance through the DAL.Deliberately not doing:
Assembler. That would makeDaldepend onSemantics, and hands an incremental component (Synchronizeis additive and handles deletes) an invariant it cannot check, since it does not know when resolution runs.SysML2.NET.REST.Sessionto do this, which would couple transport to semantics and leave the file path unable to reuse it.Sessionshould feed the new layer instead.ILibraryTypeIndex.TryGetType(qualifiedName, out IType)is the right seam, andAddSysML2Semanticsalready declines to register one — the new layer simply becomes that caller.Follow-ups once the invariant holds:
NameResolutionCache.TranslateToResolutionGraph/BuildResolutionGraphIndex/IsInResolutionGraphbecome dead and should be deleted rather than kept as a silent safety net, andDeSerializermay want a constructor accepting an externally suppliedIXmiDataCache.Steps to Reproduce
System Configuration
08-Requirements([Feature]: Textual Notation validation — 08-Requirements (Requirements) #295); the writer-side workaround is described inNameResolutionCache.TranslateToResolutionGraph