Repository navigation
fix(runtime): count package-level connection usages in all T and read them as objects - #1560
Merged
Merged
Conversation
…es in all T (#1543) Co-Authored-By: jason.han <hanhuijun@gmail.com>
Contributor
Author
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
…ce-connection-usages Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # docs/project/spec-compliance.md
…h a binding class or a [n] collection (#1543) materializeMembers, the helper every namespace path makes its objects through, now materializes an owner-less connector usage through materializeConnectorAs, so a connection a bind joins before it is read, and a [2] connection, hold their ends as a bare read does; a member bound to a connector made for another usage must attach as its own connect clause says, else the binding conflicts. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…r collection's members (#1543) materializeMember is the one way a namespace path makes an object of a usage, under the next identity or a reserved one: requiredMember, which makes the members of a collection past eagerLowerBound as they are reached, now goes through it too, so connection pair : Link[1001] holds its ends on every member as a [2] does. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…behaviors start (#1543) materializeMember leaves the behaviors of the object it makes for the caller to start once the usage denotes it, as every namespace path already does for a part, so a behavior of the connector reading its own usage reaches the connector being made instead of re-entering its materialization as a cycle. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…start fails (#1543) Every namespace path that makes a connector usage's objects (occurrenceOf, occurrencesOf, a binding class, a namespace subsetting) now opens one journal over attaching, recording and starting them, so a connector whose own behavior wrote to an older object before failing leaves no write, message or object behind, as a nested connector's start does. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…s start (#1543) The journal a namespace path spans over a connector usage's attach, record and start now comes with a run boundary, as a nested connector's does: the older behaviors the connector woke answer only once it is committed, a failure of theirs is their own and the connector is kept, rather than the rollback undoing their writes under a machine that already moved. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…once its usage is recorded (#1543) connectorJournal's commit only makes the connector durable; the drain of the older behaviors it woke runs once the namespace path has recorded the usage and cleared its own guards, so a behavior reading a subsetted or bound usage it was woken through finds the recorded objects rather than a false cycle. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…fies them Co-Authored-By: jason.han <hanhuijun@gmail.com>
This was referenced Oct 27, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Closes #1543. A
connection,interfaceorallocationusage a package declares was omitted fromall Tand read as an undetermined value, while the same usage nested in a part counted and read as one object with its ends attached. The reporter's model answeredsize(all Q::Link) = 1,Q::outer = <undetermined>,Q::r violated.Root cause: one predicate, two readers.
objectUsageKind(runtime/instance.go) did not admitast.UsageConnection,ast.UsageInterface,ast.UsageAllocation, soobjectFeature,isOccurrenceUsageandnamesOneObjectwere all false for a package-level connection: the extent censusmodelUsages(runtime/extent.go,objectFeature || namesOneObject) never listed it as a root, and the bare readQ::outerfell throughoccurrenceReference → resolvedWithoutValue → withoutValueto an undetermined value whose countsize()returned. The nested walk usesextentHeldFeature, which did admit connections — henceinnercounted andouterdid not.The fix is at the predicate:
objectUsageKindadmitsUsageConnection,UsageInterface,UsageAllocation(a ConnectionUsage is a PartUsage).occurrenceOfroutes a namespace-level connector usage through the existing connector path (namespaceConnectorOccurrence→materializeConnectorAs), so its ends are attached to what theconnectclause names and the occurrence is recorded as for any namespace root;attachConnectorEndreads the ends of an owner-less connector in the usage's own namespace scope (a nested connector without an owner is still refused).materializeMember(the one way a namespace path makes an object of a usage, under the next identity or a reserved one:occurrenceOf,materializeMembersfor a[n]collection inoccurrencesOf, abindclass inresolveNamespaceClassand a namespace subsetting, andrequiredMemberfor a collection pasteagerLowerBoundmade as it is reached) materializes an owner-less connector usage's object with its ends attached (materializeConnectorStarting), leaving its classifier behaviors for the caller to start once the usage denotes it — as every one of those paths already does for a part, so a behavior reading its own usage reaches the connector being made; each of those paths spans one journal and run boundary (connectorJournal) over attaching, recording and starting a connector usage's objects, so a start that fails is undone whole, and the older behaviors the connector woke answer only once it is committed and the path has recorded the usage and cleared its own guards (namespaceCollecting,resolvingNamespaceClasses), their failure their own with the connector kept — as a nested connector's start does — soconnection pair : Link[2] connect …denotes two connectors each attached,bind c = dover two package-level connections shares one connector with its ends whichever is read first, and a[0..*]one denotes none. A member abindjoins to a connector made for another usage must attach as its ownconnectclause says (checkBoundConnectorEnds);bind c = doverconnect x to yandconnect x to ziserrBindingConflict.extentHeldFeaturedrops the three kindsobjectFeaturenow covers (action/state/flow stay there).Decision table of the widened predicate's callers
objectFeature(condition.go) →holdsObjects,extentHoldsObjects,extentHeldMember,namespaceObjectUsage, validation descentIsConnectorObjectUsage, checked beforeCompositeTypeOf) is unchanged, and the full runtime, model and corpus suites show no movement but the sweep baseline below.isOccurrenceUsage→namesOneObject,namesObjects,untypedOccurrenceUsage,denotedObjects,::reads (eval.go,signal.go,calc_usage.go,assign_chain.go)Q::outeris one object,Q::outer.a,==,!=work as nested.namesOneObject→occurrenceOfQ::outer.awas a fresh object andQ::outer.a == Q::sys.xfalse).namesObjects→occurrencesOf→materializeMembers/requiredMember→materializeMember[0..*]none,[n]n connectors each attached (a nestedconnection c : Link[2]is still refused bymaterializeConnectorFeatureValueas on develop — see the question below).resolveNamespaceClass,namespacedSubsetObjects→materializeMembers→materializeMemberbind c = dcase: the class made a bare object with no ends); members bound to a connector made for another usage are checked to attach alike.modelUsages(census roots)objectFeature/namesOneObject; no special case.extentCandidates,extentHeldFeatureobjectFeature.startClassifierBehaviorsmaterializeConnectorAsalready does for nested ones.objectUsageKindoutsideinternal/exec/runtime(grep -rn objectUsageKind— all in the runtime package); lowered IR is untouched.Out of scope, measured: package-level
action/state/flowUnchanged. Before and after (
/tmp/sysml-devbuilt from a clean worktree at develop48f60b4b8, vs this head), on a package withaction a : Act; state s : Sta; flow f of Sig from src.o to dst.i;and the same three nested inpart holder:diffof the two outputs is empty. Reading them as objects would start their behaviors as namespace roots (occurrenceOf→startClassifierBehaviors); recommended follow-up: decide whether a package-level performance is a representative object whose behavior runs, or stays model-level, and apply the same predicate route either way. The compliance row now states this.Specification basis
Verified against the rendered pinned PDFs (
build/spec-docs/sysml.txt,kerml.txt):all"evaluates to a sequence of all instances of the named type"; §7.3.2.1 "the set of things classified by a type is the extent of the type".[1..1]needs the usage to be an item usage "except if it is a connection usage" (condition 1) and owned by "a definition or another usage (not a package)" (condition 2); a package-level connection fails both, as a package-level part fails the second. The representative-object policy row ofdocs/project/spec-compliance.md(one object for a valueless usage stating and inheriting no multiplicity, none for[0..*]) is applied to it unchanged.all: it is not model-level evaluable there (docs/project/pilot-execution-referee.md), so no pilot agreement is claimed.Moves the
all Trow and the representative-object row ofdocs/project/spec-compliance.md(text only, no status change).How it was verified
Reporter's command, before (develop
48f60b4b8, clean worktree build):After (
make build-sysmlon this head):Tests:
internal/exec/runtime/testdata/conformance/extent_namespace_connections.sysml(+.expected.json):size(all Thing) == 1,size(all Link) == 5(nestedinner, package-levelouter, the two ofpair : Link[2], the onec1/c2share throughbind c1 = c2;none : Link[0..*]contributes nothing),size(pair) == 2,pair#(1) != pair#(2),pair#(2).a === sys.x,c1 == c2,c1.a === sys.x,c2.b === sys.y,size(all Tie) == 1(package-level only, no nested sibling),size(all Ifc) == 1,size(all Aloc) == 1,size(outer) == 1,outer == outer,outer != sys.inner,outer.a === sys.x,outer.b === sys.y,sys.inner.a === sys.x,size(none) == 0,wire.x === sys.px,alloc.y === sys.y.extent_namespace_connections_requirement.sysml: the reporter's model,Q::rsatisfied through the requirement path (same evaluator the Go client'sVerifyRequirementuses; confirmed by the conformancerequirementtype).extent_test.go:TestNamespaceConnectorUsagesBoundAndCounted:bind c = dread throughcfirst is one connector with both ends (c === d,c.a === sys.x,d.b === sys.y),pair : Link[2]is two connectors each attached,many : Link[1001](held lazily pasteagerLowerBound) attachesmany#(1)andmany#(1001)as they are reached,all Linkcounts 1004 and every live Link holds two ends.extent_test.go:TestNamespaceConnectorBehaviorReadsItsOwnUsage:Linkexhibits a state whose entry readscandpair— the behavior reaches the connectors being made (seen= 3 on each), not a cycle.extent_test.go:TestNamespaceConnectorWhoseStartFailsLeavesNoTrace: the namespace twin ofconnector_test.go:testConnectorWhoseStartFailsLeavesNoTrace— a package-level connector (and a[2]one) whose behavior writes to an older object, sends it a signal and then fails leaves no write, message or object behind.extent_test.go:TestNamespaceConnectorAnsweredByAFailingBehaviorIsKept: the namespace twin oftestConnectorAnsweredByAFailingBehaviorIsKept— an older listener failing to answer a package-level connector's signal reports its own failure; the connector is kept, recorded, attached, not rerun.extent_test.go:TestNamespaceSubsettedConnectorIsReadableByTheBehaviorsItWakes: a listener woken by the connector filling a subsettedlinks : Link[2]readssize(links)as 2, not a cycle.extent_test.go:TestNamespaceConnectorUsageDenotesOneObject:all Link/Ifc/Alocbefore any read,outerread twice is the same object,occurrences[outer]holds it once, both ends attached.robustness_extent_namespace_connections_test.go:TestRuntimeRobustnessExtentNamespaceConnections: an end naming an unresolvable feature (on one connector, on a[2]collection, on a connector abindjoins to another), an extent reaching it, two bound connectors attached to different features, and a connector naming itself each return a typederrConnectorEnd/errBindingConflict/ErrCyclicFeatureValue, no Link left behind, no occurrence recorded.Both conformance fixtures were red on develop before the fix (
no value for feature outer;requirement satisfied = false, want true).Baseline movement, accounted against develop
docs/project/library-and-prefix-sweep-outcomes.txt(re-recorded with-update-library-prefix-sweep), 3 rows changed, 61 added, 0 removed otherwise:object Probe::cn1,object Probe::if1,object Probe::al1:undetermined <undetermined>→object:Probe::cn1/if1/al1— the three package-level connector probes (connection cn1 : Cn1 connect p2 to p2;,interface if1 : If1 connect p1.pt to p1.pt;,allocation al1 : Al1 allocate p2 to p2;) now denote objects.readrows: the library and model features the sweep reads off every object it reaches, now read off those three — the sameItems::Item/Parts::Partset the package-levelProbe::p2already shows (boundingShapes,isSolidconstant true,self/startthe object,doneundetermined, the rest empty), plus the connector's own ends (cn1.e1/e2→object:Probe::p2,if1.p1/p2→Probe::Pt,al1.a1/a2→Probe::p2) and the library connector endssource/target(BinaryConnection,BinaryInterface,Allocation) bound to them;if1.participantis undetermined as for nested interfaces.object Probe::fl1(package-level flow) staysundetermined— the out-of-scope kinds did not move.No other fixture, golden or corpus expectation moved.
origin/develop(25c9d42f7, #1556) is merged in; the one conflict was textual, in the multiplicity-default row ofdocs/project/spec-compliance.md(develop's solver sentence kept, this PR's representative-object row kept), and the sweep baseline did not move on the merged tree.Gate (corpora fetched,
OPENSYSML_REQUIRE_TRAINING_CORPUS/PILOT_CORPORA/SPEC_EXAMPLES/PILOT_LIBRARY_XMI/PSSM_SUITE=1):go build ./...,go vet ./...,gofmt -l .empty,go test ./...— every package passes;cmd/sysmlandtests/codegenhit Go's 10 m default under the parallel run (goroutine dump, no test failure) and pass alone with-count=1 -timeout 40m(233 s and 268 s).make build-sysmlrebuilt and ran the reporter's command above.make docs-countsandpython3 scripts/changelog.py checkclean.Question for the maintainer
A package-level
connection pair : Link[2] connect sys.x to sys.y;now denotes two connectors each attached tosys.x/sys.y(asaction a[2]is two performances), while the same usage nested in a part is still refused bymaterializeConnectorFeatureValue("a connector of more than one object has no set of ends to attach"), as on develop. The nested rule is out of this PR's scope; it could follow the same route (n connectors, each attached) in a follow-up if that reading is wanted.Checklist
make testandmake lintpass locallychanges/unreleased/<slug>.<section>.md, not as an edit toCHANGELOG.mdmake docs-countsrun if a gate count moved (compliance rows need nothing: the census is counted at docs build)F4,K5) in the body, docs, or changelog