Skip to content

Why use an extra check?

A routine dependency update exposes the gap.

Suppose @example-org/platform owns /uv.lock. Stampbot recognizes an allowed dependency pull request and approves its current head. The change has the review your automation policy asked for, but GitHub's native Require review from Code Owners rule still waits for a person.

Keep people in CODEOWNERS

You could try adding the App's bot login to CODEOWNERS. Extra CODEOWNERS deliberately doesn't depend on that approach. GitHub documents code owners as users or teams with explicit write access; it doesn't document a GitHub App bot account as a supported owner type.

The extra check leaves human ownership where GitHub expects it and records App authority somewhere that can express the missing limits.

The decision in one pull request

For each changed path, Extra CODEOWNERS applies the last matching CODEOWNERS rule and groups paths by their effective owner set. It then looks for one of two kinds of evidence:

Appropriate human approved the exact current head
                            \
                             -> owner set is satisfied
                            /
Enrolled App approved the exact current head
and a delegation covers the path and owner

Every distinct owner set represented by the pull request must be satisfied. In the implemented evaluator contract, a human can cover an ordinary application change while an enrolled App covers a delegated lockfile in the same pull request. The project has not yet recorded a dated live GitHub run of the App-review and required-check behavior.

The result is a Check Run named Extra CODEOWNERS / approval. The service doesn't submit a review, change branch protection, merge the pull request, or grant an App access.

Why the App policy is separate

A bot login alone doesn't answer the questions that matter:

  • Which GitHub App did an organization administrator enroll?
  • Which paths may that App cover in this repository?
  • Which human owner may it stand in for?
  • Which labels narrow the delegation?
  • Which paths reject App substitution altogether?

Extra CODEOWNERS answers them in two policy scopes.

Organization policy enrolls the App by App ID, bot user ID, and slug. It also adds organization guardrails: path patterns where no enrolled App may replace a human approval.

Repository policy opts one repository in and delegates a smaller set of paths and effective owners. It may add required or forbidden labels. It can't enroll a new App or remove an organization guardrail.

CODEOWNERS: people and teams --------+
Organization policy: App + guards ---+-> evaluator -> required check
Repository policy: delegation -------+

This split means /uv.lock can be delegated to Stampbot for the platform team without making Stampbot a general replacement for that team.

What “human-only” means

Extra CODEOWNERS calls a path non-delegable when an App approval may not satisfy its code-owner requirement. That rule controls approval evidence, not the file's contents.

A workflow may invoke an App. Repository policy may list an enrolled App. CODEOWNERS continues to list its human users and teams. But when a pull request changes one of those protected files, an enrolled App can't stand in for the effective human owner.

The built-in non-delegable set covers standard CODEOWNERS locations, the effective repository policy, Stampbot's root policy, GitHub workflows, and local actions. Organization policy should add the approving App's other control files and any transitive decision code. The configuration reference lists the exact patterns and explains the process-wide insecure escape hatch.

These patterns don't assign ownership. Your normal CODEOWNERS file must give them an effective human owner if you want a human approval requirement.

What changes in repository rules

Native code-owner review and ordinary approval count answer different questions:

  • Did the pull request collect enough approvals overall?
  • Did an eligible owner satisfy each owned path?

Keep GitHub's numeric approval rule for the first question. In a disposable test repository, disable only Require review from Code Owners and require Extra CODEOWNERS / approval from the expected Extra CODEOWNERS App for the second.

ordinary approval count is satisfied
AND Extra CODEOWNERS / approval succeeds from the expected App
AND every other required rule succeeds

Extra CODEOWNERS doesn't alter GitHub's numeric count. GitHub's public documentation also doesn't promise that a third-party App review satisfies that count. Issue #1 tracks a dated live probe, so test that behavior in your disposable repository and keep the numeric rule.

The repository-rules guide gives the complete test and rollback procedure.

The unresolved commit boundary

The check's evidence belongs to a pull request: its base, changed paths, labels, and reviews. GitHub attaches the resulting Check Run to a commit.

If another pull request opens or retargets to the same head, it can briefly display the earlier success before GitHub delivers the event that lets Extra CODEOWNERS revoke and recompute the result. Shared-head detection, durable invalidation, and generation guards reduce that window after the event arrives. They can't protect the time before delivery.

This is the project's main production blocker. Keep native code-owner enforcement on production repositories until the project status says the live contract is closed.

Keep CODEOWNERS valid

Continue to use GitHub's error API after any ownership change. With Bash and an authenticated GitHub CLI session, set the three example values and run:

OWNER=example-org
REPOSITORY=example-repository
BRANCH_OR_COMMIT=main
gh api --method GET "repos/${OWNER}/${REPOSITORY}/codeowners/errors" \
  -f ref="${BRANCH_OR_COMMIT}" \
  --jq '.errors'

Replace the values with the repository and exact revision you want GitHub to inspect. An error-free file returns an empty array. That result proves the file parses; it does not prove that an undocumented App identity works as a native code owner.

For GitHub's native behavior, see About code owners and the CODEOWNERS errors API.