Skip to content

feat(db): add closed_state_consistency CHECK to execution_workspaces (SHA-2492)#13

Open
Mlaz-code wants to merge 1 commit into
sharpapi-on-v2026.428.0from
sha-2492-closed-state-check-constraint
Open

feat(db): add closed_state_consistency CHECK to execution_workspaces (SHA-2492)#13
Mlaz-code wants to merge 1 commit into
sharpapi-on-v2026.428.0from
sha-2492-closed-state-check-constraint

Conversation

@Mlaz-code
Copy link
Copy Markdown
Owner

Summary

Adds a CHECK constraint to execution_workspaces making the contradictory state representationally impossible: closed_at IS NULL OR status IN ('archived', 'cleanup_failed'). Companion to PR #12 (PATCH route normalize-on-reopen).

Without this DB-level invariant, every cleanup author has to remember to pair closed_at = now() with a closed status. Historically that's failed in multiple places (stale_7d_SHA-{1835,2221}, source_issue_terminal recipe, and a Coordinator manual PATCH) — see SHA-2492 for the full forensic.

Why NOT VALID

There are 1037 existing violator rows on the prod paperclip DB. ADD CONSTRAINT ... CHECK ... NOT VALID skips the table-scan-and-validate step at migration time, so the deploy is fast and lock-free. The constraint takes effect for all INSERTs and UPDATEs immediately. Validating against the existing data happens as a separate manual step:

ALTER TABLE execution_workspaces
  VALIDATE CONSTRAINT execution_workspaces_closed_state_consistency;

…which must run after the violator rows are backfilled. Pre-audit on the prod DB confirmed 0 violator rows have a non-terminal source issue, so the backfill is safe:

UPDATE execution_workspaces
SET status = 'archived', updated_at = now()
WHERE closed_at IS NOT NULL
  AND status NOT IN ('archived', 'cleanup_failed');
-- expected: UPDATE 1037

Deploy / rollout order

  1. Merge PR fix(execution-workspaces): clear closed_at on reopen via PATCH (SHA-2492) #12 (PATCH route normalize-on-reopen) first. Without it, the new CHECK will reject any reopen that didn't clear closed_at.
  2. Merge this PR. Migration 0077_workspace_closed_state_consistency.sql ships the NOT VALID constraint.
  3. Manual backfill on prod paperclip DB (Board approval needed): the UPDATE above (1037 rows). Pre-audit attached in SHA-2492 confirms safety.
  4. Manual VALIDATE CONSTRAINT on prod paperclip DB (Board approval needed): proves no violators remain forever.

Changes

  • packages/db/src/schema/execution_workspaces.ts — add check() constraint to the drizzle schema for consistency with the SQL migration.
  • packages/db/src/migrations/0077_workspace_closed_state_consistency.sql — hand-rolled migration with NOT VALID (drizzle-kit would generate a validating ADD which would reject on the existing 1037 violators).
  • packages/db/src/migrations/meta/_journal.json — register migration 77.

Companion work

Test plan

  • Schema TS + SQL match (both write the same constraint name and predicate).
  • Journal JSON validates.
  • CI runs migration apply against a test DB.
  • After merge + manual backfill + manual VALIDATE: SELECT count(*) FROM execution_workspaces WHERE closed_at IS NOT NULL AND status NOT IN ('archived','cleanup_failed') → 0 forever.

…(SHA-2492)

execution_workspaces rows must satisfy closed_at IS NULL OR
status IN ('archived','cleanup_failed'). The contradictory state
(closed_at stamped, status='active'/'idle') fails-closed in
isClosedIsolatedExecutionWorkspace and 409s issue comments/PATCHes.

Added with NOT VALID so the deploy is fast and does not lock the table
while validating every existing row. The constraint takes effect for
all INSERTs and UPDATEs immediately.

Existing violators (1037 rows on prod paperclip DB at filing time) need
to be backfilled to status='archived' before
ALTER TABLE execution_workspaces VALIDATE CONSTRAINT
execution_workspaces_closed_state_consistency runs as a follow-up.
A pre-validate audit confirmed 0 violator rows have a non-terminal
source issue, so the bulk archive is safe.

Companion: PR sha-2492-patch-normalize-closed-at fixes the PATCH route
so reopens clear closed_at (otherwise the new CHECK would reject them).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant