Skip to content

fix(member-fields): normalize live reconciliation - #154

Merged
2000game merged 5 commits into
eqrm:mainfrom
bwl21:fix/member-field-api-normalization
Aug 26, 2026
Merged

fix(member-fields): normalize live reconciliation#154
2000game merged 5 commits into
eqrm:mainfrom
bwl21:fix/member-field-api-normalization

Conversation

@bwl21

@bwl21 bwl21 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • normalize the live group-member-field response variants observed across ChurchTools versions
  • unwrap the live { type, field } row shape while preserving its ownership scope
  • compare option metadata semantically, ignoring server-assigned option ids and resolving id-backed defaults to their declared option names
  • prefer the current owner-local state id during plan, apply, and reference resolution
  • refuse a replacement create when a state-bound field id is missing from the live response
  • persist live member-field ids under the owning group during ct adopt group --with-member-fields

Why

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-field state shape; affected workspaces can re-adopt the live groups instead.

Follow-up to #135 and #141.

Verification

  • 1013 tests passed, 5 skipped
  • typecheck
  • lint
  • build
  • tracked-file Prettier check
  • handbook staleness check
  • live read-only re-adoption and plan against a test workspace: all 36 member fields converged with zero creates and zero updates

bwl21 and others added 5 commits August 26, 2026 12:22
…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
@2000game
2000game merged commit 6ccd5e9 into eqrm:main Aug 26, 2026
3 checks passed
@bwl21
bwl21 deleted the fix/member-field-api-normalization branch August 26, 2026 12:36
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