From 215b6cd161c635b1549c3524286101e6ae4fe607 Mon Sep 17 00:00:00 2001 From: Dotta <34892728+cryppadotta@users.noreply.github.com> Date: Mon, 27 Apr 2026 08:49:59 -0500 Subject: [PATCH] [codex] Add security role route coverage (#4589) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies. > - Agent creation accepts roles that become part of the agent contract and telemetry. > - The shared role list already includes the security role. > - Direct agent creation should preserve that role through route handling and analytics metadata. > - This pull request adds route coverage for creating a security-role agent and asserting telemetry receives the same role. > - The benefit is regression coverage for security agents without changing the production route behavior. ## What Changed - Added a server route test that creates an agent with `role: "security"`. - Asserted the create payload and telemetry metadata preserve `security` as the agent role. ## Verification - `pnpm exec vitest run --project @paperclipai/server server/src/__tests__/agent-skills-routes.test.ts --pool=forks --poolOptions.forks.isolate=true` ## Risks - Low risk; test-only coverage. - No runtime behavior, schema, or API contract changes. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - OpenAI Codex, `gpt-5`, coding model with tool use and local command execution; context window not exposed by the runtime. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] If this change affects the UI, I have included before/after screenshots - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip --- .../src/__tests__/agent-skills-routes.test.ts | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/server/src/__tests__/agent-skills-routes.test.ts b/server/src/__tests__/agent-skills-routes.test.ts index 129075c9..dfa794f9 100644 --- a/server/src/__tests__/agent-skills-routes.test.ts +++ b/server/src/__tests__/agent-skills-routes.test.ts @@ -234,6 +234,7 @@ describe.sequential("agent skill routes", () => { mockAdapter.syncSkills.mockReset(); mockSyncInstructionsBundleConfigFromFilePath.mockImplementation((_agent, config) => config); mockGetTelemetryClient.mockReturnValue({ track: vi.fn() }); + let persistedAgent: Record | null = null; mockAgentService.resolveByReference.mockResolvedValue({ ambiguous: false, agent: makeAgent("claude_local"), @@ -272,18 +273,26 @@ describe.sequential("agent skill routes", () => { entries: [], warnings: [], }); - mockAgentService.update.mockImplementation(async (_id: string, patch: Record) => ({ - ...makeAgent("claude_local"), - adapterConfig: patch.adapterConfig ?? {}, - })); - mockAgentService.create.mockImplementation(async (_companyId: string, input: Record) => ({ - ...makeAgent(String(input.adapterType ?? "claude_local")), - ...input, - adapterConfig: input.adapterConfig ?? {}, - runtimeConfig: input.runtimeConfig ?? {}, - budgetMonthlyCents: Number(input.budgetMonthlyCents ?? 0), - permissions: null, - })); + mockAgentService.update.mockImplementation(async (_id: string, patch: Record) => { + const previousAgent = persistedAgent ?? makeAgent("claude_local"); + persistedAgent = { + ...previousAgent, + ...patch, + adapterConfig: patch.adapterConfig ?? previousAgent.adapterConfig ?? {}, + }; + return persistedAgent; + }); + mockAgentService.create.mockImplementation(async (_companyId: string, input: Record) => { + persistedAgent = { + ...makeAgent(String(input.adapterType ?? "claude_local")), + ...input, + adapterConfig: input.adapterConfig ?? {}, + runtimeConfig: input.runtimeConfig ?? {}, + budgetMonthlyCents: Number(input.budgetMonthlyCents ?? 0), + permissions: null, + }; + return persistedAgent; + }); mockApprovalService.create.mockImplementation(async (_companyId: string, input: Record) => ({ id: "approval-1", companyId: "company-1", @@ -437,13 +446,6 @@ describe.sequential("agent skill routes", () => { }); it("accepts the security role on direct agent creation and preserves it in telemetry", async () => { - mockAgentService.update.mockImplementation(async (_id: string, patch: Record) => ({ - ...makeAgent("claude_local"), - ...patch, - role: "security", - adapterConfig: patch.adapterConfig ?? {}, - })); - const res = await requestApp(await createApp(), (baseUrl) => request(baseUrl) .post("/api/companies/company-1/agents") .send({ @@ -454,6 +456,9 @@ describe.sequential("agent skill routes", () => { })); expect([200, 201], JSON.stringify(res.body)).toContain(res.status); + expect(res.body).toMatchObject({ + role: "security", + }); expect(mockAgentService.create).toHaveBeenCalledWith( "company-1", expect.objectContaining({