state has no project registry — the list of what exists lives only in the operator's head #25

Closed
opened 2026-07-13 19:20:49 +00:00 by dan-claude-bot · 1 comment
dan-claude-bot commented 2026-07-13 19:20:49 +00:00 (Migrated from github.com)

Problem

The state repo cannot say which projects exist. environments.yaml is keyed
by environment — but the state it increasingly needs to hold is scoped by
project × environment:

  • the destination a resource lands on (#21)
  • smoke_target: core — which names incubator's compose app, under a key that
    claims to be about the environment, and will be simply wrong the day a
    second project deploys into prod
  • and the project list itself, which does not exist anywhere at all

github_apps.<repo> already solved this correctly, by keying on the repo. It is
the registry in embryo. Make it first-class:

projects:
  incubator:
    repo: heavy-duty/incubator
    environments: [prod, staging]
    # project × environment state moves here (see #21)

Why it matters beyond tidiness

Three things become possible, and none of them can be done today:

  1. Fleet operationscast diff --all / apply --all need a list to
    iterate. Today "every project" is a thing the operator remembers.
  2. Rebuild-from-state (the DR claim) — "restore this Coolify from the infra
    repo" is currently an assumption. It cannot even be attempted without
    knowing what was on it. The registry is the difference between a documented
    recovery and an archaeology exercise.
  3. A home for project-scoped state that is not overloaded onto an
    environment key.

Coordination

⚠️ #21 is being worked in a parallel session. That issue covers
destinations and the project-scoped state shape; this one covers the
registry itself (the list of projects, and fleet iteration over it). They
touch the same file and should land in a compatible order — whichever goes
first should define the projects: block, and the other should build on it
rather than inventing a second shape.

## Problem The state repo cannot say **which projects exist**. `environments.yaml` is keyed by *environment* — but the state it increasingly needs to hold is scoped by **project × environment**: - the **destination** a resource lands on (#21) - `smoke_target: core` — which names *incubator's* compose app, under a key that claims to be about the environment, and will be simply **wrong** the day a second project deploys into `prod` - and the project list itself, which does not exist anywhere at all `github_apps.<repo>` already solved this correctly, by keying on the repo. It is the registry in embryo. Make it first-class: ```yaml projects: incubator: repo: heavy-duty/incubator environments: [prod, staging] # project × environment state moves here (see #21) ``` ## Why it matters beyond tidiness Three things become possible, and none of them can be done today: 1. **Fleet operations** — `cast diff --all` / `apply --all` need a list to iterate. Today "every project" is a thing the operator remembers. 2. **Rebuild-from-state (the DR claim)** — "restore this Coolify from the infra repo" is currently *an assumption*. It cannot even be attempted without knowing what was on it. The registry is the difference between a documented recovery and an archaeology exercise. 3. **A home for project-scoped state** that is not overloaded onto an environment key. ## Coordination ⚠️ **#21 is being worked in a parallel session.** That issue covers **destinations** and the project-scoped state *shape*; this one covers the **registry itself** (the list of projects, and fleet iteration over it). They touch the same file and should land in a compatible order — whichever goes first should define the `projects:` block, and the other should build on it rather than inventing a second shape.
dan-claude-bot commented 2026-07-13 19:47:27 +00:00 (Migrated from github.com)

#28 delivered the shape. What remains is the authority.

environments.<env>.projects.<org>/<repo> now exists, keyed by full slug with a bare-repo fallback — exactly the github_apps precedent this issue asked for, and smoke_target has already moved into it. The structural half of this issue is done.

But the block is optional ("an environment whose server hosts one project declares neither"), and that is correct for its purpose — a destination you don't need shouldn't have to be declared. It does mean the block is not yet a registry: a project that exists but declares nothing is invisible to it.

That gap is what the two dependents actually need:

  • #26 (--all) — iterating projects: today iterates the projects that happened to need per-project state, not the projects that exist. A fleet diff that silently skips an undeclared project reads exactly like a clean one.
  • The DR rebuild — "restore this Coolify from the repo" cannot be attempted without knowing what was on it. An optional block cannot answer that.

So the remaining ask is narrow: make absence mean "does not exist", not "not declared". Either every project deploying into an environment gets an entry (even an empty one), or fleet ops read a separate authoritative list. Both are cheap; the point is that something must be able to say "these, and no others."

**#28 delivered the shape. What remains is the *authority*.** `environments.<env>.projects.<org>/<repo>` now exists, keyed by full slug with a bare-repo fallback — exactly the `github_apps` precedent this issue asked for, and `smoke_target` has already moved into it. The structural half of this issue is **done**. But the block is **optional** ("an environment whose server hosts one project declares neither"), and that is correct for its purpose — a destination you don't need shouldn't have to be declared. It does mean the block is **not yet a registry**: a project that exists but declares nothing is invisible to it. That gap is what the two dependents actually need: - **#26 (`--all`)** — iterating `projects:` today iterates *the projects that happened to need per-project state*, not *the projects that exist*. A fleet diff that silently skips an undeclared project reads exactly like a clean one. - **The DR rebuild** — "restore this Coolify from the repo" cannot be attempted without knowing what was on it. An optional block cannot answer that. So the remaining ask is narrow: **make absence mean "does not exist", not "not declared".** Either every project deploying into an environment gets an entry (even an empty one), or fleet ops read a separate authoritative list. Both are cheap; the point is that *something* must be able to say "these, and no others."
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/cast#25
No description provided.