[READ-ONLY] Mirror of https://github.com/bombshell-dev/playground.
0

Configure Feed

Select the types of activity you want to include in your feed.

๐Ÿ“ spec: node owns a scope, eval runs inline (non-serial)

Charles Lowell (Jun 27, 2026, 4:10 PM +0300) ffcb9972 b108c4d6

+21 -19
+21 -19
packages/freedom/specs/freedom-spec.md
··· 96 96 list of children, and a parent (except the root). A node's in-memory identity is 97 97 its object reference; its externalizable identity is its `id`. The Node's data 98 98 fields are read-only โ€” all property mutations go through the context API. Each 99 - node maintains an eval loop that accepts operations via `node.eval()`, enabling 100 - scoped execution. `node.remove()` delegates to the `remove` context API 101 - operation, which halts the node's scope and tears down all descendants. 102 - Middleware on `remove` participates in teardown. 99 + node owns an Effection scope; `node.eval()` runs operations inline in that scope. 100 + `node.remove()` delegates to the `remove` context API operation, which halts the 101 + node's scope and tears down all descendants. Middleware on `remove` participates 102 + in teardown. 103 103 104 104 **Component.** A generator function of type `() => Operation<void>` that runs 105 105 within a node's Effection scope for the node's entire lifetime. Components ··· 240 240 241 241 ### 5.3 Scoped Evaluation 242 242 243 - Each node maintains an **eval loop** โ€” a spawned child task within the node's 244 - scope that accepts and executes operations submitted via `node.eval()`. The eval 245 - loop uses a channel- based "fire and await" pattern: the caller submits an 246 - operation and waits for its result; the eval loop executes the operation in the 247 - node's scope and resolves the result. 243 + Each node owns an Effection **scope** (captured when its component task starts). 244 + `node.eval(op)` runs `op` **inline** in the caller's routine with the routine's 245 + scope temporarily repointed at the node's scope, then restored. There is no eval 246 + loop and no channel. 248 247 249 248 N-eval1. `node.eval(op)` runs `op` within the node's Effection scope and returns 250 249 `Result<T>`. If `op` completes successfully, the result is ··· 253 252 N-eval2. Operations yielded inside `op` see the node's scope context โ€” including 254 253 all middleware installed in the node's scope and its ancestors. 255 254 256 - N-eval3. The eval loop executes operations sequentially. If multiple callers 257 - submit operations concurrently, they are processed in order. 255 + N-eval3. `eval` is immediate โ€” it runs `op` synchronously inline โ€” and 256 + non-serial: it does not queue. Re-entrant and concurrent `eval` calls nest, each 257 + restoring the previous scope when it completes. The only serialization point in 258 + the system is the tree's dispatch queue (ยง7). 258 259 259 - N-eval4. The eval loop is spawned as a child of the node's scope. It runs 260 - alongside the component and shares the same parent scope, so middleware 261 - installed by the component is visible to operations executed via `eval`. 260 + N-eval4. Because `op` runs with the node's scope as the current scope, middleware 261 + installed by the node's component is visible to operations executed via `eval`. 262 262 263 263 ### 5.4 Lifecycle 264 264 265 - N7. A node is created by `append()`, which spawns a new Effection child scope 266 - within the parent node's scope and runs the component within it. 265 + N7. A node is created by `append()`, which spawns a task within the parent node's 266 + scope and runs the component within it. The task captures its scope 267 + (`useScope()`) as the node's scope, so the node's scope inherits the parent's 268 + contexts. 267 269 268 270 N8. A node is destroyed by `node.remove()`, which halts the node's spawned task. 269 - This triggers structured teardown of the component, the eval loop, all 270 - middleware installed in the scope, and all descendant nodes. `remove()` does not 271 - return until teardown is complete. 271 + This triggers structured teardown of the component, all middleware installed in 272 + the scope, and all descendant nodes. `remove()` does not return until teardown is 273 + complete. 272 274 273 275 N9. When a node is destroyed, its middleware disappears automatically because 274 276 the Effection scope is destroyed.