Repository navigation
fix(runtime): judge a behavior's write count by the feature's effective multiplicity - #1556
Merged
Merged
Conversation
…ve multiplicity A behavior writing a feature it declares took countJudged = true unconditionally in checkNamedWriteHeld and checkWrite, so an action or state definition's own attribute subsetting a [0..*] feature refused a second value against the representative one while the same write to a part's feature (checkAdmits, MultiplicityDefaulted) held both. writeTargetOf now takes the multiplicity and the count judgment from featureRange, the decision effectiveFeature already takes; writeTarget.stated keeps the declaration rule for body-locals. 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)".
|
Resolves the write-conformance type row of docs/project/spec-compliance.md as it reads after the representative-object policy docs and this fix. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…o lines Co-Authored-By: jason.han <hanhuijun@gmail.com>
This was referenced Oct 27, 2026
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
A behavior writing a feature it declares judged the value's count unconditionally:
Context.checkNamedWriteHeld(reached fromActionExecutor.setFeatureandStateExecutor.assignAttributethroughcheckNamedWriteAs) andContext.checkWrite(tool outputs) passedcountJudged = truetocheckTargetHeld, so a feature stating and inheriting no multiplicity —writeTargetIngave it the representative1..1— refused a second value, while the same write to a part's feature (Instance.SetFeatureValue→Context.checkAdmits) consultedEffectiveFeature.MultiplicityDefaultedand held both.Before, on develop
0286c9845:part def P { attribute many : Real[0..*]; attribute x : Real :> many; action a { assign x := (3.0, 4.0); } }— the part'sxholds(3.0, 4.0).action def A { attribute many : Real[0..*]; attribute x : Real :> many; assign x := (3.0, 4.0); }— refused:action A: assignment to x: multiplicity violation: 2 value(s) bound to a feature with multiplicity upper bound 1.state def, written by an entry action — refused the same way (state machine S: assignment to x: …).After: all three hold both values.
attribute z : Real;(implicit[1..1]) still refuses(3.0, 4.0)on all three paths; a stated[0..*]holds three values and a stated[1..2]holds two and refuses one or three, on every path.The fix takes the count judgment from one place:
runtime/shape.gofeatureRangenow also reports whether a declaration, inheritance or the parameter rule states the range (besidedefaulted, unchanged in meaning).runtime/write_conformance.go: newContext.writeTargetOf(sym, name)builds awriteTargetfromfeatureRange— the same decisioneffectiveFeaturegives an object's feature andcheckAdmitsjudges — settingcountJudged = !defaulted(a write's count rule) andstated(the existing body-local declaration rule: a local omitting a multiplicity keeps its initializer's count; that rule is unchanged, it just has its own flag now).writeTargetInandtoolOutputuse it.checkTarget/checkTargetAs/checkTargetHeldlost thecountJudgedparameter and readtarget.countJudged;checkBodyDeclarationHeld,shapeDeclaredand the compiled tier'slocalCheckForreadtarget.stated, as they did before under the old flag's meaning.%setand gRPCSetFeatureValuego throughInstance.SetFeatureValue→checkAdmits(already right); calc/outassignment andbindCalcParametersgo throughcheckNamedWriteHeld(now unified) andcalcMemberDecl.check(declaration-based, unchanged); generated code (codegen.compileDeclare) declines a subsetting local as unsupported and checks only a stated multiplicity on declarations, so it has no write check of its own to bring in line — no codegen parity case was added for that reason.Specification basis
0..*), §7.3.4.5 (a redefining/subsetting feature stating none keeps the one it inherits —x :> manyis[0..*]).[1..1]):attribute z : Real;keeps refusing two values.docs/project/spec-compliance.md: the write-conformance type row (impl cell:writeTargetIn→writeTargetOf, onefeatureRangedecision; text: the element-count sentence now states the unified rule for part/action/state defs) and the write-count row (text, impl, tests). No status marker moves.Against #1550 (
docs/representative-object-policy, merged into develop at48f60b4b8): both PRs edited the type row. The merge commitd380d5dc0(origin/develop48f60b4b8merged in, one conflict on that row) resolves it to the row as it reads after both: #1550's rewritten element-count sentence with its split clause ("…while the same declaration owned by the running action itself still refuses two values … a split recorded in the status rather than claimed away") replaced by this PR's unified wording, this PR's impl cell, and #1550's status cell minus its "count-free … honoured on one path and not yet on the other" sentence.How it was verified
New fixtures, side by side:
conformance/write_default_multiplicity_part_def.sysml,write_default_multiplicity_action_def.sysml,write_default_multiplicity_state_def.sysml— a subsetting feature holds two values, a stated[0..*]three, a stated[1..2]two. The action-def and state-def forms fail on develop with the refusal above (checked by stashing the code change).conformance/write_implicit_one_part_def.sysml,write_implicit_one_action_def.sysml,write_implicit_one_state_def.sysml— two values into an implicit-[1..1]attribute refused on each path.runtime/robustness_write_default_multiplicity_test.go(TestRuntimeRobustnessWriteDefaultMultiplicity): defaulted subset and stated[0..*]hold many; implicit one refuses two withErrMultiplicityViolation;[1..2]refuses three and none, holds two.Baselines: none moved. A first cut that judged body-local declarations by the implicit
[1..1]too movedcalc_sample_pair_arithmetic,calc_integer_range,calc_body_local_declared_type, the analysis-library census (SampledFunctions::interpolateLineardeclaresattribute domainValues = …with two values),terminate_freezes_root_bindingand theclock_accept_after_parametertrace — i.e. the declaration rule is load-bearing for the standard library itself — so the declaration rule keeps its own flag and nothing moves.Gates on
7be167161(the fix) and re-run ond380d5dc0(after merging develop48f60b4b8);c0931c13don top only shortens three comments (gofmt, vet, the new fixtures and robustness test re-run ✔):go build ./...✔,go vet ./...✔,gofmt -l .empty ✔ (both heads)go test -count=1 ./internal/exec/runtime/...✔ (156 s on7be167161; conformance, robustness, trace, census and tool suites re-run ✔ ond380d5dc0)python3 scripts/changelog.py check✔,mkdocs build --strict✔ (both heads)go test -count=1 ./...on7be167161with the five corpora fetched and theirOPENSYSML_REQUIRE_*set: 121 packages ok,cmd/sysmlhit the default 10 m package timeout while the whole module ran in parallel on this machine (the test it was starting,TestSyncAnnotateAddressesByShortName, was at 0 s — a CPU-contention timeout, not a failure; the package touches nothing this PR changes); re-run alone it passes (ok cmd/sysml 275 s), and the whole module re-run ond380d5dc0with the sameOPENSYSML_REQUIRE_*set and-timeout 30mpasses: 122 packages ok, no failures.Dry-merges (detached worktrees of this head):
origin/develop48f60b4b8: merged in (d380d5dc0).fix/solve-default-multiplicityhead2b81ce1c2: merges clean;TestExecutionConformance,TestRuntimeRobustness*,TestExecutionTracepass on the merged tree (itssolve_*fixtures included).chore/pilot-pin-2026-09head9ca600a59: conflicts indocs/project/spec-compliance.md(lines ~2348–2510) anddocs/project/validation-constraints.md— the same two files conflict when9ca600a59is merged onto bareorigin/develop(it is 77 commits behind), none of the hunks is in a row this PR edits, and it touches no file this PR touches besides that docs file.54ae14545: one conflict, the type row described above; resolved as stated,mkdocs build --strict✔.Checklist
make testandmake lintpass locally (build, vet, gofmt, fullgo test ./...with corpora, see above)changes/unreleased/write-default-multiplicity.fixed.md, not as an edit toCHANGELOG.mdmake docs-countsrun if a gate count moved — nothing movedLink to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/54ad0d1f901741eabc14996344dce0c5
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/54ad0d1f901741eabc14996344dce0c5?variant=devin
Requested by: @HuiJun