@kyneta/machine 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +12 -0
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -252,10 +252,22 @@ handle.dispose()
252
252
 
253
253
  **Fx parameterization.** The third type parameter `Fx` defaults to `Effect<Msg>` for the common closure case, but accepts any type. This single generic makes the same `Program` shape work for both `runtime()`-interpreted programs and programs with data effects that use a custom executor — no wrapper types, no separate interfaces.
254
254
 
255
+ ## Stale-Sibling-Effect Hazard
256
+
257
+ When `update` returns multiple effects `[model, fx1, fx2, ...]`, all effects execute before any reentrant messages are processed. If `fx1` dispatches a message re-entrantly, that message is queued. `fx2` executes next, against the same model state. The queued message is processed only after all effects from this transition complete.
258
+
259
+ **Consequence:** `fx2` may operate in a world that `fx1` has already changed at the application layer (outside the model). The model itself is consistent — but any external state mutated by `fx1`'s callback is invisible to `fx2`.
260
+
261
+ **Mitigation:** Effects that create or mutate external state should be **idempotent** — check whether the target state already exists before acting. The `ensure-*` naming convention (used in `@kyneta/exchange`) communicates this requirement: an `ensure` effect declares that a state should exist, and is a no-op if it already does.
262
+
263
+ This is not a bug in the algebra. The `Program` type is intentionally simple — effects from a single transition are co-products of that transition, planned against the same model snapshot. The hazard arises only when effects have cross-cutting side-effects on shared mutable state outside the model. Programs with pure data effects (no shared mutable state) are immune.
264
+
255
265
  ## Relationship to the Synchronizer
256
266
 
257
267
  The Synchronizer in `@kyneta/exchange` is a `Program<SynchronizerMessage, SynchronizerModel, Command>` where `Command` is a discriminated union of data effects (send message, build offer, apply snapshot, etc.). Its interpreter batches commands and executes them against live channels and substrates. The pure `update` function is tested exhaustively without any I/O.
258
268
 
269
+ The Synchronizer's `cmd/ensure-doc` and `cmd/ensure-doc-dismissed` commands are the primary example of the idempotent-effect pattern described in "Stale-Sibling-Effect Hazard" above. When `handlePresent` batches multiple `cmd/ensure-doc` commands, the first command's callback may cascade-create state that the second command also targets. The `ensure-*` naming convention makes the idempotency contract explicit.
270
+
259
271
  ## Peer Dependencies
260
272
 
261
273
  None.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kyneta/machine",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Universal Mealy machine algebra — Program, Effect, Dispatch, runtime",
5
5
  "author": "Duane Johnson",
6
6
  "license": "MIT",