Skip to content

fix(state rm): a reference counts as a declaration, so --force is required when it should not be #180

Description

@2000game

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageUnsorted intake — decide in the weekly sweep

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions