Spec-vs-Pilot Gap Register¶
Overview¶
OpenSysML is validated largely by differential harnesses against the OMG SysML v2 pilot
implementation. Those harnesses find where OpenSysML disagrees with the pilot; they cannot find
where both fall short of, or read ambiguously, the KerML 1.0 and SysML v2 specification text.
The redefinition of a feature chain (attribute :>> mid.leaf.value = 99.0;) was such a case: both
tools accepted it and both silently gave it no effect, until the specification authors confirmed
that the chain's host feature is redefinable (fixed in #634).
This register lists every place found where OpenSysML's behaviour rests on "the pilot does this" or on no recorded rationale at all, and where the pilot itself is known or suspected to fall short of, or to interpret ambiguously, the specification. Each retained item carries the model text, what each tool does, the clauses that bear on it, an assessment, and a question that could be put to the specification authors. Items are ranked by modeler impact: silent wrong results > spurious errors > missed diagnostics > cosmetic.
Pilot: SysML v2 Pilot Implementation
release 2026-08, commit 692170b71867353b8f90341e61556f49a5beb0e5 — the pin in
scripts/pilot-pin.sh. Where a source record observed an earlier release (2026-05, 2026-07),
the item says so; those observations have not been re-run against 2026-08 for this register
unless stated.
Specification text: the clause numbers and quotations below were taken from the OMG
publications downloaded on 2026-09-27: KerML 1.0 at https://www.omg.org/spec/KerML/1.0/PDF,
whose title page reads formal/2026-03-01, and SysML v2.0 Part 1 (Language) at
https://www.omg.org/spec/SysML/2.0/Language/PDF, formal/2026-03-02. The KerML revision
requested for this audit, formal/2025-12-01, is not what that URL serves today and no copy exists
in the repository or the pilot pin (scripts/download-pilot-grammars.sh fetches the pilot's Xtext
grammars, not the OMG documents). Clause numbers are therefore those of formal/2026-03-01/-02; a
reader checking them against formal/2025-12-01 should expect the same numbering but must verify.
Every "the specification says" statement below is backed by text found in those two PDFs; every
"the pilot does" statement is either an observation recorded in the linked docs/project/ record
or a reading of the pinned validator source, and is labelled as one or the other.
The register does not change any behaviour. Behaviour changes are follow-ups, each of which should cite the item it closes.
Summary table¶
The Class column is the audit's verdict on the item: spec clear, both short (the specification says X, both tools do Y), spec ambiguous (needs an author opinion), pilot short, ours follows spec (a pilot gap this register keeps because differential runs alone would have accepted the pilot's silence), or pilot-following justified (a candidate that turned out to rest on a clause after all — retained so it is not re-audited).
| # | Impact | Item | Pilot | OpenSysML | Class |
|---|---|---|---|---|---|
| 1 | Silent wrong result | Redefinition whose target is a feature chain (the motivating case) | accepts, no effect | redefines the chained feature (since #634) | spec clear (author-confirmed); pilot short |
| 2 | Silent wrong result | Natural / Natural evaluates to a Rational against a Natural[1] return declaration |
evaluates 5/2 to 2.5 |
types the operator Rational, as the pilot; the named function NaturalFunctions::'/' follows its declaration |
spec ambiguous (library text vs. evaluator) |
| 3 | Silent wrong result | Two enabled branches of one choice (state machine or decide); which is taken |
no executor; not exercised | first enabled in declaration order | spec silent |
| 4 | Silent wrong result | Filter conditions that read through a multi-hop feature chain, or a computed value, rooted in an unfeatured feature | evaluates them | reported as not evaluated and the filter not applied; fixed in #642 | spec clear; ours short |
| 5 | Silent wrong result | Repeated literal values bound to a unique multi-valued feature (Integer[*] = (1, 1)) |
silent | reports the duplicate | spec clear; pilot short |
| 6 | Silent wrong result | Invocation leaving a required in parameter unbound (F(1.0) with two inputs) |
silent | advisory only | spec ambiguous (no constraint stated) |
| 7 | Spurious error | Bare import X::*; without a visibility indicator |
parse error | warning, then imports | pilot-following justified by the grammar; severity is ours |
| 8 | Spurious error | send x to port warned to use via |
warning (checkSendActionUsage) |
same warning | spec has no such constraint; pilot-following |
| 9 | Spurious error | validateClassifierMultiplicityDomain fired on a classifier reached through an alias |
error on a valid model | not reported | spec clear; pilot short (filed upstream) |
| 10 | Spurious error | A calculation usage named as a value is the calculation, not its result (n as String) |
warns | warns, as the pilot | spec ambiguous |
| 11 | Missed diagnostic | Eight control-node succession constraints (SysML §8.3.17) | unimplemented TODOs |
reported | spec clear; pilot short |
| 12 | Missed diagnostic | Non-Boolean transition guard | accepted with the full library | rejected | spec clear; pilot short |
| 13 | Missed diagnostic | Indistinguishable memberships reported as a warning; repeated anonymous perform a; depends on bodies in the pilot |
warning; silent for bodiless repeats | warning for every repeat | spec clear (a validation constraint), both tools under-report |
| 14 | Missed diagnostic | Constraints the pilot declares but never reports, absent from the published specification (validateSubsettingPortionConformance, validateBindingConnectorArgumentTypeConformance, validatePartUsageType, validateItemUsageType, …) |
declared, unreported | not implemented | needs author opinion: are these normative? |
| 15 | Missed diagnostic | Lower-tier errors suppress later diagnostics the pilot still reports | reports secondary diagnostics over unresolved names | gates higher tiers | not a spec matter; recorded so it is not mistaken for one |
| 16 | Missed diagnostic | Body expression { … } as a value typed BooleanEvaluation when its result is Boolean |
so typed | as the pilot | spec ambiguous (implied typing of an expression body) |
| 17 | Cosmetic | Identifiers restricted to ASCII letters, digits and _ |
rejects non-ASCII basic names | same | pilot-following justified by KerML §8.2.2.3 |
| 18 | Cosmetic | Every reference subsetting after the first is reported | reports each extra | same | pilot-following justified by KerML §8.3.3.3 (at most one) |
| 19 | Cosmetic | part p : ItemDef; — a part typed only by a non-part definition |
accepted | accepted | pilot-following justified: parts supplies Part through subsetting |
| 20 | Cosmetic | Alias identity: an alias is a name, not an element |
resolves to the aliased element | fixed to match | pilot-following justified by KerML §8.3.2.4 (Membership) |
| 21 | Spurious error | A succession whose ends are qualified names (first r::move then r::grip; at package level) |
Must be an accessible feature (use dot notation for nesting) at each end; the same ends on a connect are accepted |
accepted, featured by the ends' innermost common featuring type, and executed | spec clear (KerML §8.3.4.5.3); pilot short |
| 22 | Spurious error | A KerML metaclass as the type of a SysML metadata usage (metadata m : KerML::Classifier;), the case KERML-90 was resolved to admit |
three errors | one error | spec ambiguous: the text both tools follow does not achieve the KERML-90 resolution |
| 23 | Cosmetic | The implied subsetting of outgoingHBLink by a decision node's outgoing successions and of incomingHBLink by a merge node's incoming ones (SYSML21-306) |
adds both, from DecisionPerformance where the OCL names MergePerformance |
adds neither; its checks and execution do not consult them | spec defect (OCL, filed); ours short |
| 24 | Spurious error | The default [1..1] of a usage (SysML §7.6.3, non-normative: SYSML21-185) |
by metaclass; any owned subsetting of a type-owned feature withholds it, but a subsetting of a package-owned feature does not | the same | spec ambiguous (prose only); both follow the pilot's rule for a package-owned target |
| 25 | Cosmetic | The declaration production of a case usage (SYSML2-783) | ActionUsageDeclaration |
the same syntax | no observable difference |
| 26 | — | Five validation constraints the pilot source marks TODO |
implemented under the TODO |
implemented | no difference; the TODOs are stale |
| 27 | Spurious error | An explicitly declared subject or return parameter of a variation usage | accepted: every ParameterMembership is exempt |
rejected: only objectives are exempt | spec clear (validateUsageVariationOwnedFeatureMembership), both short for objectives; pilot short for parameters |
| 28 | Cosmetic | A negative model-level-evaluable multiplicity bound (KERML-199) | rejected, by a -2 marker the OCL does not have |
rejected | spec OCL short of its prose; both follow the prose |
Grouped inventories follow the detailed items: the spec-compliance.md rows whose
justification is the pilot or nothing,
the code comments that rest on the pilot,
and the negative-corpus cases.
Detailed items¶
1. Redefinition whose target is a feature chain¶
Model text
part def Leaf { attribute value : Real = 1.0; }
part def Mid { part leaf : Leaf; }
part def Top { part mid : Mid; attribute :>> mid.leaf.value = 99.0; }
Pilot: parses the declaration and reports nothing; mid.leaf.value keeps 1.0
(observation recorded in the #634 discussion; not a diagnostic difference, so no differential
harness saw it).
OpenSysML: the same until #634; since #634 the chain's host feature redefines value and the
evaluator answers 99.0.
Specification: KerML §7.3.4.6 Feature Chaining — a feature chain in a feature declaration
"parses to a feature hosting the chain"; §8.3.3.3.4 Feature: chainingFeature, and
deriveFeatureType: "If the Feature has chainingFeatures, then the union also includes the types
of the last chainingFeature." §8.3.3.3.8 Redefinition places no restriction on a redefined
feature being chained. The reading that the host is redefinable was confirmed with the
specification authors (recorded in #634).
Assessment: spec clear, both tools were short; ours is fixed. Kept as item 1 because it is
the template for everything below: no rule in spec-compliance.md cited a clause for it, and the
implicit justification was the pilot's silence.
Question for the authors: none outstanding — closed by the confirmation recorded in #634.
2. Natural / Natural — the declared Natural[1] return against the pilot's Rational answer¶
Model text
attribute a : ScalarValues::Natural = 5;
attribute b : ScalarValues::Natural = 2;
attribute q : ScalarValues::Natural = a / b;
Pilot: evaluates a / b to LiteralRational 2.5 and reports nothing on q
(observation, pilot 2026-05, recorded in omg-issues.md).
Its evaluator dispatches on the value's kind, so the division runs through
RationalFunctions::'/'.
OpenSysML: the type checker types Natural/Natural and Integer/Integer division as
Rational "as the reference evaluator types it" (spec-compliance.md, operator result types);
the runtime answers a Real, and a non-whole quotient bound to a Natural-typed feature is
reported rather than truncated. The function called by name, NaturalFunctions::'/'(a, b),
follows its declaration and reports a non-whole quotient as an arithmetic-domain error.
Specification: KerML §9 Kernel Function Library, NaturalFunctions:
function '/' specializes IntegerFunctions::'/' { in x: Natural[1]; in y: Natural[1]; return : Natural[1]; }
(quoted from the vendored library, which is the normative library text). IntegerFunctions::'/'
returns Rational[1]. A specializing function's return that is Natural where the general
function's is Rational is well-formed as a redefinition; the text says nothing about how an
operator on two Natural operands is dispatched.
Assessment: spec ambiguous. The library declares a Natural result and the pilot never
produces one; OpenSysML follows the pilot for the operator and the declaration for the named
function, which is two answers to one question. A modeler binding a / b to a Natural attribute
gets a silently non-integral value from the pilot and an error from us.
Question for the authors: is NaturalFunctions::'/' meant to return Rational[1] (matching
IntegerFunctions::'/'), or is a conforming evaluator expected to truncate (or reject) a
non-whole quotient of two Natural operands written with the / operator?
3. Two enabled branches of one choice¶
Model text (the shape of fixture state_choice_dynamic_conflict)
state def M {
attribute level : ScalarValues::Integer; attribute route : ScalarValues::Integer;
state idle; state low; state high;
transition first idle accept Go do assign level := 8 then pick; // pick is a choice
transition first pick if level > 5 then low; // both guards hold
transition first pick if level > 7 then high;
}
Pilot: the pinned pilot has no state-machine executor; no observation.
OpenSysML: reads the branches in declaration order and takes the first enabled one, recording
the choice; exploration reports the admissible set {low, high} (recorded as Open: which
enabled branch is taken in behavior-semantic-oracle.md).
Specification: ControlPerformances.kerml DecisionPerformance::outgoingHBLink :
HappensBefore[1] — exactly one branch follows; TransitionPerformances.kerml places the
segment's effect before what the segment leads to, so both guards are read after level := 8.
Nothing found in the library or in SysML §8.3.18 (TransitionUsage) ranks two branches whose
guards both hold. (SysML §8.3.17 DecisionNode has the same shape for actions.)
Assessment: spec silent. Any deterministic choice is an implementation policy; a model whose guards overlap has an execution result that differs between conforming executors without any diagnostic.
Question for the authors: when more than one outgoing transition of a choice (or succession
of a decide node) has a guard that evaluates true, is the choice implementation-defined, is
declaration order normative, or should a validator report the overlap?
4. Filter conditions read through a feature chain¶
Model text
package P {
feature root { feature n : ScalarValues::Integer = 1; feature m : ScalarValues::Integer = n + 1;
feature inner { feature k : ScalarValues::Integer = 3; } }
}
package Q { private import P::*[root.inner.k == 3]; } // likewise [root.m == 2]
Pilot: evaluates the condition (recorded in the code comment chainLimitation of
internal/semantic/semantics/filter.go before #642: "the reference accepts chains rooted in a
feature with no featuring type").
OpenSysML: before #642, only a one-hop chain to a literal value (root.n == 1) was evaluated;
root.inner.k and root.m were reported as not evaluated and the filter was not applied. #642
evaluates every hop and the read feature's value expression; a chain through a feature with its
own value, a non-numeric/boolean terminal value, or a metaclass feature still reports the
limitation.
Specification: KerML §8.3.4.13.2 ElementFilterMembership,
validateElementFilterMembershipConditionIsModelLevelEvaluable: "The condition Expression must be
model-level evaluable." §8.3.4.8.5 FeatureReferenceExpression::modelLevelEvaluable: evaluable
if the referent "has no featuringTypes and, if it has a FeatureValue, the valueExpression is
model-level evaluable." §8.3.4.8.4 FeatureChainExpression is an OperatorExpression over its
argument, so a chain rooted in an unfeatured feature is evaluable when the rooted reference is.
Assessment: spec clear, ours is short and the pilot is right; the limitation is honest (reported, not silent) but a filtered import with an unfiltered result is a wrong model. Listed because the only rationale recorded is the pilot's acceptance, not the clause above.
Question for the authors: none needed for the rule; the follow-up is #642.
5. Repeated values bound to a unique feature¶
Model text
attribute xs : ScalarValues::Integer[*] = (1, 1);
attribute os : Collections::OrderedSet { :>> elements = (1, 1, 2); }
Pilot: silent (observation, pilot-differential.md).
OpenSysML: reports the repeated value on the unique feature.
Specification: KerML §7.3.4.2: "The default is that the feature is unique"; §8.3.3.3.1
+isUnique : Boolean = true; nonunique is the one piece of concrete syntax that sets it
false. Collections.kerml declares Collection::elements nonunique and every redefinition
carries a note "Redefinition of 'elements' is unique by default", with no nonunique keyword —
the redefinition therefore is unique by the §7.3.4.2 default (the note is descriptive).
Recorded in omg-issues.md.
Assessment: spec clear, pilot short; kept because a differential run adjudicated it as
"only ours" and a maintainer could reasonably have read that as a spurious diagnostic. The
residual question is about the library: whether the UniqueCollection redefinition relying on the
default is intended, or whether Collection::elements being nonunique was meant to be inherited.
Question for the authors: is a value binding that repeats a value on a feature with
isUnique = true a well-formedness error (to be reported by a validator), an evaluation-time
matter, or neither?
6. Invocation leaving a required input parameter unbound¶
Model text
Pilot: the SysML and KerML validators report nothing; the evaluator forms and evaluates the
call (observation, pilot 2026-07, omg-issues.md).
OpenSysML: adjudicated the pilot's behaviour as the specification's reading and reports an advisory only.
Specification: KerML §8.3.4.8.8 InvocationExpression constrains the arguments an
invocation writes (validateInvocationExpressionParameterRedefinition,
validateInvocationExpressionNoDuplicateParameterRedefinition) and states no constraint on
parameters left without an argument. A bare input parameter has the effective range [0..*];
y[1] explicitly requires one value, so the instance described has a feature with no value
where one is required, but no constraint on the expression says so.
Assessment: spec ambiguous; both tools accept, and a modeler gets a result computed with an unbound operand (the pilot) or an advisory (ours). This is the closest analogue to item 1: the "validates clean" verdict rests on the pilot.
Question for the authors: is an InvocationExpression that leaves an input parameter of the
invoked type unbound — no argument, no default, lower bound 1 — intended to validate clean, and
if so what value does its result have?
7. Bare import without a visibility indicator¶
Model text
Pilot: mismatched input 'import' expecting '}' — a parse error that also breaks the rest of
the file (observation, pilot-differential.md).
OpenSysML: parses it, reports a warning, and imports as if private.
Specification: KerML §8.2.3.4.2 Import = visibility = VisibilityIndicator 'import' ...
with VisibilityIndicator = 'public' | 'private' | 'protected' — the assignment is not optional,
unlike the member prefix, where the indicator is. The SysML grammar (SysML.xtext
ImportPrefix) matches.
Assessment: pilot-following is justified by the grammar clause; the specification is not
ambiguous. What is ours alone is the recovery (warning rather than error), which
pilot-differential.md records as deliberate. Listed because the project record says the
question of mandatory visibility "is not settled", which the grammar text above settles.
Question for the authors: none about the grammar. A question worth putting is whether a
tool may recover a bare import as private import (the pilot's reading of the default
visibility for members) without being non-conforming.
8. send … to port warned to use via¶
Model text
Pilot: warns that a receiver that is a PortUsage should be given with via
(SysMLValidator.checkSendActionUsage; reading of the pinned source, and observed).
OpenSysML: validateSendActionUsageReceiver — the same warning, "the specification places no
such constraint, so the diagnostic is a warning as the reference's is" (spec-compliance.md).
Specification: SysML §8.3.17.15 SendActionUsage: deriveSendActionUsageReceiverArgument
("The receiverArgument of a SendActionUsage is its third argumentExpression"),
validateSendActionParameters; nothing in the constraint list distinguishes a port from any other
receiver. The via clause populates the sender/receiver pair per §7.16 textual notation, but
the text found does not forbid a port as a to target.
Assessment: the specification has no such constraint; the warning exists because the pilot's does. It is advisory, so its cost is a spurious warning on a model the specification accepts.
Question for the authors: is send x to <port> (a PortUsage as receiver, rather than as
the via argument) meant to be discouraged or ill-formed, or is the pilot's warning a style
preference?
9. validateClassifierMultiplicityDomain fired through an alias¶
Model text
Pilot: reports "A classifier's multiplicity has no featuring type" on a valid model in which
the classifier is reached through an alias or reference membership (observation, shape recorded
in validation-constraints.md and
omg-issues.md;
filed as Systems-Modeling/SysML-v2-Pilot-Implementation#802 and fixed upstream
on ST6RI-975 (commit 1563e068, 2026-10-02; not yet included in a release).
OpenSysML: does not report it; the multiplicity is owned by C, which has no featuring type.
Specification: KerML §8.3.3.2.2 Classifier, validateClassifierMultiplicityDomain: the
multiplicity of a Classifier, if any, must have no featuringType. §8.3.3.1.10 Type:
multiplicity is the Multiplicity among the type's owned members, not one reached through an
alias or reference membership.
Assessment: spec clear, pilot short; OpenSysML declines to follow the pilot. Retained so that the next differential run does not re-open the disagreement.
Question for the authors: none; this is a pilot defect already filed upstream.
10. A calculation usage named as a value is the calculation, not its result¶
Model text
calc def Name { return : ScalarValues::String; }
part def P { calc n : Name; attribute s = n as ScalarValues::String; }
Pilot: warns that n does not conform to String (observation recorded in the code comment
in internal/semantic/semantics/operator_conformance.go).
OpenSysML: featureResultTypes treats n as the calculation, as the pilot does, and warns.
Specification: KerML §7.4.9.4 Base Expressions — a feature reference expression whose
referent is an Expression evaluates to the result of that expression when it is
model-level evaluable (§8.3.4.8.5); SysML §8.3.19.3 CalculationUsage is an Expression. Whether
naming a calculation usage as an operand denotes the calculation (a feature typed by a function)
or its result (its result parameter) is not stated in one place; the two readings give
different conformance verdicts.
Assessment: spec ambiguous. The consequence is a warning on a model that, under the other reading, is well-typed.
Question for the authors: when a calc usage is named as an operand of a cast or an operator,
does the name denote the calculation feature (typed by the calculation definition) or its
result parameter?
11. Eight control-node succession constraints the pilot does not implement¶
Model text
action def A {
fork f; action a; action b;
first a then f; first b then f; // two incoming successions to a fork
}
Pilot: SysMLValidator declares nine ControlNode constants and reports only
validateControlNodeOwningType; the eight succession constraints are TODOs (reading of the
pinned source, recorded in omg-issues.md).
OpenSysML: reports each (internal/check/passes/control_node.go); the nine ours-only
negative-corpus cases (inventory C) are these.
Specification: SysML §8.3.17.2–§8.3.17.6 ControlNode, DecisionNode, ForkNode,
JoinNode, MergeNode — e.g. validateForkNodeIncomingSuccessions "A ForkNode must have at
most one incoming Succession", validateJoinNodeOutgoingSuccessions,
validateMergeNodeOutgoingSuccessions, validateDecisionNodeIncomingSuccessions, and the
outgoing/incoming counterparts.
Assessment: spec clear, pilot short, ours follows the specification. Retained because the pilot's silence on these would have been indistinguishable from acceptance in a differential run.
Question for the authors: none.
12. Non-Boolean transition guard¶
Model text
Pilot: checkTransitionFeatureMembership implements the check and its own Xpect fixture
expects "Must be a Boolean expression." — but only with a reduced library; with the full library
loaded the guard is accepted (observation, pilot 2026-07,
omg-issues.md).
OpenSysML: rejects the guard.
Specification: SysML §8.3.18.8 TransitionFeatureMembership,
validateTransitionFeatureMembershipGuardExpression: a guard transitionFeature must be an
Expression whose result specializesFromLibrary('ScalarValues::Boolean').
Assessment: spec clear, pilot short in its shipped configuration.
Question for the authors: none.
13. Indistinguishable memberships: severity, and anonymous performed actions¶
Model text
Pilot: Duplicate of other owned member name as a warning; for perform a; perform a;
it warns only when the repeats have bodies (observation, pilot 2026-07,
adjudications.md). On part def A; attribute def A;, and on the KerML
class A; datatype A;, it also warns Duplicate of other owned member name, twice: its
Membership_isDistinguishableFrom_InvocationDelegate.java compares the names only and carries
// TODO: Add member element metaclass check (pilot 2026-08). The same omission accounts for
55 of the 76 Duplicate of inherited member name warnings its Xpect suite declares beside a
typing error — for example 'self' from Action, Part at ActionUsage_invalid.sysml.xt:40,
where Actions::Action::self is an ActionUsage and Parts::Part::self a ReferenceUsage —
and for the 'p' from A2 warning of RedefinitionDiamond_Invalid.sysml.xt and
RedefinitionDiamond1_invalid.sysml.xt, where part p (a PartUsage) meets the inherited
p :>> p (a ReferenceUsage).
OpenSysML: a warning for every repeat, bodies or not; part def A; part def A; is likewise
a warning. part def A; attribute def A; and class A; datatype A; are clean, as are the 55
diamonds and the two RedefinitionDiamond members above: two memberships whose member elements'
metaclasses conform in neither direction are distinguishable whatever their names
(resolve.Resolver.DistinguishableByMetaclass). Same or specializing metaclasses
(part def A; item def A;, class A; class A;, the Part/Port and DataValue/Occurrence self
diamonds) still warn, and so does a pair where either metaclass is unknown or an alias target
does not resolve. The Xpect harness builds each fixture from the library files its XPECT_SETUP
names, which never include SysML.sysml or KerML.kerml, so no metaclass is known there and
those rows still agree in pilot-xpect.md.
Specification: KerML §8.3.2.4.5 Namespace, validateNamespaceDistinguishibility (so spelt in the PDF): "All
memberships of a Namespace must be distinguishable from each other." §8.3.2.4.3
Membership::isDistinguishableFrom compares memberShortName/memberName only where the
member elements' metaclasses are related: its first disjunct,
not (memberElement.oclKindOf(other.memberElement.oclType()) or other.memberElement.oclKindOf(memberElement.oclType())),
makes two memberships distinguishable whatever their names when neither member element's
metaclass is the other's or a specialization of it. §8.3.3.3.4
Feature::effectiveName: a feature with no declared name takes the effective name of its
namingFeature(); SysML §8.3.17.14 PerformActionUsage::namingFeature "is its performedAction".
Two perform a; therefore both have memberName a, whatever their bodies.
Assessment: spec clear on both counts. The rule is a validate… constraint — a violating model
is not well-formed — so a warning under-reports it; both tools do so (a warning was chosen so that
duplicated names in the training corpus do not fail its clean gate). The body-sensitivity is the
pilot's alone; ours follows the specification. On the metaclass clause the specification is
clear and the pilot is short: it does not implement the clause, and ours does.
Question for the authors: is a namespace with indistinguishable memberships intended to be
rejected (an error) or is a tool free to report it as a warning and keep resolving? And for
two perform a; in one body, are the memberships indistinguishable regardless of whether the
usages have bodies?
14. Constraints the pilot declares that the published specification does not contain¶
Model text
classifier C { portion feature p : C; }
classifier D :> C { feature q :> p; } // subsets a portion feature, is not itself a portion
Pilot: KerMLValidator/SysMLValidator declare the constants and messages
validateSubsettingPortionConformance ("A feature subsetting a portion feature is a portion"),
validateBindingConnectorArgumentTypeConformance, validatePartUsageType,
validateItemUsageType and others listed under declared, unreported in
validation-constraints.md; none is reported by any @Check
(reading of the pinned source).
OpenSysML: does not implement them.
Specification: none of these names appears in KerML formal/2026-03-01 or SysML
formal/2026-03-02 (a search of both extracted texts). The neighbouring constraints that do
exist are cited in validation-constraints.md. validatePartUsagePartDefinition (SysML
§8.3.11.3) exists and is satisfied by checkPartUsageSpecialization for every part usage (item
19); validatePartUsageType does not.
Assessment: needs author opinion. Either these are constraints that were dropped from the published text and the pilot's declarations are stale, or they are intended and the text is missing them. Neither tool reports them, so a model violating one passes both.
Question for the authors: are validateSubsettingPortionConformance and
validateBindingConnectorArgumentTypeConformance (a feature subsetting a portion feature must be
a portion; a binding connector's ends must have conforming types) normative constraints that a
future revision will carry, or were they deliberately withdrawn?
15. Tier gating suppresses diagnostics the pilot still reports¶
Model text
Pilot: reports the unresolved Nowhere and then type-checks the partially resolved model,
reporting Bound features should have conforming types on b (observation,
pilot-differential.md, the class of secondary diagnostics over an
unresolved reference).
OpenSysML: reports Nowhere and gates the type tier behind it (AGENTS.md §4), so b is not
reported until a resolves.
Specification: the specification defines well-formedness, not diagnostic ordering; no clause bears on it.
Assessment: not a specification question. Listed so the difference is not mistaken for a missing rule: the type-conformance rule exists on our side and fires once the lower tier is clean.
Question for the authors: none.
16. { … } written as a value is typed as an evaluation¶
Model text
Pilot: types the body expression as a BooleanEvaluation when its result is Boolean
(observation recorded in the code comment bodyExprType, internal/semantic/semantics/valuetype.go).
OpenSysML: the same, "as the pilot reads it".
Specification: KerML §7.4.9.4 Base Expressions: an expression body { … } "is an
expression whose result is that of its result expression"; §8.3.4.8 Expression implied
specialization is of Performances::Evaluation. The text found does not say the body expression
implicitly specializes BooleanEvaluation when its result is Boolean — that is the pilot's
implied-typing rule.
Assessment: spec ambiguous at the level of implied typing; the modeler-visible effect is a
conformance verdict on cond, which the two readings may differ on.
Question for the authors: does an expression body whose result is Boolean implicitly
specialize Performances::BooleanEvaluation, or only Performances::Evaluation?
17. ASCII-only basic names¶
Model text
Pilot: rejects; the name must be quoted 'Größe' (observation, negative corpus).
OpenSysML: the same.
Specification: KerML §8.2.2.3 Names: BASIC_NAME = BASIC_INITIAL_CHARACTER
BASIC_NAME_CHARACTER*, ALPHABETIC_CHARACTER = any character 'a' through 'z' or 'A' through 'Z';
UNRESTRICTED_NAME allows "any printable character other than backslash or single_quote"
between single quotes.
Assessment: pilot-following justified by clause; the code comment cites the pilot where it could cite §8.2.2.3.
Question for the authors: none.
18. Every reference subsetting after the first is reported¶
Model text
Pilot / OpenSysML: each ::> after the first is reported (code comment in
internal/check/passes/w8c_reference_subsetting.go: "as the pilot does").
Specification: KerML §8.3.3.3.4 Feature: ownedReferenceSubsetting : ReferenceSubsetting
[0..1] — at most one. Which of several to report is a diagnostic-placement choice.
Assessment: pilot-following justified by the multiplicity; only the placement is the pilot's.
Question for the authors: none.
19. A part typed only by a non-part definition¶
Model text
Pilot / OpenSysML: accepted (both).
Specification: SysML §8.3.11.3 PartUsage, validatePartUsagePartDefinition: "At least one
of the itemDefinitions of a PartUsage must be a PartDefinition." But checkPartUsageSpecialization
requires every PartUsage to specialize Parts::parts, whose type is Parts::Part, and KerML
§8.3.3.3.4 deriveFeatureType makes a feature's type "the union of the types of its typings and
the types of the Features it subsets", so partDefinition is never empty. §7.10's note says as
much: "every part usage is always directly or indirectly defined by at least one part definition,
implicitly if not explicitly."
Assessment: pilot-following justified by clause; the validation-constraints.md row that
marks the constraint "unobservable" is right for the reason above, and can cite it.
Question for the authors: none.
20. An alias is a name, not an element¶
Model text
Pilot: test::A_alias resolves to the element whose qualified name is test::A
(observation, pilot-xpect.md).
OpenSysML: answered test::A_alias until the alias was made a second membership of the
existing element; now the same as the pilot.
Specification: KerML §8.3.2.4.3 Membership: an alias declares a Membership whose
memberElement is the existing element and whose memberName is the alias; §7.2.5 Namespaces
(alias members).
Assessment: pilot-following justified by clause and fixed. Listed because the adjudication record says "the pilot was right" without the clause.
Question for the authors: none.
21. A succession whose ends are qualified names¶
Model text
package R {
part def Robot { perform action move; perform action grip; }
part r : Robot;
first r::move then r::grip;
}
Pilot: 2026-08 (jupyter-sysml-kernel 0.62.0) reports
nsq.sysml:4:11: error: Must be an accessible feature (use dot notation for nesting) and the
same at 4:24. The same two ends on connect r::move to r::grip; validate clean. The
spec-compliance.md example first part1::action1 then requirement1; draws the same error at
its first end. The dot-chain form first b.g then b.m; validates clean.
OpenSysML: accepts the succession. It is featured by Robot, the innermost common
featuring type of Robot::move and Robot::grip, and orders grip after move on every
Robot it runs (behavior-semantic-oracle.md).
Ends with no common featuring type (first p1::a then p2::b across two package-level parts)
are rejected with the pilot's message, and the pilot rejects them too.
Specification:
- KerML §8.3.4.5.3 deriveConnectorDefaultFeaturingType: "the innermost common direct or
indirect featuringType of the relatedFeatures".
- checkConnectorTypeFeaturing: each related feature has each featuring type of the
connector as a direct or indirect featuring type.
- §8.4.4.6.1 and Table 11 note 2: an implied TypeFeaturing to the defaultFeaturingType is
added when the connector has no owning type, no owned TypeFeaturing, and a non-null
default.
- A Succession is a Connector (§8.3.4.5.4), and SysML §8.4.9.4 applies the same to
SuccessionAsUsage.
Nothing in these clauses treats a succession's ends differently from a connector's.
Assessment: pilot short, ours follows the specification. The pilot accepts the connector form, which suggests that its succession check does not consult the default featuring type. That suggestion comes from comparing behavior, not from reading the pilot's source.
Question for the authors: is a package-owned succession whose ends name members of one type, by qualified name, intended to be well formed by the implied TypeFeaturing of KerML §8.4.4.6.1, as the same connector is?
22. A KerML metaclass as the type of a SysML metadata usage (KERML-90)¶
Model text
with, for comparison, the KerML spelling classifier A { metadata m : KerML::Classifier; } and
package P { metadata def MC :> KerML::Classifier; part def A { metadata m : MC; } }.
Pilot: 2026-08 (jupyter-sysml-kernel 0.62.0), validate-sysml-batch (observation):
kerml90.sysml:1:26: error: A metadata usage must be typed by one metadata definition.
kerml90.sysml:1:26: error: Must have exactly one metaclass
kerml90.sysml:1:26: error: Must have a concrete type
The KerML spelling (validate-kerml) and the metadata def MC spelling validate clean. In the
pinned source (reading), KerMLValidator.checkMetadataFeature opens with
// TODO: Submit new issue to revise this to actually fix the problem KERML-90 was trying to address.
// validateMetadataFeatureMetaclass
if (mf.type.filter(Metaclass).size() != 1) {
and checks validateMetadataFeatureMetaclassNotAbstract as mf.type.exists[abstract], over every
type rather than the metaclass; SysMLValidator.checkMetadataUsage adds
checkOneType(usg, Metaclass, …), which requires FeatureUtil.getAllTypesOf to return exactly
one type. The model has two: KerML::Classifier and the abstract Metadata::MetadataItem, so
all three checks fail.
OpenSysML:
kerml90.sysml:1:26: error: A metadata usage must be typed by one metadata definition.
sysml: kerml90.sysml did not analyse cleanly; no check was made
The error is the type tier's (internal/check/passes/typecheck.go, with the message from
pilotTypingMessage in internal/check/passes/w10b_usage_typing.go): a SysML metadata usage
must be typed by a metadata definition (usageWantsDefKind), and KerML::Classifier is a KerML
metaclass. The error stops the later tiers, so the two KerML-side messages are not
reached. The KerML spelling and the metadata def MC spelling are no errors.
Specification:
- KERML-90 (https://issues.omg.org/issues/KERML-90, closed in KerML 1.0b2) reported that
MetadataFeature::metaclass, a [0..1] redefinition of type, left a metadata usage typed by
a KerML metaclass two types, because SysML checkMetadataUsageSpecialization adds
Metadata::MetadataItem. The resolution changed metaclass to a subset of type: KerML
§8.3.4.12.3 MetadataFeature, "/metaclass : Metaclass [0..1] {subsets type}".
- The same clause keeps validateMetadataFeatureMetaclass: "A MetadataFeature must have exactly
one type that is a Metaclass." (type->selectByKind(Metaclass).size() = 1), and
validateMetadataFeatureMetaclassNotAbstract: not metaclass.isAbstract.
- SysML §8.3.27.3 MetadataUsage, checkMetadataUsageSpecialization: "A MetadataUsage must
directly or indirectly specialize the base MetadataUsage Metadata::metadataItems from the
Systems Model Library." metadataItems is typed by Metadata::MetadataItem, declared
abstract metadata def MetadataItem :> Metaobject, Item; a metadata definition is a
Metaclass. The same clause declares /metadataDefinition : Metaclass [0..1] {redefines
itemDefinition, metaclass}.
The constraint that rejects the model is therefore KerML validateMetadataFeatureMetaclass,
inherited by MetadataUsage: checkMetadataUsageSpecialization makes MetadataItem a type
of m, KerML::Classifier does not specialize it, and both are Metaclasses, so
selectByKind(Metaclass) has size 2. KERML-90 removed the [0..1] conflict on metaclass
but not this count, because the second type is itself a Metaclass. In KerML the implied
supertype is Metaobjects::metaobjects, typed by Metaobjects::Metaobject, which
KerML::Classifier specializes, so one type remains. SysML 2.0 Part 1 states no constraint
named validateMetadataUsageType, the pilot's first message.
Assessment: both tools follow the published text, and the text does not achieve what KERML-90 was resolved to allow. This is a specification question, not an implementation gap. If the text were revised to admit the model, OpenSysML would have to change its type-tier rule as well as its metaclass count; the pilot would have to change its two counts.
Question for the authors: is a KerML metaclass that does not specialize
Metadata::MetadataItem intended to type a SysML metadata usage? If so, should
validateMetadataFeatureMetaclass discount the type implied by checkMetadataUsageSpecialization,
or should that constraint not apply to such a usage? If not, should SysML say so (for instance by
typing metadataDefinition by MetadataDefinition)? Drafted in
omg-issues.md.
23. Decision-node outgoing and merge-node incoming successions are not given their implied subsetting (SYSML21-306)¶
Model text
package P {
action def A {
attribute x : ScalarValues::Integer;
first start;
then decide d;
if x > 0 then a;
if x <= 0 then b;
action a;
action b;
then merge m;
first a then m;
first b then m;
}
}
Pilot: validates clean (observation, 2026-08). Reading: SuccessionAsUsageAdapter.addDefaultGeneralType
calls addDecisionNodeOutgoingSuccessionSpecialization and
addMergeNodeIncomingSuccessionSpecialization, which add an implied specialization of the chain
from the node to ControlPerformances::DecisionPerformance::outgoingHBLink and
ControlPerformances::MergePerformance::incomingHBLink (ImplicitGeneralizationMap, keys
decision and merge). The first carries
* TODO: Update checkDecisionNodeOutgoingSuccessionSpecialization
*
* OCL refers to MergePerformance::outgoingHBLink rather than DecisionPerformance::outgoingHBLink.
* See SYSML21-306
OpenSysML: validates clean. It adds neither implied subsetting:
rg -n 'outgoingHBLink|incomingHBLink' internal finds only the library declarations and their
comments (ControlPerformances.kerml lines 22–48, Actions.sysml lines 303–317) and one comment in
internal/exec/runtime/state_route.go. The control-node constraints
(internal/check/passes/behavior/control_node.go, ControlNodeSuccessionPass) count a node's
successions and read their declared end multiplicities; execution takes exactly one outgoing
succession of a decision procedurally (internal/exec/runtime/state_route.go, followOut, citing
DecisionPerformance::outgoingHBLink: HappensBefore[1]). Neither consults the subsetting.
Specification:
- SysML §8.3.17.7 DecisionNode, checkDecisionNodeOutgoingSuccessionSpecialization: "All
outgoing Successions from a DecisionNode must subset the inherited outgoingHBLink feature of the
DecisionNode", with OCL
resolveGlobal('ControlPerformances::MergePerformance::outgoingHBLink'). MergePerformance
declares incomingHBLink, not outgoingHBLink; DecisionPerformance declares
outgoingHBLink (ControlPerformances.kerml).
- §8.3.17.13 MergeNode, checkMergeNodeIncomingSuccessionSpecialization: "All incoming
Successions to a MergeNode must subset the inherited incomingHBLink feature of the MergeNode."
- §8.4.13.4 Control Nodes repeats the mix-up in prose: "checkDecisionNodeOutgoingSuccessionSpecialization
requires that any incoming Succession to a MergeNode specialize the Feature
DecisionPerformance::outgoingHBLink".
- SYSML21-306 (https://issues.omg.org/issues/SYSML21-306, open) reports the OCL's qualified name.
Assessment: a specification defect, filed; the pilot implements the evident intent. OpenSysML does not materialise either implied relationship, which is an OpenSysML limitation: a model's specializations as OpenSysML reports them (reflective queries, exported models) lack the two subsettings the pilot adds. Validation and execution do not depend on them.
Question for the authors: none beyond SYSML21-306. The §8.4.13.4 sentence should be corrected with it.
24. Default multiplicity of a usage (SYSML21-185)¶
Model text: each probe declares a usage without a multiplicity in a definition D and
redefines it with [0..*] in a specialization, so that the redefinition warnings
(validateRedefinitionMultiplicityConformance, validateSubsettingMultiplicityConformance)
appear exactly when the original took the default [1..1]:
package P { enum def E { enum a; enum b; } part def D { enum e : E; } part def F :> D { enum :>> e [0..*]; } }
Pilot: reading. ImplicitGeneralizationMap:
// TODO: Update SysML specification to formalize default multiplicities.
// See SYSML21-185
put(MultiplicityImpl.class, "feature", "Base::exactlyOne");
The default is added when isAddMultiplicity() holds. AttributeUsageAdapter,
ItemUsageAdapter (and so every part usage) and PortUsageAdapter return
UsageAdapter.isAddDefaultMultiplicity(); ConnectionUsageAdapter returns isEnd():
return target.isEnd() ||
target.getOwningType() != null &&
target.getOwnedSubsetting().stream().
map(Subsetting::getSubsettedFeature).
filter(f->f != null).
map(FeatureUtil::getBasicFeatureOf).
noneMatch(f->f != null && f.getOwningType() != null);
getOwnedSubsetting returns every owned Subsetting, which includes Redefinition,
ReferenceSubsetting and CrossSubsetting (KerML §8.3.3.3.8–§8.3.3.3.10). getBasicFeatureOf
is the last chaining feature of a feature chain and the feature itself otherwise.
OpenSysML: semantics.ImplicitMultiplicityApplies (internal/semantic/semantics/multiplicity.go)
requires a usage owned by a type (featureOwnedByType: not owned by a package or namespace),
whose metaclass is an attribute usage (including an enumeration usage), an item usage (including
part, view, rendering, actor and stakeholder usages) or a port usage, and with no RelSubsets,
RelRedefines, RelReferences or RelCrosses relationship whose target, read to the last
feature of a chain, is owned by a type. Connection usages and their subclasses, metadata usages,
subjects and objectives take no default. An end feature takes [1..1]
separately (internal/check/passes/multiplicity_conformance.go, conformanceMultiplicity,
declaresEndFeature).
Observations, 2026-08 and this tree, each model in a file of its own (D, E, F as above):
| Case | Model (inside package P { … }) |
Pilot | OpenSysML |
|---|---|---|---|
attribute, part, port, ref part, in a part or attribute definition |
part def D { part x; } part def E :> D { part :>> x [0..*]; } (and the other keywords) |
both warnings | both warnings |
| occurrence | part def D { occurrence x; } part def E :> D { occurrence :>> x [0..*]; } |
none | none |
| enumeration usage | enum def E { enum a; enum b; } part def D { enum e : E; } part def F :> D { enum :>> e [0..*]; } |
both warnings at 1:98 |
both warnings at 1:98 |
| view usage | view def V; part def D { view v : V; } part def F :> D { view :>> v [0..*]; } |
both warnings at 1:79 |
both warnings at 1:79 |
| rendering usage | rendering def R; part def D { rendering r : R; } part def F :> D { rendering :>> r [0..*]; } |
both warnings at 1:94 |
both warnings at 1:94 |
| actor, stakeholder | part def U; requirement def R { subject s : U; actor a : U; stakeholder k : U; } requirement def R2 :> R { subject :>> s; actor :>> a [0..*]; stakeholder :>> k [0..*]; } |
both warnings at 1:145 and 1:171 |
both warnings at 1:145 and 1:171 |
| subsetting a type-owned feature | part def D { part a [0..*]; part b :> a; } part def E :> D { part :>> b [0..*]; } |
none | none |
| redefining a type-owned feature | part def D { part a; } part def E :> D { part :>> a; } part def F :> E { part :>> a [0..*]; } |
none | none |
| reference-subsetting a type-owned feature | part def D { part a [0..*]; part b ::> a; } part def E :> D { part :>> b [0..*]; } |
none | none |
| subsetting a chain ending in a type-owned feature | part p { part a [0..*]; } part def D { part b :> p.a; } part def E :> D { part :>> b [0..*]; } |
none | none |
| subsetting a package-owned feature | part q [0..*]; part def D { part b :> q; } part def E :> D { part :>> b [0..*]; } |
both warnings at 1:83 |
both warnings at 1:83 |
| end feature | connection def C { end part a; end part b; } connection def C2 :> C { end part :>> a [0..*]; end part :>> b; } |
End feature must have multiplicity 1 at 1:83; the upper-bound warning at 1:96 |
the same two, the first with OpenSysML's explanation appended |
"Both warnings" is warning: Redefining feature should not have smaller multiplicity lower bound
and warning: Subsetting/redefining feature should not have larger multiplicity upper bound at
the redefining feature.
Specification: SysML §7.6.3 Usages (non-normative): "a tighter default of [1..1] is implicitly
declared for the usage if all of the following conditions hold: 1. The usage is an attribute usage,
an item usage (including a part usage, except if it is a connection usage), or a port usage. 2. The
usage is owned by a definition or another usage (not a package). 3. The usage does not have any
explicit owned subsettings or owned redefinitions." An EnumerationUsage is an AttributeUsage
(§8.3.8.3), ViewUsage and RenderingUsage are PartUsages (§8.3.26.11, §8.3.26.6), and actors
and stakeholders are PartUsages owned through ActorMembership/StakeholderMembership. The end
default is separate, §7.13.2: "If a multiplicity is not explicitly declared for an end feature,
then a default of 1..1 is implicitly declared for it (regardless of the usual conditions for
default multiplicity given in 7.6.3)". SYSML21-185
(https://issues.omg.org/issues/SYSML21-185, open) records that Clause 8 states none of this
normatively.
Assessment: the two tools agree on every case above. Both select the default by usage
metaclass, as condition 1 does, and both count every owned subsetting (:>, :>>, ::> and
cross subsetting) as condition 3 does, with one exception: both keep the default when every
subsetted feature is owned by no type, where condition 3 withholds it for any explicit owned
subsetting. Both follow the pilot's rule rather than the prose there, a question of which rule
is intended.
Question for the authors: when SYSML21-185 formalizes the default, is condition 3 meant literally (any owned subsetting or redefinition withholds it), or only a subsetting of a feature that has a featuring type, as the pilot implements?
25. The declaration production of a case usage (SYSML2-783)¶
Model text
Pilot: validates clean (observation). SysML.xtext (reading):
// TODO: Correct erroneous use of ConstraintUsageDeclaration for CaseUsage from resolution of SYSML2-783.
CaseUsage returns SysML::CaseUsage :
OccurrenceUsagePrefix CaseUsageKeyword ActionUsageDeclaration CaseBody
;
OpenSysML: validates clean (case is parsed as a usage declaration in
internal/syntax/parser/defusage.go).
Specification: SysML §8.2.2.22 CaseUsage = OccurrenceUsagePrefix 'case' ConstraintUsageDeclaration CaseBody.
§8.2.2.20 ConstraintUsageDeclaration : ConstraintUsage = UsageDeclaration ValuePart? and
§8.2.2.17.2 ActionUsageDeclaration : ActionUsage = UsageDeclaration ValuePart?. SYSML2-783
(https://issues.omg.org/issues/SYSML2-783, closed in SysML 2.0b2) revised the notation
productions.
Assessment: the two productions have the same right-hand side; only the declared target
metaclass differs, and the CaseUsage rule creates the element. No observable difference. The
pilot's comment says the published production, not its own, is in error.
26. Validation constraints marked TODO in the pilot source¶
The pilot source marks five constraints TODO and implements each immediately after the
comment; OpenSysML implements all five. The eight control-node constraints the pilot marks
TODO: Check … (?) (SysMLValidator.xtend, checkControlNode and its siblings) and does not
implement are item 11.
| Constraint | Pilot TODO (KerMLValidator.xtend) |
OpenSysML |
|---|---|---|
validateRedefinitionMultiplicityConformance |
// TODO: Add validateRedefinitionMultiplicityConformance, then the lower-bound check |
internal/check/passes/multiplicity_conformance.go, constraintChecker.checkMultiplicityConformance |
validateSubsettingMultiplicityConformance |
// TODO: Add validateSubsettingMultiplicityConformance, then the upper-bound check |
the same function |
validateBindingConnectorTypeConformance |
// TODO: Add validateBindingConnectorTypeConformance, then //Binding type conformance |
internal/check/passes/w9c_bound_feature_types.go, W9CBoundFeatureTypesPass.Run |
validateFlowEndSubsetting |
// TODO: Add validateFlowEndSubsetting? validateFlowEndImplicitSubsetting?, then getSubsettedNotRedefinedFeaturesOf(flowEnd).isEmpty |
internal/check/passes/w8d_flow_end.go, W8DFlowEndPass.Run |
validateOperatorExpressionCastConformance |
// TODO: Add validateOperatorExpressionCastConformance, then the as check |
internal/check/passes/typecheck_expr.go, exprChecker.checkCast |
The models are the census probes under tools/census/validation/testdata/probes/, each a
package P; pilot validate-kerml, 2026-08, and this tree (observations):
class A { feature x [1..2]; } class B specializes A { feature y [0..2] redefines x; }— both:5:54: warning: Redefining feature should not have smaller multiplicity lower bound.class A { feature x [0..2]; } class B specializes A { feature y [0..5] subsets x; }— both:5:52: warning: Subsetting/redefining feature should not have larger multiplicity upper bound.class C { feature x : ScalarValues::String; feature y : ScalarValues::Integer; binding x = y; }— both:4:82: warning: Bound features should have conforming types.class A { out feature o : ScalarValues::Integer; } class B { in feature i : ScalarValues::Integer; } class C { feature a : A; feature b : B; flow from A::o to B::i; }— both:6:43: error: Must have at least two related elements, then at6:53and again at6:61error: Cannot identify flow end (use dot notation)anderror: Must be an accessible feature (use dot notation for nesting).feature s : ScalarValues::String; feature n = s as ScalarValues::Integer;— pilot:5:15: warning: Cast argument should have conforming types; OpenSysML:5:15: warning: cast argument is typed by String, unrelated to the target Integer: neither type specializes the other, so the cast selects no value.
Assessment: no difference in verdict. Every one of the five TODOs is stale, not only the
cast's. The census rows for these constraints are in
validation-constraints.md.
27. Variation parameters and stakeholder specialization¶
Model text
package P {
calc def C;
variation calc vc : C { variant calc c1 : C; }
variation requirement vr { variant requirement r1; }
variation case vk { variant case k1; }
}
and, one per file, variation calc vp { in p; variant calc c2; },
part def X; variation requirement vr { subject s : X; variant requirement r1; },
variation calc vc { return r : ScalarValues::Real; variant calc c1; },
variation case vk { objective o; variant case k1; }; and
part def X; requirement def R { subject y : X; stakeholder s : X; } against
part def X; part def Y { stakeholder s : X; }.
Pilot: reading. SysMLValidator.checkUsage, for validateUsageVariationOwnedFeatureMembership:
// NOTE: Need to allow parameters and objectives because they are currently physically inserted by transform implementation.
// TODO: Add allowance of parameters and objectives in variations to spec? Or remove when possible?
if (!(mem instanceof ParameterMembership || mem instanceof ObjectiveMembership)) {
PartUsageAdapter.isRequirementStakeholder adds the implied specialization of
Requirements::RequirementCheck::stakeholders only when the stakeholder's owning type is a
requirement definition or usage, noting that "checkPartUsageStakeholderSpecialization OCL doesn't
explicitly require the owningType to be a RequirmentDefinition or RequirementUsage".
Observations, 2026-08, and this tree:
| Model | Pilot | OpenSysML |
|---|---|---|
| the three variations above | clean | clean |
in p in a variation calc |
2:25: error: An owned usage of a variation must be a variant. |
the same |
subject s : X in a variation requirement |
clean | 1:52: error: An owned usage of a variation must be a variant. |
return r in a variation calc |
clean | 1:33: error: An owned usage of a variation must be a variant. |
objective o in a variation case |
clean | clean |
stakeholder in a requirement definition |
clean | clean |
stakeholder in a part definition |
3:18: error: mismatched input 'stakeholder' expecting '}' and 4:1: error: extraneous input '}' expecting EOF |
3:18: error: 'stakeholder' declares a stakeholder of a requirement and is only allowed in a requirement body; move it into the requirement it belongs to |
OpenSysML: internal/check/passes/w8d_variability.go, checkMembers, exempts an objective,
a metadata usage and, in an enumeration, an enumerated value, and reports a subject member and
every other non-variant usage. A stakeholder outside a requirement body is a parser diagnostic
(internal/syntax/parser/defusage.go).
Specification: SysML §8.3.6.4 Usage, validateUsageVariationOwnedFeatureMembership: "If a
Usage is a variation, then it must not have any ownedFeatureMemberships."
(isVariation implies ownedFeatureMembership->isEmpty()); §8.3.6.2 states the same for a
definition. A ParameterMembership and an ObjectiveMembership are FeatureMemberships. SysML
§8.3.11.3 PartUsage, checkPartUsageStakeholderSpecialization: "If a PartUsage is owned via a
StakeholderMembership, then it must directly or indirectly specialize either
Requirements::RequirementCheck::stakeholders."
Assessment: the parameters a variation gets without writing them, and the stakeholder
restriction, make no observable difference: the inserted parameters draw nothing in either tool,
and both grammars admit stakeholder only in a requirement body, where the pilot's condition
always holds. The parameter exemption does differ for a parameter written in the model: the pilot
exempts every ParameterMembership, so an explicit subject or return parameter is accepted,
while OpenSysML exempts only objectives and rejects it. By the OCL both are rejected, and so is
the objective both tools accept. Neither tool is fully faithful.
Question for the authors: the pilot's own: should a variation be allowed parameters and an
objective? If it should, validateUsageVariationOwnedFeatureMembership and
validateDefinitionVariationOwnedFeatureMembership need to exempt them. If not, should the
inserted ones be inserted at all?
28. Multiplicity bound non-negativity (KERML-199)¶
Two corrections the pilot maintainers have made or ruled on are already recorded, and only the second needs a register entry:
- Systems-Modeling/SysML-v2-Pilot-Implementation#802,
Type::multiplicityreached through alias and reference memberships, fixed upstream on ST6RI-975, is item 9 and omg-issues.md. - Systems-Modeling/SysML-v2-Pilot-Implementation#803 is recorded in omg-issues.md and pilot-differential.md. What follows is the OCL point behind it.
Model text
and package P { feature n : ScalarValues::Natural = 2; feature e [n]; }.
Pilot: validate-kerml, 2026-08 (observation):
kerml199.kerml:1:64: error: Must have a Natural value; the second model is clean. Reading:
KerMLValidator.checkMultiplicityRange:
// TODO: Correct validateMultiplicityBoundResults OCL from KERML-199.
// validateMultiplicityRangeBoundResultTypes
for (b: mult.bound) {
if (if (b.isModelLevelEvaluable) mult.valueOf(b) == -2 else !b.isInteger) {
and MultiplicityRange_valueOf_InvocationDelegate returns -2 "to represent a "null" result".
OpenSysML: the same error at 1:64; the second model is no errors.
internal/check/passes/w8c_multiplicity_bounds.go, multiplicityBoundsChecker.checkBound: an
evaluable bound must fold to a non-negative integer or *; any other bound must have an
Integer-conforming result type.
Specification: KerML 1.1 Beta 2 §8.3.4.11.2 MultiplicityRange,
validateMultiplicityRangeBoundResultTypes; the clause has the same number in KerML 1.0
(formal/2026-03-01), from which this quotation is taken: "The results of the bound Expression(s)
of a MultiplicityRange must be typed by ScalarValues::Intger from the Kernel Data Types Library.
If a bound is model-level evaluable, then it must evaluate to a non-negative value." (so spelt),
with OCL
bound->forAll(b |
b.result.specializesFromLibrary('ScalarValues::Integer') and
let value : UnlimitedNatural = valueOf(b) in
value <> null implies value >= 0
)
The same clause's valueOf returns null for a bound that is not model-level evaluable, whose
evaluation is not a single element, or whose LiteralInteger value is negative (if value >= 0
then value else null), so value >= 0 holds whenever value <> null and the clause cannot
fail. KERML-199 (https://issues.omg.org/issues/KERML-199, closed) introduced the
non-negativity sentence.
Assessment: the OCL is short of its prose. Both tools implement the prose, the pilot by
reading valueOf's null as its -2 marker. No difference.
Question for the authors: should validateMultiplicityRangeBoundResultTypes test the
evaluated bound directly, for example bound.isModelLevelEvaluable implies valueOf(b) <> null,
so that its OCL rejects a negative bound as its prose does?
Inventory A — spec-compliance.md rows justified by the pilot or by nothing¶
Every row of docs/project/spec-compliance.md was classified as (a) citing a specification
clause, (b) citing the pilot or reference behaviour, or (c) citing neither. The extraction script
and its output are described under How this list was produced.
Of the rows examined, 442 cite a clause, 290 cite the pilot or reference (some also cite a
clause), and 217 cite neither. The (b) and (c) rows fall into the groups below; a row is
semantic when it decides what a model means or whether it is well-formed, and implementation
when it describes a surface the specification does not govern.
Section of spec-compliance.md |
(b) pilot-citing | (c) uncited | Nature | Disposition |
|---|---|---|---|---|
| Current Implementation Status | 2 | 3 | status prose | none needed |
| Detailed Semantic Compliance Map | 290 | 217 | semantic | see below |
| What We Don't Yet Support | 3 | 3 | limitations | none needed |
| Model Persistence and RDF Interchange | 4 | 25 | implementation (RDF vocabulary, round-trip) | not a spec matter; the SysML v2 API/RDF mapping is a separate OMG document not audited here |
| Source-Preserving Model Editing | 1 | 12 | implementation | none needed |
| gRPC Service Layer | 3 | 32 | implementation | none needed |
| Language Server | 1 | 4 | implementation | none needed |
| Analysis Engines | 1 | 4 | implementation | none needed |
| Constraint Solving | 8 | 50 | implementation (SMT encoding) | none needed; the encoding is an OpenSysML extension |
Within the Detailed Semantic Compliance Map, the pilot-citing rows are of four kinds:
- Rows whose rule cites a clause and whose wording, severity or placement cites the pilot (the majority of the 290). These are conformance rows; the pilot reference is about the diagnostic text or where it is anchored. Nothing to adjudicate beyond items 13 and 18 above.
- Rows whose rule itself rests on the pilot — the source of items 2, 8, 10 and 16, plus the rows grouped as "SysML Notation the Reference Accepts and We Reject — the ten classes", which record where we are stricter than the pilot with a grammar citation for each (no gap).
- Rows recording a pilot gap ("the pinned pilot implements only …", "adjudicated as pilot gaps") — items 9, 11, 12, and the declared, unreported set of item 14.
- Rows on evaluation gating ("matching the pilot's model-level-evaluable gating",
internal/semantic/semantics/eval.go) — the gating is KerML §8.3.4.8modelLevelEvaluable, so the citation should be the clause; behaviourally covered by item 4.
The 217 uncited semantic rows are of two kinds. Most are implementation-mapping rows (rule →
file:function → test → status) whose rule name is a validate…/check… constraint name from
the specification, so the name is the citation; this audit checked the names quoted in the
detailed items against the PDF text but did not verify every one of the 217 individually, which
is the first thing a re-run should do (search the extracted PDF text for each name). The rest are
OpenSysML-specific passes (multiplicity bound ordering, specialization cycles, unresolved
references) whose justification is well-formedness rather than a clause; none decides model
meaning.
Inventory B — Go comments that rest on the pilot¶
Of the pilot-mentioning comments in non-test sources under internal/ and tools/, those that
record a behaviour decision are:
| File | Decision | Item |
|---|---|---|
internal/semantic/semantics/operator_conformance.go featureResultTypes |
a calculation usage as an operand is the calculation, not its result | 10 |
internal/semantic/semantics/filter.go chainLimitation |
chains rooted in an unfeatured feature are evaluable (the reference does; we do not) | 4 |
internal/semantic/semantics/valuetype.go bodyExprType |
{ … } is a BooleanEvaluation when Boolean |
16 |
internal/semantic/semantics/eval.go |
unsupported-evaluation gating "matching the pilot's model-level-evaluable gating" | 4 (rule is KerML §8.3.4.8) |
internal/check/passes/w8c_reference_subsetting.go |
every extra reference subsetting reported | 18 |
internal/check/passes/w10b_usage_typing.go |
usage-typing diagnostics worded and placed as the pilot's | justified: SysML /…Definition {redefines definition} typing (e.g. /occurrenceDefinition : Class) |
internal/check/passes/w8d_connector_featuring.go |
connector-end featuring checks alongside the pilot's | the file cites its clauses; the pilot reference is for placement |
internal/check/passes/variant_owner.go |
variant-owner check | justified: cites SysML §7.20 alongside the pilot |
internal/check/passes/behavior/state_transition.go |
diagnostic wording from the pilot | wording only |
The remaining hits are rendering styles, DOT/diagram output, XMI and API shape, keyword lists derived from the pilot grammars, and test-harness plumbing — implementation compatibility, not model semantics. The full hit list with its per-hit classification is reproduced by the command under How this list was produced.
Inventory C — negative corpus¶
tools/referee/reject/testdata/negative/ holds 306 cases; the committed baseline is
bothReject 297, pilotOnlyRejects 0, oursOnlyRejects 9, bothAccept 0. Every case carries a
rationale line, and every rationale cites either a specification clause or a grammar production
(the one that looked uncited, grammar/g60-alias-keyword-as-name.sysml, cites AliasMember /
Identification, terminal ID, i.e. KerML §8.2.2.3 — item 17's clause). No case is rejected
because the pilot rejects it without a clause. The nine oursOnlyRejects are the control-node
succession cases of item 11 — cases where the pilot accepts and the specification rejects — and
are the negative corpus's evidence for that item.
How this list was produced, and how to keep it current¶
The register is an audit of five sources, each re-runnable from the repository root:
spec-compliance.mdrows. Every table row and list item under each##section was classified by regular expression: (a) mentionsKerML/SysMLwith a§or8.xclause number; (b) mentionspilot,reference,Xpect,jupyter-sysml-kernel,SysMLValidatororKerMLValidator; (c) neither. A row can be both (a) and (b). Re-run with a short script overdocs/project/spec-compliance.mdand compare the per-section counts in Inventory A; a count that moves means a row was added or reworded and should be classified into one of the four kinds above.- Adjudication records.
pilot-differential.md,pilot-corpora.md,pilot-xpect.md,pilot-rejection.md,pilot-execution-referee.md,adjudications.md,validation-constraints.md,omg-issues.md,behavior-semantic-oracle.md,training-examples.md,errata-overlay.mdandtools/oracle/errata/errata.gowere searched forpilot is right,follow(s|ing) the pilot,as the pilot,ambiguous,needs (an )?opinion,unclear,not settled,spec(ification)? is silent,Open:,Question, not a bug report. Each hit was read in context; the errata registry had none (its entries carry clause citations). - Go sources.
rg -n -i 'pilot|spec is silent|not in the spec|ambiguous|unclear|TODO' internal tools --glob '!*_test.go', then each hit was classified as a behaviour decision, wording/placement, or implementation compatibility. Only the first kind is listed. -
Negative corpus. Every case's rationale line was checked for a clause or grammar citation against the committed referee baseline (
tools/referee/reject, see pilot-rejection.md). -
Pilot source. The pinned tag, cloned sparsely (
org.omg.kerml.xtext,org.omg.sysml.xtext,org.omg.sysml.logic), was searched withrg -n 'TODO|NOTE:|KERML-[0-9]+|SYSML2-[0-9]+|SYSML21-[0-9]+'for the places where the pilot's own source admits a departure from the specification or cites an OMG issue. Each hit was read against the OpenSysML code and, where a model shows it, both validators were run. Items 22–28 and the membership-distinguishability entry in omg-issues.md come from this sweep.
Specification text. Download the two PDFs from the OMG URLs in the overview, extract text
(any PDF text extractor; this audit used pypdf), and grep for the constraint names quoted in an
item. Record the document numbers printed on the title pages; if they differ from the ones stated
above, re-verify every clause number before editing an item.
When to add an item. Any change that (a) adds a rule or a behaviour whose rationale in
spec-compliance.md, a code comment or a PR is "the pilot does this" without a clause, (b)
adjudicates a differential disagreement as "the pilot is right" or "ambiguous", or (c) declines
to evaluate or report something the specification requires, gets a row here with the six fields
of the detailed items. Close an item by linking the PR or the upstream answer that settles it, and
move it to a Closed section rather than deleting it, so that the next audit does not re-derive
it.
Author questions. The open questions are items 2, 3, 5, 6, 7, 8, 10, 13, 14, 16, 22, 24, 27 and 28. They are drafted for a maintainer to raise with the specification authors (as with the nested-redefinition question); nothing has been posted upstream from this register, and the register should be updated with the answer when one arrives.