diff --git a/.github/codeowners/areas.yaml b/.github/codeowners/areas.yaml index ea6c2beedc4d..eee210b0608b 100644 --- a/.github/codeowners/areas.yaml +++ b/.github/codeowners/areas.yaml @@ -552,45 +552,18 @@ shared: owners: - ops - runtime -# CODEOWNERS is a generated artifact, so co-ownership here is about who may -# approve it, not who reviews its contents. Every area is listed because under -# "any one approves" any single team satisfies the gate: without this, adding a -# file in an uncovered location needs an areas.yaml edit (ops) AND the -# regenerated CODEOWNERS (process), two approvals to land one file. The -# artifact cannot drift from the reviewed source -- emit_codeowners.py is a -# pure function of areas.yaml, and the workflow's regenerate-and-diff step -# fails on any hand-edit -- so widening approval here costs no safety. -- glob: CODEOWNERS - owners: - - process - - agents - - backend-sglang - - backend-trtllm - - backend-vllm - - diffusion - - docs - - epp - - fault-tolerance - - frontend - - gms - - kv-memory - - multimodal - - observability - - operator - - ops - - performance - - planner - - rl - - router - - runtime - - tokenspeed - - xpu -# areas.yaml is the routing decision itself, not a generated artifact, so it -# does not get the wide list above: nothing stops a rule here from re-routing -# review authority away from a team, and the strict gate only proves coverage, -# not that the owner is the right one. Ops and process both hold repo-wide -# process authority, and under "any one approves" either can unblock a routing -# change without a third team having to weigh in on paths it does not own. +# CODEOWNERS stays on the process area alone. GitHub requests every team on +# the matching line, even when one approval satisfies the gate, so listing +# every area here pages every codeowner group for a generated-file edit. +# areas.yaml is already shared by ops and process, so one process review +# still covers the source and the artifact. +# areas.yaml is the routing decision itself, not a generated artifact. +# Nothing stops a rule here from re-routing review authority away from a +# team, and the strict gate only proves coverage, not that the owner is the +# right one. Ops and process both hold repo-wide process authority, but a +# routing change also regenerates CODEOWNERS, which process owns alone, so a +# process approval is required. No third team has to weigh in on paths it +# does not own. - glob: .github/codeowners/areas.yaml owners: - ops diff --git a/.github/codeowners/test_codeowners.py b/.github/codeowners/test_codeowners.py index f7c2ad5ca827..69afbdcd8c37 100644 --- a/.github/codeowners/test_codeowners.py +++ b/.github/codeowners/test_codeowners.py @@ -1134,6 +1134,16 @@ def test_script_in_policy_dir_is_policy(self) -> None: def test_codeowners_output_is_policy(self) -> None: assert is_policy_change(["CODEOWNERS"], self._AREAS, ".") is True + def test_codeowners_file_requests_only_process(self) -> None: + # GitHub requests every owner on the last matching line. A wide list + # on CODEOWNERS pages every codeowner group when the generated file + # changes, which is what happened on pull request 15170. + root = Path(__file__).resolve().parents[2] + rules = parse_codeowners((root / "CODEOWNERS").read_text()) + assert resolve_owners(rules, "CODEOWNERS") == [ + "@ai-dynamo/dynamo-process-codeowners" + ] + def test_unrelated_change_is_not_policy(self) -> None: assert ( is_policy_change(["src/foo.py", "owned/b.txt"], self._AREAS, ".") is False diff --git a/CODEOWNERS b/CODEOWNERS index c92d47e0dda8..17c4a35ca347 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -575,7 +575,6 @@ requirements.txt /.codex/ @ai-dynamo/dynamo-process-codeowners @ai-dynamo/dynamo-agents-codeowners /agents/ @ai-dynamo/dynamo-process-codeowners @ai-dynamo/dynamo-agents-codeowners /.agents/ @ai-dynamo/dynamo-process-codeowners @ai-dynamo/dynamo-agents-codeowners -/CODEOWNERS @ai-dynamo/dynamo-process-codeowners @ai-dynamo/dynamo-agents-codeowners @ai-dynamo/dynamo-backend-sglang-codeowners @ai-dynamo/dynamo-backend-trtllm-codeowners @ai-dynamo/dynamo-backend-vllm-codeowners @ai-dynamo/dynamo-diffusion-codeowners @ai-dynamo/dynamo-docs-codeowners @ai-dynamo/dynamo-epp-codeowners @ai-dynamo/dynamo-fault-tolerance-codeowners @ai-dynamo/dynamo-frontend-codeowners @ai-dynamo/dynamo-gms-codeowners @ai-dynamo/dynamo-kv-memory-codeowners @ai-dynamo/dynamo-multimodal-codeowners @ai-dynamo/dynamo-observability-codeowners @ai-dynamo/dynamo-operator-codeowners @ai-dynamo/dynamo-ops-codeowners @ai-dynamo/dynamo-performance-codeowners @ai-dynamo/dynamo-planner-codeowners @ai-dynamo/dynamo-rl-codeowners @ai-dynamo/dynamo-router-codeowners @ai-dynamo/dynamo-runtime-codeowners @ai-dynamo/dynamo-tokenspeed-codeowners @ai-dynamo/dynamo-xpu-codeowners /agent-docs/ @ai-dynamo/dynamo-process-codeowners @ai-dynamo/dynamo-agents-codeowners /.lycheeignore @ai-dynamo/dynamo-ops-codeowners @ai-dynamo/dynamo-docs-codeowners /tests/deploy/ @ai-dynamo/dynamo-ops-codeowners @ai-dynamo/dynamo-operator-codeowners