Skip to content

✨ feat(flow): the body interpreter — bodied flows execute - #185

Merged
roninjin10 merged 5 commits into
mainfrom
engine-body-interpreter
Aug 13, 2026
Merged

✨ feat(flow): the body interpreter — bodied flows execute#185
roninjin10 merged 5 commits into
mainfrom
engine-body-interpreter

Conversation

@roninjin10

@roninjin10 roninjin10 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Bodied flows execute. Interpreter.layer(flow) registers a generic handler over the FlowRuntime port: Graph.build against the real payload → dependency-order driving of the compiled drafts → activities through the existing persistence machinery (implementations resolved from an Activity.Implementations table), branch predicates evaluated on real values with only the taken arm executing, Ref-path projections threading upstream results, and journaled replay on execution-id reuse (re-execution returns settled results without re-running effects).

  • Bodied flows reject toLayer (BodyDefinesBehavior error) — behavior comes from exactly one place.
  • Missing activity implementations fail typed, before any effects run.
  • Seam choice: interpretation lives in @smthrs/flow against the port, so every runtime implementation gets identical semantics; // OPEN QUESTION (2026-08-11): PlanScheduler as the driver marks where the durable scheduler plugs in later.
  • Also in this stack (owning-PR fix, rebased through): PR1's vitest coverage tweak was reverted after the packages/flows conformance suite caught it — the pinned-config law stands; Planned.ts's one unreachable-by-construction directive is now in the pinned allowlist with its justification.

Depends-on: #184

Verification

  • Root npm run check across all 20 workspaces — clean; lint clean (flow, engine, plan, flows); browser + circular checks pass
  • New Interpreter.test.ts (17) + BodyInterpreter.test.ts (4, engine integration); full suites at the rebuilt tip: flow 16 files / 158 tests, engine 20 files / 149 tests, plan 100% (451/451 statements), packages/flows conformance all green (independently re-verified after the stack rebase)

Decisions taken (were open questions — review these)

  1. The interpreter drives nodes with an inline memoized dependency walk, not PlanScheduler (Interpreter.ts:208). Rationale: PlanScheduler has zero production call sites, its NodeExecutor seam has no FlowRuntime implementation, and wiring it requires the durable-store stack in every runtime including in-memory tests. The inline walk gives identical settle-once semantics through the same activity persistence. PlanScheduler integration remains the upgrade path once plans persist per-execution — the marker stays at the seam where it plugs in.
  2. Interpretation lives in @smthrs/flow against the FlowRuntime port, not inside @smthrs/engine. Every runtime implementation (in-memory, durable) gets identical body semantics for free; the engine stays a port implementation.

…Error

The authoring half of the plan: seven closure-free node variants (Succeed,
All, Map, AndThen, FlowCall, ActivityCall, Branch) with digested deferred
functions in out-of-band tables, the Planned<T> branded placeholder whose
proxy throws GraphBuildError on any computation (toPrimitive, valueOf,
toString, toJSON, application), and the GraphBuildError vocabulary.

Docs: docs/specs/Concepts/Unified Flow Authoring.md
(cherry picked from commit 64624ac)
Activity.make gains a string-first overload: a named, schema-typed atom
(payload/success/error/tier/idempotencyKey) whose implementation attaches
separately via .toLayer(execute) through the existing registration and
activity machinery, and whose .call(payload) records an ActivityCall node
for plans. The inline options form is unchanged.

Docs: docs/specs/Concepts/Unified Flow Authoring.md
(cherry picked from commit b883814)
…otations

Flow.make accepts an optional plan-time body (required once the migration
completes), Flow.prototype.call records a FlowCall node, the trampoline
outcome vocabulary lands as pure data (Flow.done / flow.to / Flow.park over
the existing Waiting reasons), and Capabilities / EffectsDeclaration /
Placement join the annotation keys.

Docs: docs/specs/Concepts/Unified Flow Authoring.md
Docs: docs/specs/Concepts/Trampoline Loops.md
(cherry picked from commit ad7df62)
@roninjin10
roninjin10 force-pushed the engine-body-interpreter branch from 42ee5ce to 72f9b1e Compare August 12, 2026 05:48
@roninjin10

Copy link
Copy Markdown
Contributor Author

Review round 1 complete (sol + fable, independent). Findings for this PR were fixed and folded into its commit; the full ledger (15 fixes, 6 resolved-as-designed) lives in the stack's review notes. All 14 branches rebased and re-verified: whole-repo check green, all package suites at 100% coverage, rebuilt tip byte-identical to the verified fix tree.

Evaluates a body against its real payload with strict Planned placeholders
for step results, expands branch arms as static topology, splices inline
flow calls with capability intersection, rejects recursive calls with
recursion_requires_boundary, reads effects/capabilities/placement from the
authoring annotations, and emits NodeDrafts that Plan.compile keys directly.

Docs: docs/specs/Concepts/Unified Flow Authoring.md
(cherry picked from commit f8bf150)
Interpreter.layer(flow) registers a generic handler over the FlowRuntime
port: Graph.build against the real payload, dependency-order driving of the
compiled drafts, activity calls through the existing persistence machinery
with declared implementations resolved from an Implementations table, branch
predicates on real values running only the taken arm, Ref-path projection
threading, and journaled replay on execution-id reuse. Bodied flows reject
toLayer; behavior comes from exactly one place.

Docs: docs/specs/Concepts/Unified Flow Authoring.md
(cherry picked from commit 42ee5ce)
(cherry picked from commit 72f9b1e)
@roninjin10
roninjin10 force-pushed the engine-body-interpreter branch from 72f9b1e to 7a586e5 Compare August 12, 2026 06:51
@roninjin10

Copy link
Copy Markdown
Contributor Author

Swap pass complete — review loop closed. Both seats have now reviewed every PR. Swap findings (6: nested-catch subject scoping, foreign-flow wait tokens, two __proto__ cloner recurrences, drafts-field refusal, re-drive test + lineage-id doc) fixed and folded into owning PRs; tail rebuilt tree-identical; whole-repo check + all suites green at 100% coverage.

@roninjin10
roninjin10 changed the base branch from flow-graph-build to main August 13, 2026 01:13
@roninjin10
roninjin10 merged commit 7a586e5 into main Aug 13, 2026
0 of 2 checks passed
@roninjin10
roninjin10 deleted the engine-body-interpreter branch August 13, 2026 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant