feat(db): add goose 00037 task_run_manifests
Persist typed RunManifest bytes and SHA per run, and allow a finalizing status so U8 can freeze a unique terminal without sharing env databases. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
-- +goose Up
|
||||
-- U8: typed RunManifest persistence and optional finalizing status.
|
||||
|
||||
ALTER TABLE task_runs DROP CONSTRAINT IF EXISTS chk_task_run_status;
|
||||
|
||||
ALTER TABLE task_runs ADD CONSTRAINT chk_task_run_status CHECK (
|
||||
status IN (
|
||||
'pending',
|
||||
'running',
|
||||
'finalizing',
|
||||
'completed',
|
||||
'failed',
|
||||
'cancelled',
|
||||
'finalizing_failed',
|
||||
'quality_failed',
|
||||
'interrupted',
|
||||
'pending_author_confirmation',
|
||||
'blocked_pending_governance'
|
||||
)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS task_run_manifests (
|
||||
run_id TEXT PRIMARY KEY REFERENCES task_runs(run_id) ON DELETE CASCADE,
|
||||
canonical_bytes BYTEA NOT NULL,
|
||||
sha256 CHAR(64) NOT NULL,
|
||||
workspace_id TEXT NOT NULL DEFAULT '',
|
||||
storage_identity TEXT NOT NULL DEFAULT '',
|
||||
members JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT uq_task_run_manifests_run UNIQUE (run_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_task_run_manifests_sha ON task_run_manifests (sha256);
|
||||
|
||||
-- +goose Down
|
||||
DROP INDEX IF EXISTS idx_task_run_manifests_sha;
|
||||
DROP TABLE IF EXISTS task_run_manifests;
|
||||
|
||||
ALTER TABLE task_runs DROP CONSTRAINT IF EXISTS chk_task_run_status;
|
||||
|
||||
ALTER TABLE task_runs ADD CONSTRAINT chk_task_run_status CHECK (
|
||||
status IN (
|
||||
'pending',
|
||||
'running',
|
||||
'completed',
|
||||
'failed',
|
||||
'cancelled',
|
||||
'finalizing_failed',
|
||||
'quality_failed',
|
||||
'interrupted',
|
||||
'pending_author_confirmation',
|
||||
'blocked_pending_governance'
|
||||
)
|
||||
);
|
||||
@@ -100,6 +100,7 @@ func TestGooseM2_00034to00036_DownThenUp(t *testing.T) {
|
||||
"00034_2026-09-14-workspace-lease-fencing.sql",
|
||||
"00035_2026-09-14-durable-event-delivery.sql",
|
||||
"00036_2026-09-14-final-artifact-vault.sql",
|
||||
"00037_2026-09-15-task-run-manifests.sql",
|
||||
}
|
||||
joined := strings.Join(files, "\n")
|
||||
for _, name := range want {
|
||||
@@ -135,7 +136,7 @@ func TestGooseM2_00034to00036_DownThenUp(t *testing.T) {
|
||||
t.Fatalf("up: %v", err)
|
||||
}
|
||||
ver, err := CurrentVersion(ctx, pool)
|
||||
if err != nil || ver < 36 {
|
||||
if err != nil || ver < 37 {
|
||||
t.Fatalf("version after up: %d %v", ver, err)
|
||||
}
|
||||
|
||||
@@ -148,7 +149,7 @@ func TestGooseM2_00034to00036_DownThenUp(t *testing.T) {
|
||||
if err := goose.DownToContext(ctx, sqlDB, migDir, 33); err != nil {
|
||||
t.Fatalf("down to 33: %v", err)
|
||||
}
|
||||
for _, table := range []string{"event_envelopes", "event_deliveries", "event_dead_letters", "final_artifacts"} {
|
||||
for _, table := range []string{"event_envelopes", "event_deliveries", "event_dead_letters", "final_artifacts", "task_run_manifests"} {
|
||||
var n int
|
||||
if err := pool.QueryRow(ctx, `SELECT COUNT(*) FROM information_schema.tables WHERE table_name=$1`, table).Scan(&n); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -49,3 +49,13 @@ func SkipIfNoTestDatabaseURL(t *testing.T) string {
|
||||
}
|
||||
return dsn
|
||||
}
|
||||
|
||||
// RefuseSharedEnvDSN fails if the DSN targets a shared env database.
|
||||
func RefuseSharedEnvDSN(t *testing.T, dsn string) {
|
||||
t.Helper()
|
||||
if strings.Contains(dsn, "5433") || strings.Contains(dsn, "agent_system_dev") ||
|
||||
(strings.Contains(dsn, "5434") && strings.Contains(dsn, "agent_system_test")) ||
|
||||
strings.Contains(dsn, "5435") {
|
||||
t.Fatal("TEST_DATABASE_URL points at a shared env port; refuse agent_system_dev/test/main")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user