From 91a66e65bbcd16e95588a0c1fa1ece4a2662f374 Mon Sep 17 00:00:00 2001 From: Dotta Date: Sun, 23 Aug 2026 21:22:49 +0000 Subject: [PATCH] test(runner): align hosted server contracts Co-Authored-By: Paperclip --- .../__tests__/heartbeat-context-summary.test.ts | 8 ++++++-- .../paperclip-runner-claude-managed.test.ts | 14 ++++++++++++-- server/src/realtime/runner-prp-ws.test.ts | 13 +++++++++++-- ...rclip-runner-real-server.integration.test.ts | 17 +++++++++++++---- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/server/src/__tests__/heartbeat-context-summary.test.ts b/server/src/__tests__/heartbeat-context-summary.test.ts index 05f4057175c..b80d6d9801e 100644 --- a/server/src/__tests__/heartbeat-context-summary.test.ts +++ b/server/src/__tests__/heartbeat-context-summary.test.ts @@ -51,7 +51,9 @@ describe("buildPaperclipTaskMarkdown", () => { }, }); - expect(acceptedConfirmation).toContain("Create child issues from the approved plan only"); + expect(acceptedConfirmation).toContain( + "Use create_task to create exactly one standard implementation child containing the complete approved plan", + ); expect(acceptedConfirmation).not.toContain("Make the plan only."); }); @@ -68,7 +70,9 @@ describe("buildPaperclipTaskMarkdown", () => { }); expect(acceptedConfirmation).toContain("Accepted plan directive:"); - expect(acceptedConfirmation).toContain("Create child issues from the approved plan only"); + expect(acceptedConfirmation).toContain( + "Use create_task to create exactly one standard implementation child containing the complete approved plan", + ); expect(acceptedConfirmation).not.toContain("- Work mode: \"planning\""); }); diff --git a/server/src/__tests__/paperclip-runner-claude-managed.test.ts b/server/src/__tests__/paperclip-runner-claude-managed.test.ts index 794c5bde0d2..b86f7fcf442 100644 --- a/server/src/__tests__/paperclip-runner-claude-managed.test.ts +++ b/server/src/__tests__/paperclip-runner-claude-managed.test.ts @@ -58,14 +58,24 @@ describe("paperclip_runner Claude Agent environment probe", () => { expect(result.status).toBe("warn"); expect(result.checks.some((check) => check.code === "claude_managed_profile_qualified")).toBe(true); - expect(calls).toHaveLength(3); + expect(calls.map((call) => call.url)).toEqual([ + "https://api.anthropic.com/v1/agents/agent_test?beta=true", + "https://api.anthropic.com/v1/environments/env_test?beta=true", + "https://api.anthropic.com/v1/agents/agent_test/versions?beta=true", + "https://api.anthropic.com/v1/skills?limit=1&source=custom", + ]); expect(calls.every((call) => (call.init.method ?? "GET") === "GET")).toBe(true); for (const call of calls) { expect(call.init.headers).toMatchObject({ "x-api-key": "anthropic-config-secret", - "anthropic-beta": "managed-agents-2026-04-01", }); } + expect(calls.slice(0, 3).every((call) => + (call.init.headers as Record)["anthropic-beta"] === "managed-agents-2026-04-01" + )).toBe(true); + expect(calls[3]?.init.headers).toMatchObject({ + "anthropic-beta": "skills-2025-10-02", + }); expect(JSON.stringify(result)).not.toContain("anthropic-config-secret"); }); diff --git a/server/src/realtime/runner-prp-ws.test.ts b/server/src/realtime/runner-prp-ws.test.ts index 518648d8e74..a8dc9c00cce 100644 --- a/server/src/realtime/runner-prp-ws.test.ts +++ b/server/src/realtime/runner-prp-ws.test.ts @@ -1,3 +1,4 @@ +import { existsSync } from "node:fs"; import { createServer } from "node:http"; import { tmpdir } from "node:os"; import { resolve } from "node:path"; @@ -16,6 +17,14 @@ import { setupRunnerPrpWebSocketServer, } from "./runner-prp-ws.js"; +const fakeCodexAppServer = resolve( + import.meta.dirname, + "../../../packages/paperclip-runner/runner/target/debug/fake-codex-app-server", +); +const runnerBinariesAvailable = + existsSync(defaultCapabilityRunnerdBinary()) && existsSync(fakeCodexAppServer); +const runnerBinaryIt = runnerBinariesAvailable ? it : it.skip; + describe("runner PRP websocket route", () => { afterEach(() => runnerPrpWebSocketInternals.resetForTests()); @@ -95,7 +104,7 @@ describe("runner PRP websocket route", () => { server.close(); }); - it("carries an authorized runnerd tool call over the shared Paperclip route", async () => { + runnerBinaryIt("carries an authorized runnerd tool call over the shared Paperclip route", async () => { const server = createServer(); await new Promise((resolveListen) => server.listen(0, "127.0.0.1", resolveListen)); const address = server.address(); @@ -104,7 +113,7 @@ describe("runner PRP websocket route", () => { const runId = "00000000-0000-4000-8000-000000000779"; const bundle = createRunnerdCodexTransport({ runnerBinary: defaultCapabilityRunnerdBinary(), - codexCommand: resolve(import.meta.dirname, "../../../packages/paperclip-runner/runner/target/debug/fake-codex-app-server"), + codexCommand: fakeCodexAppServer, codexArgs: [], prpIdentity: { runnerInstanceId: "runner-server-route", diff --git a/server/src/services/native-runtime/paperclip-runner-real-server.integration.test.ts b/server/src/services/native-runtime/paperclip-runner-real-server.integration.test.ts index 1c853aef923..af5cb2c44dd 100644 --- a/server/src/services/native-runtime/paperclip-runner-real-server.integration.test.ts +++ b/server/src/services/native-runtime/paperclip-runner-real-server.integration.test.ts @@ -1,5 +1,6 @@ -import { createServer } from "node:http"; +import { existsSync } from "node:fs"; import { mkdtemp, rm } from "node:fs/promises"; +import { createServer } from "node:http"; import { tmpdir } from "node:os"; import { resolve } from "node:path"; @@ -19,6 +20,14 @@ import { } from "../../realtime/runner-prp-ws.js"; import { PaperclipRunnerToolAuthority } from "./paperclip-runner-tool-authority.js"; +const fakeCodexAppServer = resolve( + import.meta.dirname, + "../../../../packages/paperclip-runner/runner/target/debug/fake-codex-app-server", +); +const runnerBinariesAvailable = + existsSync(defaultCapabilityRunnerdBinary()) && existsSync(fakeCodexAppServer); +const runnerBinaryIt = runnerBinariesAvailable ? it : it.skip; + describe("paperclip-runner real server vertical slice", () => { let temporary: Awaited>; const companyId = "00000000-0000-4000-8000-000000000701"; @@ -36,7 +45,7 @@ describe("paperclip-runner real server vertical slice", () => { await temporary.cleanup(); }); - it("runs Rust runnerd through Paperclip PRP and reads the real bound task", async () => { + runnerBinaryIt("runs Rust runnerd through Paperclip PRP and reads the real bound task", async () => { const db = createDb(temporary.connectionString); await db.insert(companies).values({ id: companyId, name: "Real runner slice", issuePrefix: "RRS" }); await db.insert(agents).values({ @@ -78,7 +87,7 @@ describe("paperclip-runner real server vertical slice", () => { const stateDirectory = await mkdtemp(resolve(tmpdir(), "paperclip-runner-real-resume-")); const bundle = createRunnerdCodexTransport({ runnerBinary: defaultCapabilityRunnerdBinary(), - codexCommand: resolve(import.meta.dirname, "../../../../packages/paperclip-runner/runner/target/debug/fake-codex-app-server"), + codexCommand: fakeCodexAppServer, codexArgs: [], stateDirectory, lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null }, @@ -147,7 +156,7 @@ describe("paperclip-runner real server vertical slice", () => { }); const restored = createRunnerdCodexTransport({ runnerBinary: defaultCapabilityRunnerdBinary(), - codexCommand: resolve(import.meta.dirname, "../../../../packages/paperclip-runner/runner/target/debug/fake-codex-app-server"), + codexCommand: fakeCodexAppServer, codexArgs: [], stateDirectory, lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null },