docs(skills/swamp-workflow): document when to use nested workflows#1165
Merged
docs(skills/swamp-workflow): document when to use nested workflows#1165
Conversation
Add a "When to Use Nested Workflows" section to the swamp-workflow skill covering the three cases where a child workflow is the right shape — most importantly, forEach over an async list (`data.latest()`, `data.findByTag()`, etc.). `forEach.in` is evaluated synchronously so promise-returning CEL functions never resolve in that position and the step fails with a misleading "got: object" error. Task `inputs:` ARE awaited for both model_method and workflow tasks, so the canonical fix is to resolve the async call in the parent's task.inputs and let the child iterate over a plain `inputs.<name>`. Also cross-references the new section from expressions-and-foreach.md so readers landing there find the escape hatch, and updates the SKILL.md reference pointer so the topic is discoverable from the index. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
Code Review
Blocking Issues
None.
Suggestions
- Pre-existing: SKILL.md exceeds 500-line limit — The file is currently 557 lines (558 after this PR), above the 500-line cap in CLAUDE.md. This is not introduced by this PR (net +1 line), but worth a future cleanup pass to move more content into
references/files.
Notes
- Technical accuracy verified: The core claims about
forEach.insynchronous evaluation (celEvaluator.evaluate()atexecution_service.ts:1553) and asynctask.inputsevaluation (evalService.evaluateData()at lines 334 and 1974) are confirmed against the source code. - Cross-references are well-placed: The pointer from
expressions-and-foreach.mdtonested-workflows.md#when-to-use-nested-workflowsensures readers landing on the forEach doc find the escape hatch for the async limitation. - "When NOT to nest" section is a good addition — it prevents over-use of the pattern.
- No code changes, no import boundary concerns, no security issues. DDD review not applicable (docs only).
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.
Summary
Adds a "When to Use Nested Workflows" section to the
swamp-workflowskill covering the three cases where a child workflow is the right shape — most importantly, the one that's invisible until you hit it:forEach over an async list.
forEach.inis evaluated synchronously at expansion time (src/domain/workflows/execution_service.ts:1543), so CEL expressions that return a Promise —data.latest(),data.findByTag(),data.findBySpec()— never resolve in that position, and the step fails with a misleadingforEach.in must evaluate to an array or object, got: object(the "object" is the unresolved Promise).Task
inputs:ARE awaited for bothmodel_methodandworkflowtasks (execution_service.ts:329and:1964, both usingevalService.evaluateDatawhich callscelEvaluator.evaluateAsync). So the canonical fix is to resolve the async call in the parent'stask.inputsand let the child iterate over a plaininputs.<name>:Changes
references/nested-workflows.md— new top section covering three "when to nest" cases (async list → forEach, reusable sub-process, independent cadence) and an explicit "when NOT to nest" list (usedependsOnfor ordering, direct CEL for shared values, 10-level nesting cap).references/expressions-and-foreach.md— new "forEach.in Cannot Await Promises" subsection right after the forEach variables table, with a pointer tonested-workflows.md#when-to-use-nested-workflowsso readers landing on the forEach doc find the escape hatch.SKILL.md— reference index pointer updated to surface "when to split a workflow into parent + child (including the async-list-into-forEach pattern)".Follow-up issue filed for the misleading
got: objecterror message itself (the docs fix the user-facing workaround; the error message fix is orthogonal).Test Plan
deno fmt --checkpassesdeno lintpassesdeno run testswamp-workflowskill)🤖 Generated with Claude Code