What happens
ct state rm <type> <key> refuses a key that the config only references,
reporting it as declared:
✗ "mainz" is still declared in the config, so removing it from state would make
the next plan propose CREATING a resource that already exists on this host.
Remove the declaration first, or pass --force if you are deleting both in the
same change.
At that point ct.config.ts declared no campuses at all — the CAMPUSES table
and its for (const c of CAMPUSES) ct.campus(c) loop had both been removed, and
grep -nE 'ct\.(campus|groupType|department|personStatus|commentViewer)\('
matched nothing. verify agreed: declared: 216 resource(s) where it had been
266.
Reference vs declaration
Same state file, same config, two keys:
| key |
occurrences in config |
result |
campus/horgen |
0 |
Would remove campus.horgen (#36) — accepted |
campus/mainz |
60, all campus: "mainz" |
refused as "still declared" |
The only difference is that mainz is referenced by other resources. So the
check appears to look for the key's presence rather than for a declaration.
Why the stated consequence does not follow
The message says the next plan would propose creating a resource that already
exists. But verify classifies exactly these references as resolved against the
live instance:
~ skidscheckinopsmz.groupTypeId = group-type:struktur (logical ref, resolved live — not compared)
A logical ref asks the host for the id. It does not ask ct to create the
referenced object, so removing the state entry should be safe — which is what
--force ends up asserting by hand.
Why it matters
This is the exact situation the tier-0 OpenTofu cutover lands in: tier-0 moves to
terraform-provider-churchtools, the tables leave ct.config.ts, and the 232
surviving campus: / groupType: / department: / commentViewer: references
stay behind as logical refs. --force is then mandatory for 49 of 50 entries,
and it suppresses a check that would be worth keeping for the case it is actually
meant to catch — a key still genuinely declared.
Suggested fix
Decide "declared" from the resources the config actually declares (what verify
counts), not from the key appearing somewhere in it. Then --force is needed
only when a declaration really is still present, and the cutover needs no
blanket force.
Repro is in eqrm/ct-structure#85's stack; ct-cli 3.9.1.
https://claude.ai/code/session_016XVmiQY44pjx1u9Fu4iSDP
What happens
ct state rm <type> <key>refuses a key that the config only references,reporting it as declared:
At that point
ct.config.tsdeclared no campuses at all — theCAMPUSEStableand its
for (const c of CAMPUSES) ct.campus(c)loop had both been removed, andgrep -nE 'ct\.(campus|groupType|department|personStatus|commentViewer)\('matched nothing.
verifyagreed:declared: 216 resource(s)where it had been266.
Reference vs declaration
Same state file, same config, two keys:
campus/horgenWould remove campus.horgen (#36)— acceptedcampus/mainzcampus: "mainz"The only difference is that
mainzis referenced by other resources. So thecheck appears to look for the key's presence rather than for a declaration.
Why the stated consequence does not follow
The message says the next plan would propose creating a resource that already
exists. But
verifyclassifies exactly these references as resolved against thelive instance:
A logical ref asks the host for the id. It does not ask ct to create the
referenced object, so removing the state entry should be safe — which is what
--forceends up asserting by hand.Why it matters
This is the exact situation the tier-0 OpenTofu cutover lands in: tier-0 moves to
terraform-provider-churchtools, the tables leavect.config.ts, and the 232surviving
campus:/groupType:/department:/commentViewer:referencesstay behind as logical refs.
--forceis then mandatory for 49 of 50 entries,and it suppresses a check that would be worth keeping for the case it is actually
meant to catch — a key still genuinely declared.
Suggested fix
Decide "declared" from the resources the config actually declares (what
verifycounts), not from the key appearing somewhere in it. Then
--forceis neededonly when a declaration really is still present, and the cutover needs no
blanket force.
Repro is in eqrm/ct-structure#85's stack; ct-cli 3.9.1.
https://claude.ai/code/session_016XVmiQY44pjx1u9Fu4iSDP