From 6e5a43011820d12aeeb4ad10defb2cb27c886b07 Mon Sep 17 00:00:00 2001 From: Dotta Date: Mon, 2 Nov 2026 20:04:13 -0600 Subject: [PATCH] fix(evals): preserve assignment permission in team fixture request Use the complete public permissions schema and retain the independently observed same-company CEO assignment capability. Reject foreign or unverified actor metadata before writing, and verify the effective capability remains unchanged. Co-Authored-By: Paperclip --- .../external-team-install.test.ts | 20 ++++++++++++++++++- .../external-team-install.ts | 11 ++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/tests/runner-e2e/chat-catalog-domains/external-team-install.test.ts b/tests/runner-e2e/chat-catalog-domains/external-team-install.test.ts index 073657e31c1..42453245eee 100644 --- a/tests/runner-e2e/chat-catalog-domains/external-team-install.test.ts +++ b/tests/runner-e2e/chat-catalog-domains/external-team-install.test.ts @@ -1,8 +1,9 @@ import type { ChatCatalogContext } from "../chat-catalog-contract.js"; import type { Agent } from "../../../packages/shared/src/types/agent.js"; import { CHAT_OPENCODE_MODEL } from "../chat-runtime-profile.js"; +import { updateAgentPermissionsSchema } from "../../../packages/shared/src/validators/agent.js"; import { catalogTeamInstallSchema } from "../../../packages/shared/src/validators/teams-catalog.js"; -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { teamInstallExternalCases, selectedTeamInstallRuntime, teamInstallOverrides, verifyInstalledTeamRuntime } from "./external-team-install.js"; function valid() { const team = { @@ -212,3 +213,20 @@ it("real resource counts and actor attribution cannot conceal wrong member provi (o: ReturnType) => { o.state.selectedRuntime.model = "openrouter/other/model"; o.state.after.agents.forEach(agent => { agent.adapterConfig.model = "openrouter/other/model"; }); }, ]) { const o = valid(); change(o); expect(teamInstallExternalCases[0]!.grade(o).passed).toBe(false); } }); + +it.each([true,false])("sends the complete permission schema without changing effective assignment=%s",async canAssignTasks=>{ + const actor={id:"ceo",companyId:"company",role:"ceo",access:{canAssignTasks}}; + const patch=vi.fn(async(_route:string,body:unknown)=>{expect(updateAgentPermissionsSchema.parse(body)).toEqual({canCreateAgents:true,canAssignTasks});throw new Error("stop-after-validated-permission-request");}); + const ctx={input:{fixtures:{company:{id:"company"},agent:{id:"ceo"}},api:{get:async(route:string)=>route==="/api/teams/catalog"?[{slug:"product-engineering",compatibility:"compatible"}]:actor,patch}}} as unknown as ChatCatalogContext; + await expect(teamInstallExternalCases[0]!.setup(ctx)).rejects.toThrow("stop-after-validated-permission-request"); + expect(patch).toHaveBeenCalledOnce(); +}); +it.each(["foreign company","wrong agent","not CEO","missing effective assignment"])("refuses %s identity before permission mutation",async invalid=>{ + const actor={id:"ceo",companyId:"company",role:"ceo",access:{canAssignTasks:true}}; + if(invalid==="foreign company")actor.companyId="foreign"; + if(invalid==="wrong agent")actor.id="other"; + if(invalid==="not CEO")actor.role="engineer"; + if(invalid==="missing effective assignment")delete(actor as {access?:unknown}).access; + const patch=vi.fn(); const ctx={input:{fixtures:{company:{id:"company"},agent:{id:"ceo"}},api:{get:async(route:string)=>route==="/api/teams/catalog"?[{slug:"product-engineering",compatibility:"compatible"}]:actor,patch}}} as unknown as ChatCatalogContext; + await expect(teamInstallExternalCases[0]!.setup(ctx)).rejects.toThrow("observed installer");expect(patch).not.toHaveBeenCalled(); +}); diff --git a/tests/runner-e2e/chat-catalog-domains/external-team-install.ts b/tests/runner-e2e/chat-catalog-domains/external-team-install.ts index be46cfd27c5..87cdff06496 100644 --- a/tests/runner-e2e/chat-catalog-domains/external-team-install.ts +++ b/tests/runner-e2e/chat-catalog-domains/external-team-install.ts @@ -7,7 +7,7 @@ import { gradeTeamConditions, } from "./external-team-conditional.js"; import { expect } from "@playwright/test"; -import type { Agent } from "../../../packages/shared/src/types/agent.js"; +import type { Agent, AgentDetail } from "../../../packages/shared/src/types/agent.js"; import type { CatalogTeam, CatalogTeamImportPreviewResult, @@ -277,19 +277,22 @@ export const teamInstallExternalCases = [ throw new Error( "Setup blocker: real compatible Product Engineering team catalog package is unavailable", ); + const beforePermissions = await ctx.input.api.get(`/api/agents/${ctx.input.fixtures.agent.id}`); + if (beforePermissions.id !== ctx.input.fixtures.agent.id || beforePermissions.companyId !== ctx.input.fixtures.company.id || beforePermissions.role !== "ceo" || typeof beforePermissions.access?.canAssignTasks !== "boolean") throw new Error("Team fixture requires the independently observed installer identity and assignment authority"); await ctx.input.api.patch( `/api/agents/${ctx.input.fixtures.agent.id}/permissions`, - { canCreateAgents: true }, + { canCreateAgents: true, canAssignTasks: beforePermissions.access.canAssignTasks }, ); await ctx.input.api.patch(root(ctx), { requireBoardApprovalForNewAgents: false, }); - const actor = await ctx.input.api.get( + const actor = await ctx.input.api.get( `/api/agents/${ctx.input.fixtures.agent.id}`, ); if ( actor.lifecycleState !== "ready" || - actor.permissions?.canCreateAgents !== true + actor.permissions?.canCreateAgents !== true || + actor.access?.canAssignTasks !== beforePermissions.access.canAssignTasks ) throw new Error( "Setup blocker: actual selected installer actor lacks a ready account or creation authority",