Hamburger Cross Icon
CODEOWNERS Guardrails - Lunar Policy for Repository And Ownership

CODEOWNERS Guardrails

Policy Stable Repository And Ownership

Enforce code ownership standards including CODEOWNERS file presence, syntax validity, catch-all rules, minimum owner counts, and team-based ownership. Ensure every file has a responsible owner.

Add codeowners to your lunar-config.yml:
uses: github://earthly/lunar-lib/policies/codeowners@v1.0.0

Included Guardrails

This policy includes 8 guardrails that enforce standards for your repository and ownership.

Guardrail

exists

Requires a CODEOWNERS file to be present in the repository. Checks standard locations: root, .github/, or docs/.

codeowners code ownership file exists
View Guardrail
Guardrail

valid

Validates that the CODEOWNERS file has correct syntax. Checks that all owner references use valid formats (@user, @org/team, or email).

codeowners syntax validation owner format
View Guardrail
Guardrail

catchall

Requires a default catch-all rule (*) in CODEOWNERS so that every file in the repository has at least one owner.

codeowners catch-all default owner coverage
View Guardrail
Guardrail

min-owners

Ensures each CODEOWNERS rule has a minimum number of owners for redundancy. Configurable via the min_owners_per_rule input.

codeowners minimum owners bus factor redundancy
View Guardrail
Guardrail

team-owners

Requires at least one team-based owner (@org/team) in the CODEOWNERS file for better ownership continuity.

codeowners team ownership org team
View Guardrail
Guardrail

no-individuals-only

Ensures each CODEOWNERS rule includes at least one team owner, not just individual users. Stricter than team-owners which only checks that teams exist somewhere in the file.

codeowners team ownership individual owners
View Guardrail
Guardrail

no-empty-rules

Flags CODEOWNERS rules that have no owners assigned, which effectively un-assigns ownership for matching files.

codeowners empty rules unassigned ownership
View Guardrail
Guardrail

max-owners

Flags CODEOWNERS rules with too many owners. Excessive owners often means diffused responsibility where nobody truly owns the code. Configurable via the max_owners_per_rule input.

codeowners maximum owners diffused ownership
View Guardrail

How Guardrails Fit into Lunar

Lunar guardrails define your engineering standards as code. They evaluate data collected by integrations and produce pass/fail checks with actionable feedback.

Policies support gradual enforcement—from silent scoring to blocking PRs or deployments—letting you roll out standards at your own pace without disrupting existing workflows.

Learn How Lunar Works
1
Integrations Gather Data
Collectors extract metadata from code, CI pipelines, tool outputs, and scans
2
{ } Centralized as JSON
All data merged into each component's unified metadata document
3
Guardrails Enforce Standards This Policy
Real-time feedback in PRs and AI workflows

Required Integrations

This policy evaluates data gathered by one or more of the following integration(s). Make sure to enable them in your lunar-config.yml.

Configuration

Configure this policy in your lunar-config.yml.

Inputs

Input Required Default Description
min_owners_per_rule Optional 2 Minimum number of owners required per CODEOWNERS rule (for min-owners check)
max_owners_per_rule Optional 10 Maximum number of owners allowed per CODEOWNERS rule (for max-owners check)

Documentation

View on GitHub

CODEOWNERS Guardrails

Enforce code ownership standards via CODEOWNERS file validation

Overview

These policies validate the CODEOWNERS file to ensure code ownership is properly defined across the repository. They check for file presence, syntax validity, catch-all coverage, owner counts, and team-based ownership. Development teams should use this policy to maintain clear ownership of every file in their repository. Requires the codeowners collector to be configured.

Policies

This plugin provides the following policies (use include to select a subset):

Policy Description
exists CODEOWNERS file must be present
valid CODEOWNERS syntax must be valid (no invalid owner formats)
catchall Must have a default catch-all * rule
min-owners Each rule must have at least N owners (configurable via min_owners_per_rule, default: 2)
team-owners At least one team owner (@org/team) must be present
no-individuals-only Each rule must include at least one team owner
no-empty-rules No rules should un-assign ownership (0 owners)
max-owners No rule should have more than N owners (configurable via max_owners_per_rule, default: 10)

Required Data

This policy reads from the following Component JSON paths:

Path Type Provided By
.ownership.codeowners.exists boolean codeowners collector
.ownership.codeowners.valid boolean codeowners collector
.ownership.codeowners.errors array codeowners collector
.ownership.codeowners.rules array codeowners collector
.ownership.codeowners.owners array codeowners collector
.ownership.codeowners.team_owners array codeowners collector
.ownership.codeowners.individual_owners array codeowners collector

Note: Ensure the codeowners collector is configured before enabling this policy.

Installation

Add to your lunar-config.yml:

collectors:
  - uses: github://earthly/lunar-lib/collectors/codeowners@v1.0.0
    on: ["domain:your-domain"]

policies:
  - uses: github://earthly/lunar-lib/policies/codeowners@v1.0.0
    on: ["domain:your-domain"]
    enforcement: report-pr
    # with:
    #   min_owners_per_rule: "2"
    #   max_owners_per_rule: "10"

Examples

Passing Example

A repository with a well-structured CODEOWNERS file:

{
  "ownership": {
    "codeowners": {
      "exists": true,
      "valid": true,
      "errors": [],
      "owners": ["@acme/platform", "@acme/backend", "@jane"],
      "team_owners": ["@acme/platform", "@acme/backend"],
      "individual_owners": ["@jane"],
      "rules": [
        {"pattern": "*", "owners": ["@acme/platform", "@jane"], "owner_count": 2, "line": 2},
        {"pattern": "/src/", "owners": ["@acme/backend", "@jane"], "owner_count": 2, "line": 4}
      ]
    }
  }
}

This passes because: file exists, syntax is valid, catch-all * rule present, every rule has >= 2 owners, team owners are used in every rule.

Failing Example

A repository with ownership gaps:

{
  "ownership": {
    "codeowners": {
      "exists": true,
      "valid": true,
      "errors": [],
      "owners": ["@alice"],
      "team_owners": [],
      "individual_owners": ["@alice"],
      "rules": [
        {"pattern": "/src/", "owners": ["@alice"], "owner_count": 1, "line": 1}
      ]
    }
  }
}

Failure messages:

  • catchall: "CODEOWNERS has no default catch-all rule (). Add a ' @your-team' rule so every file has an owner."
  • team-owners: "CODEOWNERS has no team-based owners (@org/team)."
  • min-owners: "Rule '/src/' has 1 owner(s), minimum is 2"

Remediation

When these policies fail, update your CODEOWNERS file:

  1. Missing file: Create a CODEOWNERS file in the repository root, .github/, or docs/ directory
  2. No catch-all rule: Add a * @your-org/your-team line as the first rule so every file has an owner
  3. Too few owners: Add additional owners to rules — at least 2 per rule reduces bus factor risk
  4. No team owners: Use @org/team-name references instead of only individual @username references, so ownership survives team changes
  5. Empty rules: Remove or add owners to rules that have no owners assigned (these un-assign ownership for matching files)
  6. Invalid syntax: Fix owner references to use valid formats: @user, @org/team, or email@example.com

Open Source

This policy is open source and available on GitHub. Contribute improvements, report issues, or fork it for your own use.

View Repository

Ready to Automate Your Standards?

See how Lunar can turn your engineering wiki, compliance docs, or postmortem action items into automated guardrails with our 100+ built-in guardrails.

Works with any process
check Infrastructure conventions
check Post-mortem action items
check Security & compliance policies
check Testing & quality requirements
Automate Now
Turn any process doc into guardrails
Book a Demo