From ebd45b62cdc9a30124ab5946f20feec88f64437e Mon Sep 17 00:00:00 2001 From: dotta Date: Sat, 28 Mar 2026 11:40:48 -0500 Subject: [PATCH 1/2] Provision local node_modules in issue worktrees Co-Authored-By: Paperclip --- scripts/provision-worktree.sh | 1 - .../src/__tests__/workspace-runtime.test.ts | 100 ++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/scripts/provision-worktree.sh b/scripts/provision-worktree.sh index 7b84acaf..1dd481b1 100644 --- a/scripts/provision-worktree.sh +++ b/scripts/provision-worktree.sh @@ -346,7 +346,6 @@ list_base_node_modules_paths() { ! -path './.paperclip/*' \ | sed 's#^\./##' } - if [[ -f "$worktree_cwd/package.json" && -f "$worktree_cwd/pnpm-lock.yaml" ]]; then needs_install=0 diff --git a/server/src/__tests__/workspace-runtime.test.ts b/server/src/__tests__/workspace-runtime.test.ts index b5e1dc31..3b1bee0d 100644 --- a/server/src/__tests__/workspace-runtime.test.ts +++ b/server/src/__tests__/workspace-runtime.test.ts @@ -899,6 +899,106 @@ describe("realizeExecutionWorkspace", () => { ); }, 30_000); + it("provisions worktree-local pnpm node_modules instead of reusing base-repo links", async () => { + const repoRoot = await createTempRepo(); + await fs.mkdir(path.join(repoRoot, "scripts"), { recursive: true }); + await fs.mkdir(path.join(repoRoot, "packages", "shared"), { recursive: true }); + await fs.mkdir(path.join(repoRoot, "server"), { recursive: true }); + await fs.writeFile( + path.join(repoRoot, "package.json"), + JSON.stringify( + { + name: "workspace-root", + private: true, + packageManager: "pnpm@9.15.4", + }, + null, + 2, + ), + "utf8", + ); + await fs.writeFile( + path.join(repoRoot, "pnpm-workspace.yaml"), + ["packages:", " - packages/*", " - server", ""].join("\n"), + "utf8", + ); + await fs.writeFile( + path.join(repoRoot, "packages", "shared", "package.json"), + JSON.stringify( + { + name: "@repo/shared", + version: "1.0.0", + private: true, + type: "module", + exports: "./index.js", + }, + null, + 2, + ), + "utf8", + ); + await fs.writeFile(path.join(repoRoot, "packages", "shared", "index.js"), "export const value = 'shared';\n", "utf8"); + await fs.writeFile( + path.join(repoRoot, "server", "package.json"), + JSON.stringify( + { + name: "server", + private: true, + type: "module", + dependencies: { + "@repo/shared": "workspace:*", + }, + }, + null, + 2, + ), + "utf8", + ); + await fs.writeFile(path.join(repoRoot, "server", "index.js"), "export {};\n", "utf8"); + await fs.copyFile(provisionWorktreeScriptPath, path.join(repoRoot, "scripts", "provision-worktree.sh")); + await fs.chmod(path.join(repoRoot, "scripts", "provision-worktree.sh"), 0o755); + await runPnpm(repoRoot, ["install"]); + await runGit(repoRoot, ["add", "."]); + await runGit(repoRoot, ["commit", "-m", "Add pnpm workspace fixture"]); + + const workspace = await realizeExecutionWorkspace({ + base: { + baseCwd: repoRoot, + source: "project_primary", + projectId: "project-1", + workspaceId: "workspace-1", + repoUrl: null, + repoRef: "HEAD", + }, + config: { + workspaceStrategy: { + type: "git_worktree", + branchTemplate: "{{issue.identifier}}-{{slug}}", + provisionCommand: "bash ./scripts/provision-worktree.sh", + }, + }, + issue: { + id: "issue-1", + identifier: "PAP-551", + title: "Provision local workspace dependencies", + }, + agent: { + id: "agent-1", + name: "Codex Coder", + companyId: "company-1", + }, + }); + + expect((await fs.lstat(path.join(workspace.cwd, "node_modules"))).isSymbolicLink()).toBe(false); + expect((await fs.lstat(path.join(workspace.cwd, "server", "node_modules"))).isSymbolicLink()).toBe(false); + await expect(fs.realpath(path.join(workspace.cwd, "server", "node_modules", "@repo", "shared"))).resolves.toBe( + await fs.realpath(path.join(workspace.cwd, "packages", "shared")), + ); + await expect(fs.realpath(path.join(repoRoot, "server", "node_modules", "@repo", "shared"))).resolves.toBe( + await fs.realpath(path.join(repoRoot, "packages", "shared")), + ); + }); + it("records worktree setup and provision operations when a recorder is provided", async () => { const repoRoot = await createTempRepo(); const { recorder, operations } = createWorkspaceOperationRecorderDouble(); From 53ffa506386f1a6bd54ce15e9b4906d4c18bf4b7 Mon Sep 17 00:00:00 2001 From: dotta Date: Wed, 1 Apr 2026 09:35:22 -0500 Subject: [PATCH 2/2] Clean up opencode rebase and stabilize runtime test Co-Authored-By: Paperclip --- server/src/__tests__/workspace-runtime.test.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/server/src/__tests__/workspace-runtime.test.ts b/server/src/__tests__/workspace-runtime.test.ts index 3b1bee0d..5f4fc645 100644 --- a/server/src/__tests__/workspace-runtime.test.ts +++ b/server/src/__tests__/workspace-runtime.test.ts @@ -899,7 +899,9 @@ describe("realizeExecutionWorkspace", () => { ); }, 30_000); - it("provisions worktree-local pnpm node_modules instead of reusing base-repo links", async () => { + it( + "provisions worktree-local pnpm node_modules instead of reusing base-repo links", + async () => { const repoRoot = await createTempRepo(); await fs.mkdir(path.join(repoRoot, "scripts"), { recursive: true }); await fs.mkdir(path.join(repoRoot, "packages", "shared"), { recursive: true }); @@ -997,7 +999,9 @@ describe("realizeExecutionWorkspace", () => { await expect(fs.realpath(path.join(repoRoot, "server", "node_modules", "@repo", "shared"))).resolves.toBe( await fs.realpath(path.join(repoRoot, "packages", "shared")), ); - }); + }, + 15_000, + ); it("records worktree setup and provision operations when a recorder is provided", async () => { const repoRoot = await createTempRepo();