fix(diff): Coolify's own generated vars are not orphans (#87) #88
No reviewers
Labels
No labels
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-triage
ready
release
scope:apply
scope:capture
scope:coolify-api
scope:fleet
scope:manifest
scope:secrets
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/cast#88
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/generated-vars-not-orphans"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #87. With #86 merged, this is the last thing standing between a correct box and a clean diff.
The problem
A prod box with zero drift reported
2 change(s)— sixteen lines oflive-only (orphan var — apply never removes), every one a var Coolify minted:SERVICE_FQDN_APIfor a compose app's per-container domains,SERVICE_PASSWORD_POSTGRES/POSTGRES_*for the one-click umami service's bundled datastore.remove-candidatemeans "a live-only var the manifest does not declare; apply never removes it; read it by eye." For a name cast did not put there, cannot declare in any vocabulary, and will never remove, that's a category error. And a report that can never say clean is how an operator learns to skim it — #78's own Impact section made the argument: "an operator who learns these always show change stops trusting the diff." #78 then sat misdiagnosed for exactly that long.The fix
cast already held this judgment —
draft.tshas used it since #27 to refuse copying these into a draft.diffEnvjust never asked. So the vocabulary moves toreserved.ts, which already owns "names whose meaning belongs to the PLATFORM", and both callers consult it.The two families stay distinct, because the danger runs opposite ways:
SOURCE_COMMIT,COOLIFY_*)SERVICE_FQDN_*, …)Two widths, deliberately
Over-matching is safe in a draft and unsafe in a diff. A future reader who "unifies" these will silently blind the diff, so
reserved.tssays so at the definition:DATABASE_URLinto a new box that boots against the old box's database.DATABASE_URLstill pointing at a box nobody declares is the single orphan most worth printing — and it matches the wide rule. Not theoretical: probed against prod, the wide bucket on a real application held exactlyDATABASE_URLandREDIS_URL— both of them cast's own declared vars.type+service_domains+ an env_template is the entire vocabulary; the rest is the template's.A real gap the tests found
The pair-rule missed
POSTGRES_DBoutright —[POSTGRES, DB]is datastore + datastore, with no connection word. A db name is a connection coordinate like any other (host, port, user, password, dbname), soDBjoins that set. It's precisely the var a one-click service mints for its bundled Postgres, and the wide rule silently didn't cover it.Also: correcting #79's record
LiveEnvVar's comment still cited "a stored column Coolify does not recompute on an in-place PATCH — so it goes stale". False —real_valueis an appended accessor recomputed fromvalueon every read, and it tracksvalueon every row of a real box. The split #79 made is still right (real_valueis an escaped rendering —'true'is nottrue), but its motivation was wrong; the drift it chased was #85's preview shadow.Tests
SERVICE_*reads clean (the exact prod shape).DATABASE_URLon an application is still reported — the narrow rule's whole purpose.LEGACY_FLAG) is still reported — width must not become "services report nothing".npm run checkclean · 528 tests pass (npm run build && npm test, as CI runs).🤖 Generated with Claude Code