From 308d273bb09bb9db2ca4def1f8143576d17dabf0 Mon Sep 17 00:00:00 2001 From: Dotta Date: Sat, 31 Oct 2026 19:02:56 -0500 Subject: [PATCH] fix: make project defaults and instance restart discoverable --- server/src/routes/openapi.ts | 8 ++++++-- .../services/native-runtime/runner-api.test.ts | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/server/src/routes/openapi.ts b/server/src/routes/openapi.ts index 0cf546e8db6..927e29bf3dc 100644 --- a/server/src/routes/openapi.ts +++ b/server/src/routes/openapi.ts @@ -1297,6 +1297,7 @@ function registerCurrentRoute(input: { path: string; tags: string[]; summary: string; + description?: string; query?: z.ZodTypeAny; body?: z.ZodTypeAny; responses?: Record; @@ -1316,6 +1317,7 @@ function registerCurrentRoute(input: { ...(input["x-paperclip-transport"] ? { "x-paperclip-transport": input["x-paperclip-transport"], "x-paperclip-connection-management": input["x-paperclip-connection-management"] } : {}), tags: input.tags, summary: input.summary, + ...(input.description ? { description: input.description } : {}), ...(request ? { request } : {}), responses: input.responses ?? { 200: r.ok(), @@ -5056,7 +5058,8 @@ registry.registerPath({ method: "patch", path: "/api/projects/{id}", tags: ["projects"], - summary: "Update a project", + summary: "Update project properties and defaults", + description: "Save project properties and defaults. The description holds shared project context and guidance, including checklists; goalIds links the project to its goals. Read the project back to verify the saved fields. Updating description or goalIds does not rewrite existing tasks or the inputs already admitted to active native runs; changing existing work is a separate operation. Environment and executionWorkspacePolicy fields configure separate project settings and are not required to save description or goalIds.", request: { params: z.object({ id: z.string() }), body: jsonBody(updateProjectSchema), @@ -10360,7 +10363,8 @@ registerCurrentRoute({ method: "post", path: "/api/health/dev-server/restart", tags: ["health"], - summary: "Request a managed dev-server restart", + summary: "Request a managed dev-server instance restart", + description: "Request a restart of this Paperclip instance through its managed development-server supervisor. In authenticated deployments this requires a board identity; an agent credential does not inherit board access from its responsible user or from a chat request. Hand that operation to an authorized board/operator when the route denies the current agent. Returns 404 when no managed supervisor is available and 409 when no restart is required. Acceptance is not proof that the restart completed. This operation does not delete stored conversations or task comments.", responses: { 202: r.ok(), 403: r.forbidden, diff --git a/server/src/services/native-runtime/runner-api.test.ts b/server/src/services/native-runtime/runner-api.test.ts index f5ad26f3e51..ad6f68a3816 100644 --- a/server/src/services/native-runtime/runner-api.test.ts +++ b/server/src/services/native-runtime/runner-api.test.ts @@ -176,6 +176,22 @@ describe("runner API catalog", () => { ).toContain("search_tasks"); expect(searchRunnerApi({ query: "nothing-zzzzzzzzzz" }).total).toBe(0); }); + it("finds the actual instance-restart operation and explains its authority boundary", () => { + const operation = searchRunnerApi({ query: "restart instance", limit: 1 }).results[0]!; + expect(operation.operationId).toBe("POST /api/health/dev-server/restart"); + expect(operation.authorization.actor).toBe("board"); + expect(operation.description).toMatch(/authenticated.*board/i); + expect(operation.description).toMatch(/agent credential/i); + expect(operation.description).toMatch(/supervisor/i); + }); + it("discovers where to save project defaults without rewriting existing tasks", () => { + const operation = searchRunnerApi({ query: "save project defaults", limit: 1 }).results[0]!; + expect(operation.operationId).toBe("PATCH /api/projects/{id}"); + expect(operation.description).toContain("goalIds"); + expect(operation.description).toContain("description"); + expect(operation.description).toMatch(/existing tasks/i); + expect(operation.requestBody?.content["application/json"].schema.properties).toHaveProperty("goalIds"); + }); it("paginates without duplicates and rejects stale or mismatched cursors", () => { const first = searchRunnerApi({ query: "project", limit: 1 }); const second = searchRunnerApi({