Skip to content

fix(semantics,query): serve the relationship ends and expression operators a query used to refuse - #2140

Draft
devin-ai-integration[bot] wants to merge 4 commits into
developfrom
fix/reflective-relationship-ends
Draft

devin-ai-integration[bot] wants to merge 4 commits into
developfrom
fix/reflective-relationship-ends

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

What and why

A query that read one of these metamodel properties stopped with the path error expected <Class>::<property> derived by OpenSysML, although the semantic layer already held the answer. They are now derived in the reflective layer (internal/semantic/semantics), which the query evaluator, -run-query and the symbol-backed element structure all read:

  • Relationship::source, target and relatedElement on a connector — KerML binding n of f = g;, succession n first f then g;, connector n from f to g;, SysML binding n bind a1 = a2;, succession n first a1 then a2; — as its sourceFeature, targetFeature and relatedFeature, and on dependency n from A to C; as its clients and suppliers. A connector with an end that references nothing (connector n from Missing to g;) answers none of the three, so the end that did resolve is not read as the source.
  • Disjoining::typeDisjoined / disjoiningType, FeatureInverting::featureInverted / invertingFeature, TypeFeaturing::featureOfType / featuringType on the keyword-first members disjoining n disjoint A from B;, inverting n inverse f of g;, featuring n of f by A;, with the source, target and relatedElement they redefine. Their owningType, owningFeature and owningFeatureOfType are empty, as a namespace owns them.
  • Expression::operator: . on a feature chain expression (a.b), collect on a.{in q; q}, select on a.?{in q; true}.
  • FeatureTyping::target of the typing a transition's accept trigger writes (transition t first s1 accept Sig then s2;). The trigger's parts carry no scope of their own, so the first read resolved the type against nothing and the resolver remembered the miss for every later read; they now resolve in the scope the trigger is written in. The typing of an ordinary feature f : A; or part p : A; already answered its target.

One correction rides along: a keyword-first relationship whose source is a feature chain (inverting n inverse g.f of h;) answered the chain's last feature as its source. It now leaves that end underived, as it already did for a chain target, since the end is the chain feature the relationship owns.

Overlap with #2135

#2135 landed first with a connector arm for relatedElement, source and target in ReflectiveElements. This branch merges it and keeps one arm: it reads through declaredRelationshipEnds, which reuses #2135's connectorEndRedefinition and adds what that arm did not have, the dependency ends and the unresolved-end guard. The keyword-first relationship ends, the three operators and the trigger typing do not touch #2135's code.

Specification basis

KerML 1.1 Beta 2: 8.3.4.5.3 Connector (sourceFeature redefines source, targetFeature redefines target, relatedFeature redefines relatedElement); 8.3.2.2.2 Dependency (client redefines source, supplier redefines target); 8.3.3.1.4 Disjoining, 8.3.3.3.6 FeatureInverting, 8.3.3.3.11 TypeFeaturing (each end redefines source or target; the textual grammar writes inverse featureInverted of invertingFeature); 8.3.4.8.2 CollectExpression (operator = 'collect'), 8.3.4.8.18 SelectExpression (operator = 'select'), 8.3.4.8.4 FeatureChainExpression (operator = '.'). No compliance row moves.

How it was verified

  • internal/semantic/semantics/reflective_relationship_ends_test.go: each property on the tree and on its recorded form, the unresolved and chain ends, non-relationships, the three operators, the trigger typing with no document walk before it, a connector with an unresolved end. internal/doc/queryexec/relationship_ends_test.go: one Project test per property group. Every new test fails on develop.
  • sysml <fixture> -run-query over a model with each construct returns the elements where develop returned the path error. The qvts recipe audit (scripts/check-synthetic.sh against this build) goes from 46 unverifiable probe instances to 0, with 0 mismatches.
  • internal/translate/export/testdata/structure_differential_expected.txt re-recorded, five lines against develop: Relationship::source, target and relatedElement gain agreements in the examples and pilot roots (a disjoint … from …, a featuring … of … by … and five relationships with a chain source); nothing else moves.
  • On the merge with develop after fix(semantics,export): a connector's Relationship::relatedElement, source and target are the features its relatedFeature, sourceFeature and targetFeature redefine them as — the query leg answers them for a connection, a flow and a then succession, and the RDF writer states them for a plain end, as the pilot exports them #2135: go build ./..., go vet ./..., gofmt -l . and make lint clean; go test -timeout 120m ./... passes; go test -C tools ./referee/apijson passes; the qvts audit was re-run (0 mismatches, 0 unverifiable). pilot-apijson-diff -check was not run locally; no writer under internal/translate reads the reflective layer.

Known limitations

These are outside this change and unchanged by it:

  • On the query surface a feature a row inherits from a library type is read before the metaclass property of the same name. A connection, interface, allocation or flow usage and an association therefore still read source and target as the empty Links::BinaryLink ends, and a metadata usage reads annotatedElement empty, though the reflective layer derives them: part def D; part a : D; part b : D; part def Ctx { connection n connect a to b; } projects source = (none) beside relatedElement = [a, b].
  • An association's relatedElement (relatedType, sourceType, targetType) is not derived.
  • The RDF and API JSON writers state the first-written end of inverse a of b as invertingFeature, the reverse of the grammar; the structure differential keeps those rows as differences. They also write no operator on feature chain, collect and select expressions.
  • A transition's accept trigger reflects its payload's typing on the accept, on the payload and, when the payload is named, on the named parameter, all under one element id.
  • Connector::sourceFeature and targetFeature still read the resolved end of a connector whose other end references nothing as its source; only the three Relationship properties are left underived there.
  • A dependency read from a recorded document derives no ends, as its client and supplier do not.

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

🟢 Devin Review found nothing to fix · View findings · Follow Devin's work

Devin Review

…ators a query used to refuse

Relationship: :source, target and relatedElement answer on a connector as the features its ends reference and on a dependency as its clients and suppliers; a keyword-first disjoining, inverting or featuring answers the ends its metaclass declares; Expression::operator reads '.', 'collect' and 'select' on a feature chain, collect and select expression; the typing a transition's accept trigger writes resolves in the scope the trigger is written in. A keyword-first relationship's chain source stays unresolved, as its chain target does.
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

…rs no source, target or relatedElement

The resolved end was read as the source, since the related features drop the missing position before the first is taken.

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

@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 found 1 finding. Devin automatically fixed it.

Fixed by Devin (1)
  • Unresolved connector end shifts source — A connector with an end that references nothing (connector cn from Missing to g;) read the resolved end as its source. It now leaves source, target and relatedElement underived; TestConnectorRelationshipEndsUnresolved covers it.

View findings in Devin Review · Follow Devin's work

Devin Review

devin-ai-integration Bot and others added 2 commits November 11, 2026 19:49
Keeps one arm for a connector's relatedElement, source and target: it reads through declaredRelationshipEnds, which reuses connectorEndRedefinition and keeps the dependency ends and the unresolved-end guard.

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

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

# Conflicts:
#	internal/translate/export/testdata/structure_differential_expected.txt

This branch has not been deployed

No deployments
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