Uh oh!
There was an error while loading. Please reload this page.
Re-land automations diff - #25
Merged
Merged
Conversation
`automations update` was all-or-nothing: you either trusted the spec or eyeballed two full JSON trees. `diff` shows what the update would change — trigger/step additions, removals, reparenting, and field changes. The hard part is identity. GET and PUT are different dialects, so `key` is resynthesized from live order on one side and hand-authored on the other; comparing them literally reports every step as changed. Steps and triggers are matched by `id`, with `key`/`parent_key`/`prefix` excluded as per-side naming rather than content. Reparenting and `go_to_automation_step` references are resolved to the matched identity of their target, so they still surface a real change without tripping on a cosmetic rekey. A spec item carrying an `id` that matches no live step is treated as an addition, and the live step it would have displaced as a removal, rather than merged into it by position — that is what the PUT would actually do, and the previous position-merge silently swallowed the unknown id.
`diff_wire_payloads`'s docstring said its return value was "the same shape `roundtrip_automation`'s `drift` field already uses." The three keys match; the `path` convention does not. `drift` comes from `semantic_diff`, which identifies steps positionally (`steps[3].field`) and carries no id anywhere, so it is not a precedent for these id-octet-labelled paths.
jbedient-kizenforce-pushed
the
feat/reland-automations-diff
branch
from
September 1, 2026 16:30
35b9dc6 to
b5cf588Compareannaliu-kizen
approved these changes
Sep 1, 2026
Uh oh!
There was an error while loading. Please reload this page.
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 freeto 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.
This adds no new work.
kizen automations diff <api_name> --spec-file <path>was built, reviewed, and merged as PR #12 — and it is not onmain.What happened
Same root cause as #24. PR #12 was merged into
bug/automation-update-deactivates-without-askingrather thanmain, because that item hadn't merged yet. It then merged tomainand its branch was deleted, so #12's content — which lived only on top of that branch — never propagated. GitHub records #12 as MERGED.Verified:
git merge-base --is-ancestor 6c104316 mainreturns non-ancestor,main'scli/automations.pyhas nodiffsubcommand, andmain'sscripts/cli-tree-baseline.txthas no entry for it.Why this one is a rebase, not a merge
Unlike #24, this branch does not merge cleanly. It was cut in mid-August and
cli/automations.pyhas since taken PR #20'smerge_fieldsintegration. A two-dot diff against the branch shows ~6,900 deletions purely because the branch lacks everythingmaingained since — merging it would drag that staleness in as a resolution exercise across ~63 files.So the two commits were cherry-picked onto current
maininstead, which keeps the reviewable diff to just the feature.The two conflicts, and how they were resolved
Both were benign and are worth stating explicitly so a reviewer can check the judgement rather than take it on trust:
src/kizen_builder/cli/automations.py— both sides added an import at the same location (KizenAPIErroronmain,_read_specon the branch). Kept both; confirmed both are actually used (:16,:17, used at:515and:310).CHANGELOG.md— competing additive entries in[Unreleased]. Kept both blocks in full (49 lines frommain, 15 from the branch). Nothing dropped.scripts/cli-tree-baseline.txtauto-merged and passesextra_checksunmodified.Verification
bin/check.sh builder-cli --dir <worktree>— lint, format, typecheck, test, extra_checks all pass:main's baseline is 1250 passed, so all 19 tests these commits add are present and passing, including thetests/test_translate.pyandtests/test_cli.pyadditions.The specific regression risk here was that a careless resolution would silently revert
main'smerge_fieldswork. Confirmed intact:tools/planners/automations.pystill importsmerge_fieldsand still callsmerge_fields.renderat all three sites (:1669,:1741,:2102).uv run kizen automations diff --helprenders, andautomations diffis present in the regenerated CLI tree.Two known caveats — neither introduced here, both worth reading before merge
diffsharesroundtrip's client-side-only limitation. Validation is client-side; the mismatches that matter are server-side. A cleandiffdoes not prove a subsequentupdatewould be accepted. The second commit here (Stop claiming the wire diff shares roundtrip's drift path convention) already walks back one overclaim of this kind. The general fix is tracked separately, along with makingroundtripstate what it did not check.diffdoes not report phantom drift on notify steps. An earlier note predicted it would — that prediction is disproven, and is recorded here because it would otherwise lead a reviewer to distrust a non-empty diff for the wrong reason.The prediction was reasonable: the spec→payload builders for
notify_member_via_text/_via_emailassemble from a key allowlist (team_member/content/html_content/base_message_idandteam_member/cc_team_member/id) that never readsname. Butdiff's live side is not the raw GET —translate.live_to_payloadruns each live step through that same_STEP_BUILDERSregistry (translate.py:38, used at:179). Both sides therefore dropnameidentically, it is absent on both, and the symmetric key walk in_diffemits nothing:The server's own schema confirms dropping
nameis correct rather than lossy:ActionNotifyMemberViaText(read) requiresname, whileActionNotifyMemberViaTextRequest(write) has no such property —base_message_id,content,html_content,team_memberonly. Same forWriteActionNotifyMemberViaEmailRequest. It is read-only server state, so the write dialect has no way to carry it.What remains real, and is not this PR's surface:
roundtrip --executeperforms a write, and because the PUT cannot carryname, the server blanks it (observed as"…Version 3" → ""). That is a pre-existing write-side limitation ofroundtrip, tracked separately, and unaffected by anything here. A related cleanup is that both notify config models useextra="allow", so anamewritten into a hand-authored spec validates and is then discarded — accepting a key and ignoring it is the part worth fixing.