Skip to content

fix: report .codeowner files that reference an unregistered team - #116

Open
sarastrasner wants to merge 1 commit into
rubyatscale:mainfrom
sarastrasner:fix-directory-codeowner-invalid-team
Open

fix: report .codeowner files that reference an unregistered team#116
sarastrasner wants to merge 1 commit into
rubyatscale:mainfrom
sarastrasner:fix-directory-codeowner-invalid-team

Conversation

@sarastrasner

Copy link
Copy Markdown

Problem

DirectoryMapper::entries looks each directory owner up in the team registry and skips the entry when the name doesn't resolve:

let team = team_by_name.get(&directory_codeowner_file.owner);
if let Some(team) = team {
    entries.push(Entry { ... });
}

Nothing else reports the unresolved name, so a .codeowner naming a team that was renamed, deleted, or simply typo'd is completely silent:

  • generate emits no line for that directory
  • the directory instead inherits the nearest ancestor owner
  • validate exits 0

The file still looks authoritative, but it's inert — and the ownership it was written to express quietly belongs to whichever team owns the parent directory. Because validate stays green, these accumulate rather than getting caught on the PR that introduced them.

There's also a failure mode where the advice is actively counterproductive: an unresolvable owner drops that directory's line from the generated file, so validate reports CODEOWNERS out of date and points you at codeowners generate — which "fixes" it by erasing the line, making the real problem disappear.

Annotations (invalid_team_annotation) and package ownership (invalid_package_ownership) are already validated against the registry. Directory ownership is the one surface that isn't.

Fix

Adds invalid_directory_ownership as a third check in validate_invalid_team, reusing the existing InvalidTeam error.

Reusing InvalidTeam keeps the output shape and exit codes unchanged, which seemed worth preserving given the wrapping code_ownership gem parses these category headlines. The consequence is that a .codeowner violation is reported under Found invalid team annotations, which is now a little loose — though it already covers package.yml. Happy to give it its own category instead if you'd prefer; I avoided it only because it changes output the gem consumes.

Test

New fixture tests/fixtures/invalid-directory-codeowner: a nested .codeowner naming an unregistered team, underneath an ancestor .codeowner naming a real one. That shape matters — ownership resolves cleanly to the ancestor, so no other check fires and the generated CODEOWNERS looks entirely reasonable.

Before this change that fixture exits 0 with empty stdout and stderr. After, it reports:

Found invalid team annotations
- app/services/nested/.codeowner is referencing an invalid team - 'Web3'

Verification

  • cargo test — all pass (added 1)
  • cargo clippy --all-targets --all-features -- -D warnings — clean
  • cargo fmt --all -- --check — clean

Unrelated wrinkle, noted in passing

I first tried this as a unit test and hit something you may want to know about: the ownership! macro in src/common_test.rs binds tempdir() to a local that drops at the end of the macro block, so the temp project directory is deleted before the caller ever uses the returned Ownership. Any test that touches the filesystem afterwards — get_codeowners_file(), for instance — silently sees an empty repo and reports a spurious stale-CODEOWNERS diff.

Nothing currently depends on it, because every existing caller only reads the already-built in-memory Project. It does mean Ownership::validate() can't be unit-tested through that helper today, which is why this PR uses an integration test. Happy to open a separate issue or fix it if useful.

`DirectoryMapper::entries` looks each directory owner up in the team registry and
skips the entry when the name does not resolve. Nothing else reports the name, so
a typo'd or renamed team in a `.codeowner` is completely silent: the directory
inherits the nearest ancestor owner, `generate` emits no line for it, and
`validate` exits 0.

That makes the file inert while still looking authoritative, and the ownership it
was written to express quietly belongs to whichever team owns the parent
directory. Annotations and package ownership are already validated against the
registry; this extends the same check to directory ownership, reusing the
existing `InvalidTeam` error so output and exit codes are unchanged in shape.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sarastrasner
sarastrasner requested a review from a team as a code owner August 13, 2026 17:14
@github-project-automation github-project-automation Bot moved this to Triage in Modularity Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

1 participant