Skip to content

Read, round-trip and edit automations that contain loops - #96

Merged
2 commits merged into
mainfrom
bug/loop-automations-fail-to-write
Oct 27, 2026
Merged

2 commits merged into
mainfrom
bug/loop-automations-fail-to-write

Conversation

@jbedient-kizen

@jbedient-kizen jbedient-kizen commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Kizen's 2026-10-26 release added loop steps, and the CLI read a loop's config from action_loop, which doesn't exist. Every read-modify-write verb then PUT the loop with action_loop: {} and no step_loop, and got HTTP 400: … step_loop must be provided for loop after a clean --dry-run. So activate, deactivate, move, steps add|edit|remove and roundtrip --execute failed on any automation with a loop, and show didn't render them.

Solution

A loop now gets a real builder, sending step_loop with its array and item variables by name (ids 400). next_or_finish and exit_loop are configless closers, like branch and merge_branches. A loop is modelled as one more branch-group initiator, reusing that machinery: next_or_finish closes it the way merge_branches closes a group, the body hangs off the loop and the steps after it hang off the closer.

validate_payload learns the six loop rules the server enforces, so --dry-run names the broken step. show labels each loop with its array and item variables and puts the body under it. steps add --parent <loop> puts the new step at the head of the body, steps add builds an exit_loop, and steps remove refuses to leave a loop unclosed. The spec model accepts the three types in their wire shape. Translate now refuses a live step carrying a config block it doesn't read, rather than silently emptying it on PUT; it ignores scalar envelope keys, so a new one like step_error can't lock every automation out.

Testing

Local checks after merging main: lint, format, typecheck, pytest (2933 passed, 4 skipped, 1 xfailed) and the CLI-tree diff, passed: lint format typecheck test extra_checks.

The 22 non-loop fixtures translate byte-identically on main and this branch, and every readable automation on a test tenant (GETs only) gets the same translate-and-validate outcome on both. Live, on a scratch automation with two loops, a merged condition and an exit_loop inside a loop: steps add --parent <loop>, activate/deactivate, steps remove, steps add of an exit_loop and steps edit on the loop were each followed by roundtrip --execute with zero drift. A dry-run reparenting the closer exits 1 with both rule texts. Drift -k "loop or coverage": 6 passed. The full drift tier has 4 failures, all also failing on main: its schema snapshot predates the 2026-10-26 release.

Design notes / tradeoffs

Rejected: refusing loop automations with a clear error. Safer to write, but it leaves them uneditable now that loops are in the UI. The graph rules are kept to the six seen as live 400s, and the accepted shapes are pinned too, so a rule can't falsely reject what the server accepts.

#97 (the loop-spec authoring PR) (authoring loops from a spec) and #98 (the steps add loop PR) (steps add completing a loop) stack on this one.

Overlaps: #95 (the webhook-auth fix) and #99 (the Call LLM MCP-server fix) also change tools/planners/automations.py, docs/specs/automation.md and CHANGELOG.md, and #99 (the Call LLM MCP-server fix) adds to tests/drift/test_roundtrip_automations.py; #100 (the mcp-servers command group) and #101 (the service-accounts command group) also change scripts/cli-tree-baseline.txt. The second of these to merge should need a small conflict fix. The merge from main also refreshes scripts/cli-tree-baseline.txt for #92's new kizen upgrade --check help text, which main's baseline lacks; every PR in this set carries that same line.

Kizen's 2026-10-26 release added loop steps, which the CLI read with no
config block. Every read-modify-write verb then PUT the loop without its
step_loop and got a 400 after a clean --dry-run, so activate, deactivate,
move, steps and roundtrip --execute failed on any automation with one.

Loops now translate with their array and item variables by name, and
next_or_finish and exit_loop as the configless closers they are.
--dry-run checks the server's loop rules and names the broken step, show
puts each loop's body under it, and steps add and remove keep a loop
closed. Translate also refuses a live step carrying a config block it
doesn't read, instead of silently dropping it on PUT.
@jbedient-kizen

Copy link
Copy Markdown
Contributor Author

Included in the 0.4.0 release PR #102; merge that instead.

@sangyeT sangyeT left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed StepLoopConfig, the configless next_or_finish/exit_loop handling, the unread-block guard in _live_step_to_wire, the server-rule validator (_loop_problems, _in_loop_body) and the show/steps changes. Fixes a real write failure on any automation with a loop and the rules are each tied to a confirmed live 400. Approving.

@jbedient-kizen jbedient-kizen closed this pull request by merging all changes into main in 39beea1 Oct 27, 2026
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.

2 participants