Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
a4e1f6e
fix(runtime): track body feature bindings
devin-ai-integration[bot] Oct 3, 2026
74f9e96
fix(runtime): reuse materialized body binding values
devin-ai-integration[bot] Oct 3, 2026
9b73a77
fix(runtime): address body binding review findings
devin-ai-integration[bot] Oct 3, 2026
ef83516
Merge remote-tracking branch 'origin/develop' into fix/body-feature-v…
devin-ai-integration[bot] Oct 3, 2026
bb79c89
Merge remote-tracking branch 'origin/develop' into fix/body-feature-v…
devin-ai-integration[bot] Oct 3, 2026
c40d4a5
fix(runtime): preserve body binding frame state
devin-ai-integration[bot] Oct 3, 2026
cd182c3
test(runtime): make qualified member fixture schedule-independent
devin-ai-integration[bot] Oct 3, 2026
733a290
Merge remote-tracking branch 'origin/develop' into local/body-binding…
devin-ai-integration[bot] Oct 5, 2026
9d0dd49
Merge remote-tracking branch 'origin/develop' into local/body-binding…
devin-ai-integration[bot] Oct 8, 2026
b885923
Merge remote-tracking branch 'origin/develop' into fix/body-feature-v…
devin-ai-integration[bot] Oct 8, 2026
98423fe
fix(runtime): keep an unvalued mark on a frame whose map is not yet a…
devin-ai-integration[bot] Oct 8, 2026
650768e
Merge remote-tracking branch 'origin/develop' into fix/body-feature-v…
devin-ai-integration[bot] Oct 8, 2026
109e048
ci: re-run the race suite after the runtime shard hit its 45m limit o…
devin-ai-integration[bot] Oct 8, 2026
c9df2f7
Merge remote-tracking branch 'origin/develop' into fix/body-feature-v…
devin-ai-integration[bot] Oct 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/unreleased/body-feature-bindings.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **Track `=` values in action, calculation, statement-block, and state-machine bodies.** Their dependencies are re-evaluated when read after a source changes, while `:=`, `default`, and values assigned to a binding retain their existing write behavior. An unsupplied inherited action parameter such as `z = x * 2` now follows a later write to `x` (`action_inherited_default_root`: `z` and `r` change from 6 to 20); the supplied argument in `action_inherited_supplied_parameter` remains held. Body-local calc usages remain evaluated once per declaration. Explore/check now agrees on the shared-writers `=` case with `{3}` rather than `{1,2,3}`.
4 changes: 4 additions & 0 deletions changes/unreleased/case-bindings-unvalued-mark.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- **A case-local a run left declared without a value stays unvalued in the frame its conditions
read.** `frame.markUnvalued` allocated its map on a copy of the frame, so a mark on a frame that
held no unvalued name yet was lost, and an objective or assertion naming such a local could read
through to a binding of the same name instead of being undecided for the missing value.
47 changes: 45 additions & 2 deletions docs/project/spec-compliance.md

Large diffs are not rendered by default.

123 changes: 100 additions & 23 deletions internal/exec/runtime/action_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ func (e *ActionExecutor) performanceFeatures() []lower.Attribute {
features = append(features, lower.Attribute{
Name: name, Direction: usage.Direction, IsResult: usage.IsResult, Type: lower.TypeText(usage),
Value: value, Node: usage, Scope: scope, Optional: e.ctx.admitsNoValue(member),
Binding: value != nil && !usage.ValueIsInitial && !usage.ValueIsDefault,
})
}
return features
Expand Down Expand Up @@ -1313,8 +1314,21 @@ func (e *ActionExecutor) initializeAttributes() error {
return fmt.Errorf("%w: read %s of object #%d: %w",
ErrActionPerformanceOccurrence, attr.Name, e.occurrence.ID, err)
}
if value := fv.HeldValue(); value.Kind != ValInvalid {
e.root.data[e.root.key(attr.Name)] = value
if attr.Binding && !fv.Written && !e.occurrenceRedefinesAttribute(attr, fv) {
value, seeded, err := e.ctx.seedBodyBindingFromFeatureValue(e.root.cells, e.root.key(attr.Name), fv)
if err != nil {
return fmt.Errorf("eval attribute default %s: %w", attr.Name, err)
}
if seeded {
if err := e.streamInitialOutput(attr.Name, value); err != nil {
return err
}
continue
}
}
if value := fv.HeldValue(); value.Kind != ValInvalid &&
(!attr.Binding || fv.Written || e.occurrenceRedefinesAttribute(attr, fv)) {
e.ctx.writeBodyValue(e.root.cells, e.root.data, e.root.key(attr.Name), value)
if err := e.streamInitialOutput(attr.Name, value); err != nil {
return err
}
Expand All @@ -1327,17 +1341,29 @@ func (e *ActionExecutor) initializeAttributes() error {
}
continue
}
value, err := evalDefaults().evalIn(attr.Scope).Eval(attr.Value)
var value Value
var err error
if attr.Binding {
value, err = e.ctx.deriveBodyCell(
e.root.cells, e.root.key(attr.Name), e.root.cells.existingCell(e.root.key(attr.Name)),
)
} else {
value, err = evalDefaults().evalIn(attr.Scope).Eval(attr.Value)
}
if err != nil {
return fmt.Errorf("eval attribute default %s: %w", attr.Name, err)
}
if err := e.holdAttributeAsReal(attr, &value); err != nil {
return fmt.Errorf("eval attribute default %s: %w", attr.Name, err)
}
if value, err = e.mirrorOccurrence(attr.Name, value); err != nil {
return err
if !attr.Binding {
if value, err = e.mirrorOccurrence(attr.Name, value); err != nil {
return err
}
}
if !attr.Binding {
e.ctx.writeBodyValue(e.root.cells, e.root.data, e.root.key(attr.Name), value)
}
e.root.data[e.root.key(attr.Name)] = value
if err := e.streamInitialOutput(attr.Name, value); err != nil {
return err
}
Expand Down Expand Up @@ -1384,7 +1410,7 @@ func (e *ActionExecutor) bindContextDefault(attr lower.Attribute) bool {
if err != nil {
return false
}
e.root.data[e.root.key(attr.Name)] = value
e.ctx.writeBodyValue(e.root.cells, e.root.data, e.root.key(attr.Name), value)
return true
}

Expand Down Expand Up @@ -1430,16 +1456,23 @@ func (e *ActionExecutor) materializeOccurrence() (*Instance, error) {
if err != nil {
return nil, err
}
e.occurrence = inst
e.performances.occurrence = inst
for _, attr := range e.features {
if value, held := e.root.data[e.root.key(attr.Name)]; held {
if cell := e.root.cells.existingCell(e.root.key(attr.Name)); cell != nil && cell.binding != nil && !cell.fv.Written {
if _, err := e.mirrorBindingOccurrence(attr.Name, value, cell); err != nil {
return nil, fmt.Errorf("%w: seed %s of object #%d: %w",
ErrActionPerformanceOccurrence, attr.Name, inst.ID, err)
}
continue
}
if err := inst.BindFeatureValue(e.ctx, attr.Name, value); err != nil {
return nil, fmt.Errorf("%w: seed %s of object #%d: %w",
ErrActionPerformanceOccurrence, attr.Name, inst.ID, err)
}
}
}
e.occurrence = inst
e.performances.occurrence = inst
e.ctx.beginPerformanceLife(inst, e.ctx.newActivation())
return inst, nil
}
Expand Down Expand Up @@ -1477,6 +1510,24 @@ func (e *ActionExecutor) mirrorOccurrence(name string, value Value) (Value, erro
return fv.HeldValue(), nil
}

// mirrorBindingOccurrence exposes a tracked body feature through its occurrence.
func (e *ActionExecutor) mirrorBindingOccurrence(name string, value Value, cell *bodyCell) (Value, error) {
if e.occurrence == nil || !e.declaresAttribute(name) {
return value, nil
}
mirrored, err := e.ctx.mirrorBodyCell(e.occurrence, name, cell, value)
if err != nil {
return value, fmt.Errorf("%w: write %s of object #%d: %w",
ErrActionPerformanceOccurrence, name, e.occurrence.ID, err)
}
return mirrored, nil
}

// occurrenceRedefinesAttribute reports whether the occurrence supplies another declaration's default.
func (e *ActionExecutor) occurrenceRedefinesAttribute(attr lower.Attribute, fv *FeatureValue) bool {
return occurrenceHasRedefinedDefault(fv, attr, e.graph.Scope)
}

// setFeature writes into the action's feature space, through the performance
// occurrence for a feature the action declares: the occurrence is authoritative
// for those, and data mirrors what it holds after the write.
Expand All @@ -1491,7 +1542,7 @@ func (e *ActionExecutor) setFeature(name string, value Value) error {
// rather than by the write to that occurrence.
return err
}
e.root.data[e.root.key(name)] = value
e.ctx.writeBodyValue(e.root.cells, e.root.data, e.root.key(name), value)
e.moved = true
return e.streamOutput(name, value)
}
Expand Down Expand Up @@ -1550,19 +1601,27 @@ func (e *ActionExecutor) hasFlow() bool {
e.dynamicsKind != lower.NotStateSpace)
}

// completeRoot freezes the root bindings and ends its performance.
func (e *ActionExecutor) completeRoot() error {
if err := e.ctx.freezeBodyCells(e.root.cells); err != nil {
return err
}
e.state = StateCompleted
e.endRootPerformance()
return nil
}

// completeWithoutFlow completes an action stating no flow: it performs no step,
// so its performance begins, takes its inputs, and ends at once.
func (e *ActionExecutor) completeWithoutFlow() error {
if err := e.checkResultParameters(); err != nil {
return err
}
e.ctx.beginPerformanceLife(e.occurrence, e.ctx.newActivation())
e.beginRootPerformance()
if err := e.bindInputs(); err != nil {
return err
}
e.state = StateCompleted
e.ctx.endPerformanceLife(e.occurrence)
return nil
return e.completeRoot()
}

// bindInputs writes the supplied inputs into the performance, then the
Expand All @@ -1583,6 +1642,20 @@ func (e *ActionExecutor) bindInputs() error {
return nil
}

// beginRootPerformance starts the root performance's dependency lifetime.
func (e *ActionExecutor) beginRootPerformance() {
if e.root.began == 0 {
e.root.began = e.ctx.newActivation()
}
e.ctx.beginPerformanceLife(e.occurrence, e.root.began)
}

// endRootPerformance ends the root performance's dependency lifetime.
func (e *ActionExecutor) endRootPerformance() {
e.ctx.endPerformanceLife(e.occurrence)
e.ctx.endActivation(e.root.began)
}

type inputBindingError struct{ Err error }

func (e inputBindingError) Error() string { return e.Err.Error() }
Expand Down Expand Up @@ -1699,7 +1772,7 @@ func (e *ActionExecutor) initialize() error {
return err
}

e.ctx.beginPerformanceLife(e.occurrence, e.ctx.newActivation())
e.beginRootPerformance()
if err := e.bindInputs(); err != nil {
return err
}
Expand All @@ -1710,9 +1783,7 @@ func (e *ActionExecutor) initialize() error {
if err := checkStreamsReceived(e.root); err != nil {
return err
}
e.state = StateCompleted
e.ctx.endPerformanceLife(e.occurrence)
return nil
return e.completeRoot()
}
e.seedTokens(e.root, starts, 0)

Expand Down Expand Up @@ -2261,8 +2332,7 @@ func (e *ActionExecutor) retireToken(tokenIdx int) error {
if err := checkStreamsReceived(e.root); err != nil {
return err
}
e.state = StateCompleted
e.ctx.endPerformanceLife(e.occurrence)
return e.completeRoot()
}
return nil
}
Expand Down Expand Up @@ -2600,7 +2670,7 @@ func (e *ActionExecutor) stepNestedAction(tokenIdx int) error {
}
}

perf, err := e.beginPerformance(token.frame, graph, usage, nil)
perf, err := e.beginPerformance(token.frame, graph, usage, nil, nil)
if err != nil {
return err
}
Expand Down Expand Up @@ -3319,10 +3389,17 @@ func (e *ActionExecutor) State() ExecutionState {
// each nested non-repeated node's latest performance and under `part.attribute` what the one
// object each of its own parts denotes holds; a performed usage's mirror its occurrence.
func (e *ActionExecutor) Results() map[string]Value {
results, _ := e.ResultsWithError()
return results
}

// ResultsWithError returns the values the action's features hold and the first binding
// derivation error; a feature whose binding fails to derive is omitted.
func (e *ActionExecutor) ResultsWithError() (map[string]Value, error) {
results := make(map[string]Value, len(e.root.data))
e.root.collect("", results)
err := e.root.collect("", results)
e.collectPartsHeld(results)
return results
return results, err
}

// collectPartsHeld adds the attributes held by the object each part or item usage the
Expand Down
Loading
Loading