Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/api-coverage.md

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ where it was used. Raw numeric ids remain a valid escape hatch everywhere; see
[`examples/portable.config.ts`](https://github.com/eqrm/ct-cli/blob/main/examples/portable.config.ts)
for a zero-numeric-id config.

**Exception: `groupStatusId` (a group's lifecycle status) is numeric-only,
always.** ChurchTools exposes no REST catalog to resolve a status by name
(`/group/memberstatus` is a different dimension, member statuses; #67), so a
`status:` declaration fails fast at eval time rather than resolving against the
wrong dimension.
For a group's lifecycle status, prefer the stable technical name, for example
`status: "active"`. ct resolves it through
`/person/masterdata.groupStatuses`. Numeric `groupStatusId` remains supported as
a backward-compatible escape hatch. This catalog is distinct from
`/group/memberstatus` (membership statuses) and `/statuses` (person statuses).

## Campus assignment

Expand Down
11 changes: 7 additions & 4 deletions docs/handbuch/blueprints.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ sources:
- src/config/context.ts
- src/engine/graph.ts
- src/engine/hierarchy.ts
sources_hash: 6f4be8d3a93113ce
reviewed: 2026-08-28
sources_hash: 15a942cea09195e1
reviewed: 2026-08-29
---

# Blueprints (parametrized, reusable config)
Expand Down Expand Up @@ -55,6 +55,9 @@ plan` renders it as `campusId = <campus:mainz (created this apply)>`.

The same portability applies to the group type: `groupType: "ministry_team"`
resolves against the live catalog per host, no hardcoded `groupTypeId`.
The group's lifecycle status works the same way: `status: "active"` resolves
through `/person/masterdata.groupStatuses`, whose numeric ids may differ by
host. `groupStatusId` remains the numeric escape hatch (#157).

```ts
function kidsArea(ct: ConfigContext, campus: string): void {
Expand All @@ -69,8 +72,8 @@ The **numeric escape hatch** stays available: pass `campusId: <existing id>`
`groupTypeId: 2` to target one instance's id directly. `ct plan` diffs a campus
assign/move/clear as a normal field update — see
[`docs/group-field-decisions.md`](https://github.com/eqrm/ct-cli/blob/main/docs/group-field-decisions.md). Declaring both the
logical and the numeric form for one field (`campus` + `campusId`) is a conflict
and throws at eval time.
logical and the numeric form for one field (`campus` + `campusId`, `status` +
`groupStatusId`) is a conflict and throws at eval time.

## The loop-over-campuses pattern and `${campus}_`-prefixed keys

Expand Down
32 changes: 17 additions & 15 deletions docs/handbuch/dynamic-groups.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ sources:
- src/engine/dynamic.ts
- src/engine/synthetic.ts
- src/application/operations/adopt-group.ts
sources_hash: e38b8c0f6032d5cc
reviewed: 2026-08-28
sources_hash: f5d6029e65ce3936
reviewed: 2026-08-29
---

# Auto-groups (dynamic groups)
Expand Down Expand Up @@ -158,7 +158,8 @@ no-op — it does not re-`PUT` on every apply). Two equivalent ways to author it
```

Simple marker `kind`s carry a single `key` (the logical key / slug):
`campus`, `group`, `group-type`. A **role** (`role.id`) uses the compound
`campus`, `group`, `group-type`, `group-status`. A **role** (`role.id`) uses
the compound
`group-type-role` marker instead — `{ "__ctRef": true, "kind":
"group-type-role", "groupType": "<group-type-key>", "role": "<role-name>" }` —
because a ruleset's `role.id` is a **groupTypeRoleId** (a role scoped to a
Expand Down Expand Up @@ -210,14 +211,15 @@ position that maps to a **managed** logical key is rewritten to its `{ __ctRef }
marker; every other id is left numeric. The `var → RefKind` catalog it keys off
(`VAR_REF_KINDS`) is:

| ChurchQuery `var` | marker `kind` | source catalog / state |
| --------------------- | ----------------- | -------------------------------------------------- |
| `ctgroup.id` | `group` | managed state (no REST catalog) |
| `ctgroup.campusId` | `campus` | `/campuses` |
| `person.campusId` | `campus` | `/campuses` |
| `ctgroup.groupTypeId` | `group-type` | `/group/grouptypes` |
| `role.id` | `group-type-role` | `/group/roles` (by `groupTypeId` + name) |
| `role.id` | `role-def` | managed state — only when the pair collides (#125) |
| ChurchQuery `var` | marker `kind` | source catalog / state |
| ----------------------- | ----------------- | -------------------------------------------------- |
| `ctgroup.id` | `group` | managed state (no REST catalog) |
| `ctgroup.campusId` | `campus` | `/campuses` |
| `person.campusId` | `campus` | `/campuses` |
| `ctgroup.groupTypeId` | `group-type` | `/group/grouptypes` |
| `ctgroup.groupStatusId` | `group-status` | `/person/masterdata` → `groupStatuses` |
| `role.id` | `group-type-role` | `/group/roles` (by `groupTypeId` + name) |
| `role.id` | `role-def` | managed state — only when the pair collides (#125) |

The same `group-type-role` rewrite also covers the **out-of-query** integer
field `process.*.handleMembership.groupTypeRoleId` (the target role a
Expand Down Expand Up @@ -305,7 +307,7 @@ At **capture** time (`ct adopt … --with-dynamic`) the state file and the
```text
! rulesets/jugend.json keeps 5 host-specific id(s) — NOT portable to another host:
ctgroup.id: 1246 left numeric — not under management — `ct adopt group <id>` for each (then re-adopt) makes them portable
ctgroup.groupStatusId: 1, 2 left numeric — group statuses have no REST catalog (#67) — no logical form exists
ctgroup.groupStatusId: 99 left numeric — no group-status catalog row on this host carries these ids
person.id: 5703, 4389 left numeric — person ids are NEVER portable — ct does not manage people, so this ruleset names DIFFERENT people on another host. Remove the clause or accept the divergence
```

Expand Down Expand Up @@ -345,9 +347,9 @@ reason it never checked:

The capture-time reasons are distinct because the fixes are: an **unmanaged**
target (adopt it), a **role unknown to `/group/roles`**, a role whose **group
type is unmanaged**, or a dimension with **no logical form at all**
(`ctgroup.groupStatusId` — group statuses have no REST catalog, #67; this one
needs no lookup, so the plan-time scan reports it too).
type is unmanaged**, or an **unknown group-status id**. Group statuses normally
resolve through `/person/masterdata.groupStatuses`; an id absent from that live
catalog stays numeric and is reported.

**`--strict-rulesets`** turns the warning into a refusal: adopt writes nothing
if the ruleset would still contain a host-specific id. Use it in a repo that has
Expand Down
4 changes: 2 additions & 2 deletions docs/handbuch/group-member-fields.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
sources_hash: c18b710bff24503d
sources_hash: 84cc15a0514f7d05
title: Group member fields
sources:
- src/engine/member-fields.ts
- src/engine/synthetic.ts
- src/config/context.ts
- src/application/operations/adopt-group.ts
- src/application/operations/destroy.ts
reviewed: 2026-08-28
reviewed: 2026-08-29
---

# Group member fields (#135, #158)
Expand Down
13 changes: 7 additions & 6 deletions docs/handbuch/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ sources:
- src/resolve/resolver.ts
- src/resolve/refs.ts
- src/config/context.ts
sources_hash: c3a6740c4d4bc134
reviewed: 2026-08-28
sources_hash: 2c0e7cbf30d33880
reviewed: 2026-08-29
---

# Permissions (`ct.groupRole` / `ct.groupTypeRole` / `ct.status`)
Expand Down Expand Up @@ -236,10 +236,11 @@ The two DSL functions manage two different ChurchTools "domain types," and
check, not a truthiness one.

> **Person status ≠ group status.** `groupStatusId` (`ct.group`) is a
> different dimension with **no** REST catalog at all (#67) and must always be
> written as a number. Person statuses do have one (`GET /statuses`, flat
> array of `{id, name}` — live-verified 2026-08-10 on eqrm prod), so they
> resolve by name like campuses and group types.
> different dimension. Its read-only catalog is nested under
> `GET /person/masterdata` → `groupStatuses`, so `status: "active"` resolves
> by technical name (#157); numeric `groupStatusId` remains an escape hatch.
> Person statuses instead use `GET /statuses` (flat array of `{id, name}` —
> live-verified 2026-08-10 on eqrm prod).

Since #96 the status itself is also **declarable**, via `ct.personStatus`:

Expand Down
Loading
Loading