diff --git a/cli/src/__tests__/worktree.test.ts b/cli/src/__tests__/worktree.test.ts index 33551af1ecb..a67dffe5d16 100644 --- a/cli/src/__tests__/worktree.test.ts +++ b/cli/src/__tests__/worktree.test.ts @@ -85,6 +85,9 @@ function mockVerifiedSeedResult() { migrationRevision: "0142_test.sql", pausedScheduledRoutines: 0, executionQuarantine: { + cancelledHeartbeatRuns: 0, + deletedPendingWakeups: 0, + clearedIssueMonitors: 0, disabledTimerHeartbeats: 0, resetRunningAgents: 0, quarantinedInProgressIssues: 0, @@ -662,6 +665,9 @@ describe("worktree helpers", () => { ...mockVerifiedSeedResult(), pausedScheduledRoutines: 2, executionQuarantine: { + cancelledHeartbeatRuns: 1, + deletedPendingWakeups: 1, + clearedIssueMonitors: 1, disabledTimerHeartbeats: 1, resetRunningAgents: 1, quarantinedInProgressIssues: 1, @@ -1490,6 +1496,7 @@ describe("worktree helpers", () => { experimental: { enableWorktreeRunExecution: true, worktreeRunExecutionInstanceNonce: "9ed115ac-9e93-4fe9-a4f1-eb4ea2b0fb24", + worktreeRunExecutionSeedEpoch: "cb1137c7-5630-44b9-a213-762203cc4a38", worktreeRunExecutionActivatedAt: "2026-07-16T12:00:00.000Z", worktreeRunExecutionActivationInstanceId: "source-instance", enableSmokeLab: true, @@ -1503,6 +1510,7 @@ describe("worktree helpers", () => { expect(settings?.experimental).toMatchObject({ enableWorktreeRunExecution: false, worktreeRunExecutionInstanceNonce: null, + worktreeRunExecutionSeedEpoch: expect.any(String), worktreeRunExecutionActivatedAt: null, worktreeRunExecutionActivationInstanceId: null, enableSmokeLab: true, diff --git a/cli/src/commands/worktree.ts b/cli/src/commands/worktree.ts index 013f62c9110..92a9fe03c8d 100644 --- a/cli/src/commands/worktree.ts +++ b/cli/src/commands/worktree.ts @@ -18,7 +18,6 @@ import path from "node:path"; import { execFileSync } from "node:child_process"; import { randomUUID } from "node:crypto"; import { createServer } from "node:net"; -import { randomUUID } from "node:crypto"; import { Readable } from "node:stream"; import * as p from "@clack/prompts"; import pc from "picocolors"; @@ -1494,7 +1493,6 @@ export async function quarantineSeededWorktreeExecutionState( summary.stoppedExecutionWorkspaceRuntimes += 1; } - const now = new Date(); const stoppedRuntimeServices = await tx .update(workspaceRuntimeServices) .set({ @@ -4370,7 +4368,7 @@ async function backupWorktreeReseedTarget(input: { } async function runWorktreeReseed(opts: WorktreeReseedOptions): Promise { - const seedMode = opts.seedMode ?? "minimal"; + const seedMode = opts.seedMode ?? "full"; if (!isWorktreeSeedMode(seedMode)) { throw new Error(`Unsupported seed mode "${seedMode}". Expected one of: minimal, full.`); } diff --git a/server/src/__tests__/instance-settings-service.test.ts b/server/src/__tests__/instance-settings-service.test.ts index 3e5454900ab..fb86350ea53 100644 --- a/server/src/__tests__/instance-settings-service.test.ts +++ b/server/src/__tests__/instance-settings-service.test.ts @@ -7,6 +7,7 @@ import { } from "../services/instance-settings.js"; const INSTANCE_NONCE = "9ed115ac-9e93-4fe9-a4f1-eb4ea2b0fb24"; +const SEED_EPOCH = "4dcd3d50-ff32-4a71-aee6-69aac032e1e6"; describe("instance settings service", () => { it("ignores retired experimental flags without resetting current settings", () => { @@ -57,6 +58,7 @@ describe("instance settings service", () => { enableSandboxDuplexBridge: false, enableWorktreeRunExecution: false, worktreeRunExecutionInstanceNonce: null, + worktreeRunExecutionSeedEpoch: null, worktreeRunExecutionActivatedAt: null, worktreeRunExecutionActivationInstanceId: null, issueGraphLivenessAutoRecoveryLookbackHours: 48, @@ -198,6 +200,7 @@ describe("instance settings service", () => { { enableWorktreeRunExecution: false, worktreeRunExecutionInstanceNonce: INSTANCE_NONCE, + worktreeRunExecutionSeedEpoch: SEED_EPOCH, }, { enableWorktreeRunExecution: true }, { @@ -219,6 +222,7 @@ describe("instance settings service", () => { { enableWorktreeRunExecution: true, worktreeRunExecutionInstanceNonce: INSTANCE_NONCE, + worktreeRunExecutionSeedEpoch: SEED_EPOCH, worktreeRunExecutionActivatedAt: "2026-07-10T12:00:00.000Z", worktreeRunExecutionActivationInstanceId: INSTANCE_NONCE, }, @@ -241,6 +245,7 @@ describe("instance settings service", () => { { enableWorktreeRunExecution: false, worktreeRunExecutionInstanceNonce: INSTANCE_NONCE, + worktreeRunExecutionSeedEpoch: SEED_EPOCH, }, { enableWorktreeRunExecution: true }, { @@ -286,6 +291,7 @@ describe("instance settings service", () => { { enableWorktreeRunExecution: false, worktreeRunExecutionInstanceNonce: "e7904e84-5d6a-44af-bd5f-1c93d9636bc3", + worktreeRunExecutionSeedEpoch: "cb1137c7-5630-44b9-a213-762203cc4a38", worktreeRunExecutionActivatedAt: "2026-07-10T12:00:00.000Z", worktreeRunExecutionActivationInstanceId: "copied-instance", }, @@ -300,12 +306,14 @@ describe("instance settings service", () => { expect(next.worktreeRunExecutionActivatedAt).toBeNull(); expect(next.worktreeRunExecutionActivationInstanceId).toBeNull(); expect(next.worktreeRunExecutionInstanceNonce).toBeNull(); + expect(next.worktreeRunExecutionSeedEpoch).toBeNull(); }); it("resolves worktree run execution as armed only when the cutoff matches the current instance", async () => { const experimental = normalizeExperimentalSettings({ enableWorktreeRunExecution: true, worktreeRunExecutionInstanceNonce: INSTANCE_NONCE, + worktreeRunExecutionSeedEpoch: SEED_EPOCH, worktreeRunExecutionActivatedAt: "2026-07-10T12:00:00.000Z", worktreeRunExecutionActivationInstanceId: INSTANCE_NONCE, }); @@ -322,6 +330,8 @@ describe("instance settings service", () => { armed: true, cutoff: "2026-07-10T12:00:00.000Z", activationInstanceId: INSTANCE_NONCE, + instanceNonce: INSTANCE_NONCE, + seedEpoch: SEED_EPOCH, reason: null, }); }); @@ -330,6 +340,7 @@ describe("instance settings service", () => { const experimental = normalizeExperimentalSettings({ enableWorktreeRunExecution: true, worktreeRunExecutionInstanceNonce: INSTANCE_NONCE, + worktreeRunExecutionSeedEpoch: SEED_EPOCH, worktreeRunExecutionActivationInstanceId: INSTANCE_NONCE, }); @@ -352,6 +363,7 @@ describe("instance settings service", () => { const experimental = normalizeExperimentalSettings({ enableWorktreeRunExecution: true, worktreeRunExecutionInstanceNonce: INSTANCE_NONCE, + worktreeRunExecutionSeedEpoch: SEED_EPOCH, worktreeRunExecutionActivatedAt: "2026-07-10T12:00:00.000Z", worktreeRunExecutionActivationInstanceId: "f6690751-2ed0-4113-9403-241c2cc3ace9", }); diff --git a/server/src/__tests__/routines-service.test.ts b/server/src/__tests__/routines-service.test.ts index 11e1a813949..5d83cce7f0b 100644 --- a/server/src/__tests__/routines-service.test.ts +++ b/server/src/__tests__/routines-service.test.ts @@ -214,6 +214,7 @@ describeEmbeddedPostgres("routine service live-execution coalescing", () => { const experimental = { enableWorktreeRunExecution: true, worktreeRunExecutionInstanceNonce: instanceId, + worktreeRunExecutionSeedEpoch: "00000000-0000-4000-8000-000000000002", worktreeRunExecutionActivatedAt: cutoff.toISOString(), worktreeRunExecutionActivationInstanceId: instanceId, }; diff --git a/server/src/__tests__/workspace-runtime.test.ts b/server/src/__tests__/workspace-runtime.test.ts index 59aacf00e8f..1540d379bae 100644 --- a/server/src/__tests__/workspace-runtime.test.ts +++ b/server/src/__tests__/workspace-runtime.test.ts @@ -4619,7 +4619,7 @@ describe("ensureRuntimeServicesForRun", () => { // A Paperclip dev runtime must answer `/api/health` semantically before it may // be published, so the fake serves the same shape a real one does. const serviceCommand = - "node -e \"require('node:http').createServer((req,res)=>{if(req.url==='/api/health'){res.setHeader('content-type','application/json');res.end(JSON.stringify({status:'ok'}));return;}res.end(process.env.PAPERCLIP_HOME)}).listen(Number(process.env.PORT), '127.0.0.1')\""; + "node -e \"require('node:http').createServer((req,res)=>{if(req.url==='/api/health'){res.setHeader('content-type','application/json');res.end(JSON.stringify({status:'ok'}));return;}res.end(process.env.WORKSPACE_MARKER)}).listen(Number(process.env.PORT), '127.0.0.1')\""; const config = { workspaceRuntime: { services: [