fix(member-fields): normalize live reconciliation - #154
Merged
Conversation
…aborts Review findings on eqrm#154: - synthetic: a state-bound member-field id missing from the live response made the WHOLE group undiffable (build.ts turns rows: undefined into fetchFailed), silently freezing name/parents/dynamic and every other member field. Scope it to the affected pseudo-field instead — dropped from the desired side, so no replacement POST — and name the recovery (`ct destroy --member-field <identity>`) in the error. - adopt-group: an id-less row or two rows slugging to the same local key threw, and saveState only runs after the whole --children-of loop, so one bad group discarded every group already adopted. Warn and skip the group's member fields, matching the read-failure path. - member-fields: guard the defaultValue↔option-id match on both sides being non-null; "undefined" === "undefined" reported the field converged forever and never wrote the declared default. - member-fields: merge the { type, field } wrapper over the nested definition instead of keeping only `type`, so a wrapper-held id survives, and gate the unwrap on the nested object looking like a definition. - resolver: a stale state binding filtered by id alone, then errored "group eqrm#12 has no member field \"wahl\" (available: \"Wahl\")" — name the stale binding and how to clear it instead. - adopt-group: don't write `memberFields: {}`; destroy deletes the key when the map empties, so an empty map churned the state file. Claude-Session: https://claude.ai/code/session_016JBGhwKsanNFAK9cs7N9Uv
Group member fields: adoption now warns and skips on rows it cannot capture
cleanly (no id, colliding local keys) instead of aborting the run; a stale
state binding degrades to that one field rather than the whole group, and
names `ct destroy --member-field` as the way out; the `{ type, field }`
wrapper keeps whatever only the wrapper carries.
dynamic-groups.md and permissions.md are re-signed unchanged — the synthetic
and resolver edits are confined to the member-field paths those pages do not
document.
Claude-Session: https://claude.ai/code/session_016JBGhwKsanNFAK9cs7N9Uv
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
{ type, field }row shape while preserving its ownership scopect adopt group --with-member-fieldsWhy
The group-member-field endpoint can wrap group-owned definitions in different envelopes, serialize ids as strings, and report rows as
{ type, field }. ChurchTools also enriches option definitions with ids and may return a default option by id although the portable config names it symbolically. Treating these responses as missing or materially different can plan a duplicate POST or a perpetual update. Re-adoption also emitted portable config but did not refresh the current owner-local identity map in state.This follow-up keeps the portable config id-free while storing host-specific ids only in the instance state. It deliberately does not add compatibility code for the earlier first-class
group-member-fieldstate shape; affected workspaces can re-adopt the live groups instead.Follow-up to #135 and #141.
Verification