fix: build update bodies from fetched actuals, desired-side hierarchy gate, plan/apply robustness - #38
Merged
Conversation
#28) The parents fold keyed on managed groups existing in the pre-apply state file, so a fresh/empty state returned early: create items carried no parents change and the first apply produced a flat hierarchy (exit 0). Gate on 'some desired group opted into parents' instead — the first apply now writes edges, and the /groups/hierarchies fetch is still skipped when nobody opts in (#17 item 5).
…27) computePlan diffed desired vs actual, but executePlan built the write body from the state-file snapshot, so any update reverted a sibling field that had drifted in the CT UI — a write the plan never showed. Thread the fetched actual onto each update PlanItem; PATCH resources now send only the changed fields, PUT resources send actual ∪ changes. Post-apply state records actual ∪ changes regardless of verb. (Also adds the fetch-failed PlanNote + detail scaffolding to types.ts/plan.ts that #33 item 2 wires up.)
…ard 2xx JSON (#33) Item 1: assert the minimum CT version in authedSession via one cached /info GET — plan/apply/destroy now hard-fail below the minimum instead of only warning in auth login. Item 2: thread fetch-failed keys (non-404 errors) from buildPlan into computePlan so they render as '? <key> — fetch failed (<status>)' and are excluded from create/recreate/stale classification — a transient 500 no longer reads as 'recreate — missing in ChurchTools'. Item 4: guard res.json() on non-204 2xx bodies — empty body returns undefined, non-JSON body throws a CtApiError naming method+path instead of a raw SyntaxError.
This was referenced Jul 9, 2026
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.
Fixes three verified bugs from the 2026-07-08 codebase review.
#27 — apply reverts drifted fields
Update bodies were built from the state-file snapshot, so a field edited in the CT UI got silently clobbered by any unrelated update — with a write the plan never showed. Plan items now carry the fetched actual: PATCH resources send only the changed fields; PUT resources send actual ∪ changes; post-apply state records what was actually written.
#28 — first apply on fresh state silently drops all hierarchy edges
parentsField.foldgated on managed groups in the pre-apply state, so a fresh state (the exact #23 bootstrap path) created all groups but no edges, exit 0. The gate is now desired-side (d.type === "group" && d.parents !== undefined), which also stops fetching/groups/hierarchieswhen nothing opts in — resolving #17 item 5.#33 items 1, 2, 4 (item 3 ships with the #29 fix)
CtClient.assertMinVersion()(one cached/infoGET) called fromauthedSession— plan/apply/destroy now hard-fail below the documented minimum instead of half-applying.computePlanand render as? <key> — fetch failed (<status>), excluded from create/stale classification.undefined; non-JSON →CtApiErrornaming method + path, never a raw SyntaxError.Verification: 224 passed / 4 skipped, typecheck + lint clean.
Closes #27. Closes #28.
Addresses #33 items 1, 2, 4 (item 3 tracked with #29).