Repository navigation
fix(runtime): report a condition-less constraint as no value - #1554
Merged
Merged
Conversation
A constraint is a BooleanEvaluation whose result only its body expression binds (KerML 1.1 Beta 2 7.4.8.2, SysML v2.1 Beta 2 7.20.2), so a constraint declaring no condition binds no result: it reads as ErrNoValue with the cause as the detail, the way an unbound calc result does, instead of the distinct ErrNoConditions verdict, which is removed from the runtime and the solver. A requirement over no required constraint still holds vacuously, an assumed subrequirement requiring nothing included, and the solver denies that vacuous claim as false instead of refusing it. 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)".
|
A requirement over no required condition holds vacuously whether it is denied or violated, so its assumptions are not read, as the evaluator does not read them: a false or untranslatable assumption no longer turns the violation query of its denial unsatisfiable. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
6 tasks done
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
Follow-up to #1537, which made an unbound calculation result read as
ErrNoValue. One path stayed distinct: a constraint carrying no condition (and a negated constraint stating only assumptions) yielded its ownErrNoConditions— "no verdict". A constraint is aBooleanEvaluationwhoseresult : Boolean[1]only its body expression binds; with no body nothing binds the result, which is the same unbound-result case #1537 settled as no value. "No conditions" is not a notion the specification has, so this folds it:ErrNoValueon every surface that reports a check — REPL-constraint/-requirement/-satisfy/%check/%configure, LSP, gRPCVerifyConstraint/VerifyRequirement, MCPverify_constraint/verify_requirement, the solver (solve.Constraint/Satisfaction/Asserted), thesmtengine and the validation report all go throughevaluateConditionsorsolve/translate.go, so the one change covers them. The cause stays as the diagnostic detail throughconditionCheck.noResultValue, which reusesunboundResultHintfrom fix(runtime): report an unbound calc result as no value, like an unassigned out #1537 (no second detail mechanism):constraint empty: no value: the constraint declares no condition;constraint cneg: no value: the constraint declares no required condition, only assumptions; and for areturn r;the body never binds, the calc's own hint (result parameter r binds no value; write the result as the trailing expression …).runtime.ErrNoConditionsandsolve.ErrNoConditionsare removed with every reference. Two callers were not about conditions at all — a missing assertion symbol (action_holds.go,sequence_executor.go,statements.go,smt/property.go) — and now wrapErrNotAConstraint; thesmtengine's refusal list carriesErrNoValuein place of the removed sentinel.assume r0;subrequirement that requires nothing included (it is a satisfied check with nothing required, not a condition-less constraint). The solver'svacuousSubjectbecamerequirementSubject(the Requirement-rule subjects) and a denial of a vacuous requirement is now(not true)— unsatisfiable, agreeing with the evaluator, which reportsassert not satisfyof it violated — instead of refusing with the removed sentinel; the assumptions of such a requirement are not read whether it is denied or violated, as the evaluator does not read them, so a false or untranslatable assumption cannot turn the denial's violation query unsatisfiable (translator.translate,Translator.Conditions).%configureof a family whose only assertion holds vacuously now answers with the existing "no variation point is read" diagnostic instead of the removed one.attribute b : Boolean = p3.cneg;) is now undetermined, as any unbound result is (read_return_parameterscnegNoCondition, previously the error).Not touched:
repl/view.goerrNoConcernCondition("states no condition to evaluate") is a different sentinel for a concern that resolves to no requirement (ErrNoRequirement), not for a condition-less check, so it stays. HistoricalCHANGELOG.mdentries namingErrNoConditionsare history and are not edited.Specification basis
KerML 1.1 Beta 2 §7.4.8.2 (a
BooleanExpression'sresult : Boolean[1]is bound by its body) and SysML v2.1 Beta 2 §7.20.2 (a constraint is aBooleanEvaluation); requirement vacuity fromRequirements::RequirementConstraintCheck(allTrue(assumptions()) implies allTrue(constraints()), SysML v2 §9.2.14).docs/project/spec-compliance.md: the constraint row ("A constraint carrying no condition …") and the negated-assumptions row are rewritten toErrNoValuewith the clauses; the requirement-vacuity row gains the denial-is-false semantics, the new fixtures andrequirementSubject; the constraint-body-statements row (steps-only body) and the inherited-result-parameter row drop theirErrNoConditionsclaims. Status stays ✅ on all of them.Changed assertions (intentional semantics change, not a weakening)
Each previously asserted
ErrNoConditions; now:runtime/action_holds_test.goHolds(nil, nil)ErrNotAConstraint(no assertion named — never about conditions)runtime/condition_test.go:TestNegatedConstraintWithOnlyAssumptionsIsNotAVerdictErrNoValue+ "no required condition"runtime/constraint_test.go:TestConstraintWithoutConditionsIsNotAVerdictErrNoValue+ "declares no condition"runtime/owned_constraint_value_test.go:TestOwnedConstraintValueIsNotAConditionErrNoValueruntime/robustness_constraint_body_steps_test.go(empty body steps)ErrNoValueruntime/robustness_referenced_requirement_binding_test.go(therequire wagonSpeccycle)ErrNoValue+ "names the requirement being checked"runtime/robustness_subrequirement_test.go(constraint over nothing)ErrNoValueruntime/robustness_vacuous_requirement_test.go(alphaconstraint ×2, condition-less constraints)ErrNoValue(+ "the constraint declares no condition")runtime/testdata/conformance/read_return_parameters.expected.jsoncnegNoCondition{"type": "Undetermined"}(was the error string)solve/asserted_vacuous_test.goaffirmedemptyConstraint→runtime.ErrNoValuesolve/differential_test.gorefusal filterruntime.ErrNoValuesolve/expression_test.govacuous(negated constraint, nothing required)runtime.ErrNoValuesolve/satisfy_test.go:TestSatisfactionWithoutConditionsIsVacuousdenial(not true)instead of refusingsolve/translate_test.go(×2,TestNoConditions)runtime.ErrNoValuesolve/vacuous_requirement_test.go(constraint assuming, requiring nothing)runtime.ErrNoValue; newTestDeniedVacuousRequirementIgnoresItsAssumptions(denial(not true), its violationtrue, a false and an untranslatable assumption unread)solve/violation_test.go:TestViolationWithoutClaimRefusesruntime.ErrNoValuerepl/configure_family_test.go:TestConfigureAFamilyWhoseAssertionsStateNoConditionrepl/satisfy_vacuous_test.goHow it was verified
condition_less_constraint_def,condition_less_constraint_usage(error),condition_less_requirement_def,condition_less_requirement_usage,condition_less_subrequirement(assume r0;requiring nothing — satisfied); goldentests/parser/testdata/parse/condition_less_check;runtime/robustness_condition_less_check_test.go(def, usage, unboundreturn r;hint, requirement def/usage vacuous, assumed subrequirement vacuous).go build ./...,go vet ./...,gofmt -l .empty;go test -count=1 ./...with the training, pilot, spec-example, pilot-library-XMI and PSSM corpora fetched and required: all packages ok (cmd/sysmlandtests/codegenhit the 10-minute default timeout only under the full parallel run on this VM and pass alone, 251s/286s).python3 scripts/changelog.py checkok;mkdocs build --strictok.tests/corpusok as committed).origin/develop(0286c98) clean; against fix(runtime): follow the library body of SequenceFunctions::excludingAt #1540 (87d5670) clean, andinternal/exec/runtime,internal/exec/solve,internal/exec/smt,internal/frontend/replon the merged tree:runtime,solve,smtandreplall ok. Against chore(pilot): pin the OMG pilot implementation to release 2026-09 (0.63.0) #1529 (9ca600a) the merge conflicts indocs/project/spec-compliance.mdanddocs/project/validation-constraints.md, but those conflicts already exist between bareorigin/developand chore(pilot): pin the OMG pilot implementation to release 2026-09 (0.63.0) #1529 and none of the hunks touch the rows this PR edits.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 changelogLink to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/c245709a88bb406696146e243581dfc7
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/c245709a88bb406696146e243581dfc7?variant=devin
Requested by: @HuiJun