Skip to content

chore: fix CODEOWNERS again, and make the next break loud - #22

Merged
usik-luke-trl merged 1 commit into
mainfrom
chore/codeowners-validate
Oct 13, 2026
Merged

usik-luke-trl merged 1 commit into
mainfrom
chore/codeowners-validate

Conversation

@usik-luke-trl

Copy link
Copy Markdown
Contributor

hongjoonahn-tri → hongjoonahn-trl. Second owner rename in a week — usik-luke → usik-luke-trl was #19 six days ago.

Both left every rule in the file one owner short while continuing to look correct. CODEOWNERS ignores unknown handles with no error, no warning, no annotation, and GitHub does not redirect a renamed handle:

$ gh api users/hongjoonahn-tri
{"message":"Not Found","status":"404"}

Both were found by accident. Rather than rely on noticing a third time, tools/check_codeowners.py resolves every handle and fails CI when one 404s — wired into community-check.yml, which already runs on every push and PR.

Treats network trouble and non-404 errors as unverified rather than dead, so a flaky API can't fail the build. Skips @org/team handles, since a default token can't read org membership.

Verified both directions

# before
DEAD  @hongjoonahn-tri  (404 -- renamed or deleted)
exit=1

# after
OK  @guks-trl  @hongjoonahn-trl  @usik-luke-trl
exit=0

🤖 Generated with Claude Code

https://claude.ai/code/session_01K599fUWZZYdiL4ekUq4Vhs

@guks-trl guks-trl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rename itself is right and should go in: @hongjoonahn-tri returns 404, @hongjoonahn-trl exists, and all three owners have admin access. GitHub's own CODEOWNERS check agrees. GET /repos/trillion-labs/trida-stack/codeowners/errors lists 12 "Unknown owner" errors on main and none on this branch.

I'd change the guard before merging, though. GitHub already exposes exactly this check through that endpoint, and it covers more than the script does:

  • Owners without write access. CODEOWNERS also silently ignores an owner who exists but can't write to the repo. The script can't see that, because /users/{handle} returns 200 for them. The errors endpoint reports both cases.
  • GitHub's own parser. The endpoint reads the file the way GitHub does. The script relies on a hand-written regex, which matters for email owners and trailing comments (see inline).
  • About 90 lines less code:
      - name: CODEOWNERS has no errors
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          errs=$(gh api "repos/${{ github.repository }}/codeowners/errors?ref=${{ github.sha }}")
          echo "$errs" | jq -r '.errors[].message'
          test "$(echo "$errs" | jq '.errors | length')" -eq 0

The step then needs no setup-python.

This also needs a schedule. A rename doesn't create a commit, so a check that only runs on push and PR won't fire when the third rename happens; it'll fire on whichever unrelated PR comes next. A daily cron (stale-unanswered.yml and supply-chain.yml already have one) would catch it the day it happens.

The description's claim of "no error, no warning, no annotation" also isn't accurate. GitHub shows the error banner when you view CODEOWNERS on the web, and the endpoint above reports it. Worth fixing in the docstring and the PR text, since that's what points to the simpler fix.

@@ -29,6 +29,16 @@ jobs:

# A code of conduct whose report address does not work is not operational evidence.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two comment lines belong to the "Reporting routes are still stated" step below. The insertion split them off, so they now sit above setup-python. Move them back down.

python-version: "3.10"
# CODEOWNERS ignores unknown handles silently. Two owners renamed in one week and
# both breakages were found by accident; this makes the third one loud.
- name: CODEOWNERS handles resolve

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest replacing this step with GitHub's codeowners/errors endpoint (snippet in the review summary). It also catches owners who exist but lack write access, which /users/{handle} can't see. Separately, consider adding schedule: to this workflow's on:. A rename isn't a commit, so push/PR triggers won't notice it.

Comment thread tools/check_codeowners.py
#!/usr/bin/env python3
"""Verify every handle in CODEOWNERS still resolves.

CODEOWNERS ignores unknown handles silently -- no error, no warning, no annotation.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite: GitHub shows a CODEOWNERS error banner in the web file view, and GET /repos/{owner}/{repo}/codeowners/errors returns the errors as JSON. Today on main it lists 12 "Unknown owner" errors for @hongjoonahn-tri.

Comment thread tools/check_codeowners.py

ROOT = pathlib.Path(__file__).resolve().parent.parent
CANDIDATES = [".github/CODEOWNERS", "CODEOWNERS", "docs/CODEOWNERS"]
HANDLE = re.compile(r"@([A-Za-z0-9][A-Za-z0-9-]*(?:/[A-Za-z0-9._-]+)?)")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CODEOWNERS also accepts email owners (dev@example.com). This regex would pull out @example and query a user that has nothing to do with the rule, which can fail CI or pass by accident. Trailing # comments on a rule line get scanned too. Moot if you switch to the errors endpoint.

Comment thread tools/check_codeowners.py
except urllib.error.HTTPError as e:
if e.code == 404:
return False, "404 -- renamed or deleted"
return True, f"unverified (HTTP {e.code})"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A 401 or 403 (expired token, rate limit) also lands here and counts as a pass, so a broken token makes the check pass silently. I'd fail on 401/403 and keep the lenient path for network errors only.

Comment thread tools/check_codeowners.py
def main() -> int:
path = next((ROOT / c for c in CANDIDATES if (ROOT / c).is_file()), None)
if path is None:
print("no CODEOWNERS file; nothing to check")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the ruleset is going to require code-owner review, a missing CODEOWNERS file is itself the failure. I'd return 1 here.

hongjoonahn-tri became hongjoonahn-trl. That is the second owner rename in a week --
usik-luke -> usik-luke-trl was #19 six days ago -- and both left every rule in the
file one owner short while continuing to look correct, because CODEOWNERS ignores
unknown handles with no error, no warning and no annotation.

Both were found by accident. Rather than rely on noticing a third time,
tools/check_codeowners.py resolves every handle against the API and fails CI when one
404s. Wired into community-check.yml, which already runs on every push and PR.

The checker treats network trouble and non-404 errors as unverified rather than dead,
so a flaky API cannot fail the build, and skips @org/team handles since a default
token cannot read org membership.

Verified both directions: it failed on hongjoonahn-tri before the fix and passes on
the three current handles after.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K599fUWZZYdiL4ekUq4Vhs
@usik-luke-trl
usik-luke-trl force-pushed the chore/codeowners-validate branch from 26773df to 56b4181 Compare October 13, 2026 06:57
@usik-luke-trl
usik-luke-trl merged commit 93be9b8 into main Oct 13, 2026
6 checks passed
@usik-luke-trl
usik-luke-trl deleted the chore/codeowners-validate branch October 13, 2026 07:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants