diff --git a/packages/core/src/__tests__/agent-permission-policy.test.ts b/packages/core/src/__tests__/agent-permission-policy.test.ts index 8542aa0883..e327b401f1 100644 --- a/packages/core/src/__tests__/agent-permission-policy.test.ts +++ b/packages/core/src/__tests__/agent-permission-policy.test.ts @@ -13,8 +13,17 @@ import { AGENT_PERMISSION_POLICY_ACTION_CATEGORIES } from "../types.js"; import { ACTION_GATE_TASK_AGENT_MANAGEMENT_TOOLS, COORDINATION_EXEMPT_TOOLS, + READONLY_FN_TOOLS, } from "../../../engine/src/gating-classifications.js"; +// FN-7733: the GitLab browse tools are read-only discovery tools (they list issues/MRs without +// creating task rows) and must never be governed as task_agent_mutation examples. +const GITLAB_BROWSE_TOOLS = [ + "fn_task_browse_gitlab_project_issues", + "fn_task_browse_gitlab_group_issues", + "fn_task_browse_gitlab_merge_requests", +] as const; + describe("agent-permission-policy", () => { it("returns the canonical built-in preset catalog", () => { const presets = getBuiltInAgentPermissionPolicyPresets(); @@ -122,6 +131,16 @@ describe("agent-permission-policy", () => { } }); + it("excludes read-only GitLab browse tools from task_agent_mutation examples and pins them as READONLY_FN_TOOLS", () => { + // FN-7733: regression coverage for the invariant, not just the reported repro — the browse + // tools must never appear as mutation examples, and must remain positively classified as + // read-only so this cannot silently regress. + for (const toolName of GITLAB_BROWSE_TOOLS) { + expect(AGENT_PERMISSION_POLICY_CATEGORY_TOOL_EXAMPLES.task_agent_mutation).not.toContain(toolName); + expect(READONLY_FN_TOOLS.has(toolName)).toBe(true); + } + }); + it("keeps exempt examples aligned with coordination-exempt classifications", () => { for (const toolName of AGENT_PERMISSION_POLICY_EXEMPT_TOOL_EXAMPLES) { expect(COORDINATION_EXEMPT_TOOLS).toContain(toolName); diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index a8c98f5525..1fc4093034 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -6736,7 +6736,8 @@ export const AGENT_PERMISSION_POLICY_CATEGORY_TOOL_EXAMPLES: Record< command_execution: ["bash (non-git)", "fn_run_verification", "fn_acquire_repo_worktree", "read", "find", "grep", "ls"], network_api: ["fn_research_run (web/research)", "fn_research_cancel", "fn_web_fetch", "worktrunk_install"], /* FNXC:ToolGovernance 2026-06-27-16:51: Dashboard policy examples must mirror action-gate mutation exports. Identity reflection is exempt heartbeat coordination, so it is intentionally not advertised as task_agent_mutation. - * FNXC:WorkflowAuthoringTools 2026-06-29-23:40: Published workflow authoring tools are now agent-visible, so policy examples include the mutating workflow create/update/delete/settings/select surface operators can approve or block. */ + * FNXC:WorkflowAuthoringTools 2026-06-29-23:40: Published workflow authoring tools are now agent-visible, so policy examples include the mutating workflow create/update/delete/settings/select surface operators can approve or block. + * FNXC:ToolGovernance 2026-07-09-09:36: FN-7733 — the GitLab browse tools (fn_task_browse_gitlab_project_issues, fn_task_browse_gitlab_group_issues, fn_task_browse_gitlab_merge_requests) are read-only discovery tools that never create task rows and are already classified under READONLY_FN_TOOLS in gating-classifications.ts; they were never members of ACTION_GATE_TASK_AGENT_MANAGEMENT_TOOLS. Listing them here as task_agent_mutation examples broke the invariant that this list must be a subset of the action-gate mutation classification, so they are intentionally excluded. The mutating fn_task_import_gitlab_* variants (which do create task rows) remain listed below. */ task_agent_mutation: [ "fn_task_create", "fn_delegate_task", @@ -6745,9 +6746,6 @@ export const AGENT_PERMISSION_POLICY_CATEGORY_TOOL_EXAMPLES: Record< "fn_task_import_gitlab_project_issues", "fn_task_import_gitlab_group_issues", "fn_task_import_gitlab_merge_requests", - "fn_task_browse_gitlab_project_issues", - "fn_task_browse_gitlab_group_issues", - "fn_task_browse_gitlab_merge_requests", "fn_spawn_agent", "fn_update_agent_config", "fn_task_update",