Repository navigation
fix(runtime): mark order dependence on chained places and same-step sibling reads - #1619
Merged
Merged
Conversation
…p reads Failing-first conformance fixtures: a feature reached through a part written by racing branches, the expression over it, the values read from them, a two-level chain, a chained read no branch races (stays bare), and a sibling branch reading a feature its sibling writes in the same step. The default run leaves these bare while explore proves two outcomes. 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)".
|
…ibling reads Results of an action with parts derived the parts' bound features under a fresh run state, losing the marks of the run just ended before any surface read them; ResultsWithError now reads under the executor's own run, and an object's feature valued by an expression records what its expression read (Instance.materializeDerived), so sensor.raw, sensor.scaled and the values read from them stand on the writers' choice points. A write meeting a read of the same place at a choice point now touches what the reading alternative writes from that read (lower.Footprint.Flows, one data flow per body statement; every write where the read is a guard's) and no longer the place read, which one write fixes whichever order ran; a pin is each performance's own and is neither raced for nor touched. Trace goldens move accordingly, each movement matching the check oracle's divergent set; a differential test asserts over every order fixture that the names marked are those exploration varies, two pinned approximations aside. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…en attribute Under the order-mark rule, the machine's one write of lit at t=3 stands whichever behavior ran first; the action's saw, read from it, is what the two witnesses tell apart, as the check's own divergence line says. Co-Authored-By: jason.han <hanhuijun@gmail.com>
A chained place remembers the feature it was reached through (Place.Via; the part's object, Instance.heldBy, for a write or derivation the run makes), so two parts of one definition hold two places of its feature: left.raw racing leaves right.raw, right.scaled and a read of them bare, as exploration has them. A write-order choice touches the part's place too, and a part-qualified result name matches only that part's place. Co-Authored-By: jason.han <hanhuijun@gmail.com>
The junction branch's two writes, one from the guard's feature and one from nothing, both stand on the order; the explore-vs-marks differential now also covers the state order fixtures. Co-Authored-By: jason.han <hanhuijun@gmail.com>
… to decide Place.Conflicts is the checker's independence relation and features may alias (ref part twin = cell), so it stays conservative and ignores Via. The run decides identity where marks are read, rewritten, met at a choice point and reported (Context.heldAlike: the parts denote an object in common), so left.raw racing leaves right.raw bare while twin.mark written beside cell.mark races it. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ained-places Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # docs/project/spec-compliance.md
…ained-places Co-Authored-By: jason.han <hanhuijun@gmail.com>
This was referenced Oct 30, 2026
Merged
perf(runtime): carry a derived value's marks through the memoized footprint without copying it
#1747
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Two families of result values were left bare in the default run although
-schedule exploreproves more than one outcome. Both are fixed with the existingorderMarksrepresentation (no second marking mechanism, no explore re-run). The rule throughout: a value is marked if and only if exploration proves more than one outcome for it.A. Values reached through a part (
sensor.rawwritten by racing branches,sensor.scaled = raw * 10,z := sensor.raw,zz := sensor.scaled): none carried a mark — not even plain attributes once the action declared a part. Root cause:ActionExecutor.ResultsWithErrorderives what each part's object holds (collectPartsHeld→GetFeatureValue), and every top-level derivation begins a run of its own (beginRun→nestRun→ a freshrunStatethat stays installed), so the marks and choice points of the run just ended were gone before any surface read them.ResultsWithErrornow reads under the executor's own run (enterRun(e.driven.state)). Separately, an object's feature valued by an expression recorded nothing of what it read:Instance.materializeDerivednow calls the sameContext.deriveMarksthe action's own bound attributes use, sosensor.scaledstands whererawdoes.placeNamedaccepts<part>.<name>for a feature no performer owns.A place is the part's:
lower.Place.Viais the feature a chain reaches the place through (footprintBuilder.place), and for a write-order choice or a derivation the run makes on an object, the part that object is held as (Instance.heldBy). Whether two such places hold one value is the run's to decide, not the declarations' — features may alias (ref part twin = cell), soPlace.Conflicts, the checker's independence relation, stays conservative and ignoresVia.Context.heldAlike(the two parts denote an object in common) is applied where marks are read (orderMarks.of), replaced (at/rewrite), met at a choice point (ChoicePoint.meets) and reported (placeNamed→partNamedHolds):left.rawracing leavesright.raw,right.scaledand a read of them bare, whiletwin.markwritten besidecell.markraces it.B. A sibling branch reading a feature its sibling writes in the same step (
a { x := 1 }besideb { z := x }):touchMeetingtouched only the place written. It now touches what the reading alternative writes from the read — the body statements whose data flow reads the place (lower.Footprint.Flows, one flow per statement, built infootprintBuilder.statements;Footprint.WritesReading), or every write when the read is a guard's or the move is described whole. The place read is no longer touched by a write/read meeting: one write of it stands whichever order ran, and explore showsx = 1in every linearization — a mark on it would be the false positive the rule keeps out. A pin is each performance's own, delivered to rather than raced for, and is neither touched nor a read that touches (shared).Specification basis
KerML 1.1 §9.2.4 Occurrences / §9.2.6 Performances (
HappensBeforeholds between the occurrences a succession joins and between a send and its accept; nothing orders concurrent branches), so a bare default-run value asserts a determinism the specification does not give. A feature chain names the feature it reaches (KerML 1.1 §7.3.4.6), sosensor.rawis one place whatever path writes or reads it — the feature of the object the part denotes, which another part of the same definition does not denote and a reference part bound to it does. Moves the order-dependence marking row indocs/project/spec-compliance.md(status unchanged, ✅; implementation and test columns extended).Behaviour changes
sensor.raw,sensor.scaled,z,zz(chained fixture) reportorderDependent: 2on every surface (REPL text,-json, gRPCorder_dependent, traceTouches); the two-level chaincar.engine.rpmmarks the value read from it; a chained read of a place no branch races stays bare; with two parts of one definition, a race onleft.rawleavesright.raw,right.scaledand a value read from them bare; a reference part bound to a part (twin = cell) races it.x := 1besidez := xgiveszorderDependent: 1andxbare (develop markedxand leftzbare). Tracechoice … order-dependent: <names>lines move the same way (details below).Place.Conflictsis as conservative as before.Fallout (measured)
action_order_chained_write_of_racing_writers,action_order_chained_two_level_write,action_order_chained_read_of_unraced_stays_bare,action_order_sibling_read_of_racing_write,action_order_chained_parts_apart(two parts of one definition),action_order_chained_alias_of_racing_writers(ref part twin = cell),state_order_guard_and_effect_read_racing_write(a junction branch's two effects under a guard read racing the sibling's write: both marked, both vary) — each with.expected.json(explored admissible set),.check.expected.json, three trace goldens, and sections indocs/project/behavior-semantic-oracle.md.order_marks_differential_test.go:TestOrderMarksAgreeWithExploredOutcomes— over everyaction_order_*andstate_order_*case with an admissible set (23), the names the default run marks equal the names exploration finds at more than one value. Two pre-existing disagreements are pinned by name with their reason (not re-recorded):action_order_explicit_argument_binds_pinmarksfixed/lit.resultbecause a performed action's footprint is dynamic and stands on every mark;action_order_send_payload_from_racing_writerreports the accept's payload twice, marked asreceiver.msgand bare asmsg.order-dependent:name lists; every movement matches the fixture's.check.expected.jsondivergent set:action_explore_statement_order_{calc,dependent,if,read_first}seed-1):x/v→y/doubled— the once-written place is fixed, the value written from the read varies.clock_action_state_due_together, 3):lit→sawLit(the watcher's read result varies;litis written once).state_concurrent_inline_do_bodies3,state_do_step_cuts_nested_perform3,state_do_step_cuts_typed_do3):+ i/+ n— the do body's counter written from a read the other move writes.state_junction_{guards_read_once,beyond_a_draw_read_once,drawn_as_its_transition_fires}+_metadata, 18):d/armed→route— the guard read makes the branch's effect dependent; the guarded attribute itself is written once.choice_test.go:TestNotesOfATransitionBlockedBeforeFiringAreDropped— the region-order choice now standsorder-dependentwith no names (levelis written once; the blocked guards write no feature);cmd/sysml/check_engine_test.go:TestEngineCheckWitnessOfBehaviorsOnOneClockReplays— the lamp witnesses' choice line namessaw(the action's read result, which the check's own line reports asends as false or true) instead oflit, which the machine writes once at t=3. Each test's subject is unchanged..expected.jsonof an existing fixture moved; no"schedule": "declared"pinning was needed.pssm-referee -check): pass 56 / fail 6 / not-expressible 34 / differs-by-design 7 — reproduces the committed baseline.pilot-exec-diff, 514 cases, develop → branch): identical buckets — agree 229, kind-only 1, order-only 0, disagree 38, differs-by-design 5, pilot-unevaluated 167, pilot-silent 24, pilot-error 7, ours-error 10, ours-undetermined 17, both-error 16, nondeterministic 0.GOOS=js -tags sysml_re, gzip -9): develop 15,251,297 → branch 15,257,496 bytes (+6,199, +0.04%); budget 15,600,000 untouched.make test-shard SHARD=runtime(-race): green at the current head (2593 s); no allocation bound moved.Pre-existing gaps met and left alone
touchexcludes them), sokeep.vinobject_flow_merge_shared_inputvaries under explore but prints bare.msg) is unmarked whilereceiver.msgis marked (pinned in the differential test).lit.resultfrom a literal argument (pinned).%checksurface reports only the action's own features, notpart.attributenames; a reference part's features are not among the reported results.log/hits/orderappended by both regions at a completion-pool, do-step or run-to-completion choice;state_join_segment_reads_its_payloadmarksleft/rightthat do not vary). None of their trace goldens moved in this PR; the state-surface marking of those choice kinds is a separate gap.= exprattributes;%satisfylabel format.How it was verified
go build ./...,go vet ./...,gofmt -l .empty,go test ./internal/exec/runtime/ ./internal/ir/... ./internal/exec/... ./internal/frontend/... ./tests/grpc/... ./cmd/sysml/, the-raceruntime shard,make docs-check,scripts/check-doc-ids.py,scripts/changelog.py check; the explore-vs-marks differential above; both referees before/after; manual CLI runs of both finding models (-action,-json,-schedule explore).Checklist
make testandmake lintpass locallydocs/internals/design/scheduling.md, oracle sections, compliance row)changes/unreleased/order-marks-chained-places.fixed.mdmake docs-countsrun if a gate count moved (no gate count moved)Link to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/dd66ed592fb6499ea1ffe0ec2fbe8493
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/dd66ed592fb6499ea1ffe0ec2fbe8493?variant=devin
Requested by: @HuiJun