RFC#2843 #32: fix prevStatus enum-COALESCE that silenced the reconcile trigger (follow-up to #3002) #3004

Merged
core-devops merged 2 commits from fix/rfc2843-32-prevstatus-enum-coalesce into main 2026-06-16 23:42:35 +00:00
2 changed files with 17 additions and 2 deletions
@@ -898,10 +898,18 @@ func (h *RegistryHandler) Heartbeat(c *gin.Context) {
// flip, independent of evaluateStatus. evaluateStatus still owns the OTHER
// recovery transitions (offline/degraded/awaiting_agent/failed→online),
// which the inline CASE does not touch.
//
// IMPORTANT (enum scan): `status` is a NOT-NULL `workspace_status` ENUM.
// Do NOT wrap it in COALESCE(status, '') — the '' literal is coerced to
// the enum type and Postgres rejects it with `invalid input value for
// enum workspace_status: ""`, failing the WHOLE row scan. That left
// prevStatus = "" on every heartbeat, so the prevStatus=='provisioning'
// reconcile trigger NEVER fired (the #32 regression returned). Select the
// column bare; it is never NULL.
var prevTask string
var prevSpend int64
var prevStatus string
if err := db.DB.QueryRowContext(ctx, `SELECT COALESCE(current_task, ''), COALESCE(monthly_spend, 0), COALESCE(status, '') FROM workspaces WHERE id = $1`, payload.WorkspaceID).Scan(&prevTask, &prevSpend, &prevStatus); err != nil {
if err := db.DB.QueryRowContext(ctx, `SELECT COALESCE(current_task, ''), COALESCE(monthly_spend, 0), status FROM workspaces WHERE id = $1`, payload.WorkspaceID).Scan(&prevTask, &prevSpend, &prevStatus); err != nil {
log.Printf("registry heartbeat: prev_task query failed for workspace %s: %v", payload.WorkspaceID, err)
}
@@ -271,7 +271,14 @@ func TestHeartbeatHandler_ProvisioningToOnline(t *testing.T) {
// prevTask + prevStatus SELECT — prevStatus='provisioning' is the state a
// freshly-created workspace is in before its first heartbeat.
mock.ExpectQuery("SELECT COALESCE\\(current_task").
//
// The matcher pins `status` selected BARE (not COALESCE-wrapped): `status`
// is a NOT-NULL workspace_status ENUM, and COALESCE(status, '') coerces ''
// to the enum → Postgres `invalid input value for enum workspace_status: ""`
// → the whole row scan fails → prevStatus stays "" → this reconcile trigger
// NEVER fires (the live #32 regression). Requiring `, status FROM workspaces`
// here makes a re-introduced COALESCE(status, ...) fail this unit test.
mock.ExpectQuery("SELECT COALESCE\\(current_task, ''\\), COALESCE\\(monthly_spend, 0\\), status FROM workspaces").
WithArgs("ws-provisioning").
WillReturnRows(sqlmock.NewRows([]string{"current_task", "monthly_spend", "status"}).AddRow("", 0, "provisioning"))