Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions .changeset/brave-machines-update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@typeonce/effect-machine": minor
---

Add `to.local.update(...)` and `to.branch.<path>.update(...)` for replacing an active compound or parallel state's value without reconstructing its active descendants.

Updates accept decoded values through `target(value)` or schema make input through `target.from(input)`. They preserve descendant configuration and state-owned work by default, support named branches and declinable resolvers, and expose the updated owner through transition inspection.
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,47 @@ choice destinations remain calls such as `to.full.Running()`. Runtime named
branch builders remain callable, including `select.unchanged()`, because their
result carries the selected branch evidence.

### Update an active scope value

Use `to.local.update(...)` to replace the value owned by the nearest active
compound scope without rebuilding its active child. Use
`to.branch.<path>.update(...)` for a valued compound or parallel ancestor of the
handler source:

```ts
Increment: ;
;((to) =>
to.branch.root.session.update(({ ancestors, target }) => target.from({ count: ancestors["root.session"].count + 1 })))
```

The update keeps the exact active descendants, their values, history records,
completion outputs, and unrelated parallel regions. It runs no exit or entry
actions and does not restart state-owned work. Eventless stabilization still
runs, so an `always` transition can react to the new value.

`update` is callable when used directly and is also a static selection for a
named branch:

```ts
to.branches({
changed: { target: to.local.update },
unchanged: { target: to.none }
}).resolve(({ select, event }) =>
event.changed
? select.changed.from({ count: event.count })
: select.unchanged()
)
```

The resolver must return `target(value)` or `target.from(input)`. It may return
`decline()` only with `{ declinable: true }`. Pass `{ reenter: true }` on event
or invocation transitions when the handler source should exit and enter again.
Reentry applies to that source, not to the ancestor whose value changed.

The selector omits `update` for schema-less scopes, atomic and final states,
inactive branches, parallel sibling regions, and choice resolvers. Updating a
parallel sibling requires an event handled by that region.

Use `declinable: true` when a resolver may decide that its transition is not
enabled. Only that resolver receives `decline()`, and its return type expands to
accept the opaque declined result:
Expand Down
17 changes: 17 additions & 0 deletions docs/agent-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,23 @@ Do not read the clock, generate randomness, call a service, or await work while
choosing a transition. Receive such values in an event or produce them through
state-owned work first.

When only an active compound or parallel state's value changes, use its static
update selection instead of reconstructing its active descendants:

```ts
Changed: (to) =>
to.local.update(({ ancestors, target }) =>
target.from({ revision: ancestors.document.revision + 1 })
)
```

`to.local.update` addresses the nearest valued compound scope.
`to.branch.<path>.update` addresses a valued compound or parallel ancestor of
the handler source. Both preserve the complete active descendant
configuration. Neither runs lifecycle actions or restarts state-owned work by
default. Use an event handled inside a parallel sibling when that sibling owns
the value that must change.

## Test paths and invariants

Test the statechart as a graph. Send domain events, inspect reached states, and
Expand Down
Loading