Files
triggerdotdev--trigger.dev/internal-packages/run-store/src/PostgresRunStore.dedicatedRepro.test.ts
T
2026-07-13 13:32:57 +08:00

1556 lines
66 KiB
TypeScript

// Store-level regression suite for the run-ops split.
//
// These tests EMPIRICALLY PROVE the critical/high store-level correctness issues against the REAL
// dedicated subset schema (`heteroRunOpsPostgresTest.prisma17` is a real `RunOpsPrismaClient` over
// the @internal/run-ops-database SUBSET schema) and the full legacy schema on a SEPARATE physical
// PG container (`prisma14`). An earlier harness masked every one of these by backing the "#new"
// store with the FULL legacy schema and globally minting run-ops id, so the split never ran against the
// dedicated schema.
//
// Each case either asserts the fixed behavior directly or, for a still-open item, wraps the broken
// behavior so the suite documents it. They are runnable (not skipped) so the behavior is
// demonstrated end-to-end against two physical DBs.
import { heteroRunOpsPostgresTest } from "@internal/testcontainers";
import type { PrismaClient } from "@trigger.dev/database";
import type { RunOpsPrismaClient } from "@internal/run-ops-database";
import { describe, expect } from "vitest";
import { PostgresRunStore } from "./PostgresRunStore.js";
import { RoutingRunStore } from "./runOpsStore.js";
import type { CreateRunInput, RunStoreSchemaVariant } from "./types.js";
type AnyClient = PrismaClient | RunOpsPrismaClient;
// ownerEngine classifies by internal-id LENGTH (runOpsResidency.ts): 25 chars → cuid → LEGACY,
// a v1 body (version "1" at index 25) → run-ops id → NEW. A `run_`-prefixed friendly id strips the first underscore first.
const CUID_25 = "c".repeat(25); // → LEGACY (#legacy / control-plane DB, full schema)
const NEW_ID_26 = "k".repeat(24) + "01"; // → NEW (#new / dedicated run-ops DB, subset schema)
// On the dedicated subset there are no Organization/Project/RuntimeEnvironment models (the run-ops
// rows carry FK-free scalar ids), so we mint synthetic owning ids. On legacy we seed the real rows
// the kept FKs require.
async function seedEnvironment(
prisma: AnyClient,
schemaVariant: RunStoreSchemaVariant,
suffix: string
) {
if (schemaVariant === "dedicated") {
return {
organization: { id: `org_${suffix}` },
project: { id: `proj_${suffix}` },
environment: { id: `env_${suffix}` },
};
}
const organization = await (prisma as PrismaClient).organization.create({
data: { title: `Org ${suffix}`, slug: `org-${suffix}` },
});
const project = await (prisma as PrismaClient).project.create({
data: {
name: `Project ${suffix}`,
slug: `project-${suffix}`,
externalRef: `proj_${suffix}`,
organizationId: organization.id,
},
});
const environment = await (prisma as PrismaClient).runtimeEnvironment.create({
data: {
type: "DEVELOPMENT",
slug: "dev",
projectId: project.id,
organizationId: organization.id,
apiKey: `tr_dev_${suffix}`,
pkApiKey: `pk_dev_${suffix}`,
shortcode: `short_${suffix}`,
},
});
return { organization, project, environment };
}
function buildCreateRunInput(params: {
runId: string;
friendlyId: string;
organizationId: string;
projectId: string;
runtimeEnvironmentId: string;
}): CreateRunInput {
return {
data: {
id: params.runId,
engine: "V2",
status: "PENDING",
friendlyId: params.friendlyId,
runtimeEnvironmentId: params.runtimeEnvironmentId,
environmentType: "DEVELOPMENT",
organizationId: params.organizationId,
projectId: params.projectId,
taskIdentifier: "my-task",
payload: '{"hello":"world"}',
payloadType: "application/json",
context: { foo: "bar" },
traceContext: { trace: "ctx" },
traceId: `trace_${params.runId}`,
spanId: `span_${params.runId}`,
runTags: [],
queue: "task/my-task",
isTest: false,
taskEventStore: "taskEvent",
depth: 0,
createdAt: new Date("2024-01-01T00:00:00.000Z"),
},
snapshot: {
engine: "V2",
executionStatus: "RUN_CREATED",
description: "Run was created",
runStatus: "PENDING",
environmentId: params.runtimeEnvironmentId,
environmentType: "DEVELOPMENT",
projectId: params.projectId,
organizationId: params.organizationId,
},
};
}
function makeDedicatedStore(prisma17: RunOpsPrismaClient) {
return new PostgresRunStore({
prisma: prisma17 as never,
readOnlyPrisma: prisma17 as never,
schemaVariant: "dedicated",
});
}
function makeLegacyStore(prisma14: PrismaClient) {
return new PostgresRunStore({
prisma: prisma14,
readOnlyPrisma: prisma14,
schemaVariant: "legacy",
});
}
// The REAL production split topology: #new = dedicated subset on prisma17, #legacy = full schema on
// prisma14. Two physically-distinct DBs, dedicated schema on #new — exactly what a single-schema
// harness never wires.
function makeSplitRouter(prisma14: PrismaClient, prisma17: RunOpsPrismaClient) {
const legacyStore = makeLegacyStore(prisma14);
const newStore = makeDedicatedStore(prisma17);
return {
router: new RoutingRunStore({ new: newStore, legacy: legacyStore }),
legacyStore,
newStore,
};
}
describe("run-ops split — store-level behavior against the REAL dedicated schema", () => {
// ===========================================================================================
// continueRunIfUnblocked dedicated-schema relation-select validation throw.
// `continueRunIfUnblocked` reads edges with `select:{ waitpoint:{...} }`
// (waitpointSystem.ts); the dedicated `TaskRunWaitpoint` model has NO `waitpoint`
// relation (only a `waitpointId` scalar), and `PostgresRunStore.findManyTaskRunWaitpoints`
// must strip/hydrate rather than pass the args straight through. Against the real run-ops subset
// client an un-stripped select is a Prisma validation error → every waitpoint-blocked run hangs.
// ===========================================================================================
// The EXACT caller select from continueRunIfUnblocked step 1 no longer throws on the dedicated
// client. With no edges seeded it returns []; the dedicated strip/hydrate branch
// (PostgresRunStore.findManyTaskRunWaitpoints) handles the missing `waitpoint` relation.
heteroRunOpsPostgresTest(
"findManyTaskRunWaitpoints with the continueRunIfUnblocked select does NOT throw on the DEDICATED client",
async ({ prisma17 }) => {
const store = makeDedicatedStore(prisma17);
const rows = await store.findManyTaskRunWaitpoints({
where: { taskRunId: `run_${NEW_ID_26}` },
select: {
id: true,
batchId: true,
batchIndex: true,
waitpoint: {
select: { id: true, status: true, type: true, completedAfter: true },
},
},
});
expect(rows).toEqual([]);
}
);
// A co-resident block edge on the dedicated client hydrates its `waitpoint`
// relation from the scalar `waitpointId`, returning the requested fields (no Prisma throw).
heteroRunOpsPostgresTest(
"the dedicated waitpoint relation-select hydrates a co-resident waitpoint",
async ({ prisma17 }) => {
const store = makeDedicatedStore(prisma17);
const env = await seedEnvironment(prisma17, "dedicated", "gap4hyd_new");
const runId = `run_${NEW_ID_26}`;
const waitpointId = `waitpoint_${NEW_ID_26}`;
await prisma17.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_gap4hyd",
type: "MANUAL",
status: "PENDING",
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: env.project.id,
environmentId: env.environment.id,
},
});
await prisma17.taskRunWaitpoint.create({
data: { taskRunId: runId, waitpointId, projectId: env.project.id },
});
const rows = await store.findManyTaskRunWaitpoints({
where: { taskRunId: runId },
select: {
id: true,
waitpoint: { select: { id: true, status: true } },
},
});
expect(rows).toHaveLength(1);
expect(rows[0].waitpoint).toEqual({ id: waitpointId, status: "PENDING" });
}
);
// Control: the LEGACY full schema HAS the `waitpoint` relation, so the same select must NOT throw.
// This proves the throw is specific to the dedicated subset schema, not the query shape.
heteroRunOpsPostgresTest(
"control: the SAME select does NOT throw on the LEGACY full schema",
async ({ prisma14 }) => {
const store = makeLegacyStore(prisma14);
const rows = await store.findManyTaskRunWaitpoints({
where: { taskRunId: `run_${NEW_ID_26}` },
select: {
id: true,
waitpoint: { select: { id: true, status: true } },
},
});
expect(rows).toEqual([]);
}
);
// The full router path (continueRunIfUnblocked fans to BOTH stores via
// RoutingRunStore.findManyTaskRunWaitpoints) no longer throws — the #new (dedicated) leg strips
// the relation and the router re-resolves `waitpoint` cross-DB. Empty result with no edges seeded.
heteroRunOpsPostgresTest(
"RoutingRunStore.findManyTaskRunWaitpoints does NOT throw even though the #new leg is dedicated",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const rows = await router.findManyTaskRunWaitpoints({
where: { taskRunId: `run_${NEW_ID_26}` },
select: {
id: true,
waitpoint: { select: { id: true, status: true, type: true, completedAfter: true } },
},
});
expect(rows).toEqual([]);
}
);
// ===========================================================================================
// Cross-DB waitpoint hydration through the router.
// A run-ops run (on #new) blocked by a waitpoint that lives on the OTHER DB (#legacy). The block
// edge co-resides with the run on #new; the token is on #legacy. A single store hydrates the
// edge's `waitpoint` from its own client → null → the run hangs / loses output. The
// router must re-resolve the token across BOTH DBs.
// ===========================================================================================
// Co-resident control (the run-ops id happy path): a run-ops run blocked by a run-ops waitpoint,
// both on #new, hydrates through the router with the real status/output.
heteroRunOpsPostgresTest(
"cross-DB: a run-ops run blocked by a CO-RESIDENT run-ops waitpoint hydrates the real status via the router",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const env = await seedEnvironment(prisma17, "dedicated", "cores_new");
const runId = `run_${NEW_ID_26}`;
const waitpointId = `waitpoint_${NEW_ID_26}`;
await prisma17.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_cores",
type: "MANUAL",
status: "COMPLETED",
output: '{"resumed":"co-resident"}',
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: env.project.id,
environmentId: env.environment.id,
},
});
await prisma17.taskRunWaitpoint.create({
data: { taskRunId: runId, waitpointId, projectId: env.project.id },
});
const rows = await router.findManyTaskRunWaitpoints({
where: { taskRunId: runId },
select: {
id: true,
waitpoint: { select: { id: true, status: true, output: true } },
},
});
expect(rows).toHaveLength(1);
expect(rows[0].waitpoint).toEqual({
id: waitpointId,
status: "COMPLETED",
output: '{"resumed":"co-resident"}',
});
}
);
// The cross-DB topology. The block edge is on #new (co-resident with the
// run-ops run), the completing token is on #legacy. The router resolves the token across both DBs
// and returns its REAL status and OUTPUT (the wrong-result guard) — a single store would
// hydrate null here and strand the run.
heteroRunOpsPostgresTest(
"cross-DB: a run-ops run completed by a waitpoint on the OTHER DB hydrates the real status + output via the router",
async ({ prisma14, prisma17 }) => {
const { router, newStore } = makeSplitRouter(prisma14, prisma17);
const newEnv = await seedEnvironment(prisma17, "dedicated", "xdb_new");
const legEnv = await seedEnvironment(prisma14, "legacy", "xdb_leg");
const runId = `run_${NEW_ID_26}`;
const waitpointId = `waitpoint_${CUID_25}`; // cuid → lives on #legacy
// The completing token lives on #legacy (cuid MANUAL token blocking a run-ops run).
await prisma14.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_xdb",
type: "MANUAL",
status: "COMPLETED",
output: '{"resumed":"cross-db"}',
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: legEnv.project.id,
environmentId: legEnv.environment.id,
},
});
// The block edge co-resides with the run-ops id RUN on #new.
await prisma17.taskRunWaitpoint.create({
data: { taskRunId: runId, waitpointId, projectId: newEnv.project.id },
});
// Single-store guard: the #new store alone hydrates the edge's waitpoint to null (the token is
// on #legacy) — proving the bug the router fixes.
const singleStoreRows = await newStore.findManyTaskRunWaitpoints({
where: { taskRunId: runId },
select: { id: true, waitpoint: { select: { id: true, status: true, output: true } } },
});
expect(singleStoreRows[0].waitpoint).toBeNull();
// Router path: resolves the cross-DB token and returns the real status + output.
const rows = await router.findManyTaskRunWaitpoints({
where: { taskRunId: runId },
select: {
id: true,
waitpoint: { select: { id: true, status: true, output: true } },
},
});
expect(rows).toHaveLength(1);
expect(rows[0].waitpoint).toEqual({
id: waitpointId,
status: "COMPLETED",
output: '{"resumed":"cross-db"}',
});
}
);
// Hard-error contract: a blocking edge whose waitpoint exists on NEITHER DB must throw, never
// resolve to null (which would let continueRunIfUnblocked treat it as not-COMPLETED forever, or
// silently complete). The router raises rather than strand the run on a phantom blocker.
heteroRunOpsPostgresTest(
"cross-DB: a block edge whose waitpoint is on NEITHER DB throws (no silent null)",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const newEnv = await seedEnvironment(prisma17, "dedicated", "phantom_new");
const runId = `run_${NEW_ID_26}`;
const waitpointId = `waitpoint_${"p".repeat(24) + "01"}`; // run-ops-shaped, but never created anywhere
await prisma17.taskRunWaitpoint.create({
data: { taskRunId: runId, waitpointId, projectId: newEnv.project.id },
});
await expect(
router.findManyTaskRunWaitpoints({
where: { taskRunId: runId },
select: { id: true, waitpoint: { select: { id: true, status: true } } },
})
).rejects.toThrow(/not found on either run-ops DB/i);
}
);
// ===========================================================================================
// checkpoint→snapshot residency FK break.
// If `createTaskRunCheckpoint` were hardcoded to `#new` while the referencing execution snapshot
// routes by run id, a cuid run's snapshot would land on `#legacy` carrying a `checkpointId` that
// only exists on `#new` → TaskRunExecutionSnapshot_checkpointId_fkey violated; the run cannot
// suspend/checkpoint. Live V2 path (checkpointSystem.ts).
// ===========================================================================================
// createTaskRunCheckpoint routes by the OWNING run id, so a cuid run's
// checkpoint co-resides on #legacy with its snapshot. The referencing snapshot insert (routed to
// #legacy by the cuid run id) satisfies the checkpointId FK on the same DB — no throw.
heteroRunOpsPostgresTest(
"a cuid-run snapshot referencing its checkpoint satisfies the checkpointId FK on #legacy",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
// A cuid (LEGACY-resident) run — the in-flight cohort that keeps executing after split-on.
const env = await seedEnvironment(prisma14, "legacy", "gap2_leg");
const runId = `run_${CUID_25}`;
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_gap2_legacy",
organizationId: env.organization.id,
projectId: env.project.id,
runtimeEnvironmentId: env.environment.id,
})
);
// checkpointSystem path: create the checkpoint routed by the OWNING (cuid) run id → #legacy.
const checkpoint = await router.createTaskRunCheckpoint(
{
data: {
friendlyId: `checkpoint_${CUID_25}`,
type: "DOCKER",
location: "s3://bucket/cuid-run-checkpoint",
projectId: env.project.id,
runtimeEnvironmentId: env.environment.id,
},
},
runId
);
// The referencing snapshot routes by the cuid run id → #legacy. Its checkpointId now resolves
// on the same DB (the checkpoint co-resides), so the insert succeeds.
const snapshot = await router.createExecutionSnapshot({
run: { id: runId, status: "EXECUTING", attemptNumber: 1 },
snapshot: {
executionStatus: "SUSPENDED",
description: "Run suspended after checkpoint",
},
checkpointId: checkpoint.id,
environmentId: env.environment.id,
environmentType: "DEVELOPMENT",
projectId: env.project.id,
organizationId: env.organization.id,
});
expect(snapshot.checkpointId).toBe(checkpoint.id);
}
);
// Residency proof: the checkpoint, routed by its cuid owning run id,
// co-resides on #legacy (prisma14) and is ABSENT from #new (prisma17).
heteroRunOpsPostgresTest(
"createTaskRunCheckpoint co-locates the checkpoint with its owning cuid run on #legacy",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const env = await seedEnvironment(prisma14, "legacy", "gap2res_leg");
const runId = `run_${CUID_25}`;
const checkpoint = await router.createTaskRunCheckpoint(
{
data: {
friendlyId: `checkpoint_res_${CUID_25}`,
type: "DOCKER",
location: "s3://bucket/cp",
projectId: env.project.id,
runtimeEnvironmentId: env.environment.id,
},
},
runId
);
// Present on #legacy (full schema / prisma14) — where the cuid run's snapshot lives.
const onLegacy = await prisma14.taskRunCheckpoint.findUnique({
where: { id: checkpoint.id },
});
expect(onLegacy).not.toBeNull();
// Absent from #new (dedicated / prisma17).
const onNew = await prisma17.taskRunCheckpoint.findUnique({ where: { id: checkpoint.id } });
expect(onNew).toBeNull();
}
);
// Control: a run-ops run's checkpoint, routed by its owning run id, co-resides on #new.
heteroRunOpsPostgresTest(
"control: a run-ops run's checkpoint co-resides on #new with its snapshot",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const env = await seedEnvironment(prisma17, "dedicated", "gap2k_new");
const runId = `run_${NEW_ID_26}`;
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_gap2k_new",
organizationId: env.organization.id,
projectId: env.project.id,
runtimeEnvironmentId: env.environment.id,
})
);
const checkpoint = await router.createTaskRunCheckpoint(
{
data: {
friendlyId: `checkpoint_${NEW_ID_26}`,
type: "DOCKER",
location: "s3://bucket/run-ops id-run-checkpoint",
projectId: env.project.id,
runtimeEnvironmentId: env.environment.id,
},
},
runId
);
const onNew = await prisma17.taskRunCheckpoint.findUnique({ where: { id: checkpoint.id } });
expect(onNew).not.toBeNull();
const snapshot = await router.createExecutionSnapshot({
run: { id: runId, status: "EXECUTING", attemptNumber: 1 },
snapshot: { executionStatus: "SUSPENDED", description: "run-ops suspended" },
checkpointId: checkpoint.id,
environmentId: env.environment.id,
environmentType: "DEVELOPMENT",
projectId: env.project.id,
organizationId: env.organization.id,
});
expect(snapshot.checkpointId).toBe(checkpoint.id);
}
);
// ===========================================================================================
// Snapshot reads must route by run id, not hardcode `#new`.
// If `findExecutionSnapshot` / `findManyExecutionSnapshots` were hardcoded to `#new`, then for a
// cuid run (snapshots on #legacy, because createExecutionSnapshot routes by run id) these reads
// would miss it → null / empty. The getExecutionSnapshotsSince warm-restart path would then throw
// ExecutionSnapshotNotFoundError.
// ===========================================================================================
// findExecutionSnapshot routes by the OWNING run id, so a cuid run's
// #legacy snapshot is found through the router (the warm-restart `getExecutionSnapshotsSince` step 1
// shape `{ id, runId }`).
heteroRunOpsPostgresTest(
"findExecutionSnapshot FINDS a cuid run's #legacy snapshot via the router",
async ({ prisma14, prisma17 }) => {
const { router, legacyStore } = makeSplitRouter(prisma14, prisma17);
const env = await seedEnvironment(prisma14, "legacy", "gap5_leg");
const runId = `run_${CUID_25}`;
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_gap5_legacy",
organizationId: env.organization.id,
projectId: env.project.id,
runtimeEnvironmentId: env.environment.id,
})
);
// Snapshot routes by the cuid run id → physically created on #legacy.
const created = await router.createExecutionSnapshot({
run: { id: runId, status: "EXECUTING", attemptNumber: 1 },
snapshot: { executionStatus: "EXECUTING", description: "cuid run executing" },
environmentId: env.environment.id,
environmentType: "DEVELOPMENT",
projectId: env.project.id,
organizationId: env.organization.id,
});
// Sanity: the snapshot really is on #legacy (a direct legacy-store read finds it).
const onLegacy = await legacyStore.findExecutionSnapshot({
where: { id: created.id, runId },
});
expect(onLegacy).not.toBeNull();
// The router routes by `where.runId` → #legacy → the cuid run's snapshot is found.
const viaRouter = await router.findExecutionSnapshot({ where: { id: created.id, runId } });
expect(viaRouter).not.toBeNull();
expect(viaRouter!.id).toBe(created.id);
}
);
// findManyExecutionSnapshots routes by `where.runId`, so
// the warm-restart step-2 shape sees a cuid run's #legacy snapshots instead of an empty #new read.
heteroRunOpsPostgresTest(
"findManyExecutionSnapshots SEES a cuid run's #legacy snapshots via the router",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const env = await seedEnvironment(prisma14, "legacy", "gap5b_leg");
const runId = `run_${CUID_25}`;
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_gap5b_legacy",
organizationId: env.organization.id,
projectId: env.project.id,
runtimeEnvironmentId: env.environment.id,
})
);
await router.createExecutionSnapshot({
run: { id: runId, status: "EXECUTING", attemptNumber: 1 },
snapshot: { executionStatus: "EXECUTING", description: "cuid run executing 2" },
environmentId: env.environment.id,
environmentType: "DEVELOPMENT",
projectId: env.project.id,
organizationId: env.organization.id,
});
// getExecutionSnapshotsSince step 2 shape: findMany by runId on the router → routes to #legacy.
const many = await router.findManyExecutionSnapshots({
where: { runId, isValid: true },
});
expect(many.length).toBeGreaterThanOrEqual(1);
}
);
// A by-snapshot-id-only read (no runId — snapshot ids are cuids, not classifiable) fans out
// NEW→LEGACY, so a cuid run's #legacy snapshot is still found.
heteroRunOpsPostgresTest(
"findExecutionSnapshot with no runId fans out NEW→LEGACY",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const env = await seedEnvironment(prisma14, "legacy", "gap5d_leg");
const runId = `run_${CUID_25}`;
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_gap5d_legacy",
organizationId: env.organization.id,
projectId: env.project.id,
runtimeEnvironmentId: env.environment.id,
})
);
const created = await router.createExecutionSnapshot({
run: { id: runId, status: "EXECUTING", attemptNumber: 1 },
snapshot: { executionStatus: "EXECUTING", description: "cuid run executing 3" },
environmentId: env.environment.id,
environmentType: "DEVELOPMENT",
projectId: env.project.id,
organizationId: env.organization.id,
});
// No runId in the where — the router must probe both DBs to find the #legacy snapshot.
const viaRouter = await router.findExecutionSnapshot({ where: { id: created.id } });
expect(viaRouter).not.toBeNull();
expect(viaRouter!.id).toBe(created.id);
}
);
// Control: a run-ops run (on #new / dedicated) IS visible through the router — proving the read gap
// is residency-specific (only the cuid/#legacy cohort would be dropped), not a blanket failure.
heteroRunOpsPostgresTest(
"control: a run-ops run's #new snapshot IS found through the router",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const env = await seedEnvironment(prisma17, "dedicated", "gap5c_new");
const runId = `run_${NEW_ID_26}`;
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_gap5c_new",
organizationId: env.organization.id,
projectId: env.project.id,
runtimeEnvironmentId: env.environment.id,
})
);
const created = await router.createExecutionSnapshot({
run: { id: runId, status: "EXECUTING", attemptNumber: 1 },
snapshot: { executionStatus: "EXECUTING", description: "run-ops run executing" },
environmentId: env.environment.id,
environmentType: "DEVELOPMENT",
projectId: env.project.id,
organizationId: env.organization.id,
});
const viaRouter = await router.findExecutionSnapshot({ where: { id: created.id, runId } });
expect(viaRouter).not.toBeNull();
expect(viaRouter!.id).toBe(created.id);
}
);
// ===========================================================================================
// blockWaitpoint raw-CTE wrong-DB no-block.
// `RunEngine.trigger` passes `tx: prisma` (control-plane) into
// `blockRunWithWaitpoint(Lockless)`, forcing the `if (tx)` raw
// `$queryRaw` CTE branch. The CTE inserts into
// `TaskRunWaitpoint`/`_WaitpointRunConnections` and joins `FROM "Waitpoint" w WHERE w.id IN (...)`
// on the `tx`'s DB. When the waitpoint lives on the OTHER physical DB, the join returns 0 rows →
// no edge written → isRunBlocked=false → the parent is silently never suspended.
//
// SCOPING: the behavior lives in WaitpointSystem.blockRunWithWaitpoint, which requires a full
// SystemResources context (RunQueue, EventBus, RunLocker/Redis, controlPlaneResolver, worker,
// pendingVersionRunIdLookup) plus `runLock.lock` and getLatestExecutionSnapshot.
// That is not constructible as a run-store unit test; a faithful end-to-end repro needs the full
// RunEngine.trigger wiring with two physical DBs. What IS tractable here is
// the CORE MECHANISM: the exact raw CTE, run against a `tx` whose DB does NOT hold the waitpoint,
// inserts ZERO block edges. We reproduce that precisely below; the engine-level "parent ends NOT
// suspended" assertion is left to a RunEngine integration test.
// ===========================================================================================
// Proof of the mechanism: the verbatim block-edge CTE
// run on `tx = prisma14` (the control-plane / #legacy DB) inserts NOTHING when the waitpoint was
// created on prisma17 (#new), because `FROM "Waitpoint" w WHERE w.id IN (...)` finds 0 rows on
// prisma14. Asserts the wrong-DB behavior (0 edges) directly.
heteroRunOpsPostgresTest(
"mechanism: the block-edge CTE writes ZERO edges when the waitpoint is on the other DB",
async ({ prisma14, prisma17 }) => {
// A run-ops parent run + its associated waitpoint live on #new (prisma17 / dedicated).
const newEnv = await seedEnvironment(prisma17, "dedicated", "gap3_new");
const parentRunId = `run_${NEW_ID_26}`;
const waitpointId = `waitpoint_${NEW_ID_26}`;
await prisma17.taskRun.create({
data: {
id: parentRunId,
engine: "V2",
status: "EXECUTING",
friendlyId: "run_gap3_parent",
runtimeEnvironmentId: newEnv.environment.id,
environmentType: "DEVELOPMENT",
organizationId: newEnv.organization.id,
projectId: newEnv.project.id,
taskIdentifier: "my-task",
payload: "{}",
payloadType: "application/json",
traceContext: {},
traceId: `trace_${parentRunId}`,
spanId: `span_${parentRunId}`,
queue: "task/my-task",
isTest: false,
taskEventStore: "taskEvent",
depth: 0,
},
});
await prisma17.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_gap3",
type: "RUN",
status: "PENDING",
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: newEnv.project.id,
environmentId: newEnv.environment.id,
},
});
// The forced-tx branch: RunEngine.trigger passes the control-plane client (= the #legacy DB
// here) as `tx`. Run the VERBATIM block-edge CTE on prisma14 (#legacy).
await prisma14.$queryRaw`
WITH inserted AS (
INSERT INTO "TaskRunWaitpoint" ("id", "taskRunId", "waitpointId", "projectId", "createdAt", "updatedAt")
SELECT gen_random_uuid(), ${parentRunId}, w.id, ${newEnv.project.id}, NOW(), NOW()
FROM "Waitpoint" w
WHERE w.id IN (${waitpointId})
ON CONFLICT DO NOTHING
RETURNING "waitpointId"
)
SELECT COUNT(*) FROM inserted`;
// The waitpoint is on #new, so the join on #legacy matched nothing → NO edge on EITHER DB.
const edgesOnLegacy = await prisma14.taskRunWaitpoint.count({
where: { taskRunId: parentRunId },
});
const edgesOnNew = await prisma17.taskRunWaitpoint.count({
where: { taskRunId: parentRunId },
});
expect(edgesOnLegacy).toBe(0); // the CTE inserted nothing (Waitpoint join empty)
expect(edgesOnNew).toBe(0); // and it never touched the #new DB where the waitpoint lives
// Therefore countPendingWaitpoints sees no PENDING blocker for the run → the engine would
// treat isRunBlocked=false and NOT suspend the parent. (countPendingWaitpoints on #new finds
// the PENDING waitpoint by id, but with NO edge bound to the run the engine never asks.)
}
);
// Control: the SAME CTE on the DB that DOES hold the waitpoint writes the edge correctly —
// proving the failure is purely the wrong-DB join, not a malformed CTE.
heteroRunOpsPostgresTest(
"control: the block-edge CTE writes the edge when the waitpoint is co-resident",
async ({ prisma14 }) => {
const env = await seedEnvironment(prisma14, "legacy", "gap3ctl_leg");
const parentRunId = `run_${CUID_25}`;
const waitpointId = `waitpoint_${CUID_25}`;
await prisma14.taskRun.create({
data: {
id: parentRunId,
engine: "V2",
status: "EXECUTING",
friendlyId: "run_gap3ctl_parent",
runtimeEnvironmentId: env.environment.id,
environmentType: "DEVELOPMENT",
organizationId: env.organization.id,
projectId: env.project.id,
taskIdentifier: "my-task",
payload: "{}",
payloadType: "application/json",
traceContext: {},
traceId: `trace_${parentRunId}`,
spanId: `span_${parentRunId}`,
queue: "task/my-task",
isTest: false,
taskEventStore: "taskEvent",
depth: 0,
},
});
await prisma14.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_gap3ctl",
type: "RUN",
status: "PENDING",
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: env.project.id,
environmentId: env.environment.id,
},
});
await prisma14.$queryRaw`
WITH inserted AS (
INSERT INTO "TaskRunWaitpoint" ("id", "taskRunId", "waitpointId", "projectId", "createdAt", "updatedAt")
SELECT gen_random_uuid(), ${parentRunId}, w.id, ${env.project.id}, NOW(), NOW()
FROM "Waitpoint" w
WHERE w.id IN (${waitpointId})
ON CONFLICT DO NOTHING
RETURNING "waitpointId"
)
SELECT COUNT(*) FROM inserted`;
const edges = await prisma14.taskRunWaitpoint.count({ where: { taskRunId: parentRunId } });
expect(edges).toBe(1); // co-resident → the edge is written, the parent would suspend
}
);
// ===========================================================================================
// Lazy RUN-waitpoint residency split.
// `getOrCreateRunWaitpoint` creates the lazy RUN waitpoint via `createWaitpoint`
// carrying `completedByTaskRunId: runId`. Production never mints run-ops waitpoint ids, so routing by
// the waitpoint's OWN id-shape would land it on #legacy while a run-ops run is on #new → run-completion
// hydrate (associatedWaitpoint by completedByTaskRunId on the run's DB) misses it → parent hangs.
// Fix: route the create by the OWNING run id (completedByTaskRunId) so it co-resides with the run.
// ===========================================================================================
// A run-ops run's lazy RUN-waitpoint with a CUID-shaped waitpoint id (production-like: run-ops id
// waitpoint minting is off) co-resides on #new with the run, NOT on #legacy by its own id-shape.
heteroRunOpsPostgresTest(
"a run-ops run's lazy RUN-waitpoint co-resides on #new (routed by completedByTaskRunId)",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const env = await seedEnvironment(prisma17, "dedicated", "gap6_new");
const runId = `run_${NEW_ID_26}`; // run-ops run → #new
const waitpointId = `waitpoint_${CUID_25}`; // cuid waitpoint id → would route to #legacy by id-shape
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_gap6_new",
organizationId: env.organization.id,
projectId: env.project.id,
runtimeEnvironmentId: env.environment.id,
})
);
// The lazy `getOrCreateRunWaitpoint` create shape: a RUN waitpoint pointing back at its run.
await router.createWaitpoint({
data: {
id: waitpointId,
friendlyId: "wp_gap6",
type: "RUN",
status: "PENDING",
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: env.project.id,
environmentId: env.environment.id,
completedByTaskRunId: runId,
},
});
// Co-resides with the run-ops run on #new (NOT stranded on #legacy by the cuid id-shape).
const onNew = await prisma17.waitpoint.findUnique({ where: { id: waitpointId } });
expect(onNew).not.toBeNull();
const onLegacy = await prisma14.waitpoint.findUnique({ where: { id: waitpointId } });
expect(onLegacy).toBeNull();
// And the run-completion hydrate (associatedWaitpoint by completedByTaskRunId on the run's DB)
// now resolves it — proving the parent would resume rather than hang.
const run = await router.findRun({ id: runId }, { include: { associatedWaitpoint: true } });
expect((run as any).associatedWaitpoint?.id).toBe(waitpointId);
}
);
// Control: a cuid run's lazy RUN-waitpoint co-resides on #legacy with the run.
heteroRunOpsPostgresTest(
"control: a cuid run's lazy RUN-waitpoint co-resides on #legacy",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const env = await seedEnvironment(prisma14, "legacy", "gap6c_leg");
const runId = `run_${CUID_25}`; // cuid run → #legacy
const waitpointId = `waitpoint_${NEW_ID_26}`; // run-ops waitpoint id → would route to #new by id-shape
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_gap6c_legacy",
organizationId: env.organization.id,
projectId: env.project.id,
runtimeEnvironmentId: env.environment.id,
})
);
await router.createWaitpoint({
data: {
id: waitpointId,
friendlyId: "wp_gap6c",
type: "RUN",
status: "PENDING",
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: env.project.id,
environmentId: env.environment.id,
completedByTaskRunId: runId,
},
});
const onLegacy = await prisma14.waitpoint.findUnique({ where: { id: waitpointId } });
expect(onLegacy).not.toBeNull();
const onNew = await prisma17.waitpoint.findUnique({ where: { id: waitpointId } });
expect(onNew).toBeNull();
}
);
// ===========================================================================================
// Snapshot resume payload must not lose a cross-DB waitpoint's OUTPUT.
// `findLatestExecutionSnapshot` hydrates `completedWaitpoints` from the
// snapshot's own (run's) client. A run-ops run resumed by a waitpoint that completed on the OTHER DB
// (cuid token) would get the token hydrated to a stale/absent row → its OUTPUT silently vanishes from
// the resume payload (a wrong-result, not just a wrong dashboard). Fix: the router re-resolves the
// snapshot's completed waitpoints across BOTH DBs.
// ===========================================================================================
// A run-ops run's latest snapshot lists a completed waitpoint that lives on #legacy
// (cross-DB). The single #new store hydrates it null; the router recovers its real OUTPUT.
heteroRunOpsPostgresTest(
"findLatestExecutionSnapshot recovers a cross-DB completed waitpoint's OUTPUT via the router",
async ({ prisma14, prisma17 }) => {
const { router, newStore } = makeSplitRouter(prisma14, prisma17);
const newEnv = await seedEnvironment(prisma17, "dedicated", "cg1_new");
const legEnv = await seedEnvironment(prisma14, "legacy", "cg1_leg");
const runId = `run_${NEW_ID_26}`; // run-ops run → #new
const waitpointId = `waitpoint_${CUID_25}`; // cuid token → completed on #legacy
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_cg1_new",
organizationId: newEnv.organization.id,
projectId: newEnv.project.id,
runtimeEnvironmentId: newEnv.environment.id,
})
);
// The completing token lives on #legacy with its OUTPUT.
await prisma14.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_cg1",
type: "MANUAL",
status: "COMPLETED",
output: '{"resumed":"cross-db-output"}',
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: legEnv.project.id,
environmentId: legEnv.environment.id,
},
});
// The latest snapshot (on #new, co-resident with the run-ops run) lists the cross-DB token as a
// completed waitpoint via the CompletedWaitpoint join.
await router.createExecutionSnapshot({
run: { id: runId, status: "EXECUTING", attemptNumber: 1 },
snapshot: { executionStatus: "EXECUTING", description: "resumed by cross-db token" },
completedWaitpoints: [{ id: waitpointId, index: 0 }],
environmentId: newEnv.environment.id,
environmentType: "DEVELOPMENT",
projectId: newEnv.project.id,
organizationId: newEnv.organization.id,
});
// Single-store guard: the #new store alone hydrates the completed waitpoint to nothing (the
// token is on #legacy) — proving the bug the router fixes.
const singleStore = await newStore.findLatestExecutionSnapshot(runId);
const singleWp = singleStore?.completedWaitpoints?.find((w) => w.id === waitpointId);
expect(singleWp).toBeUndefined();
// Router path: re-resolves the cross-DB token and surfaces its real OUTPUT on the resume payload.
const viaRouter = await router.findLatestExecutionSnapshot(runId);
const recovered = viaRouter?.completedWaitpoints?.find((w) => w.id === waitpointId);
expect(recovered).toBeDefined();
expect(recovered!.output).toBe('{"resumed":"cross-db-output"}');
expect(recovered!.status).toBe("COMPLETED");
}
);
// ===========================================================================================
// Block-edge WRITER must not require a LOCAL waitpoint row.
// The design routes the block edge to the RUN's DB and mints standalone tokens on LEGACY, so a
// run-ops run on #new can legitimately block on a cuid token resident on #legacy (the one tolerated
// cross-DB direction — the #new `TaskRunWaitpoint` is FK-free precisely for this). If
// `blockRunWithWaitpointEdges`'s dedicated branch sourced the edge rows from
// `FROM "Waitpoint" w WHERE w.id = ANY(...)`, then when the token is NOT on the run's own DB the
// SELECT yields 0 rows → 0 edges written on #new → the run blocks at EXECUTING_WITH_WAITPOINTS with
// no edge → the token's completion (even its own timeout) can never find/resume it → permanent
// hang. The fix sources the edge rows from the waitpointId array directly (`unnest(...)`), since
// the #new DB is FK-free on these columns.
// ===========================================================================================
// A run-ops run on #new blocking on a cuid token resident on
// #legacy writes the block edge (TaskRunWaitpoint + WaitpointRunConnection) on #new, NOT requiring
// the waitpoint row to be local. The #legacy DB holds NO edge for the run-ops run.
heteroRunOpsPostgresTest(
"a NEW run blocking on a LEGACY-resident token writes the edge on NEW (no local waitpoint required)",
async ({ prisma14, prisma17 }) => {
const { router, newStore } = makeSplitRouter(prisma14, prisma17);
const newEnv = await seedEnvironment(prisma17, "dedicated", "gap3b_new");
const legEnv = await seedEnvironment(prisma14, "legacy", "gap3b_leg");
const runId = `run_${NEW_ID_26}`; // run-ops run → #new
const waitpointId = `waitpoint_${CUID_25}`; // cuid standalone token → resides on #legacy
// The run-ops run lives on #new.
await prisma17.taskRun.create({
data: {
id: runId,
engine: "V2",
status: "EXECUTING",
friendlyId: "run_gap3b_new",
runtimeEnvironmentId: newEnv.environment.id,
environmentType: "DEVELOPMENT",
organizationId: newEnv.organization.id,
projectId: newEnv.project.id,
taskIdentifier: "my-task",
payload: "{}",
payloadType: "application/json",
traceContext: {},
traceId: `trace_${runId}`,
spanId: `span_${runId}`,
queue: "task/my-task",
isTest: false,
taskEventStore: "taskEvent",
depth: 0,
},
});
// The standalone token (minted on LEGACY) lives on #legacy ONLY — it is NOT on #new.
await prisma14.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_gap3b",
type: "MANUAL",
status: "PENDING",
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: legEnv.project.id,
environmentId: legEnv.environment.id,
},
});
// Route the block edge by the blocked RUN's id → #new. The token is NOT local to #new,
// but the #new TaskRunWaitpoint is FK-free, so the edge MUST still be written.
await router.blockRunWithWaitpointEdges({
runId,
waitpointIds: [waitpointId],
projectId: newEnv.project.id,
});
// The block edge is written on #new (co-resident with the run) — a local-waitpoint join writes 0.
const edgesOnNew = await prisma17.taskRunWaitpoint.count({ where: { taskRunId: runId } });
expect(edgesOnNew).toBe(1);
// The historical WaitpointRunConnection is also written on #new.
const connectionsOnNew = await prisma17.waitpointRunConnection.count({
where: { taskRunId: runId },
});
expect(connectionsOnNew).toBe(1);
// The #legacy DB holds NO edge for the run-ops run (the safety invariant: no cross-ref on LEGACY).
const edgesOnLegacy = await prisma14.taskRunWaitpoint.count({ where: { taskRunId: runId } });
expect(edgesOnLegacy).toBe(0);
// And the edge is discoverable by the token's completion fan-out: a read keyed on the
// token's waitpointId via the router finds the #new-resident edge, so completing the LEGACY
// token would resume the NEW run rather than hang.
const byWaitpoint = await router.findManyTaskRunWaitpoints({
where: { waitpointId },
select: { id: true, taskRunId: true },
});
expect(byWaitpoint.map((e) => e.taskRunId)).toContain(runId);
// Single-store cross-check: the #new store ALSO writes the edge directly (proving the fix is in
// the store writer, not only the router routing).
const runId2 = `run_${"m".repeat(24) + "01"}`; // a second run-ops run on #new
await prisma17.taskRun.create({
data: {
id: runId2,
engine: "V2",
status: "EXECUTING",
friendlyId: "run_gap3b_new2",
runtimeEnvironmentId: newEnv.environment.id,
environmentType: "DEVELOPMENT",
organizationId: newEnv.organization.id,
projectId: newEnv.project.id,
taskIdentifier: "my-task",
payload: "{}",
payloadType: "application/json",
traceContext: {},
traceId: `trace_${runId2}`,
spanId: `span_${runId2}`,
queue: "task/my-task",
isTest: false,
taskEventStore: "taskEvent",
depth: 0,
},
});
await newStore.blockRunWithWaitpointEdges({
runId: runId2,
waitpointIds: [waitpointId],
projectId: newEnv.project.id,
});
const edgesOnNew2 = await prisma17.taskRunWaitpoint.count({ where: { taskRunId: runId2 } });
expect(edgesOnNew2).toBe(1);
}
);
// Co-resident control: a run-ops run blocking on a CO-RESIDENT run-ops token still writes the
// edge on #new (proving the fix didn't break the co-resident case the old join handled).
heteroRunOpsPostgresTest(
"control: a NEW run blocking on a CO-RESIDENT NEW token writes the edge on NEW",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const env = await seedEnvironment(prisma17, "dedicated", "gap3bco_new");
const runId = `run_${NEW_ID_26}`;
const waitpointId = `waitpoint_${NEW_ID_26}`; // run-ops token → co-resident on #new
await prisma17.taskRun.create({
data: {
id: runId,
engine: "V2",
status: "EXECUTING",
friendlyId: "run_gap3bco_new",
runtimeEnvironmentId: env.environment.id,
environmentType: "DEVELOPMENT",
organizationId: env.organization.id,
projectId: env.project.id,
taskIdentifier: "my-task",
payload: "{}",
payloadType: "application/json",
traceContext: {},
traceId: `trace_${runId}`,
spanId: `span_${runId}`,
queue: "task/my-task",
isTest: false,
taskEventStore: "taskEvent",
depth: 0,
},
});
await prisma17.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_gap3bco",
type: "MANUAL",
status: "PENDING",
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: env.project.id,
environmentId: env.environment.id,
},
});
await router.blockRunWithWaitpointEdges({
runId,
waitpointIds: [waitpointId],
projectId: env.project.id,
});
const edgesOnNew = await prisma17.taskRunWaitpoint.count({ where: { taskRunId: runId } });
expect(edgesOnNew).toBe(1);
const connectionsOnNew = await prisma17.waitpointRunConnection.count({
where: { taskRunId: runId },
});
expect(connectionsOnNew).toBe(1);
}
);
// Idempotency control: a duplicate block (ON CONFLICT DO NOTHING) must not create a second
// edge — the crash-recovery / retry contract (the engine re-writes the same edge on retry).
heteroRunOpsPostgresTest(
"a duplicate cross-DB block edge is idempotent (ON CONFLICT DO NOTHING)",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const newEnv = await seedEnvironment(prisma17, "dedicated", "gap3bidem_new");
const legEnv = await seedEnvironment(prisma14, "legacy", "gap3bidem_leg");
const runId = `run_${NEW_ID_26}`;
const waitpointId = `waitpoint_${CUID_25}`; // cuid token → #legacy
await prisma17.taskRun.create({
data: {
id: runId,
engine: "V2",
status: "EXECUTING",
friendlyId: "run_gap3bidem_new",
runtimeEnvironmentId: newEnv.environment.id,
environmentType: "DEVELOPMENT",
organizationId: newEnv.organization.id,
projectId: newEnv.project.id,
taskIdentifier: "my-task",
payload: "{}",
payloadType: "application/json",
traceContext: {},
traceId: `trace_${runId}`,
spanId: `span_${runId}`,
queue: "task/my-task",
isTest: false,
taskEventStore: "taskEvent",
depth: 0,
},
});
await prisma14.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_gap3bidem",
type: "MANUAL",
status: "PENDING",
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: legEnv.project.id,
environmentId: legEnv.environment.id,
},
});
await router.blockRunWithWaitpointEdges({
runId,
waitpointIds: [waitpointId],
projectId: newEnv.project.id,
});
// Replay the same write (a retry after a crash between the edge write and the snapshot flip).
await router.blockRunWithWaitpointEdges({
runId,
waitpointIds: [waitpointId],
projectId: newEnv.project.id,
});
const edgesOnNew = await prisma17.taskRunWaitpoint.count({ where: { taskRunId: runId } });
expect(edgesOnNew).toBe(1);
const connectionsOnNew = await prisma17.waitpointRunConnection.count({
where: { taskRunId: runId },
});
expect(connectionsOnNew).toBe(1);
}
);
// Control: a co-resident completed waitpoint (token + run on #new) is unaffected — the router
// re-resolution is idempotent.
heteroRunOpsPostgresTest(
"control: a co-resident completed waitpoint's OUTPUT is preserved through the router",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const env = await seedEnvironment(prisma17, "dedicated", "cg1c_new");
const runId = `run_${NEW_ID_26}`;
const waitpointId = `waitpoint_${NEW_ID_26}`; // co-resident on #new
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_cg1c_new",
organizationId: env.organization.id,
projectId: env.project.id,
runtimeEnvironmentId: env.environment.id,
})
);
await prisma17.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_cg1c",
type: "MANUAL",
status: "COMPLETED",
output: '{"resumed":"co-resident-output"}',
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: env.project.id,
environmentId: env.environment.id,
},
});
await router.createExecutionSnapshot({
run: { id: runId, status: "EXECUTING", attemptNumber: 1 },
snapshot: { executionStatus: "EXECUTING", description: "resumed by co-resident token" },
completedWaitpoints: [{ id: waitpointId, index: 0 }],
environmentId: env.environment.id,
environmentType: "DEVELOPMENT",
projectId: env.project.id,
organizationId: env.organization.id,
});
const viaRouter = await router.findLatestExecutionSnapshot(runId);
const recovered = viaRouter?.completedWaitpoints?.find((w) => w.id === waitpointId);
expect(recovered).toBeDefined();
expect(recovered!.output).toBe('{"resumed":"co-resident-output"}');
}
);
// ===========================================================================================
// `getWaitpoint`'s WAITPOINT_DEDICATED relations
// ({ blockingTaskRuns, connectedRuns, completedExecutionSnapshots }) are hydrated by the dedicated
// store on its OWN client only (PostgresRunStore.findWaitpoint → WAITPOINT_DEDICATED
// hydrators, all keyed by `waitpointId` on the store's single client). But a
// waitpoint's blocking edge, run connection and completing snapshot all CO-LOCATE WITH THE RUN
// (blockRunWithWaitpointEdges routes by runId; the CompletedWaitpoint + WaitpointRunConnection
// join rows are written on the run's DB). A cuid token blocking
// a run-ops run therefore has every group-A TARGET on the OTHER DB → the single-client hydrator finds
// nothing → engine.getWaitpoint (include blockingTaskRuns→taskRun) silently returns an
// empty `blockingTaskRuns`. Fix: the router (RoutingRunStore.findWaitpoint/findManyWaitpoints) strips
// these relation keys from the per-leg query and re-resolves the targets across BOTH DBs, mirroring
// findManyTaskRunWaitpoints' edge fan-out.
// ===========================================================================================
// A cuid token on #legacy blocking a run-ops run on #new. The block edge + run connection live
// on #new (the run's DB). getWaitpoint's include{ blockingTaskRuns: { select: { taskRun } } } must
// surface the cross-DB blocked run. Single-store guard proves the #legacy hydrator alone misses it.
heteroRunOpsPostgresTest(
"findWaitpoint include blockingTaskRuns surfaces a cross-DB blocked run via the router",
async ({ prisma14, prisma17 }) => {
const { router, legacyStore } = makeSplitRouter(prisma14, prisma17);
const newEnv = await seedEnvironment(prisma17, "dedicated", "gap13bt_new");
const legEnv = await seedEnvironment(prisma14, "legacy", "gap13bt_leg");
const runId = `run_${NEW_ID_26}`; // run-ops run → #new
const waitpointId = `waitpoint_${CUID_25}`; // cuid token → #legacy
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_gap13bt_new",
organizationId: newEnv.organization.id,
projectId: newEnv.project.id,
runtimeEnvironmentId: newEnv.environment.id,
})
);
await prisma14.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_gap13bt",
type: "MANUAL",
status: "PENDING",
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: legEnv.project.id,
environmentId: legEnv.environment.id,
},
});
// Real production write path: the edge + WaitpointRunConnection land on the RUN's DB (#new).
await router.blockRunWithWaitpointEdges({
runId,
waitpointIds: [waitpointId],
projectId: newEnv.project.id,
});
// Residency sanity: the edge and connection are on #new only; the token is on #legacy only.
expect(await prisma17.taskRunWaitpoint.count({ where: { waitpointId } })).toBe(1);
expect(await prisma14.taskRunWaitpoint.count({ where: { waitpointId } })).toBe(0);
expect(await prisma17.waitpoint.count({ where: { id: waitpointId } })).toBe(0);
expect(await prisma14.waitpoint.count({ where: { id: waitpointId } })).toBe(1);
// Single-store guard: the #legacy store (where the token lives) hydrates blockingTaskRuns from its
// own client → the edge (on #new) is invisible → empty. This is the bug the router fixes.
const single = (await legacyStore.findWaitpoint({
where: { id: waitpointId },
include: {
blockingTaskRuns: { select: { taskRun: { select: { id: true, friendlyId: true } } } },
},
})) as Record<string, any> | null;
expect(single?.blockingTaskRuns ?? []).toHaveLength(0);
// Router path: re-resolves blockingTaskRuns across BOTH DBs → the cross-DB blocked run surfaces.
const viaRouter = (await router.findWaitpoint({
where: { id: waitpointId },
include: {
blockingTaskRuns: { select: { taskRun: { select: { id: true, friendlyId: true } } } },
},
})) as Record<string, any> | null;
const blocking = viaRouter?.blockingTaskRuns ?? [];
expect(blocking).toHaveLength(1);
expect(blocking[0].taskRun?.id).toBe(runId);
expect(blocking[0].taskRun?.friendlyId).toBe("run_gap13bt_new");
}
);
// Sibling: connectedRuns. The WaitpointRunConnection join is co-resident with the run (#new),
// so a cuid token's connectedRuns must be re-resolved across BOTH DBs to surface the run-ops run.
heteroRunOpsPostgresTest(
"findWaitpoint include connectedRuns surfaces a cross-DB connected run via the router",
async ({ prisma14, prisma17 }) => {
const { router, legacyStore } = makeSplitRouter(prisma14, prisma17);
const newEnv = await seedEnvironment(prisma17, "dedicated", "gap13cr_new");
const legEnv = await seedEnvironment(prisma14, "legacy", "gap13cr_leg");
const runId = `run_${NEW_ID_26}`;
const waitpointId = `waitpoint_${CUID_25}`;
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_gap13cr_new",
organizationId: newEnv.organization.id,
projectId: newEnv.project.id,
runtimeEnvironmentId: newEnv.environment.id,
})
);
await prisma14.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_gap13cr",
type: "MANUAL",
status: "PENDING",
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: legEnv.project.id,
environmentId: legEnv.environment.id,
},
});
await router.blockRunWithWaitpointEdges({
runId,
waitpointIds: [waitpointId],
projectId: newEnv.project.id,
});
expect(await prisma17.waitpointRunConnection.count({ where: { waitpointId } })).toBe(1);
// Single-store guard: the token's own store sees no connection (it's on #new).
const single = (await legacyStore.findWaitpoint({
where: { id: waitpointId },
include: { connectedRuns: { select: { id: true, friendlyId: true } } },
})) as Record<string, any> | null;
expect(single?.connectedRuns ?? []).toHaveLength(0);
const viaRouter = (await router.findWaitpoint({
where: { id: waitpointId },
include: { connectedRuns: { select: { id: true, friendlyId: true } } },
})) as Record<string, any> | null;
const connected = viaRouter?.connectedRuns ?? [];
expect(connected).toHaveLength(1);
expect(connected[0].id).toBe(runId);
expect(connected[0].friendlyId).toBe("run_gap13cr_new");
}
);
// Sibling: completedExecutionSnapshots. The CompletedWaitpoint join is co-resident with the
// snapshot/run (#new), so a cuid token's completedExecutionSnapshots straddle to #new and must be
// re-resolved across BOTH DBs.
heteroRunOpsPostgresTest(
"findWaitpoint include completedExecutionSnapshots surfaces a cross-DB snapshot via the router",
async ({ prisma14, prisma17 }) => {
const { router, legacyStore } = makeSplitRouter(prisma14, prisma17);
const newEnv = await seedEnvironment(prisma17, "dedicated", "gap13cs_new");
const legEnv = await seedEnvironment(prisma14, "legacy", "gap13cs_leg");
const runId = `run_${NEW_ID_26}`;
const waitpointId = `waitpoint_${CUID_25}`;
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_gap13cs_new",
organizationId: newEnv.organization.id,
projectId: newEnv.project.id,
runtimeEnvironmentId: newEnv.environment.id,
})
);
await prisma14.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_gap13cs",
type: "MANUAL",
status: "COMPLETED",
output: '{"done":true}',
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: legEnv.project.id,
environmentId: legEnv.environment.id,
},
});
// The snapshot (on #new, co-resident with the run-ops run) records the cross-DB token as completed
// via the CompletedWaitpoint join.
const snapshot = await router.createExecutionSnapshot({
run: { id: runId, status: "EXECUTING", attemptNumber: 1 },
snapshot: { executionStatus: "EXECUTING", description: "completed by cross-db token" },
completedWaitpoints: [{ id: waitpointId, index: 0 }],
environmentId: newEnv.environment.id,
environmentType: "DEVELOPMENT",
projectId: newEnv.project.id,
organizationId: newEnv.organization.id,
});
expect(await prisma17.completedWaitpoint.count({ where: { waitpointId } })).toBe(1);
// Single-store guard: the token's own (#legacy) store sees no completing snapshot (join on #new).
const single = (await legacyStore.findWaitpoint({
where: { id: waitpointId },
include: { completedExecutionSnapshots: { select: { id: true, description: true } } },
})) as Record<string, any> | null;
expect(single?.completedExecutionSnapshots ?? []).toHaveLength(0);
const viaRouter = (await router.findWaitpoint({
where: { id: waitpointId },
include: { completedExecutionSnapshots: { select: { id: true, description: true } } },
})) as Record<string, any> | null;
const snaps = viaRouter?.completedExecutionSnapshots ?? [];
expect(snaps).toHaveLength(1);
expect(snaps[0].id).toBe(snapshot.id);
expect(snaps[0].description).toBe("completed by cross-db token");
}
);
// Control: a fully co-resident waitpoint (token + run + edge all on #new) is unaffected — the
// router re-resolution is idempotent and does not double-count or drop the local group-A targets.
heteroRunOpsPostgresTest(
"control: a co-resident waitpoint's blockingTaskRuns/connectedRuns are preserved through the router",
async ({ prisma14, prisma17 }) => {
const { router } = makeSplitRouter(prisma14, prisma17);
const env = await seedEnvironment(prisma17, "dedicated", "gap13ctl_new");
const runId = `run_${NEW_ID_26}`;
const waitpointId = `waitpoint_${NEW_ID_26}`; // co-resident on #new
await router.createRun(
buildCreateRunInput({
runId,
friendlyId: "run_gap13ctl_new",
organizationId: env.organization.id,
projectId: env.project.id,
runtimeEnvironmentId: env.environment.id,
})
);
await prisma17.waitpoint.create({
data: {
id: waitpointId,
friendlyId: "wp_gap13ctl",
type: "MANUAL",
status: "PENDING",
idempotencyKey: `idem_${waitpointId}`,
userProvidedIdempotencyKey: false,
projectId: env.project.id,
environmentId: env.environment.id,
},
});
await router.blockRunWithWaitpointEdges({
runId,
waitpointIds: [waitpointId],
projectId: env.project.id,
});
const viaRouter = (await router.findWaitpoint({
where: { id: waitpointId },
include: {
blockingTaskRuns: { select: { taskRun: { select: { id: true } } } },
connectedRuns: { select: { id: true } },
},
})) as Record<string, any> | null;
expect(viaRouter?.blockingTaskRuns ?? []).toHaveLength(1);
expect(viaRouter!.blockingTaskRuns[0].taskRun?.id).toBe(runId);
expect((viaRouter?.connectedRuns ?? []).map((r: any) => r.id)).toEqual([runId]);
}
);
});