Problem
Services dashboard renders cantaconmigo as 3 separate cards (frontend, backend, postgres), all flagged abandoned. They are one Coolify application.
Root cause
api/src/routes/services.ts:130,139 keys groups by coolify.name — a per-container slug (e.g. frontend-v13w39q44mtejeqki7659rca-190520357886). Coolify listApplications() returns the resource UUID (v13w39q44mtejeqki7659rca). Different namespaces → never matches → all 3 marked abandoned, fragmented into separate cards.
Same UUID is on every container as com.docker.compose.project.
Options memos (already written)
clients/self/projects/hermithost/specs/grouping-architecture-options-2026-04-29.md
clients/self/projects/hermithost/specs/grouping-implementation-options-2026-04-29.md
Proposed phased work
Phase 1 — hotfix (~8 LOC, services.ts only)
Swap group key from coolify.name → com.docker.compose.project. UUID matches listApplications() directly. Abandoned check works as-is.
Phase 2 — tri-state liveness model
Replace abandoned: boolean with state: 'live' | 'orphan' | 'unmanaged' | 'degraded':
- owner-system unreachable →
live (never orphan)
- no owner labels →
unmanaged (terminal)
- mixed group →
degraded (covers blue/green leftover)
Phase 3 — hierarchy + ownerKind
Add ownerKind: 'coolify' | 'compose' | 'manual', environment, containerCount. 2-level UI: Coolify project → site → containers.
Phase 4 (deferred) — OwnerResolver abstraction
Pluggable resolver per backend (Coolify, compose, swarm, k8s). Defer until second backend (k8s) lands.
Acceptance
- Cantaconmigo renders as 1 card with 3 containers
- No false-positive abandoned flag while Coolify reachable
- Coolify unreachable → groups stay
live, never orphan
- New unit tests in
api/test/groupBySite.test.ts cover: 3 containers → 1 group, abandoned semantics, missing labels fallback, PR preview isolation, infra exclusion
Problem
Services dashboard renders cantaconmigo as 3 separate cards (frontend, backend, postgres), all flagged
abandoned. They are one Coolify application.Root cause
api/src/routes/services.ts:130,139keys groups bycoolify.name— a per-container slug (e.g.frontend-v13w39q44mtejeqki7659rca-190520357886). CoolifylistApplications()returns the resource UUID (v13w39q44mtejeqki7659rca). Different namespaces → never matches → all 3 marked abandoned, fragmented into separate cards.Same UUID is on every container as
com.docker.compose.project.Options memos (already written)
clients/self/projects/hermithost/specs/grouping-architecture-options-2026-04-29.mdclients/self/projects/hermithost/specs/grouping-implementation-options-2026-04-29.mdProposed phased work
Phase 1 — hotfix (~8 LOC, services.ts only)
Swap group key from
coolify.name→com.docker.compose.project. UUID matcheslistApplications()directly. Abandoned check works as-is.Phase 2 — tri-state liveness model
Replace
abandoned: booleanwithstate: 'live' | 'orphan' | 'unmanaged' | 'degraded':live(neverorphan)unmanaged(terminal)degraded(covers blue/green leftover)Phase 3 — hierarchy + ownerKind
Add
ownerKind: 'coolify' | 'compose' | 'manual',environment,containerCount. 2-level UI: Coolify project → site → containers.Phase 4 (deferred) — OwnerResolver abstraction
Pluggable resolver per backend (Coolify, compose, swarm, k8s). Defer until second backend (k8s) lands.
Acceptance
live, neverorphanapi/test/groupBySite.test.tscover: 3 containers → 1 group, abandoned semantics, missing labels fallback, PR preview isolation, infra exclusion