Skip to content

fix(codeowners): repoint orphaned planner+operator glob at moved kubernetes_api.py - #12378

Merged
dmitry-tokarev-nv merged 2 commits into
ai-dynamo:mainfrom
glamr-agent:fix/orphaned-codeowners-entry--cc21fb8c3b1b
Aug 11, 2026
Merged

fix(codeowners): repoint orphaned planner+operator glob at moved kubernetes_api.py#12378
dmitry-tokarev-nv merged 2 commits into
ai-dynamo:mainfrom
glamr-agent:fix/orphaned-codeowners-entry--cc21fb8c3b1b

Conversation

@glamr-agent

@glamr-agent glamr-agent commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Overview:

The shared: entry that gives the operator team co-ownership of the planner's
Kubernetes API connector still points at
components/src/dynamo/planner/connectors/kubernetes_api.py, a path that no
longer exists. PR #10767 moved that file into connectors/clients/, and the
CODEOWNERS entry was not moved with it.

Because the generator silently drops globs that match no files, the result is a
routing gap that fails quietly: changes to the live file today route to
@ai-dynamo/dynamo-planner-codeowners alone, and the operator team is never
requested for review. This PR repoints the glob at the current path and
regenerates CODEOWNERS.

Details:

Two files, +2 / −2, in a single commit.

.github/codeowners/areas.yaml — the glob: value of the
planner + operator shared entry changes from
components/src/dynamo/planner/connectors/kubernetes_api.py to
components/src/dynamo/planner/connectors/clients/kubernetes_api.py. The
owners: list and the entry's position in the shared: block are unchanged.

CODEOWNERS — regenerated with
python .github/codeowners/build_codeowners.py, never hand-edited. Note that
the rule does not stay on the same line: the emitter sorts shared rules by
pattern length, and the longer path moves the rule about nine lines further
down. That relocation is expected output of the generator, not a stray edit.

Both files are in one commit deliberately. .github/workflows/codeowners.yml
regenerates CODEOWNERS and then runs
git diff --exit-code CODEOWNERS CONTRIBUTORS.md, so committing areas.yaml
without the regenerated artifact would fail that gate.

The entry is repointed, not deleted. Deleting it would leave
clients/kubernetes_api.py owned by the planner team alone, which is the bug
this issue reports rather than a fix for it.

Scope

Only the one orphaned entry is touched. A sweep of areas.yaml found eleven
other globs that match no files; none of them is a rename with a successor path,
so none can be repointed:

  • lib/llm/src/http/service/clear_kv_blocks.rs — the file was deleted outright
    in feat(frontend): add DYN_ENABLE_NVEXT and DYN_ENABLE_FRONTEND_ADMIN_API master switches #10556, so there is nothing left to co-own.
  • The two test_trtllm_gms_*.py shared entries are forward-looking: those files
    are not on main yet. Removing them would drop GMS co-ownership at the moment
    they land, reintroducing this same class of gap.
  • The remaining eight are single-owner path_globs, so no co-ownership can have
    been lost, and five of them are .gitignored generated ATTRIBUTIONS*.md
    artifacts that will never appear in git ls-files by design.

Cleaning those up is deletion work already in flight in #11869 and is left out
of this PR.

Coordination with #11869

Important

#11869 deletes the same entry this PR rewrites, so whichever lands second
will hit a textual conflict in areas.yaml. In either order, the correct
resolution is to keep the repointed glob. Taking the deletion instead leaves
clients/kubernetes_api.py owned by the planner team alone and reopens the
exact gap described in #12324.

Validation

  • Before/after routing, through the repository's own resolver:
    who_owns.py --codeowners CODEOWNERS on
    components/src/dynamo/planner/connectors/clients/kubernetes_api.py returned
    planner only before the change and planner + operator after it.
  • Negative controls: connectors/clients/remote_client.py stays planner-only
    (it moved in the same rename but never had a shared entry), and the adjacent
    connectors/kubernetes.py entry still resolves to both teams — confirming the
    glob was not broadened to the whole clients/ directory.
  • Drift gate: regenerated, then git diff --exit-code CODEOWNERS CONTRIBUTORS.md
    → exit 0. CONTRIBUTORS.md is byte-identical.
  • Coverage gate: build_codeowners.py --areas .github/codeowners/areas.yaml --repo . --strict → exit 0 (5025/5025 paths covered). Globs matching no files
    drop from 12 to 11, and the planner path is gone from the report.
  • Existing suite: pytest .github/codeowners/test_codeowners.py → 86 passed,
    matching the pre-change baseline.
  • Lint: pre-commit run --files .github/codeowners/areas.yaml CODEOWNERS --hook-stage manual → exit 0.

No new test is added. The only test this change could carry is a mirror of the
diff itself — asserting that the glob equals the literal value in the diff —
which would pass forever without catching a regression. The logic around the
data is already covered by the existing suite (test_multi_owner_passthrough,
test_shared_multi_owner_recorded, test_explicit_shared_entry_still_wins,
TestEmissionIsTreeIndependent). The meaningful proof is the before/after
routing observation above, which flips with this one-line change and only with
it.

One inherent limit worth stating: these checks run through codeowners_match,
the repository's model of GitHub's last-match-wins semantics and the same model
CI relies on. GitHub's own evaluation of the emitted file cannot be exercised
from a test environment.

Where should the reviewer start?

Start with the single changed line in .github/codeowners/areas.yaml — the
glob: value of the planner + operator shared entry — and confirm the
owners: list beneath it is untouched.

The CODEOWNERS diff is generated output; the only thing to check there is that
it was regenerated rather than hand-edited. The rule moving to a later line is
the tell that it was.

Related Issues

🔗 This PR is linked to an issue:


Open in Devin Review

Summary by CodeRabbit

  • Chores
    • Updated code ownership routing for the Kubernetes planner connector.
    • Preserved planner and operator ownership assignments for the relocated connector path.

…etes_api.py

PR ai-dynamo#10767 (e69e5fd) renamed
components/src/dynamo/planner/connectors/kubernetes_api.py to
components/src/dynamo/planner/connectors/clients/kubernetes_api.py, but the
`shared:` co-ownership entry in .github/codeowners/areas.yaml kept pointing at
the old path. Because `shared:` entries are additive and glob-matched, an entry
naming a path that no longer exists emits a CODEOWNERS rule GitHub can never
match, so the operator team silently stopped being auto-requested on that file.

Repoint the glob at the live path and regenerate CODEOWNERS with
emit_codeowners.py. who_owns.py now resolves the file to both
@ai-dynamo/dynamo-planner-codeowners and @ai-dynamo/dynamo-operator-codeowners.

Both files land in one commit because the codeowners workflow regenerates and
runs `git diff --exit-code CODEOWNERS CONTRIBUTORS.md`; splitting them would
red-X the intermediate commit.

Refs: ai-dynamo#12324
Signed-off-by: svc-glamr@nvidia.com <svc-glamr@nvidia.com>
@glamr-agent
glamr-agent requested review from a team as code owners July 29, 2026 18:54
@copy-pr-bot

copy-pr-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@glamr-agent
glamr-agent temporarily deployed to external_collaborator July 29, 2026 18:54 — with GitHub Actions Inactive
@glamr-agent
glamr-agent temporarily deployed to external_collaborator July 29, 2026 18:54 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot added external-contribution Pull request is from an external contributor fix labels Jul 29, 2026
@glamr-agent

Copy link
Copy Markdown
Contributor Author

Automated evidence

Validation status: complete

Evidence summary: [2/2 validated]

AI review assessment: sound — advisory only. This is an automated agent's
judgment that the change is logically sound on the recorded evidence. It is not a
merge authorization; CI and human reviewers hold that.

Validation result: complete — pass.

Evidence audit: complete [2/2 validated] — the evidence table is grounded in
recorded runs.

Evidence [2/2 validated]

Generated from validation/registry.jsonl — do not edit by hand.

Recipe Status Command Evidence Note
01-python-lint validated /opt/dynamo/venv/bin/pre-commit run --files .github/codeowners/areas.yaml CODEOWNERS --hook-stage manual validation/logs/2026-07-29T18-12-41.541Z-pre-commit-4bf5.log
03-python-unit-tests-mocker validated /opt/dynamo/venv/bin/python -m pytest .github/codeowners/test_codeowners.py -v -p no:cacheprovider --override-ini=addopts= --override-ini=filterwarnings= validation/logs/2026-07-29T18-20-23.971Z-python-5774.log

The full plan, change, validation, and review packets are attached as separate
collapsed comments below.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@glamr-agent

Copy link
Copy Markdown
Contributor Author
plan.md
# Plan — wi-20260729T172118Z-12324

Route: implementation

Template: debug-investigation

Engine: vllm

## Existing review request and resolution status

There is no existing review request to act on. `input.md` records none, and the
all-state provider survey below found no open request that implements this
repoint, so the publisher opens a **new** request on
`fix/orphaned-codeowners-entry--cc21fb8c3b1b` against `main`.

This work item is also not already resolved. No merged PR delivers the requested
behaviour on `main` — the current `main` file still carries the stale glob, which
is the authoritative check — and the one candidate, #11869, fails to qualify on
two independent grounds set out under *Related-work survey*: it is open, and it
deletes the entry rather than repointing it. Implementation proceeds normally.

---

## User intent

GitHub issue [#12324](https://github.com/ai-dynamo/dynamo/issues/12324) reports
that `.github/codeowners/areas.yaml` line 848 carries a `shared:` co-ownership
entry pairing the `planner` and `operator` areas against
`components/src/dynamo/planner/connectors/kubernetes_api.py` — a path that PR
[#10767](https://github.com/ai-dynamo/dynamo/pull/10767) (`e69e5fdf606`) renamed
to `components/src/dynamo/planner/connectors/clients/kubernetes_api.py`. Because
`shared:` is additive and glob-matched, an entry pointing at a path that no
longer exists emits a CODEOWNERS rule that GitHub will never match, so the
`operator` team silently stopped being auto-requested on that file. The caller
wants the glob repointed at the live path and `CODEOWNERS` regenerated, with both
files in a single commit because the `codeowners` CI workflow diffs the committed
artifact against a fresh regeneration. The Slack thread adds nothing beyond the
issue text ("One path edit plus regeneration — both must land together, CI gates
the drift"). The issue also asks to sweep for other `shared:` entries orphaned by
the same refactor; I performed that sweep and its scope decision is recorded
below.

## Non-goals

- **Pruning the other three dead `shared:` globs.** The sweep found three more
  (listed under *Discovery*). None of them is a rename, so none of them lost
  co-ownership the way this one did: two name files that have never existed on
  `main`, and one names a file that was deleted outright with no successor path.
  Repointing is impossible for all three; the only available action is deletion,
  which is precisely the scope of open PR #11869. Doing it here would duplicate
  that PR and enlarge a one-line routing fix into a policy cleanup.
- **Pruning the eight dead single-owner `path_globs`** found by the same sweep.
  A dead base glob costs nothing — the area still owns whatever it owns — and
  removing one cannot restore a lost co-owner. Same rationale: #11869's territory.
- **Adding a generic "no `shared:` glob may point at a nonexistent path"
  guard** to `.github/codeowners/test_codeowners.py`. Such a test would fail
  today on the three out-of-scope orphans, so landing it would force the scope
  creep the previous two bullets reject. Worth filing separately; not this
  work item.
- **Touching `emit_codeowners.py`, `build_codeowners.py`, `codeowners_match.py`,
  `who_owns.py`, or `.github/workflows/codeowners.yml`.** The generator behaves
  correctly; the input data was stale. No generator change is warranted, and the
  86-test suite covering it must stay green untouched.
- **Team membership.** This changes ownership *structure* only, as the workflow
  header states. Who is on `@ai-dynamo/dynamo-operator-codeowners` is managed
  elsewhere.
- **Renaming or moving any Python source.** The rename already happened in
  #10767; only the ownership policy is out of date.

## Discovery

All commands below were run read-only against the checkout at
`/home/sandbox/workspace/wi-20260729T172118Z-12324/repo`. Where a command had to
write (regeneration), I copied `.github/codeowners/` plus `CODEOWNERS` and
`CONTRIBUTORS.md` into `/tmp/co/` and exercised the change there; the source
checkout is untouched.

### The reported defect, confirmed

`.github/codeowners/areas.yaml` line 848 reads exactly as the issue quotes it,
inside the top-level `shared:` block (which begins at line 558):

```yaml
- glob: components/src/dynamo/planner/connectors/kubernetes_api.py
  owners:
  - planner
  - operator
```

On disk, `components/src/dynamo/planner/connectors/` contains `__init__.py`,
`base.py`, `global_planner.py`, `kubernetes.py`, `mdc.py`, `protocol.py`,
`virtual.py` and a `clients/` subdirectory; the actual file lives at
`components/src/dynamo/planner/connectors/clients/kubernetes_api.py`.
`git show --stat e69e5fdf606` confirms the pure rename the issue describes and
shows it moved a sibling too:

```
 .../connectors/{ => clients}/kubernetes_api.py | 0
 .../connectors/{ => clients}/remote_client.py  | 0
```

`remote_client.py` never had a `shared:` entry (grep for `planner/connectors` in
`areas.yaml` returns only lines 207, 844, 848), so #10767 orphaned exactly one
co-ownership declaration. `git log -S 'connectors/kubernetes_api.py' --
.github/codeowners/areas.yaml` attributes the entry to `c49abbce2` (#10715, "add
CODEOWNERS generated as Infrastructure-as-Code", merged 2026-07-12), and
`git ls-tree -r c49abbce2` confirms the file was still at the old path then — so
the entry was correct when written and was invalidated five days later by #10767
(merged 2026-07-17).

### Current routing, observed

```
$ python3 .github/codeowners/who_owns.py --codeowners CODEOWNERS \
    components/src/dynamo/planner/connectors/clients/kubernetes_api.py \
    components/src/dynamo/planner/connectors/kubernetes.py
components/src/dynamo/planner/connectors/clients/kubernetes_api.py
    review: @ai-dynamo/dynamo-planner-codeowners
components/src/dynamo/planner/connectors/kubernetes.py
    review: @ai-dynamo/dynamo-planner-codeowners @ai-dynamo/dynamo-operator-codeowners
```

The live path resolves to `planner` alone, exactly as the issue reports, while
its sibling `kubernetes.py` — whose `shared:` entry at line 844 still points at a
live path — correctly resolves to both teams. `CODEOWNERS` line 702 still carries
the rule against the dead path.

### Generator and gate, read

`.github/codeowners/emit_codeowners.py` is a pure function of `areas.yaml` +
`external_contributors.yaml`; its module docstring states the repository tree is
*never* read at emit time (this was deliberate — #11614, "decouple CODEOWNERS
emission from the live tree", merged 2026-07-15). Its `main()` writes `--out`
(CODEOWNERS), `--contributors-out` (CONTRIBUTORS.md), and an advisory file only
if advisory rules exist; `areas.yaml` declares `advisory: false` on its single
file-type rule, and `.github/codeowners/advisory-reviewers.yaml` does not exist,
so no advisory artifact is produced or removed by regeneration.

`.github/workflows/codeowners.yml` is the gate. It runs on every PR and has three
relevant steps:

1. **Unit tests** — `pytest .github/codeowners/test_codeowners.py -v
   -p no:cacheprovider --override-ini="addopts=" --override-ini="filterwarnings="`.
2. **Coverage gate** — `build_codeowners.py --areas ... --repo . --strict
   --changed-only --base <base.sha>`. Note `is_policy_change()`: a PR that
   touches `areas.yaml`, the codeowners scripts, or `CODEOWNERS` is judged
   **full-tree**, not diff-only. This PR is a policy change, so the full-tree
   strict gate applies to it.
3. **Regenerate and check for drift** — runs `emit_codeowners.py` then
   `git diff --exit-code CODEOWNERS CONTRIBUTORS.md`. This is the gate the Slack
   message refers to and the reason both files must land in one commit.

There is **no** codeowners hook in `.pre-commit-config.yaml`, so nothing
regenerates `CODEOWNERS` locally; the printer must run the emitter by hand.

`build_codeowners.py` also prints a non-blocking "globs matching no files" report
(`dead = [g for g in model.owned_patterns() if not any(match(g, p) for p in tree)]`)
with the comment *"Deletions never fail a gate ... so stale claims would otherwise
accumulate silently in areas.yaml. Surface them; never block on them."* That
report is the sweep instrument the issue's "silent by construction" remark points
at, and it is why this defect survived CI.

### Existing test coverage — verdict per the discovery-survey learning

`.github/codeowners/test_codeowners.py` holds 86 tests, all passing today
(`86 passed in 0.77s` under the CI invocation). The relevant classes are
`TestResolveOwners` (last-match-wins, `test_multi_owner_passthrough`),
`TestComputeResolution` (`test_shared_multi_owner_recorded`,
`test_explicit_shared_entry_still_wins`), `TestEmissionIsTreeIndependent`, and
`TestSplitCoverage`/`TestIsPolicyChange`. Verdict: **RUN, do not duplicate.** The
matcher, the emitter's tree-independence contract, and the shared-entry
resolution path are already covered against synthetic fixtures. What no test
covers — and what this work item changes — is a *data* value in the real
`areas.yaml`. Per `learnings/no-tautological-tests.md`, a new test asserting "the
glob is `.../clients/kubernetes_api.py` and its owners are planner+operator"
would be a config mirror: it re-states the diff and passes forever. The
defensible proof here is a recorded before/after behavioural observation through
`who_owns.py`, described under *Validation strategy*. The printer must **not**
add a mirror test to `test_codeowners.py`.

### Orphan sweep — the full result

I parsed `areas.yaml` with PyYAML and matched every glob against `git ls-files`
using the emitter's own anchoring semantics, then cross-checked against
`build_codeowners.py --strict`'s dead-glob report (which independently reports
12). Of 128 `shared:` entries, 485 area `path_globs`, and 0 `advisory:` entries:

**Dead `shared:` entries — 4:**

| Glob | Owners | Cause | In scope? |
|---|---|---|---|
| `components/src/dynamo/planner/connectors/kubernetes_api.py` | planner, operator | **Renamed** to `.../connectors/clients/kubernetes_api.py` by `e69e5fdf606` (#10767, on `main`) | **Yes — this is the fix** |
| `lib/llm/src/http/service/clear_kv_blocks.rs` | frontend, kv-memory | **Deleted** (238 lines, no rename pair) by `07d9806ac` (#10556, on `main`); `git ls-files \| grep clear_kv` returns nothing | No |
| `components/src/dynamo/trtllm/tests/test_trtllm_gms_mpi_bootstrap.py` | backend-trtllm, gms | **Never on `main`.** Introduced by `d2748fdf2`, which `git merge-base --is-ancestor` proves is *not* an ancestor of HEAD; also absent at `c49abbce2` when the entry was written | No |
| `components/src/dynamo/trtllm/tests/test_trtllm_gms_delay_commit.py` | backend-trtllm, gms | Same as above (`69ddf9375`, not an ancestor of HEAD) | No |

**Dead single-owner `path_globs` — 8:** `lib/bindings/python/tests/test_parsers.py`
(frontend), `lib/renderer/` (multimodal), `deploy/operator/internal/featuregate/`
(operator), and the five `ATTRIBUTIONS*.md` entries (process) — the last of which
are `.gitignore`d generated artifacts, so they will never appear in `git ls-files`
by design.

**Scope decision and justification.** Only the planner entry matches the failure
mode the issue names: a *rename* that left a live file silently short one
reviewer team. It is the only one of the twelve with a successor path, hence the
only one that can be *repointed* at all. The `clear_kv_blocks.rs` entry is a
tombstone — the code is gone, there is nothing to co-own, and the issue itself
already concedes this class is "correct on its own terms" as a #11869 deletion.
The two `test_trtllm_gms_*` entries are forward-looking: deleting them would be
actively wrong, because the branch that adds those files would then land without
GMS co-ownership — reintroducing the same silent gap in mirror image. The eight
base globs are single-owner, so no co-ownership can have been lost, and removing
them changes no routing decision. Fixing any of the eleven is therefore either
harmful, inert, or a duplicate of an open PR. **The sweep is reported, not
actioned**, and this plan records the eleven so the finding is not lost.

### Related-work survey

Performed with `gh` against `ai-dynamo/dynamo`; network access worked and no
query was blocked.

- **#11869** — `fix(codeowners): harden policy gate and document contributor
  workflow`, `"state":"OPEN"`, `"mergedAt":null`, `"closedAt":null`, head
  `codex/codeowners-routing-operator-ops`, base `main`. `gh pr diff 11869` shows
  its treatment of our glob is a four-line **deletion**:
  ```
  -- glob: components/src/dynamo/planner/connectors/kubernetes_api.py
  --  owners:
  --  - planner
  --  - operator
  ```
  plus the matching `CODEOWNERS` line removal. This is **not**
  `already-resolved` on two independent grounds. First, the PR is open, and the
  brief is explicit that an open PR never earns that disposition. Second, even
  if it merged tomorrow, deleting the tombstone leaves
  `.../clients/kubernetes_api.py` owned by `planner` alone — the co-ownership
  still would not apply. Deletion and repointing are different outcomes, and the
  issue's "Why this is separate from #11869" section says exactly that.
- **#10767** — `refactor(planner): Clean up planner core, abstract out peripheral
  logic`, `"state":"MERGED"`, `"mergedAt":"2026-07-17T11:15:00Z"`. Verified on
  `main` via `git show --stat e69e5fdf606`; the rename is present and not
  reverted (the file is at the new path in the working tree).
- **All-state PR searches.** `gh pr list --state all --search "codeowners in:title"`
  (30 results) and `--search "areas.yaml"` (30 results) were both reviewed.
  Merged codeowners PRs on `main` are #10715, #11603, #11614, #11660, #12101,
  and older pre-IaC ones; none touches this glob (`git log` on
  `.github/codeowners/areas.yaml` shows the five most recent commits as
  `0f68534a1`, `be642b296`, `6e24e02c3`, `cbd4b9e5f`, `d0f319f15`, and the
  current file still carries the stale line — the authoritative check). Open
  codeowners PRs are #11869 and **#12361** (`co-own the docs publish workflow and
  link-checker config`, `areas.yaml` +8/−0 and `CODEOWNERS` +2/−0) — additive,
  in a different region of the file, no conflict.
- **Semantic searches.** `--search "12324"` returns no PR. `--search
  "kubernetes_api"` returns only #9683 (`feat(planner): power infrastructure`,
  OPEN), which touches planner Kubernetes code, not codeowners policy.
- **Issue state.** `gh issue view 12324` → `"state":"OPEN"`, no labels, no
  cross-referencing PR.

**Survey conclusion:** no merged PR delivers the repoint on `main`, and the
current `main` file content confirms it directly. Proceed with implementation.

### Dry run of the proposed change (in `/tmp/co`, source checkout untouched)

Baseline first: regenerating from the **unmodified** `areas.yaml` reproduces the
committed `CODEOWNERS` and `CONTRIBUTORS.md` byte-for-byte (`diff -u` empty,
`rules: 615 (base 485 | shared 128 | file-type 1)`), so the tree is drift-free
today and any diff after the edit is attributable to the edit alone.

After replacing the single glob line and rerunning the emitter, the emitted
`CODEOWNERS` diff is exactly one rule moved:

```diff
-/components/src/dynamo/planner/connectors/kubernetes_api.py                  @ai-dynamo/dynamo-planner-codeowners @ai-dynamo/dynamo-operator-codeowners
+/components/src/dynamo/planner/connectors/clients/kubernetes_api.py          @ai-dynamo/dynamo-planner-codeowners @ai-dynamo/dynamo-operator-codeowners
```

with the removal at line 702 and the insertion ~9 lines later. That relocation is
expected, not a bug: the emitter orders shared rules by pattern length, and the
pattern grows from 59 to 66 characters. The printer should not "fix" it.
`CONTRIBUTORS.md` is unchanged (`diff -q` clean) and no advisory file appears.
`who_owns.py` against the regenerated file then returns both teams. Full-tree
`build_codeowners.py --strict` exits 0 and the dead-glob count falls from 12 to
11. These commands are what the validator will record; the numbers above are the
planner's read-only rehearsal, not the evidence of record.

## Symptom and hypothesis table

**Reproducible symptom.** With the repository at `main`,
`python3 .github/codeowners/who_owns.py --codeowners CODEOWNERS
components/src/dynamo/planner/connectors/clients/kubernetes_api.py` prints only
`@ai-dynamo/dynamo-planner-codeowners`. Expected: that team **and**
`@ai-dynamo/dynamo-operator-codeowners`.

| # | Hypothesis | Distinguishing observation | Outcome |
|---|---|---|---|
| H1 | The `shared:` glob names a path that no longer exists, so the emitted rule can never match the live file | Compare the `areas.yaml` glob against `git ls-files`; compare with sibling `kubernetes.py`, whose glob is live | **Confirmed.** Glob absent from the tree; sibling with a live glob resolves to both teams |
| H2 | The emitter filters or mangles `shared:` rules whose path is missing from the tree | Inspect the committed `CODEOWNERS` for a rule against the dead path | **Refuted.** `CODEOWNERS:702` carries the dead path with both owners, and the emitter docstring/#11614 make emission explicitly tree-independent. The generator is faithful; its input is stale |
| H3 | `who_owns.py` / `codeowners_match` resolves multi-owner rules incorrectly | Resolve a *live* multi-owner path | **Refuted.** `connectors/kubernetes.py` resolves to both teams; `TestResolveOwners::test_multi_owner_passthrough` covers it |
| H4 | A CI gate should already have caught this | Read `build_codeowners.py` coverage logic | **Refuted, and explains the silence.** The file is explicitly owned by `planner`, so it is neither uncovered nor catch-all-only; dead globs are reported but "never blocking" by design |

The smallest observation that separates H1 from H2/H3 is a single `who_owns.py`
invocation over the pair `{clients/kubernetes_api.py, kubernetes.py}` — one dead
glob, one live, same owner pair, same resolver. That pair is the backbone of the
validation below.

## Chosen approach

Two files, one commit.

1. **`.github/codeowners/areas.yaml`** — edit line 848 in place, changing only
   the glob value:
   ```yaml
   - glob: components/src/dynamo/planner/connectors/clients/kubernetes_api.py
     owners:
     - planner
     - operator
   ```
   Leave the `owners:` list, the entry's position in the `shared:` block, and
   every other line untouched. Do not resort the block: the in-place edit is what
   I rehearsed, and it regenerates cleanly.

2. **`CODEOWNERS`** — regenerate, never hand-edit (its own header says so), with
   the same command the CI drift step uses, run from the repo root:
   ```bash
   python3 .github/codeowners/emit_codeowners.py \
     --areas .github/codeowners/areas.yaml \
     --out CODEOWNERS \
     --external .github/codeowners/external_contributors.yaml \
     --contributors-out CONTRIBUTORS.md
   ```
   Expect exactly one rule to move as shown above, `CONTRIBUTORS.md` to come back
   unchanged, and no advisory file to appear. If `CONTRIBUTORS.md` does change,
   stop — something other than this edit is in the working tree.

Both files go in one `git commit -s`; the drift gate compares committed artifact
against fresh regeneration, so splitting them red-Xes the intermediate commit.
The conventional-commit title should be a `fix(codeowners):` subject naming the
repoint, and the PR body must carry `Summary` and `Validation` sections plus the
issue URL, per `AGENTS.md`.

No new test file. As argued under *Discovery*, the only test this change could
carry is a config mirror, which `learnings/no-tautological-tests.md` forbids and
the reviewer is instructed to reject. `change.md` must state that explicitly and
point at the recorded before/after evidence instead.

Expected diff size: one line in `areas.yaml`, two lines in `CODEOWNERS` (a
deletion and an insertion at a different offset). Anything larger is a signal
that the emitter picked up unrelated working-tree state.

One coordination note for the PR body: #11869 deletes the same four lines this
change rewrites. Whichever lands second will hit a textual conflict in
`areas.yaml`. The correct resolution in either order is **the repointed glob**,
since deleting it re-opens the exact gap this issue reports. Say so in the PR
description so the #11869 author does not resolve the conflict by taking their
deletion.

## Rejected alternatives

- **Delete the entry (match #11869).** Removes the tombstone but leaves the live
  file owned by `planner` alone. That is the bug, not the fix.
- **Add a second entry for the new path and keep the old one.** The old glob is
  dead weight that `build_codeowners.py` will keep reporting, and it invites the
  next reader to assume both paths are live. Repointing is strictly better.
- **Broaden the glob to `components/src/dynamo/planner/connectors/clients/` (the
  whole directory).** This would silently give `operator` co-ownership of
  `remote_client.py` too — a routing change nobody asked for, on a file that
  #10767 moved alongside but that never had a shared entry. Rejected as scope
  creep disguised as robustness. The plan's negative control asserts
  `remote_client.py` stays `planner`-only precisely to catch this if the printer
  drifts toward it.
- **Fix all twelve dead globs in one PR.** Rejected per the sweep justification:
  eleven of them are inert, harmful to delete, or already handled by an open PR,
  and bundling them would collide with #11869 across dozens of lines instead of
  four.
- **Add a CI gate that fails on any dead `shared:` glob.** Attractive — it would
  make this class of failure loud instead of silent — but it fails today on three
  pre-existing orphans, so it cannot land without the cleanup this plan declares
  a non-goal. It also contradicts a deliberate design decision documented in
  `build_codeowners.py` ("never blocking"), which deserves its own discussion
  rather than a drive-by change. Better as a follow-up issue.
- **Hand-edit `CODEOWNERS` alongside `areas.yaml`.** Would probably produce the
  right two lines but places the rule at the wrong sort position, failing the
  drift gate. Regeneration is not optional.

## Validation strategy

The claim to defend is narrow and fully deterministic: *after the change,
`components/src/dynamo/planner/connectors/clients/kubernetes_api.py` routes to
both the planner and operator teams, the generated artifact matches its source,
and nothing else in the ownership map moved.* No GPU, no engine, no Kubernetes,
no Docker is involved — this is metadata. Nominating `07-agg-smoke`,
`08-disagg-pair-smoke`, `09-gpu-pytest`, `11-go-operator-tests`, or
`10-perf-benchmark` would burn the A100 to prove nothing about a YAML glob, and
every nominated recipe must finish green or be dispositioned, so padding the
ladder only creates ways to fail. `05-code-inspection` is also rejected: its
Sections 1 and 4 require an existing PR number and its CI status, which does not
exist at validation time (the publisher opens the request afterwards).

Recipe `00-dynamo-editable-install` is **not** nominated. The compute environment
notes it is needed when a selected recipe imports `dynamo.*`;
`.github/codeowners/test_codeowners.py` imports only `codeowners_match`,
`build_codeowners`, `yaml`, and stdlib, and I confirmed it runs green under the
CI invocation without it. If pytest collection unexpectedly reaches `dynamo.*`,
the validator should run the idempotent Recipe 0 confirmation and record it — an
extra recorded recipe is harmless; a nominated one that never runs is not.

### `01-python-lint`

The touched surface is one YAML policy file plus one generated text artifact. The
recipe's "when to use" enumerates docs, markdown, and dotfile-style paths; neither
`areas.yaml` nor `CODEOWNERS` is literally in that list, but both are
non-executable repository metadata with no behavioural change, which is the
condition the list is standing in for. The escalation clause ("if any runtime code
is touched, this is a pre-screen only") does not fire, because no runtime code is
touched.

- **Section 1 (syntax):** N/A — no Python files touched (files:
  `.github/codeowners/areas.yaml`, `CODEOWNERS`). Record the N/A justification in
  the recipe's prescribed format rather than skipping the section.
- **Section 2 (pre-commit):** `pre-commit run --files
  .github/codeowners/areas.yaml CODEOWNERS --hook-stage manual` from the repo
  root. `pre-commit` is present at `/opt/dynamo/venv/bin/pre-commit`. Neither
  path is covered by the config's `exclude` regex, so `check-yaml`,
  `end-of-file-fixer`, `mixed-line-ending`, `trailing-whitespace`, and
  `codespell` all apply. `check-yaml` is the load-bearing one: it proves the
  edited `areas.yaml` still parses, which is a precondition for every downstream
  claim.

Pass condition: every hook `Passed`, exit 0.

### `03-python-unit-tests-mocker`

This recipe hosts the behavioural proof. Name the mismatch up front so the
validator does not have to guess: the recipe's "when to use" says *Python logic
changes*, and this change contains no Python logic at all. It is nominated
anyway, deliberately, because it is the only recipe in the catalogue carrying the
before/after proof protocol this defect needs — its Section 2 explicitly sanctions
recording a deliberately-failing before-proof and a passing after-run **under the
same recipe id**, with the most recent run resolving the recipe, and its Section 3
demands a non-tautological coverage proof, which is exactly the standard this
change must meet. The alternative (nominating nothing and asserting the fix works)
is worse. The subject under test is `who_owns.py` and `emit_codeowners.py` —
Python programs whose observable output changes with the edit — rather than a new
pytest case. Record these runs in order, all under
`03-python-unit-tests-mocker`:

1. **Before-proof (expected to FAIL, intentionally).** With the fix reverted or
   stashed and `CODEOWNERS` back at its committed state, assert that the operator
   team is *absent* from the live path's owners — e.g. a command that greps
   `who_owns.py --codeowners CODEOWNERS
   components/src/dynamo/planner/connectors/clients/kubernetes_api.py` for
   `dynamo-operator-codeowners` and exits non-zero when it is missing. A non-zero
   exit here is the demonstration that the symptom is real and that the after-run
   is not asserting a foregone conclusion.
2. **After: routing restored (expected to PASS).** With the fix applied and
   `CODEOWNERS` regenerated, the same assertion over the same path must find
   both `@ai-dynamo/dynamo-planner-codeowners` and
   `@ai-dynamo/dynamo-operator-codeowners`. This is the claim of the work item,
   observed through the same resolver GitHub's behaviour is modelled on.
3. **Negative controls (expected to PASS).** In the same recorded run or an
   adjacent one under the same id:
   `components/src/dynamo/planner/connectors/clients/remote_client.py` must still
   resolve to `planner` **only** — this is what catches an over-broad directory
   glob — and `components/src/dynamo/planner/connectors/kubernetes.py` must still
   resolve to both teams, proving the neighbouring entry was not disturbed.
4. **Drift gate, replicated exactly (expected to PASS).** Re-run
   `emit_codeowners.py` with the CI arguments, then `git diff --exit-code
   CODEOWNERS CONTRIBUTORS.md`. Exit 0 proves the committed artifact matches its
   source — the specific CI check the Slack thread flags as the reason both files
   must land together — and simultaneously proves `CONTRIBUTORS.md` was not
   collaterally modified.
5. **Coverage gate, full-tree (expected to PASS).** `build_codeowners.py --areas
   .github/codeowners/areas.yaml --repo . --strict`, exit 0. This is the mode CI
   will actually use for this PR, because `is_policy_change()` promotes any
   `areas.yaml` edit to whole-tree judgement. Capture its dead-glob report: it
   must list **11** globs, down from 12, and the planner path must be gone from
   it. That delta is the machine-checkable statement that the sweep's in-scope
   item was fixed and its out-of-scope items were deliberately left alone.
6. **Existing suite, unregressed (expected to PASS, and must be the last run
   under this id).** `/opt/dynamo/venv/bin/python -m pytest
   .github/codeowners/test_codeowners.py -v -p no:cacheprovider
   --override-ini="addopts=" --override-ini="filterwarnings="` — the exact CI
   invocation. Baseline is 86 passed. This is the "do not duplicate existing
   coverage" half of the discovery survey: the suite already proves the resolver
   and the emitter's tree-independence, so the change must leave it untouched and
   green rather than grow it.

- **Section 1 (syntax):** N/A — no Python files changed.
- **Section 3 (behavioural coverage proof):** cite the pairing of step 1 against
  step 2 over `who_owns.py` → `codeowners_match.resolve_owners`, driven by
  `.github/codeowners/areas.yaml:848`. The observation flips with the production
  change and only with it; reverting the glob restores the failure. That is
  non-tautological by the standard in `learnings/no-tautological-tests.md`, and
  `change.md` must say why no new pytest case was added instead.

Ordering matters: the intentionally-failing run must come first so that the last
recorded run of `03-python-unit-tests-mocker` is green and the recipe resolves to
`pass`, with the before-proof preserved in the evidence trail for a human reader.

Everything above runs on CPU in the sandbox in seconds. If any step is blocked
(e.g. `pre-commit` cannot fetch hook environments because of egress policy), the
validator records the exact command and the disposition rather than a fabricated
pass.

```validation-recipes
01-python-lint
03-python-unit-tests-mocker
```

## Required deliverables

Per the `debug-investigation` template, the agency produces:

- `plan.md` — this file.
- `change.md` — narrative of the repoint and the regeneration, the statement that
  the two files must be one commit, the explicit justification for adding no new
  test, and the sweep's eleven out-of-scope orphans recorded as a follow-up note.
- `change.diff` — a non-empty diff: one line in `.github/codeowners/areas.yaml`,
  two lines in `CODEOWNERS`.
- `change-validation.md` — recorded runs for `01-python-lint` and
  `03-python-unit-tests-mocker`, with the `## Investigation outcome:` section
  separate from the final verdict, ending in exactly `## Verdict: pass`, `fail`,
  or `blocked`.
- `review.md` — ending in exactly `## Assessment: sound` or `needs_changes`. The
  reviewer should specifically check that the fix repoints rather than deletes,
  that `CODEOWNERS` was regenerated rather than hand-edited (the moved sort
  position is the tell), that no directory-wide glob widened ownership onto
  `remote_client.py`, and that no tautological mirror test was added.

The publisher opens the request on
`fix/orphaned-codeowners-entry--cc21fb8c3b1b` against `main`, with the issue URL
in the body, `Summary` and `Validation` sections, DCO sign-off on the commit, and
the #11869 conflict note described above.

@glamr-agent

Copy link
Copy Markdown
Contributor Author
change.md
# Change — wi-20260729T172118Z-12324

Repoint the orphaned `planner`+`operator` `shared:` glob at the path PR
[#10767](https://github.com/ai-dynamo/dynamo/pull/10767) renamed, and regenerate
`CODEOWNERS`. Fixes [#12324](https://github.com/ai-dynamo/dynamo/issues/12324).

- **Branch:** `fix/orphaned-codeowners-entry--cc21fb8c3b1b` (from `main` at
  `b4d7a82e77d3c34f012551d6d57cad33a9d47bca`)
- **Commit:** `28e51fe866017cd14a3a8b8092dddac17d4a245d` — one commit, both files
- **Diff:** 2 files, +2 / −2 (`change.diff`, 33 lines, applies cleanly to `main`)
- **Pushed:** nowhere. Publication is the publisher stage's job.

MR URL: https://github.com/ai-dynamo/dynamo/pull/12378

## Files changed

### `.github/codeowners/areas.yaml` (1 line)

Line 848's `glob:` value changed from
`components/src/dynamo/planner/connectors/kubernetes_api.py` to
`components/src/dynamo/planner/connectors/clients/kubernetes_api.py`. The
`owners:` list (`planner`, `operator`), the entry's position inside the `shared:`
block, and every other line are untouched — the block was deliberately **not**
resorted, per the plan.

### `CODEOWNERS` (2 lines: 1 deletion, 1 insertion)

Regenerated, never hand-edited (its own header forbids hand-editing). The rule
disappears from line 702 and reappears about nine lines later. **That relocation
is expected and must not be "fixed":** the emitter orders shared rules by pattern
length, and the pattern grows from 59 to 66 characters. A hand-edit would have
produced the right two owner names at the wrong sort offset and failed the drift
gate — which is precisely why regeneration is not optional. The moved sort
position is the reviewer's tell that the file really was regenerated.

`CONTRIBUTORS.md` came back byte-identical, as the plan required. Had it changed
I was instructed to stop rather than commit it; it did not, so there was nothing
to escalate. No advisory artifact appeared (`areas.yaml` declares
`advisory: false` and `advisory-reviewers.yaml` does not exist).

## Why both files are in one commit

`.github/workflows/codeowners.yml` regenerates `CODEOWNERS` and then runs
`git diff --exit-code CODEOWNERS CONTRIBUTORS.md`. Committing `areas.yaml`
without the regenerated artifact would red-X the intermediate commit. Both files
are staged and committed together in `28e51fe86`.

## What I actually ran

All commands from the repo root at
`/home/sandbox/workspace/wi-20260729T172118Z-12324/repo`.

**1. Symptom reproduced before touching anything** (the before-proof). On the
unmodified tree, `who_owns.py --codeowners CODEOWNERS` over the live path
printed `review: @ai-dynamo/dynamo-planner-codeowners``operator` absent,
exactly as the issue reports. `git ls-files | grep -c
'planner/connectors/kubernetes_api.py'` returned `0`, confirming the glob names
a path that is not in the tree.

**2. Baseline drift check, before editing.** Ran the emitter on the *unmodified*
`areas.yaml`: `rules: 615 (base 485 | shared 128 | file-type 1)`, then
`git diff --exit-code CODEOWNERS CONTRIBUTORS.md` → exit 0, `git status` clean.
This matters: it proves the tree was already drift-free, so every line in the
final diff is attributable to my edit and not to pre-existing drift.

**3. Edit + regeneration.** Emitter re-run with the exact CI arguments; rule
count unchanged at 615 (a repoint moves a rule, it does not add or remove one).
Resulting `git diff --stat`: `areas.yaml | 2 +-`, `CODEOWNERS | 2 +-`. Exactly
the size the plan predicted; nothing larger, so the emitter did not pick up
unrelated working-tree state.

**4. Routing restored + negative controls** (the after-proof), post-commit:

```
clients/kubernetes_api.py  -> @ai-dynamo/dynamo-planner-codeowners @ai-dynamo/dynamo-operator-codeowners
clients/remote_client.py   -> @ai-dynamo/dynamo-planner-codeowners
connectors/kubernetes.py   -> @ai-dynamo/dynamo-planner-codeowners @ai-dynamo/dynamo-operator-codeowners
```

The first line is the claim of the work item. The second is the control against
the rejected "broaden the glob to the whole `clients/` directory" alternative —
`remote_client.py` moved in the same rename but never had a shared entry, and it
stays `planner`-only. The third proves the neighbouring line-844 entry was not
disturbed.

**5. Drift gate, replicated exactly against the committed state.** Emitter, then
`git diff --exit-code CODEOWNERS CONTRIBUTORS.md`**exit 0**, working tree
clean. This is the specific CI check the Slack thread flags, and it simultaneously
proves `CONTRIBUTORS.md` was not collaterally modified.

**6. Coverage gate, full-tree.** `build_codeowners.py --areas
.github/codeowners/areas.yaml --repo . --strict` → **exit 0**. This is the mode
CI will use, because `is_policy_change()` promotes any `areas.yaml` edit to
whole-tree judgement. Dead-glob count fell **12 → 11** and the planner path is
gone from the report.

**7. Existing suite, unregressed.** `/opt/dynamo/venv/bin/python -m pytest
.github/codeowners/test_codeowners.py -p no:cacheprovider --override-ini=...` →
**86 passed in 0.55s**, matching the documented baseline. Not grown, not touched.

**8. Lint on changed files only** (per `learnings/format-changed-only.md`):
`pre-commit run --files .github/codeowners/areas.yaml CODEOWNERS --hook-stage
manual` → **exit 0**. `codespell`, `check-yaml`, `check-case-conflict`,
`check-merge-conflict`, `mixed-line-ending`, `trailing-whitespace` and the marker
report all `Passed`; Python-only hooks skipped with "no files to check", correctly,
since no Python file was touched. `check-yaml` is the load-bearing one — it proves
the edited YAML still parses.

No `cargo build` or broader `pytest` run: this change touches one YAML policy file
and one generated text artifact, no Rust and no Python runtime code.

## Friction, honestly

**The dead-glob report appears to under-count, and it took a detour to confirm it
does not.** `build_codeowners.py --strict` printed the header `globs matching no
files: 11` but then listed only **10** paths, and `lib/llm/src/http/service/
clear_kv_blocks.rs` — one of the four dead `shared:` entries the plan enumerates —
was missing from the listing. That looked at first like a real discrepancy between
the count and the set, which would have undermined using "12 → 11" as evidence, so
I did not accept the number at face value. Reading the source resolved it:
`build_codeowners.py:146` is `for g in dead[:10]`, a hard display truncation. The
count is computed over the full list and is authoritative; the listing is capped
at ten by design. I confirmed by recomputing the set directly through
`compute_resolution` + `match` against `git ls-files`: 11 entries after the fix
including `clear_kv_blocks.rs`, and 12 before (recomputed from
`git show HEAD:.github/codeowners/areas.yaml`), with the planner path present
before and absent after. This is a cosmetic reporting wart in a
non-blocking advisory report, not a defect in this change, and it is explicitly
out of scope — I did not touch `build_codeowners.py`.

A smaller stumble: my first attempt to recompute the dead set used
`build_codeowners.load_model(...)`, which does not exist —
`AttributeError: module 'build_codeowners' has no attribute 'load_model'`. The
real path is `yaml.safe_load` into `codeowners_match.compute_resolution`. I read
`main()` and used the actual API.

One expected-but-worth-naming result: an intermediate drift check run *before*
committing returned exit 1. That is not a failure — `git diff` compares against
`HEAD`, so uncommitted edits necessarily show as drift. The meaningful run is the
post-commit one in step 5, which is exit 0.

## Why no new test was added

Deliberate, and mandated by the plan. The only test this change could carry is a
config mirror — something like "the glob at `areas.yaml:848` is
`.../clients/kubernetes_api.py` and its owners are `planner`+`operator`". That
re-states the diff verbatim and would pass forever without catching anything,
which is exactly the failure mode `learnings/no-tautological-tests.md` forbids and
the reviewer is instructed to reject as a blocking finding.

The behaviour that changed lives in *data*, not logic, and the logic around it is
already covered by the 86 existing tests: `TestResolveOwners::
test_multi_owner_passthrough` covers multi-owner resolution,
`TestComputeResolution::test_shared_multi_owner_recorded` and
`test_explicit_shared_entry_still_wins` cover the shared-entry path, and
`TestEmissionIsTreeIndependent` covers the emitter contract. Duplicating any of
those would be the "do not duplicate existing coverage" half of the same mistake.

The defensible proof is therefore a recorded **before/after behavioural
observation** through the real resolver, not a new pytest case: step 1 above
(operator absent, symptom real) paired against step 4 (both teams present),
driven through `who_owns.py``codeowners_match.resolve_owners` by the single
changed line `.github/codeowners/areas.yaml:848`. The observation flips with the
production change and only with it — revert the glob and the failure returns,
which is the non-tautological standard. The validator will record that pairing
under `03-python-unit-tests-mocker`, whose Section 2 explicitly sanctions a
deliberately-failing before-proof followed by a passing after-run under the same
recipe id, together with the two negative controls, the drift gate, the coverage
gate, and the unregressed 86-test suite.

## Follow-up note for the reviewer and the PR body

### Coordination with #11869

PR [#11869](https://github.com/ai-dynamo/dynamo/pull/11869) (open, head
`codex/codeowners-routing-operator-ops`) **deletes** the same four lines this
change rewrites. Whichever lands second will hit a textual conflict in
`areas.yaml`. **The correct resolution in either order is the repointed glob**,
because deleting the entry leaves `clients/kubernetes_api.py` owned by `planner`
alone — re-opening the exact gap #12324 reports. This must be said in the PR
description so the #11869 author does not resolve the conflict by taking their
deletion.

### Eleven other dead globs, found and deliberately left alone

The planner swept `areas.yaml` and found twelve dead globs. Only the one fixed
here is a *rename* with a successor path, hence the only one that can be
repointed at all. The other eleven are reported, not actioned — fixing them would
be either harmful, inert, or a duplicate of #11869:

**Dead `shared:` entries — 3 remaining (of 4 found):**

| Glob | Owners | Cause | Why left alone |
|---|---|---|---|
| `lib/llm/src/http/service/clear_kv_blocks.rs` | frontend, kv-memory | Deleted outright (238 lines, no rename pair) by `07d9806ac` (#10556, on `main`) | A tombstone — the code is gone, there is nothing to co-own. Deletion only, which is #11869's scope |
| `components/src/dynamo/trtllm/tests/test_trtllm_gms_mpi_bootstrap.py` | backend-trtllm, gms | Never on `main`; introduced by `d2748fdf2`, not an ancestor of HEAD | Forward-looking. Deleting would be *actively wrong* — the branch adding these files would land without GMS co-ownership, reintroducing this same silent gap in mirror image |
| `components/src/dynamo/trtllm/tests/test_trtllm_gms_delay_commit.py` | backend-trtllm, gms | Same (`69ddf9375`, not an ancestor of HEAD) | Same as above |

**Dead single-owner `path_globs` — 8:**
`lib/bindings/python/tests/test_parsers.py` (frontend), `lib/renderer/`
(multimodal), `deploy/operator/internal/featuregate/` (operator), and five
`ATTRIBUTIONS*.md` entries (process) — the last of which are `.gitignore`d
generated artifacts, so they will never appear in `git ls-files` **by design**.
All eight are single-owner, so no co-ownership can have been lost, and removing
one changes no routing decision.

I verified this list against the live report rather than copying it blind: the
recomputed post-fix dead set is exactly these eleven.

### Separately worth filing

A generic "no `shared:` glob may point at a nonexistent path" CI guard would make
this failure class loud instead of silent. It was **not** added here: it fails
today on the three out-of-scope orphans above, so it cannot land without the
cleanup this work item declares a non-goal, and it contradicts a deliberate
documented decision in `build_codeowners.py` ("Deletions never fail a gate …
Surface them; never block on them"). That deserves its own discussion, not a
drive-by change.

## Constraints honoured

- Non-goals respected: no other dead glob pruned, no new CI guard, no change to
  `emit_codeowners.py` / `build_codeowners.py` / `codeowners_match.py` /
  `who_owns.py` / `.github/workflows/codeowners.yml`, no Python source moved, no
  team-membership edit.
- Rejected alternatives avoided: did not delete the entry, did not add a second
  entry beside the old one, did not broaden the glob to the `clients/` directory
  (the `remote_client.py` control proves this), did not hand-edit `CODEOWNERS`.
- Branch is exactly `${GLAMR_CODING_BRANCH_NAME}`, cut from `main`. No other name
  invented.
- `git commit -s`; trailer email `svc-glamr@nvidia.com` matches the author email,
  exactly one `Signed-off-by:` line.
- Formatted/linted only the two changed files.
- Pushed nowhere.

@glamr-agent

Copy link
Copy Markdown
Contributor Author
change-validation.md
# Validation — wi-20260729T172118Z-12324

Repoint of the orphaned `planner`+`operator` `shared:` glob at
`.github/codeowners/areas.yaml:848`, plus the regenerated `CODEOWNERS`.
Commit `28e51fe866017cd14a3a8b8092dddac17d4a245d` on
`fix/orphaned-codeowners-entry--cc21fb8c3b1b`.

## Hardware target

**None, deliberately.** No preset, no cluster, no model. The sandbox reports one
A100-SXM4-80GB and CUDA 12.9, and none of it was used: the change is two files of
repository metadata — one YAML policy input and one generated text artifact — with
no runtime code, no engine, no Kubernetes, and no Docker on any path. Everything
below ran on CPU in-sandbox in seconds. The plan's argument for nominating no GPU
recipe holds on inspection of the diff: `git diff --name-only HEAD~1 HEAD` returns
exactly `.github/codeowners/areas.yaml` and `CODEOWNERS` (run 1).

`00-dynamo-editable-install` was not nominated and did not become necessary.
`.github/codeowners/test_codeowners.py` imports only `codeowners_match`,
`build_codeowners`, `yaml`, and stdlib; pytest collection never reached `dynamo.*`
and no `ModuleNotFoundError: No module named 'dynamo._core'` appeared, so the
Recipe 0 confirmation the plan held in reserve was not triggered.

## What the logs prove

### `01-python-lint`

**Section 1 (syntax): N/A — no Python files touched (files:
`.github/codeowners/areas.yaml`, `CODEOWNERS`).** I did not take that on the
plan's word; run 1 (`git diff --name-only HEAD~1 HEAD`, exit 0) enumerates the
commit's entire surface and it contains no `.py` path, so `python3 -m py_compile`
has no argument to take.

**Section 2 (pre-commit):** exit 0 (run 2). Neither changed path is caught by the
config's `exclude` regex, so the general-purpose hooks all applied and reported
`Passed`: `codespell`, `check for case conflicts`, `check for merge conflicts`,
**`check yaml`**, `check that scripts with shebangs are executable`,
`mixed line ending`, `trim trailing whitespace`, and the pytest-marker report. The
Python-specific hooks (`isort`, `black`, `flake8`, `ruff`) reported
`(no files to check) Skipped` — the correct outcome, and independent corroboration
of the Section 1 N/A from a second tool. `check yaml` is the load-bearing hook and
it is what makes every downstream claim admissible: the edited `areas.yaml` still
parses.

### `03-python-unit-tests-mocker`

The recipe is nominated for a change that contains no Python logic, which the plan
names up front. What it supplies is the before/after protocol this defect needs,
and Section 2 of the recipe explicitly sanctions recording a deliberately-failing
before-proof and a passing after-run under the same id. Six protocol steps, seven
recorded runs (step 5 is recorded twice — see below), in the prescribed order, all
under the one id.

**Step 1 — before-proof, exit 1, failed deliberately (run 3).** The two changed
files were reverted to their committed pre-change state (`git checkout HEAD~1 --
CODEOWNERS .github/codeowners/areas.yaml`), which the log confirms restored the
stale glob at line 848. `who_owns.py --codeowners CODEOWNERS
components/src/dynamo/planner/connectors/clients/kubernetes_api.py` then printed
`review: @ai-dynamo/dynamo-planner-codeowners` — operator absent, exactly the
symptom the issue reports. The assertion "operator must be present" therefore
failed and the script exited 1. This is the run that makes step 2 non-vacuous: the
symptom was real on the pre-change tree, so the after-run is not a foregone
conclusion. An `EXIT` trap restored the fix; the log shows `git status --porcelain`
empty afterwards, and I verified restoration independently outside the script
(clean tree, HEAD `28e51fe86`, line 848 carrying the `clients/` path) before
proceeding.

**Step 2 — after, exit 0 (run 4).** Same path, same resolver, fix in effect on a
clean tree: `review: @ai-dynamo/dynamo-planner-codeowners
@ai-dynamo/dynamo-operator-codeowners`. Both assertions passed. The single changed
line flips the observation, and nothing else in the run differs from step 1.

**Step 3 — negative controls, exit 0 (run 5).** Both control paths were first
confirmed present in the tree via `git ls-files --error-unmatch`, so neither result
is the trivial answer for a path that does not exist.
`components/src/dynamo/planner/connectors/clients/remote_client.py` resolves to
`@ai-dynamo/dynamo-planner-codeowners` **only** — operator absent. That is the
control that would have caught the rejected "widen the glob to the whole
`clients/` directory" alternative: `remote_client.py` moved in the same rename and
sits in the same directory, so a directory-wide glob would have silently given
`operator` co-ownership of it. It did not.
`components/src/dynamo/planner/connectors/kubernetes.py` still resolves to **both**
teams, proving the neighbouring `shared:` entry at line 844 was not disturbed by
the edit one entry below it.

**Step 4 — drift gate, exit 0 (run 6).** The log opens by quoting the CI step being
replicated out of `.github/workflows/codeowners.yml:81-89`, so the correspondence
is visible rather than asserted. From a clean tree, `emit_codeowners.py` ran with
the exact CI arguments (`rules: 615 (base 485 | shared 128 | file-type 1)`,
`0 external contributor(s)`), then `git diff --exit-code CODEOWNERS
CONTRIBUTORS.md` returned **exit 0**. The committed artifact is byte-identical to a
fresh regeneration from its source — the specific check that requires both files in
one commit — and `CONTRIBUTORS.md` was not collaterally modified. `git status
--porcelain` was empty after regeneration, so nothing else in the tree moved, and a
grep for an advisory artifact over untracked files found none, matching the plan.
This also settles by observation the reviewer's tell that `CODEOWNERS` was
regenerated rather than hand-edited: a hand-edit would have placed the rule at the
old sort offset and this diff would have been non-empty.

**Step 5 — coverage gate, full-tree, exit 0 (run 8; run 7 is a superseded earlier
attempt, see below).** `build_codeowners.py --areas .github/codeowners/areas.yaml
--repo . --strict` exited 0 with `explicitly owned: 5025/5025 (100.00%) |
catch-all only: 0`. This is the mode CI will actually apply, because
`is_policy_change()` promotes any `areas.yaml` edit to whole-tree judgement.

I was asked to verify the printer's display-truncation claim rather than accept it,
and it holds. The report printed `globs matching no files: 11` while listing 10
paths. Reading `build_codeowners.py` at the cited site shows `for g in dead[:10]`
directly beneath an f-string interpolating `len(dead)` — the count is computed over
the full list, the listing is capped. I then recomputed the dead set independently
through `yaml.safe_load``codeowners_match.compute_resolution``match` against
`git ls-files`, for both `HEAD` and `HEAD~1`:

- **12 dead globs before, 11 after.**
- The set difference `before − after` is exactly one element:
  `/components/src/dynamo/planner/connectors/kubernetes_api.py` — the stale glob.
- `after − before` is **empty**: no glob newly became dead.
- The repointed glob is not in the dead set, i.e. it now matches a real file.
- The independent recount is **11**, agreeing with the report's printed count, and
  the full untruncated set contains `/lib/llm/src/http/service/clear_kv_blocks.rs`
  — the entry missing from the capped listing that made the report look inconsistent.

So the 11-vs-10 gap is a cosmetic display truncation in a non-blocking advisory
report, confirmed from both the source and an independent recomputation. It is not
a defect in this change and I do not report it as one. The 12 → 11 delta is the
machine-checkable statement that the in-scope orphan was fixed and the eleven
out-of-scope ones were left exactly as they were.

**Run 7 — my own script bug, not the change's.** The first execution of step 5
exited 1 on two assertions. The cause was in my harness: `compute_resolution`
anchors globs, so `owned_patterns()` yields leading-slash paths, and I had compared
against unanchored constants. The substantive output of that same run already
showed the correct result (12 before, 11 after, only-in-BEFORE being the planner
glob with its leading slash). I corrected the comparison, added an assertion that
`owned_patterns()` really is anchored, and re-ran; run 8 is the corrected execution
and passes all seven assertions. I am recording the failed attempt rather than
hiding it, and I want to be precise about what it does and does not mean: it is
evidence about my script, and it exercised nothing about the production change that
run 8 does not exercise more strictly.

**Step 6 — existing suite, exit 0 (run 9), and the last run under this id.** The
exact CI invocation, `/opt/dynamo/venv/bin/python -m pytest
.github/codeowners/test_codeowners.py -v -p no:cacheprovider
--override-ini=addopts= --override-ini=filterwarnings=`, gave **86 passed in
0.60s** — matching the documented baseline exactly. The suite is neither grown nor
modified, which is the correct outcome here: `TestResolveOwners`,
`TestComputeResolution`, and `TestEmissionIsTreeIndependent` already cover the
resolver and the emitter contract, and duplicating them would be the mistake the
discovery survey warns against.

**Section 3 — behavioural coverage proof.** The non-tautological observation is the
pairing of step 1 against step 2:

```
Section 3 coverage:
- step 1 (run 3, exit 1) vs step 2 (run 4, exit 0): exercises
  .github/codeowners/areas.yaml:848 (the repointed `glob:` value) through
  who_owns.py -> codeowners_match.resolve_owners over the live path
  components/src/dynamo/planner/connectors/clients/kubernetes_api.py
- step 3 (run 5, exit 0): exercises the same resolver over
  clients/remote_client.py (must stay planner-only) and
  connectors/kubernetes.py (areas.yaml:844, must stay both)
```

This meets the standard in `learnings/no-tautological-tests.md` by the test the
learning itself sets: revert the production change and the observation reverts with
it — which is not an argument here but a recorded fact, because run 3 *is* the
reverted state and it failed. A mirror test asserting "the glob at line 848 equals
`.../clients/kubernetes_api.py`" would re-state the diff and pass forever; the
recorded pairing cannot, because it fails on the pre-change tree. The change adds
no new pytest case, and on this evidence that is the right call rather than a gap.

### Recipes deliberately not run

Per the plan, and I concur on inspection of the diff: `07-agg-smoke`,
`08-disagg-pair-smoke`, `09-gpu-pytest`, `10-perf-benchmark`, and
`11-go-operator-tests` would spend the A100 to prove nothing about a YAML glob, and
`05-code-inspection` requires a PR number and CI status that do not exist at
validation time (the publisher opens the request afterwards). No recipe beyond the
two nominated was added.

## Scope of what this validates, stated plainly

The recorded runs establish that the repository's own resolver — the one
`emit_codeowners.py` and `who_owns.py` share, so there is no second implementation
to drift — now routes the live file to both teams, that the generated artifact
matches its source under the exact CI command, that no neighbouring or sibling path
changed ownership, and that the 86-test suite is unregressed. What no in-sandbox
run can establish is GitHub's own behaviour on the emitted file; `codeowners_match`
is a faithful model of last-match-wins resolution and is what the repository's CI
and tooling rely on, but it is a model. That limit is inherent to the change, not a
gap in this validation, and no recipe in the catalogue closes it.

## Investigation outcome:

The reported defect is confirmed, its mechanism is confirmed, and the fix corrects
it without collateral movement.

The symptom was reproduced empirically on the pre-change tree (run 3): the live
path `components/src/dynamo/planner/connectors/clients/kubernetes_api.py` resolved
to the planner team alone. The plan's H1 — the `shared:` glob names a path the
rename left behind, so the emitted rule can never match — is what the evidence
supports. H3 (a resolver defect) is refuted directly by run 5: the same resolver,
in the same invocation, returns both teams for `connectors/kubernetes.py`, whose
glob is live. H2 (an emitter that filters dead rules) is refuted by run 6: the
emitter is a pure function of `areas.yaml`, the regeneration is byte-stable, and it
faithfully emitted a rule against a path that does not exist — the generator was
never wrong, its input was stale. H4 (a gate should have caught it) is refuted and
explained by run 8: the dead-glob report saw the orphan all along and is
non-blocking by explicit design, which is why the defect survived CI silently.

The fix repoints rather than deletes — the distinction that matters against open PR
#11869, since deleting the entry would leave the live file planner-only and re-open
the very gap the issue reports. It does not widen: `remote_client.py`, which moved
in the same rename and shares the directory, is still planner-only. `CODEOWNERS`
was regenerated, not hand-edited, and the drift gate proves it at exit 0. The
dead-glob population fell 12 → 11 with exactly the planner glob leaving and nothing
newly entering, so the eleven out-of-scope orphans the plan enumerated were
genuinely left alone rather than incidentally disturbed.

One observation for the reviewer that is not a defect: `build_codeowners.py:146`
truncates the dead-glob *listing* to ten entries while reporting the full *count*,
which makes an 11-count report look self-inconsistent. I verified from the source
and from an independent recomputation that the count is authoritative and the
listing is cosmetic. It is pre-existing, out of scope, and untouched here.

Both nominated recipes ran green. Every command went through the recorder; nine
runs are recorded, and the last run under each recipe id exits 0. The repository
checkout is byte-identical to the printer's commit — clean tree at
`28e51fe866017cd14a3a8b8092dddac17d4a245d` — verified after the final run.

## Verdict: pass

@glamr-agent

Copy link
Copy Markdown
Contributor Author
review.md
# Review — wi-20260729T172118Z-12324

> 🤖 **Automated AI review — advisory.** An AI agent's judgment of
> whether this change is logically sound on the evidence; not a merge
> authorization. CI, customs, and a human reviewer hold that.

## Assessment: sound

Repoint of the orphaned `planner`+`operator` `shared:` glob at
`.github/codeowners/areas.yaml:848`, plus the regenerated `CODEOWNERS`.
Commit `28e51fe866017cd14a3a8b8092dddac17d4a245d` on
`fix/orphaned-codeowners-entry--cc21fb8c3b1b`, clean tree, one DCO trailer.

Everything below was verified by me against the clone and the diff. Where the
prior packets asserted a fact, I re-derived it rather than accepting it — in
two places (the dead-glob delta and the routing resolution) with a matcher I
wrote myself rather than the repository's own, so the check does not inherit
the tooling under test.

---

## Findings

| # | file:line | Severity | Claim | Evidence |
|---|---|---|---|---|
| 1 | `.github/codeowners/areas.yaml:848` | none (verified) | The fix **repoints**; it does not delete | The line reads `- glob: components/src/dynamo/planner/connectors/clients/kubernetes_api.py` with `owners: [planner, operator]` intact directly beneath. `git show b4d7a82e7:.github/codeowners/areas.yaml \| sed -n 848p` carries the old path at the same line number, so the entry was edited in place, not removed and re-added. Deleting it (open PR #11869's approach) would leave the live file `planner`-only — the bug, not the fix |
| 2 | `CODEOWNERS:714` | none (verified) | `CODEOWNERS` was **regenerated**, not hand-edited | Three independent signals. (a) The rule left file line 702 and reappeared at 714 — shared-rule index 593 → 605. (b) I extracted all 128 shared rules and checked the emitter's own sort key `(len(pattern), pattern)` from `emit_codeowners.py:259` holds across the whole block: `True`, no violations. (c) Owner-column offsets across the entire file collapse to a single value, 77, equal to `max(len(pattern)) + 2` per `emit_codeowners.py:297`. A hand-edit would have to reproduce the sort position *and* the recomputed column width by accident. Run 6 (`.../step4_drift_gate...log`) closes it: `git diff --exit-code CODEOWNERS CONTRIBUTORS.md` after a real regeneration, exit 0 |
| 3 | `CODEOWNERS:176` | none (verified) | No directory-wide glob widened ownership onto `remote_client.py` | `areas.yaml` contains exactly three `planner/connectors` references (lines 207, 844, 848); the only new one is an exact-file glob. Resolving with my own last-match-wins implementation over the committed `CODEOWNERS`, `clients/remote_client.py` lands on `CODEOWNERS:176` `/components/src/dynamo/planner/connectors/` → planner alone — byte-identical to its pre-change resolution. `grep -n remote_client CODEOWNERS` returns nothing. `git show -M e69e5fdf606` confirms #10767 moved exactly two files and that `remote_client.py` never carried a shared entry, so leaving it planner-only preserves rather than changes routing |
| 4 | `.github/codeowners/test_codeowners.py` | none (verified) | No tautological mirror test was added, and the no-test decision is justified | The file is untouched by the commit (`git show --stat HEAD -- <path>` empty) and 1082 lines at both `b4d7a82e7` and `HEAD`. On whether that is an excuse: I read the suite. It builds synthetic fixtures and `tmp_path` repos throughout; `_AREAS` at line 700 is a bare string for path comparison in `TestIsPolicyChange`, never a content read. So the suite cannot express this change — the only in-suite test possible is "the glob at line 848 equals the value in the diff", which `learnings/no-tautological-tests.md` forbids by name. The behavioural alternative (a blanket "no dead `shared:` glob" gate) fails today on three pre-existing orphans and directly contradicts the documented decision at `build_codeowners.py:143-147` ("never blocking"). Deferring it is correct, not evasive. The substantive proof is instead the recorded revert-and-observe pairing, run 3 (exit 1) against run 4 (exit 0) |
| 5 | commit `28e51fe86` | none (verified) | Both files are in **one** commit | `git show --stat HEAD` lists exactly `.github/codeowners/areas.yaml` and `CODEOWNERS`, +2/−2. `.github/workflows/codeowners.yml:81-89` regenerates then runs `git diff --exit-code`, so a split commit would red-X the intermediate. `build_codeowners.py:66` `is_policy_change()` additionally promotes this PR to full-tree strict coverage; run 8 records that mode green at 5025/5025 |
| 6 | `CONTRIBUTORS.md` | none (verified) | Not collaterally modified | `git diff --stat b4d7a82e7 HEAD -- CONTRIBUTORS.md` is empty, and the drift gate in run 6 covers it explicitly |
| 7 | `change.md:29`, `plan.md:279` | **nit** | Prose mixes anchored and unanchored pattern lengths | Both packets say the pattern "grows from 59 to 66 characters". Anchored it is 59 → 67; unanchored, 58 → 66. Consequence: none — the emitter sorts on the anchored form (`emit_codeowners.py:255,259`) and the conclusion the sentence draws (longer pattern ⇒ later sort position ⇒ the rule legitimately moved) is correct either way. Cosmetic bookkeeping in narrative only; no code or artifact is affected |

No blocking finding. Item 7 is a prose slip a human need not act on.

---

## Evidence audit

`node agents/customs/evidence.ts report <root>`**cleared [2/2 validated]**. Both
rows correspond to recipes the plan named in its `validation-recipes` block
(`01-python-lint`, `03-python-unit-tests-mocker`); no recipe the plan named is
`missing`, and no recipe beyond the two was added.

I opened all nine logs in `validation/logs/` and confirmed each shows real
execution, not a toolchain-missing or "did not run" stub:

| Run | Recipe | Exit | Does the log show execution against the change? |
|---|---|---|---|
| 1 | 01 | 0 | `git diff --name-only HEAD~1 HEAD` prints exactly the two changed paths — the Section 1 `N/A` for `py_compile` is derived, not asserted |
| 2 | 01 | 0 | Real pre-commit output; `check yaml`, `codespell`, `mixed line ending`, `trim trailing whitespace` all `Passed` on the changed files; Python-only hooks `(no files to check) Skipped`, consistent with a YAML-and-text diff |
| 3 | 03 | **1** | Before-proof. Log shows `git checkout HEAD~1 --` restoring the stale glob at line 848, `who_owns.py` returning planner alone, and a deliberate `rc=1`. An `EXIT` trap restores the fix and `git status --porcelain` is empty afterwards |
| 4 | 03 | 0 | Same path, same resolver, clean tree at `28e51fe86` → both teams. This is the work item's claim |
| 5 | 03 | 0 | Negative controls, with `git ls-files --error-unmatch` first so neither answer is the trivial one for a nonexistent path: `remote_client.py` planner-only, `kubernetes.py` both |
| 6 | 03 | 0 | Drift gate. Quotes `.github/workflows/codeowners.yml:75-89` inline, regenerates (`rules: 615`), `git diff --exit-code` exit 0, tree clean, no advisory artifact |
| 7 | 03 | **1** | Validator's own harness bug — see below |
| 8 | 03 | 0 | Corrected coverage gate: strict full-tree exit 0, 5025/5025, seven assertions green |
| 9 | 03 | 0 | `86 passed in 0.60s` under the exact CI invocation — a genuine unregressed-baseline control, and the packets describe it as exactly that rather than as proof of the fix |

One nuance worth stating for the human reader: customs surfaces the *most recent*
run per recipe id, so the `03` row cites run 9 (the 86-test suite), which does not
itself exercise the changed data. The runs that do — 3 through 8 — are recorded
under the same id, which is precisely what `03-python-unit-tests-mocker` Section 2
prescribes for a before-proof/after-run pair. This is the designed behaviour of the
evidence table, not a green row that failed to exercise the change, so it does not
warrant a `failing` disposition.

The verdict is `## Verdict: pass` — unqualified. No hedging language ("conditionally",
"static", "partial") appears anywhere in `change-validation.md`, and the verdict is
backed by nine executed runs rather than asserted over a build that never happened.
`## Investigation outcome:` is separated from the verdict as the template requires.

**Compute-env cross-check.** `compute-env.md` reports one A100 and no Docker daemon.
No remote compute, SSH, or Slurm appears in any recorded command — every run is local
CPU. No recipe in force required a GPU, and the validator's decision to decline
`07`/`08`/`09`/`10`/`11` is correct on the diff: a YAML glob and a generated text file
have no GPU-observable behaviour. Declining `05-code-inspection` is also right, since
its Sections 1 and 4 need a PR number that does not exist until the publisher stage.

---

## The two flagged items, my own view

**`build_codeowners.py:146` display truncation.** I read the source rather than
taking either packet's word. Lines 143-147 interpolate `len(dead)` into the header
and then iterate `for g in dead[:10]`. The count is computed over the full list; the
listing is capped at ten. A printed count of 11 beside 10 paths is therefore cosmetic.
Crucially, this does **not** undermine the "12 → 11" evidence, because that number
never came from the truncated listing: I recomputed the dead set myself with my own
matcher against `git ls-files` for both `HEAD~1` and `HEAD` and got **12 before, 11
after**, with the same eleven-member post-fix set including `clear_kv_blocks.rs` — the
entry the cap hides. Leaving the truncation untouched is right. It is a non-blocking
advisory display in a file the plan lists under non-goals, and "fix the report while
you are here" is exactly the scope creep this work item was carved to avoid.

**Run 7.** I read run 7 and run 8 side by side. Section D of run 7 — the substantive
output — already reports 12 before, 11 after, and `only-in-BEFORE` containing the
planner glob *with its leading slash*. The two `FAIL` lines are string comparisons
against unanchored constants, i.e. the harness compared `/components/...` to
`components/...`. `compute_resolution` anchors patterns (`emit_codeowners.py:255`
`anchor(s["glob"])`), so the constants were wrong, not the data. Run 8 fixes the
comparison, adds an assertion that `owned_patterns()` really is anchored, and passes
all seven. This invalidates nothing substantive: run 7 exercised nothing about the
production change that run 8 does not exercise more strictly, and both sit under the
same recipe id with run 8 last, so the recipe resolves green by design. Recording the
failed attempt rather than quietly deleting it is honest self-correction and improves
the packet.

---

## The planner's scope decision

Defensible, and I checked it rather than deferring to it. My independent recompute
returns the same eleven remaining dead globs with the same membership:

- **`clear_kv_blocks.rs`**`git ls-files | grep clear_kv` is empty and `07d9806ac`
  *is* an ancestor of `HEAD`. No successor path exists, so repointing is impossible;
  only deletion, which is #11869's declared scope. Correct to leave.
- **The two `test_trtllm_gms_*` entries**`d2748fdf2` and `69ddf9375` exist in the
  clone but `git merge-base --is-ancestor ... HEAD` returns false for both, and neither
  file is in the tree. They are forward-looking. Deleting them would drop GMS
  co-ownership the moment those files land — the same silent gap this issue reports,
  in mirror image. Leaving them is not laziness; acting on them would be the error.
- **The eight single-owner `path_globs`** — single-owner by construction, so no
  co-ownership can have been lost; and five are `ATTRIBUTIONS*.md`, which
  `git check-ignore -v` confirms are matched by `.gitignore:9-10`, i.e. generated
  artifacts that will never appear in `git ls-files` by design.

On completeness against the issue: the issue's sweep clause is scoped — "other
`shared:` entries orphaned **by the same refactor**". `git show -M e69e5fdf606` shows
#10767 renamed exactly two files, and `remote_client.py` never had a shared entry on
`main`. So the sweep *as the issue framed it* returns exactly one orphan, and it was
fixed. The planner went further, swept all 128 shared entries plus 485 area globs, and
recorded the full twelve in both `plan.md` and `change.md` so the finding survives this
PR. That over-delivers on the literal ask while keeping the diff at four lines. I do
not consider the work item incomplete.

---

## Summary

The change does the one thing the issue asks and nothing else. It repoints rather
than deletes — the distinction that separates it from #11869 and the reason deletion
would not have fixed the bug. It regenerates rather than hand-edits, verifiable from
the sort invariant and column width independently of the drift gate. It does not
widen onto `remote_client.py`. It adds no mirror test, and the no-test decision
survives scrutiny of the suite rather than resting on assertion. Both files land in
one commit, `CONTRIBUTORS.md` is untouched, and the validation is an unqualified pass
whose logs show real execution against the changed source.

The one thing no in-sandbox run can establish is GitHub's own behaviour on the emitted
file; `codeowners_match` is a faithful model of last-match-wins and is what this
repository's CI relies on, but it is a model. `change-validation.md` states that limit
plainly instead of overclaiming, which is the right posture. It is inherent to the
change, not a gap in the work.

## Assessment: sound

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e05885dc-7ab7-4613-aebe-8876a5e2a51a

📥 Commits

Reviewing files that changed from the base of the PR and between ce54943 and 28e51fe.

📒 Files selected for processing (2)
  • .github/codeowners/areas.yaml
  • CODEOWNERS

Walkthrough

Updated Codeowners mappings to point from the old planner Kubernetes connector path to clients/kubernetes_api.py, preserving planner and operator co-ownership.

Changes

Ownership routing

Layer / File(s) Summary
Repoint Kubernetes client ownership
.github/codeowners/areas.yaml, CODEOWNERS
Shared and explicit Codeowners entries now target the relocated Kubernetes API client path while retaining planner and operator ownership.

Estimated code review effort: 1 (Trivial) | ~2 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the CODEOWNERS glob is being repointed for the moved kubernetes_api.py path.
Description check ✅ Passed The PR description includes the required overview, details, reviewer start point, and linked-issue section with concrete context.
Linked Issues check ✅ Passed The changes repoint the orphaned shared glob and regenerate CODEOWNERS exactly as issue #12324 requires.
Out of Scope Changes check ✅ Passed Only the targeted areas.yaml entry and regenerated CODEOWNERS are changed, with no unrelated scope evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Comment @coderabbitai help to get the list of available commands.

@glamr-agent

glamr-agent commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

CI result: passing

Observed at commit 66f23945a331309a688e70d24435aed1d0aeb3b5, the current head.
All reported checks reached a terminal state; the watch was not truncated.

Result Count Checks
Success 14 includes lychee, codeowners, pre-commit, copyright-checks, DCO, pre-merge-status-check
Failure 0
Skipped 4 operator, snapshot, rust-tests, rust-clippy

This supersedes the earlier report of a lychee failure at
28e51fe866017cd14a3a8b8092dddac17d4a245d.

What the lychee failure actually was, and what fixed it

lychee crawls the whole repository, not the diff, so a dead link anywhere
fails it regardless of what a pull request changes. This change touches two
files — .github/codeowners/areas.yaml and CODEOWNERS — and no markdown.

The dead link was inherited from the base commit. README.md on this branch
carried https://docs.nvidia.com/dynamo/design-docs/disaggregated-serving in two
places (lines 65 and 99). Docs link check on main went red at 5b11fb6fd
and green again at ee9687ab7 (#12370), whose README.md hunk repoints exactly
those two links to
https://docs.nvidia.com/dynamo/dev/knowledge-base/concepts/system-architecture/disaggregated-serving.
This branch's base b4d7a82e7 sits inside that red window. Corroborating this
independently: the pull request's merge ref paired the branch with ce5494389,
and ce5494389's own push-event run on main also failed.

The fix was therefore to bring the branch up to date rather than to change the
diff: main was merged into the branch through GitHub's own "Update branch"
(a merge — no rebase, no force push, no rewriting of published history). The new
head has the first parent 28e51fe86, so the previous head is preserved. The
dead link count in README.md is now zero.

The change itself is unchanged

git diff between the new head and its main parent is still exactly 2 files,
+2/−2 — the areas.yaml glob repoint and the regenerated CODEOWNERS line. The
approving review's subject matter did not move.

Checks re-run locally against the merged tree, chosen because a policy edit makes
the codeowners coverage gate full-tree rather than diff-aware, and the merge
brought in many new files:

  • pytest .github/codeowners/test_codeowners.py — 86 passed
  • build_codeowners.py --strict (full tree) — exit 0, no orphaned path
  • emit_codeowners.py then git diff --exit-code CODEOWNERS CONTRIBUTORS.md — no drift
  • who_owns.py components/src/dynamo/planner/connectors/clients/kubernetes_api.py
    @ai-dynamo/dynamo-planner-codeowners @ai-dynamo/dynamo-operator-codeowners

One thing a maintainer still has to do

Validation status: incomplete — for a reason outside this change.

Three checks required by the Required PR Checks ruleset — backend-status-check,
dynamo-status-check, and deploy-status-check — have never been reported at
any head of this pull request
. Full CI runs only after a maintainer comments
/ok to test <sha>. That authorization is a human gate; nothing was re-run,
re-triggered, or otherwise steered by the automation that posted this comment.

So: no check is failing, and every check that has run is green — but "all required
checks green" cannot be claimed until a maintainer authorizes full CI on
66f23945a.

@glamr-agent
glamr-agent temporarily deployed to external_collaborator July 30, 2026 01:39 — with GitHub Actions Inactive

@dmitry-tokarev-nv dmitry-tokarev-nv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thank you for the contribution!

@dmitry-tokarev-nv
dmitry-tokarev-nv enabled auto-merge (squash) August 11, 2026 15:22
@dmitry-tokarev-nv

Copy link
Copy Markdown
Contributor

/ok to test 66f2394

@dmitry-tokarev-nv
dmitry-tokarev-nv merged commit ee9d6c7 into ai-dynamo:main Aug 11, 2026
87 checks passed
dagil-nvidia added a commit that referenced this pull request Aug 12, 2026
Reconciles two overlapping redesigns of the same resolver. main removed the
advisory mechanism in #12977; this branch still implemented it and had built
required_owners and the shared-additivity gate on top.

Advisory loses: main's rejection guards stay, and every advisory code path
here is removed -- the filetype normalizer returns blocking rows only,
_dead_patterns no longer walks model.advisory/filetype_advisory (both fields
are gone on main, so those were dangling), and the advisory-behavior tests
are replaced by a required_owners equivalent. main's three rejection tests
are kept as-is.

This branch's validation survives: _normalize_owner_rules and
_normalize_filetype_rules keep their owner-token checks (main has neither
helper), required_owners is intact, and shared_additivity_violations still
gates.

Also keeps BOTH planner connector globs. main's #12378 added
connectors/clients/kubernetes_api.py alongside the live connectors/
kubernetes.py; the conflict presented that as one edited line, and taking
one side alone silently stripped operator from a tracked file.

Verified against main over all 5468 tracked files: 443 change owners and
every transition is additive except components/src/dynamo/vllm/handlers.py,
where dropping RL is this PR's intent and is asserted by
test_generic_vllm_handler_excludes_rl. The 461 paths losing
@jasonqinzhou/@dreamtalen are main's #12939 moving planner ownership to the
team. Gate exits 0, no CODEOWNERS drift, 157 tests pass.

Signed-off-by: Dan Gil <dagil@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external-contribution Pull request is from an external contributor fix size/XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

codeowners: planner+operator shared entry orphaned by planner connectors refactor

2 participants