Skip to main content
Documentation menu

Prefix: AGV- Catalog: v1.1 (new pillar). What it measures: the contract between agent autonomy and human intent — policy-as-code, approval gates, and explicit bounds on what an agent may change, merge, or deploy unattended.

Without governance, agent-driven delivery is unbounded authority. This pillar is how an organization states, in artifacts an assessor can read, where the agent stops and a human starts. There is no v1.0 AGV catalog; IDs in the AGV-1xx band are the first published criteria for this dimension.

Criteria in this pillar

AGV-100 — Autonomy bounds published

  • Level: 2 · Scope: portfolio · Check: deterministic
  • The portfolio publishes a machine-readable policy stating which paths and actions (edit, merge, deploy, secret read, production write) an agent may perform unattended. Each application in the subject is covered, or is listed as an owned exception. Absence of a bound is not an implicit grant.
  • Rationale: an agent given a repository and a token will use both. Bounds that live only in chat are not a contract an assessor — or the next agent — can read.
  • Evidence expected: a committed policy file, ruleset, or equivalent structured record; actions and path (or application) scopes enumerated; default-deny or an explicit default stated; exception list owned.

AGV-110 — Privileged actions require an approval gate

  • Level: 3 · Scope: application · Check: deterministic
  • Merge to a production-shipping ref, production deploy, and use of secrets or long-lived credentials require a recorded approval — a human review, a named policy exception, or a two-party check. An agent MUST NOT be the only actor on those actions unless AGV-100 explicitly allows that action on that path.
  • Rationale: autonomy without a gate on merge, deploy, and secrets is production write access with extra steps. The gate is how a human remains in the loop for blast-radius actions.
  • Evidence expected: branch protection, deploy approval, or equivalent on the production-shipping ref; secret use is not available to the unattended identity without the gate; a sample of recent privileged actions shows the recorded approval.

AGV-120 — Policy-as-code enforced in the pipeline

  • Level: 4 · Scope: repository · Check: deterministic
  • Autonomy bounds (AGV-100) are enforced in CI or the merge and deploy pipeline, not only documented. A change or action that exceeds the bound fails the job. A wiki policy with no failing check does not satisfy this criterion.
  • Rationale: documentation is advice. Agents follow the pipeline. Policy that cannot fail a job cannot bound an unattended operator.
  • Evidence expected: a pipeline job or required check that evaluates the bound; a failing fixture or recorded incident where an out-of-bound action was blocked; the check is required on the production-shipping ref.

AGV-130 — Agent identity is distinct and attributable

  • Level: 4 · Scope: repository · Check: deterministic
  • Agents act under a named identity distinct from any human personal account. Commits, merges, deploys, and tool calls that the agent performs record that identity. Shared human tokens, forwarded personal credentials, or an anonymous bot account used by several agents do not satisfy this criterion.
  • Rationale: when something merges at 03:00, the first question is "who". If the actor is a person's token, governance cannot tell agent action from human action, and revocation hits the wrong principal.
  • Evidence expected: a named machine or workload identity per agent (or per agent class); audit or SCM records showing that identity on agent-authored commits and deploys; human personal tokens absent from unattended paths.

AGV-140 — Portfolio exception register

  • Level: 4 · Scope: portfolio · Check: manual
  • Exceptions to autonomy bounds are enumerated per application, owned, and time-bounded. A newly added application inherits the published default (deny, unless AGV-100 states otherwise) within one review cadence or is marked not_applicable with a one-line justification. An unbounded "just this application" carve-out does not satisfy this criterion.
  • Rationale: portfolio scoring (CSPC-31) fails closed when one slice is exempt forever. The exception list is how an assessor sees where the agent is still unbounded.
  • Evidence expected: a committed exception register; owner and expiry (or review date) on each entry; k/n applications under the default bound; cadence stated.

Machine-readable artefacts

The deterministic scanner reads one JSON object per criterion, from docs/governance/ (or docs/agv/, or an agv-*.json file at the root). Each file is validated against a closed schema. Unknown fields, aliases, duplicate entries, and values outside the vocabulary below are schema errors. An object that declares the same key twice is rejected, because JSON.parse would silently keep only the last value. A leading UTF-8 byte-order mark is ignored. A file that is not a JSON object (YAML, JSONC, or free text) fails closed: a keyword scan cannot prove that a bound or gate is actually enumerated.

Every free-text field, required or optional (owner, cadence, version, description, justification, expiry, production_ref), must be a string with at least one visible letter or digit. Whitespace and invisible characters such as U+200B, U+2060, and U+FEFF do not count.

Dates are calendar dates in YYYY-MM-DD form and are evaluated in UTC against the scan time. An expiry stays in force through the end of its UTC day.

Action vocabulary

Each token names exactly one action family. A token marked privileged states the production-reaching form of its family. AGV-110 must gate merge:main, deploy:production, and secret:use. Narrowing variants are valid tokens, but they never cover their family.

The actions list in AGV-100 enumerates what the bound governs. Under default: deny it is the set of actions the policy speaks to, not a grant: an action is permitted unattended only where the bound or an owned exception says so. AGV-100 must list every privileged token so that none is left ungoverned.

TokenFamilyKind
editeditprivileged
merge:mainmergeprivileged
merge:previewmergenarrowing
deploy:productiondeployprivileged
deploy:previewdeploynarrowing
secret:usesecretprivileged
secret:rotatesecretnarrowing
production:writeproductionprivileged

Path scopes

Application paths are relative, /-separated literal segments. Each segment starts with a letter, a digit, _, or @, and continues with letters, digits, _, ., @, or -. A single trailing / is allowed. The scanner rejects the following.

  • Globs and pattern characters are rejected: *, ?, [ ], { }, !, ( ), and +.
  • . and .. segments, a leading /, an empty segment (//), and backslashes are rejected.
  • Hidden segments such as .github are rejected, because a segment cannot start with .. Bound the parent directory instead.
  • Next.js route groups and dynamic segments such as (marketing) or [slug] are rejected. Bound the enclosing literal directory instead, for example apps/site/src/app.

Files and fields

  • AGV-100 autonomy-bounds.json:
    • Required: default (deny), owner, actions (tokens above), applications[], and exceptions[].
    • Each application has an id and concrete paths, plus an optional description.
    • Each exception has an application and an owner, plus an optional justification and an optional expiry. When present, the expiry must be a real calendar date that has not passed. If any exception is past its expiry, the whole file fails AGV-100.
    • Optional at the top level: version and description.
  • AGV-110 approval-gates.json:
    • Required: gates[]. Optional: version, description, and production_ref.
    • Each gate has exactly an action (a token above) and an approval, which is one of human_review, required_review, codeowners, two_party, or named_exception, plus an optional description.
    • Any other gate field, such as required, enabled, approver, or required_approvals, is a schema error, so a gate cannot be switched off or delegated through a sibling field.
  • AGV-120 policy-as-code.json:
    • Required: required_check and on_violation, which is one of fail, block, or reject.
    • Optional: enforced_in (one of ci, merge-queue, or deploy), production_ref, version, and description.
    • Without the file, a source file named agv-guard, autonomy-check, or policy-as-code under src/ counts.
  • AGV-130 agent-identity.json:
    • Required: agents[], each with an id, an identity, and a kind, which is one of workload, machine, github-app, service-account, or bot. Each agent may also have a description.
    • Optional at the top level: version and description.
    • An identity takes one of three forms:
      • The general form starts with an ASCII letter or digit, contains only ASCII letters, digits, and : . _ @ / + = , -, and may end in [bot]. It covers github-app:wentzel-carl, GitHub App bot logins such as dependabot[bot], and IAM names such as role/carl+builder.
      • A GKE Workload Identity, such as my-project.svc.id.goog[carl/carl-scanner], optionally prefixed by an organization domain (example.com:my-project.svc.id.goog[…]).
      • An Azure resource ID that starts with /subscriptions/.
    • An identity is at most 2,048 characters, the AWS IAM ARN maximum.
    • The identity is rejected in any of these cases:
      • It is a shared or public principal: bot, shared, everyone, allUsers, allAuthenticatedUsers, or system:authenticated, ignoring case and trailing separators.
      • It names a set of principals rather than one: a principalSet: or domain: identifier, Kubernetes system:serviceaccounts (with or without a namespace), or system:masters.
      • Its final segment (after the last : or /) is exactly everyone, as in group:everyone. A named app such as github-app:everyone-notifier is still accepted.
      • It has an anonymous, unauthenticated, or personal token, or an AWSReservedSSO token (an IAM Identity Center session, which is always a person). Tokens are split on every non-alphanumeric character, so github-app:personalization-svc is still accepted.
      • It contains both @ and personal anywhere, so a role session named after a personal email address is rejected.
      • It contains ghp_ or users/ anywhere. It also fails when a gho_, ghu_, or github_pat_ credential prefix appears at the start or after any non-alphanumeric character.
      • It contains @ and is not a service-account email (exactly one @, followed by a domain that ends in .gserviceaccount.com) or an AWS IAM role ARN in the aws, aws-us-gov, or aws-cn partition. A role or session name in such an ARN may contain @ (role/svc@build), but an email-shaped name (assumed-role/ci-deployer/ryan@gmail.com) is still rejected.
    • Each id and each identity appears at most once, so one agent's actions cannot be confused with another's.
  • AGV-140 exceptions.json:
    • Required: cadence, a k/n view (integer k ≤ n, or applications[] with an id and a boolean in_bound), and exceptions[].
    • Optional at the top level: default (deny), version, and description.
    • Each entry is either an application with an owner and an expiry (plus an optional justification), or an application with not_applicable: true and a justification.
    • An expiry must be a real calendar date that has not passed and is at most 366 days ahead. The expiry day itself is still in force.