Fix the sub-workflow tool, and write the ladder into the changelog - #86
Merged
Conversation
`TemplateAgentTool` wraps a stored template as a tool an LLM agent can call.
Three defects sat on top of each other, and the top one hid the rest.
THE IMPORT WAS ONE LEVEL SHORT. `_execute_template` imported WorkflowEngine
from `...engine`, which from `modules/atomic/llm/` resolves to
`core.modules.engine` and does not exist; every neighbour in the same directory
writes four dots. So every call raised ModuleNotFoundError -- and `invoke`'s
blanket `except Exception` turned it into `{"ok": False, "error": ...}`, an
ordinary-looking tool failure. The agent reported a template that ran and
failed. A crash that never surfaced as a crash, on every call, for as long as
this was wrong.
BEHIND IT, THE WRONG SHAPE. The workflow was built as
`{"nodes": ..., "edges": ...}`. A template definition holds `steps` --
`template.invoke` builds `workflow={'steps': steps}` from the same shape -- and
the engine answers "No steps defined in workflow" to anything else. Unreachable
behind the import error, so it had never run either. An empty template now says
so here rather than coming back with the engine's wording for a different
problem.
BEHIND THAT, NO RECURSION BOUND. `template.invoke` caps nesting at 16 and
refuses past it; this path counted nothing, so a template whose agent has that
same template as a tool would spawn engines until the execution timeout. It now
reads and increments the same context key, so a mixed chain -- template.invoke
calling an agent whose tool is a template -- is counted once per level rather
than resetting at each hand-off. `True` is rejected along with negatives and
non-integers: it is an `int` in Python and would otherwise pass every
comparison as 1.
Driven against the real engine with a real module:
invoke({"text": "hi"}) -> ok: True, steps.s1.data.result == "HI"
depth 15 -> runs depth 16 -> refused
depth -1 -> refused depth True -> refused
Ten tests, and each of the three fixes severed independently: cutting the
import fails all ten, the shape fails three, the cap fails one.
The 2.31.2 section described the old five-valued `verification_status` and stopped there. The ladder itself -- the thing eleven unreleased commits are about -- had never been written down, so a reader of the changelog would have had no idea it existed. Changed covers the contract: one shared vocabulary across 483 modules, only `verified` drawable as success, and why `failed` and `indeterminate` are different answers rather than two words for the same one -- retrying a failure is safe, retrying an indeterminate write may do it twice. Plus the two things that make it reach anyone: a rung can now travel out through a raise, and an LLM agent is told the rung of every tool it calls. Fixed covers nine defects, each with the measurement rather than an adjective: the revoked webhook that completed green in a shipped recipe, the 158 of 483 modules with no `ok` key, the 3920-pixel scroll reported as a drag, the misspelled selector that reached a green tick 1495ms faster than the real element, `verify.run` failing at the registry, and the sub-workflow tool that had never executed once. Reference pages regenerated; the inventory sentences follow to 230,913 lines. `check_release_drift.py` and `check_documentation.py` both pass.
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.
Two things, both prerequisites for releasing 2.31.2.
The agent's sub-workflow tool had never executed once
TemplateAgentToolwraps a stored template as a tool an LLM agent can call — flyto's equivalent of n8n's Workflow Tool. Three defects sat on top of each other, and the top one hid the rest.The import was one level short.
_execute_templateimported WorkflowEngine from...engine, which frommodules/atomic/llm/resolves tocore.modules.engineand does not exist; every neighbour in the same directory writes four dots. Every call raised ModuleNotFoundError — andinvoke's blanketexcept Exceptionturned it into{"ok": False, "error": ...}, an ordinary-looking tool failure. The agent reported a template that ran and failed. A crash that never surfaced as a crash.Behind it, the wrong shape. The workflow was built as
{"nodes": ..., "edges": ...}. A template definition holdssteps—template.invokebuildsworkflow={'steps': steps}from the same shape — and the engine answers "No steps defined in workflow" to anything else.Behind that, no recursion bound.
template.invokecaps nesting at 16; this path counted nothing, so a template whose agent had that same template as a tool would spawn engines until the execution timeout. It now shares the same context key, so a mixed chain is counted once per level rather than resetting at each hand-off.Driven against the real engine with a real module:
Ten tests; each of the three fixes severed independently — cutting the import fails all ten, the shape fails three, the cap fails one.
The changelog
The 2.31.2 section described the old five-valued
verification_statusand stopped there. The ladder — what eleven unreleased commits are about — had never been written down.Changed covers the contract, and why
failedandindeterminateare different answers rather than two words for one: retrying a failure is safe, retrying an indeterminate write may do it twice.Fixed covers nine defects with the measurement rather than an adjective — the revoked webhook that completed green in a shipped recipe, the 158 of 483 modules with no
okkey, the 3920-pixel scroll reported as a drag, the misspelled selector that reached a green tick 1495ms faster than the real element.Verification
Full suite: 5,058 passed, 4 pre-existing failures (packaging, two live-URL visual-diff tests, version metadata). Three further failures in the first run were network flakes and pass on re-run.
check_release_drift.pyandcheck_documentation.pyboth pass.🤖 Generated with Claude Code