Generated from cat-harness/skills/conduct/conduct-core/agent-permissions.md — do not edit here.

✎ Edit this page’s source

Agent permissions — asked for, explained, granted by a person, recorded

processes/process/secure-agent-permissions.bpmn (Process_SecureAgentPermissions) is the process. It is called from session-state-machine.bpmn (Call_SecurePermissions, after the session record opens), so it is part of starting work in an instance. It also runs on demand when an agent hits a denied action. A denial is a reason to ask. It is never a reason for the agent to edit its own configuration.

A run that finds every needed permission granted and recorded ends at End_Current without asking anything. The cost of calling it every session is one comparison.

Three layers, three questions, and this skill answers the third

layerquestionwhere it lives
rolewhat does the performer of this lane need to KNOW?skills on the role (role-model)
actor permissionwhat may this participant DO in the harness, in any lane?skills/permissions/permissions.json actions, granted by ODRL rules under policies/
agent Tool permissionwhat may this coding agent do on this machine without stopping to ask?the Tool’s own config, written through its adapter; the grant recorded as below

The third layer enforces the second at the agent host. Editing hooks or settings exercises admin-settings; writing content exercises content-authoring. An agent Tool permission never widens what the actor’s ODRL grants allow. It only decides whether the host stops to ask.

The permissions the processes need

Grant only what the instance’s processes actually call, and the narrowest form that does the job.

permissionwhat it allowswhy a process needs itnotes
run the gatesrun the repository’s test, typecheck and cat check scriptsevery gate a PR must pass before merge-to-mainread-only checks first; a writer (regen) is a separate grant
push a feature branchgit push to a non-default branchcontinual-progress: commit early, push, open the PR at the first commitnever the default branch; never --force, only --force-with-lease
open and update a PRcreate a PR, comment, label itcontinual-progress, issue-working, merge-queue handovernever merge
edit agent hooks and settingschange the Tool’s hand-edited configonly when a hook or setting is itself the defectper change, never standing: the exact diff, the reason, then the owner’s yes (harness-projections)
write generated projectionsregenerate the Tool’s projections of the graphnone: CI regenerates them on the PR headnot granted to an agent at all (harness-projections)
merge to main——never a Tool permission. Every merge is its own owner-authorised run of merge-to-main. No allow-rule for a merge command is ever written.

Explaining one before it is granted

Task_Explain follows interaction-modality §4.1: context, options, recommendation, question. The test is whether the owner can answer without opening anything. For each permission:

  1. What it allows, concretely: the command or file pattern, and what the agent will then do without stopping.
  2. Why a process here needs it: name the process and the step.
  3. Options, narrowest first: the exact pattern, a wider one, or no grant (and what then keeps asking).
  4. The exact config change, as a diff in the Tool’s own format, produced by its adapter.
  5. The recommendation: least privilege, unless there is a measured reason for more.

Recording a grant (not relaxable)

Before the config is written, one record per permission, granted or refused:

agent-permission:
  by: <the person's login>
  on: <ISO-8601 date-time>
  tool: <Tool id, e.g. the coding agent's Tool node>
  permission: <a row of the table above>
  scope: session:<id> | instance | until:<ISO-8601>
  answer: granted | narrowed | refused
  diff: <sha256 of the config diff shown>
  evidence: "<the person's words, verbatim>" (<session link or comment permalink>)

Post it on the PR that carries the config change, and in its commit message. A refusal is recorded too, so the next session does not ask the same question blind.

A config rule with no grant on the record is reported as unrecorded (Task_ReadGrants). It is not treated as granted. A rule nobody can account for cannot be told from one an agent wrote for itself.

Rules no grant relaxes

  • Never self-granted. The grant is a person’s (Task_OwnerGrants is fulfilled by a person only). The config write is done by the person, or by the agent writing exactly the diff the person consented to. If you are composing the consent, you are the grantor, and you may not be. This is the rule confirmation-waiver states for waivers, applied to the agent host.
  • Generated projections are CI’s. No permission lets an agent write them (harness-projections).
  • Hand-edited agent config changes per change, with consent. A standing grant to edit settings does not exist. Each edit is its own diff, reason and yes.
  • Merging to main is never granted here. See merge-to-main.
  • A broad instruction is not a grant. “Fix all issues until green”, “get the session start working” and “stop the prompts” authorise investigation. They do not authorise widening what the agent may do. Bring the diff back.

2026-10-09, the measured reason for all of this. On one day, an auto-mode safety classifier blocked an agent that was editing .claude/settings.json, which is the agent modifying its own permissions. It also flagged the merge of seven PRs across seven repositories that run no CI as “Merge Without Review”. Both actions were taken under the same broad instruction. Both are now steps with an owner’s gate: this process for the first, merge-to-main for the second.

Per-Tool specifics belong to the Tool, not here

This skill names no vendor’s file, on purpose. Where a Tool keeps its permissions, how a rule is spelled, and which of its files are generated are properties of that Tool. They belong on its Tool node or adapter, which is the same direction bpmn-processes §”A Tool’s OWN procedure is the Tool’s subprocess” sets. For example, Claude Code keeps permission allow-rules in .claude/settings*.json. That sentence is an example, not a rule, and its home is the Claude Code adapter.

What this does not do yet

  • No coding-agent Tool nodes or adapters exist in cat-harness. Claude Code, Gemini CLI, Cursor, Copilot and Codex have no Tool node here. claude is declared only as an actor (scenarios/actors/claude.json). Until adapters exist, Task_IdentifyTools reports “no adapter” and leaves the config write to the person.
  • GW_Gap is a judgement. Computing it needs the needed set (from the processes this instance runs) and the granted set (from the config, through an adapter). No tool produces both yet.
  • The permission rows are not yet ODRL actions. They map onto existing actions (admin-settings, content-authoring) in the table above, but are not declared in permissions.json. Declaring them is a change to the permission vocabulary, and it should come with the adapters that read them.

Processes that run this skill

processstep(s) that name it
Secure agent permissionsIdentify the agent Tool(s) acting here; Compare what the processes need with what is granted; Explain each missing permission, with the exact config diff; Grant, narrow or refuse each; Record each answer: who, when, scope, the quote; Write it through the Tool’s adapter
Session state machineSecure the agent’s permissions (calls a sub-process)

folio-assistant — code under the Apache License 2.0, documentation under CC BY 3.0. Generated schema and API references are rebuilt from source on every push to main.

deployed dev