feat(automation): structured loop container (ADR-0031)#1482
Merged
Conversation
Implement structured control-flow constructs per ADR-0031, tasks 1–2: spec-define the constructs (picking representation B — nested sub-structure) and ship the loop container engine execution that replaces the no-op stub. spec (@objectstack/spec): - new automation/control-flow.zod.ts: LoopConfig (config.body), ParallelConfig (config.branches[], implicit join) and TryCatchConfig (try/catch/retry) schemas; FlowRegion schema; region well-formedness analysis (analyzeRegion, findRegionEntry) and validateControlFlow (single-entry/single-exit, acyclic; bounded loop). Canonical construct type ids kept distinct from BPMN interop. engine (@objectstack/service-automation): - registerFlow() now rejects malformed control-flow regions before run (validateControlFlow), preserving the DAG invariant for ordinary edges. - new AutomationEngine.runRegion(): executes a body region in the enclosing variable scope via a synthetic flow view, leaving the shared DAG traversal untouched. Durable pause inside a region is a clear error (follow-up). - loop executor moved to builtin/loop-node.ts: a real iteration container that binds the iterator/index variables and runs the body once per item under a hard max-iteration guard. Legacy flat-graph loops (no config.body) keep working — the construct is additive. showcase: BatchRemindersFlow demonstrates the loop container. docs: ADR-0031 records the representation decision (B). Parallel/try-catch engine execution and BPMN interop mapping are follow-ups (#1479 tasks 3–5). https://claude.ai/code/session_012ti8cx3TkdiQdjCnZXZg2Q
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Mark the per-iteration region step-log merge as a tracked follow-up (#1479) and re-run CI after the unrelated flaky redis contract-test timeout. https://claude.ai/code/session_012ti8cx3TkdiQdjCnZXZg2Q
e1fcb1d to
ae8bf94
Compare
…d8Xmr # Conflicts: # packages/services/service-automation/src/engine.ts
This was referenced Jun 1, 2026
Merged
os-zhuang
added a commit
that referenced
this pull request
Jun 2, 2026
The structured control-flow constructs (loop/parallel/try-catch) and the BPMN interop mapping are implemented and merged (#1482, #1489, #1499, #1500; docs #1497). Flip ADR-0031 from Proposed to Accepted and record the implementing PRs and the deferred follow-ups (#1504 BPMN XML plugin, #1505 region step logs). https://claude.ai/code/session_012ti8cx3TkdiQdjCnZXZg2Q Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the foundation of ADR-0031 — structured control-flow constructs — covering issue #1479 tasks 1 & 2 (spec-define the constructs + the loop container engine execution). Parallel/try-catch execution and BPMN mapping (tasks 3–5) are deliberately left as their own follow-up PRs, per the multi-agent discipline in
AGENTS.md.Representation decision: (B) nested sub-structure
ADR-0031 left the representation open (A: marker-delimited scoped regions vs B: nested mini-flow in
config). This PR picks (B) — each container carries its body as a self-contained single-entry/single-exit region inconfig(config.bodyforloop,config.branches[]forparallel,config.try/config.catchfortry_catch). Why:engine.tstraversal stays untouched — the container executor runs its body via a scopedrunRegion(); the main DAGtraverseNextnever learns about scope markers (important given the multi-agent discipline aroundengine.ts). The container's ordinary out-edges remain the after-loop continuation, so the DAG invariant for ordinary edges holds.The ADR has been updated to record this decision.
What's in this PR
@objectstack/spec—automation/control-flow.zod.ts(new)LoopConfigSchema,ParallelConfigSchema,TryCatchConfigSchema(+RetryPolicySchema),FlowRegionSchema.analyzeRegion/findRegionEntry(single-entry, single-exit, acyclic, edge integrity) andvalidateControlFlow(flow)— rejects malformed regions before a flow runs.loop/parallel/try_catch) kept distinct from the BPMN interop node types.@objectstack/service-automation— engine + loop executorregisterFlow()now callsvalidateControlFlow()after DAG cycle detection.AutomationEngine.runRegion()— executes a body region in the enclosing variable scope via a synthetic flow view (iterator var + body mutations are visible to the surrounding flow; not asubflow). Durable pause inside a region → clear error (follow-up).builtin/loop-node.ts: replaces the no-op stub with a real iteration container — bindsiteratorVariable/indexVariable, runs the body once per item, hard max-iteration guard (clamped to a ceiling). Back-compat: aloopwith noconfig.bodykeeps legacy flat-graph behavior — the construct is additive.Showcase / docs
BatchRemindersFlowdemonstrates the loop container.Tests
packages/spec/src/automation/control-flow.test.ts— 21 tests (schemas + region analysis +validateControlFlow).packages/services/service-automation/src/builtin/loop-node.test.ts— 8 tests (iteration, multi-node body, empty collection, non-array failure, max-iteration guard, body-failure propagation, malformed-body rejection at register, legacy fallback).tsc --noEmitclean.Roadmap (#1479)
Closes part of #1479.
https://claude.ai/code/session_012ti8cx3TkdiQdjCnZXZg2Q
Generated by Claude Code