Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
3fc9916
Answer what manages an object from its lists and claims, naming no fa…
nadaverell Oct 26, 2026
03fd4bf
Keep inherited claims, undecided access and the chain's stop in the m…
nadaverell Oct 26, 2026
a06d577
Name the controller a manager walk couldn't follow, from its reference
nadaverell Oct 26, 2026
d36d0fc
Give the resource context the manager answer
nadaverell Oct 26, 2026
b0bb6bb
Format the resource context manager types
nadaverell Oct 26, 2026
d9a3321
Check a claim against its manager's list state, not its members
nadaverell Oct 26, 2026
ebd3d44
Merge branch 'ownership/manager-resolution' into ownership/manager-co…
nadaverell Oct 26, 2026
b9649e7
Keep inherited claims in the compact manager answer, and tell agents …
nadaverell Oct 26, 2026
282b85a
Treat a namespaced and an unqualified claim on one Application as one
nadaverell Oct 26, 2026
40d7419
Merge branch 'ownership/manager-resolution' into ownership/manager-co…
nadaverell Oct 26, 2026
37f2ffc
Use manager answers for header chips and GitOps write warnings
nadaverell Oct 26, 2026
77f16aa
Keep unconfirmed manager warnings conservative
nadaverell Oct 26, 2026
ae93f2e
End a manager walk with an answer at an owner that's gone
nadaverell Oct 26, 2026
e24af46
Merge branch 'ownership/manager-resolution' into ownership/manager-co…
nadaverell Oct 26, 2026
9989974
Treat a controller chain ended by a gone owner as read, not unread
nadaverell Oct 26, 2026
14b9316
Merge branch 'ownership/manager-context' into ownership/manager-chip-…
nadaverell Oct 26, 2026
b73e5b6
Say each manager once in the chip's tooltip, and leave out copied claims
nadaverell Oct 26, 2026
6baaf9b
Show an inherited manager on the chip, keep a target's own opt-out, c…
nadaverell Oct 26, 2026
0b905e1
Count a Helm release's annotations on a generated object as its parent's
nadaverell Oct 26, 2026
b22f584
Answer from a claim on the walk when nothing settles it; check claims…
nadaverell Oct 26, 2026
28f11fe
Merge remote-tracking branch 'origin/ownership/manager-resolution' in…
nadaverell Oct 26, 2026
e6ab18e
Let the manager answer replace the metadata-only manager warning
nadaverell Oct 26, 2026
ffa417b
Merge remote-tracking branch 'origin/ownership/manager-context' into …
nadaverell Oct 26, 2026
0053baf
Guard: promise a revert only when list and metadata agree; never trus…
nadaverell Oct 26, 2026
2fa32c0
Keep warnings for controllers the manager answer doesn't model; skip …
nadaverell Oct 26, 2026
66aaf18
Merge remote-tracking branch 'origin/ownership/manager-context' into …
nadaverell Oct 26, 2026
005f936
Say what Membership saves: it checks the list without building it
nadaverell Oct 26, 2026
be395d9
Merge remote-tracking branch 'origin/ownership/manager-resolution' in…
nadaverell Oct 26, 2026
10f8b9a
Merge remote-tracking branch 'origin/ownership/manager-context' into …
nadaverell Oct 26, 2026
0b0ede5
Guard and chip: no revert promised from a list alone; name every cand…
nadaverell Oct 26, 2026
9997beb
Link the chip's managers whenever there's a manager answer, only to o…
nadaverell Oct 26, 2026
ad13bf7
Document the manager answer as the code behaves
nadaverell Oct 26, 2026
dec213c
Merge remote-tracking branch 'origin/ownership/manager-resolution' in…
nadaverell Oct 26, 2026
772a1ba
Say what unchecked means in the MCP manager context
nadaverell Oct 26, 2026
50bfd40
Merge remote-tracking branch 'origin/ownership/manager-context' into …
nadaverell Oct 26, 2026
e07da21
Don't confirm membership from a manager read differently earlier in t…
nadaverell Oct 26, 2026
d81ce9d
Merge remote-tracking branch 'origin/ownership/manager-resolution' in…
nadaverell Oct 26, 2026
3799d95
Merge remote-tracking branch 'origin/ownership/manager-context' into …
nadaverell Oct 26, 2026
f6c1f95
Guard and chip: no recreation promised for a CronJob's Job; tolerate …
nadaverell Oct 26, 2026
52ab312
Test the unresolved and unchecked claim checks explicitly
nadaverell Oct 26, 2026
da5ce6f
Merge remote-tracking branch 'origin/ownership/manager-resolution' in…
nadaverell Oct 26, 2026
040627e
Merge remote-tracking branch 'origin/ownership/manager-context' into …
nadaverell Oct 26, 2026
6dc46e1
Keep manager search coverage off the wire
nadaverell Oct 26, 2026
285389b
Merge remote-tracking branch 'origin/ownership/manager-resolution' in…
nadaverell Oct 26, 2026
ee5d9e0
Merge remote-tracking branch 'origin/ownership/manager-context' into …
nadaverell Oct 26, 2026
b41cbbf
Drop the removed coverage field from a guard test fixture
nadaverell Oct 26, 2026
1aa7dc3
Merge remote-tracking branch 'origin/main' into ownership/manager-res…
nadaverell Oct 26, 2026
2164ccb
Merge remote-tracking branch 'origin/ownership/manager-resolution' in…
nadaverell Oct 26, 2026
ec65856
Merge remote-tracking branch 'origin/ownership/manager-context' into …
nadaverell Oct 26, 2026
d2ee5b9
Look past a copied tracking ID for the controller an object's labels …
nadaverell Oct 26, 2026
cf6c29a
Merge remote-tracking branch 'origin/ownership/manager-context' into …
nadaverell Oct 26, 2026
dc3e9e5
Guard: an unconfirmed candidate warns even when this UI can't open it
nadaverell Oct 26, 2026
cfc3def
Guard: a list-only answer keeps its field evidence behind the condition
nadaverell Oct 26, 2026
5cf4ad6
Beside a manager answer, warn about the metadata it doesn't weigh
nadaverell Oct 26, 2026
baff9df
Merge remote-tracking branch 'origin/ownership/manager-context' into …
nadaverell Oct 26, 2026
12ea6e6
Guard: a chain end this UI doesn't know keeps the conservative warning
nadaverell Oct 26, 2026
3fd542a
Chip and guard wording from the product review
nadaverell Oct 26, 2026
1bed7ae
MCP docs: tell agree from listed, and keep the adopted-Helm risk
nadaverell Oct 26, 2026
a35f54e
Merge remote-tracking branch 'origin/ownership/manager-context' into …
nadaverell Oct 26, 2026
1caa605
Guard: name the Job's own CronJob for future runs
nadaverell Oct 26, 2026
46d29fd
Merge remote-tracking branch 'origin/main' into ownership/manager-chi…
nadaverell Oct 26, 2026
522c7ca
Docs: shorten the manager answer's pages
nadaverell Oct 26, 2026
2625200
Merge remote-tracking branch 'origin/main' into ownership/manager-chi…
nadaverell Oct 26, 2026
195ad4a
Guard: warn for managers this UI can't open; don't reuse a stale owne…
nadaverell Oct 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions docs/gitops.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,8 @@ Scoping and lifecycle:

The GitOps tab isn't the only place Argo/Flux ownership matters. Surfaces across Radar know about GitOps and route into the right detail page when they should:

- **K8s resource drawer** — every resource that carries Argo's tracking-id annotation or Flux's owner labels gets a chip in the drawer header naming that manager: `Managed by <app>` when Radar finds that manager in this cluster, `Tracked by <app>` when it doesn't, as with an Application in a management cluster's Argo CD, or one the user can't read. When Radar read every manager's list and the named manager's doesn't include the resource while another's does, the chip says `Tracked by` with a warning, and its tooltip names the managers that do list it. Click → owning Application/Kustomization/HelmRelease detail page. The generic `app.kubernetes.io/instance` label is intentionally *not* used as a signal — it's stamped by virtually every Helm chart and would false-positive plain Helm installs
- **Related Resources in the resource drawer** — **Owner** and **Children** come from owner references, the links Kubernetes' garbage collector follows; a Deployment's or Rollout's old ReplicaSets, which run no Pods and aren't its current revision, sit behind *Show N older ReplicaSets*. **Managed By** names the Argo CD Applications, Flux Kustomizations and HelmReleases whose recorded member lists (`status.resources`, `status.inventory`) name the object, and **Manages** on one of those lists its members. Managed By also names other controllers that manage the object without an owner reference, such as a cert-manager Certificate for its Secret. The header chip reads the object's tracking labels; Managed By reads the managers' status, which records what the controller compared or applied (for Argo CD, desired objects not yet created and live ones awaiting pruning too). When the chip's manager is the only one listing the resource and every list was read, the drawer shows the chip alone; when every list was read and the chip's manager doesn't list it while another does, the chip says `Tracked by` with a warning and Managed By says which manager the tracking label names. A manager whose list Radar can't read (never reconciled, or an entry that doesn't parse) falls back to the topology's link: by the object's labels for a HelmRelease, from the same status list for an Application or Kustomization. A manager deploying to another cluster names no local member, whether or not its list can be read. A list cut at 500 shows its count as a lower bound (`500+`). Flux records no destination with its inventory, so after a `spec.kubeConfig` change the inventory still lists the previous destination's objects until a reconcile applies the change. When Radar can't read everything Owner, Children or Manages needs (a kind it doesn't collect, a namespace outside its scope, a kind the user can't list), the group says what it couldn't check rather than showing a short list as complete. Managed By says when a manager lists the object for a cluster Radar can't confirm is this one, or a manager kind is still loading or refused, even with no manager listed, so an empty group doesn't read as "no GitOps manager". A manager whose list can't be read, and a manager kind discovery missed, are mentioned in its hint only: either could name any resource
- **K8s resource drawer** — the header chip reads `relationships.ownership.manager` ([states](ownership.md#what-manages-an-object)): **Managed by** for `agree`, **Listed by** for `listed` and `shared`, **Tracked by** for `claimed` and `disagree` (with a warning for `disagree`), and **via <Kind>** when a controller ancestor answers. `unknown`/`none` show no chip; native Helm keeps its Helm chip. The tooltip says what names each manager; the manager's sync status shows only for `agree`. Several candidates open a menu naming each; only managers Radar found are linked, and an Application's namespace is never guessed. Older Radars without the answer keep the metadata-based chip.
- **Related Resources in the resource drawer** — **Owner** and **Children** come from owner references, the links Kubernetes' garbage collector follows; a Deployment's or Rollout's old ReplicaSets, which run no Pods and aren't its current revision, sit behind *Show N older ReplicaSets*. **Managed By** names the Argo CD Applications, Flux Kustomizations and HelmReleases whose recorded member lists (`status.resources`, `status.inventory`) name the object, and **Manages** on one of those lists its members. Managed By also names other controllers that manage the object without an owner reference, such as a cert-manager Certificate for its Secret. The header chip reads the manager answer; Managed By reads the managers' status, which records what the controller compared or applied (for Argo CD, desired objects not yet created and live ones awaiting pruning too). The drawer hides Managed By only for a direct `agree`/`listed` answer whose single recorder exactly equals the group's single entry, with no manager coverage note; when a claim and recorder disagree, the chip says `Tracked by` with a warning and its tooltip names the claim and the recorder. Older Radars without a manager answer also name the tracking label's manager in Managed By when every list was read. A manager whose list Radar can't read (never reconciled, or an entry that doesn't parse) falls back to the topology's link: by the object's labels for a HelmRelease, from the same status list for an Application or Kustomization. A manager deploying to another cluster names no local member, whether or not its list can be read. A list cut at 500 shows its count as a lower bound (`500+`). Flux records no destination with its inventory, so after a `spec.kubeConfig` change the inventory still lists the previous destination's objects until a reconcile applies the change. When Radar can't read everything Owner, Children or Manages needs (a kind it doesn't collect, a namespace outside its scope, a kind the user can't list), the group says what it couldn't check rather than showing a short list as complete. Managed By says when a manager lists the object for a cluster Radar can't confirm is this one, or a manager kind is still loading or refused, even with no manager listed, so an empty group doesn't read as "no GitOps manager". A manager whose list can't be read, and a manager kind discovery missed, are mentioned in its hint only: either could name any resource
- **Topology** — clicking an Argo / Flux CR node opens its GitOps detail page directly, not the generic drawer
- **Timeline** — lane labels for Argo / Flux CRs route to detail
- **Helm view** — releases installed by Flux's helm-controller (detected via a HelmRelease CR lookup keyed by `<storageNamespace>/<releaseName>`, since Flux's labels live on the *managed* resources, not the release Secret) carry a `Flux` badge in the list and an amber `Managed by Flux · ns/name` link in the drawer, warning that `helm upgrade` would be reverted at the next reconcile
Expand All @@ -307,11 +307,23 @@ Radar's caches strip `managedFields` and the last-applied annotation, so the bro
- a wildcard path (`containers[*].image`) is judged per entry of the live object: the source must declare every container, or a rule cover every container, before the verdict is certain. When the source declares only some, the field is `partial` and the verdict is **may revert**; a wildcard that matches nothing (no init containers) is `empty` and adds no verdict. Set image uses the wildcard only until the dialog lists the containers; then it asks about each container by name (so the evidence can't predate the list) and judges the ones you change;
- and the owner's sync policy (auto, self-heal, prune, suspended).

For an Argo CD owner it also reports `ownerTracksTarget`, whether the Application's status lists the object. Argo CD 3 leaves the namespace out of tracking ids for Applications in its own namespace, so Radar finds the Application by name; the status listing confirms it, and without it a name-only match never waives the acknowledgment.
The response also includes `manager`, the server's answer from the live target ([states](ownership.md#what-manages-an-object)); it replaces whatever the browser cached or sent as a hint, and its lead picks the policy read. `ownerTracksTarget` says whether that Application's status lists the target. The dialog asks every time it opens. If the server couldn't finish (`managerUnavailable`), or the request fails while the cached answer names a manager, the dialog asks for an acknowledgment instead of trusting the old answer.

| Answer | Field write | Delete |
|---|---|---|
| `agree` | from the field evidence and policy; can be **will revert** | **will revert** with self-heal, else **may revert** |
| `listed` | **may revert**: Radar can't confirm the manager applies the object | same |
| `claimed` | **may revert** | same |
| `disagree`, `shared`, several claims | **may revert**, naming every candidate; no one candidate's exemption applies | same |
| via an ancestor, no claim on the object | note: generated from the parent | note: its controller recreates it (a CronJob's Job isn't recreated) |
| chain Radar couldn't read | **may revert** | same |
| `unknown` from coverage gaps, `none` | nothing | nothing |

Helm annotations beside an Argo CD or Kustomization manager (an adopted install) keep **may revert** even when that manager ignores the field. Status writes, new child objects, operator-owned deletes and empty wildcard paths keep their own rules. Values from a newer Radar this UI doesn't know count as unconfirmed; older Radars without `manager` keep the previous classifier.

It never returns managedFields or last-applied content, only these facts. Bodies are capped at 16 KiB (413) and at 64 paths; unknown fields are rejected (400). Wildcards in one request expand to at most 256 live entries; a path past that carries an `error` (read as **may revert**) instead of a verdict. Kubernetes 403/404 pass through.

The classification is `evaluateGitOpsWriteGuard` in k8s-ui (`utils/gitops-write-guard.ts`), shown by `GitOpsWriteWarning`; the hook is `web/src/hooks/useGitOpsWriteGuard.ts`. Ownership comes from the resource's relationships, or its parent workload's when it carries no owner of its own (a Pod inherits its Deployment's). When the server hasn't built either yet, the parent can't be read, or the server reports a manager the browser can't resolve (the Diagnose run's `managedBy`), ownership is treated as unknown: **may revert**, with an acknowledgment. A Radar without the endpoint (no `gitopsWriteEvidence` capability) gets the same conservative warning, whose reason says it needs a newer Radar; the write itself still works. The copy never promises a change won't be reverted.
The classification is `evaluateGitOpsWriteGuard` in k8s-ui (`utils/gitops-write-guard.ts`), shown by `GitOpsWriteWarning`; the hook is `web/src/hooks/useGitOpsWriteGuard.ts`. Ownership comes from the manager answer when present, with browser-side parent lookup retained only for older Radars. When the server hasn't built either yet, the parent can't be read, or the server reports a manager the browser can't resolve (the Diagnose run's `managedBy`), ownership is treated as unknown: **may revert**, with an acknowledgment. A Radar without the endpoint (no `gitopsWriteEvidence` capability) gets the same conservative warning, whose reason says it needs a newer Radar; the write itself still works. The copy never promises a change won't be reverted.

## MCP integration

Expand Down
43 changes: 15 additions & 28 deletions docs/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -1176,34 +1176,21 @@ See [Strimzi Kafka connector evidence](integrations.md#strimzi-kafka-connectors)

### `resourceContext.manager` — which GitOps manager to look at

`get_resource` and `diagnose` add `resourceContext.manager` when Radar answered the resource's ownership. It weighs two
kinds of evidence and names no winner they don't support:

- `listedBy`: the Argo CD Applications and Flux Kustomizations/HelmReleases whose status lists name the resource;
- `claims`: the managers its own tracking labels and annotations name, each with what checking it found (`recorded`,
`omitted` when that manager's list was read and doesn't name it, `unreadable`, `remote`, `missing`, `not-recording`
for a native Helm release, `unresolved` for a claim with no namespace, `withheld` when you may not read the manager,
`unchecked` when Radar couldn't establish what its list says). A native Helm release's claim counts only without GitOps evidence, since
Flux's helm-controller writes the same annotations. That decides the state, not the risk: Helm annotations beside an
Argo CD or Kustomization manager mean an adopted Helm install whose next upgrade or rollback can still overwrite a
change.

`state` sums them up: `agree` (one manager's list names it and the resource's metadata names that manager: the
strongest answer), `listed` (one list names it but the metadata names no manager: the list records what the manager
compared, which can include an object it doesn't apply, so don't treat it as proof the manager reconciles it),
`disagree` (a list names it and its metadata names
another manager: look at both), `shared` (several lists name it), `claimed` (only metadata names a manager), `unknown`
(no evidence, and Radar couldn't look everywhere) or `none` (no evidence in what Radar reads — **not** "unmanaged":
Terraform, `kubectl apply` or a manager in another cluster leave nothing Radar sees). When the resource's own evidence
doesn't settle it, `via` names the controller ancestor whose evidence answers, as for a Pod whose Deployment an
Application lists; `listedBy` and `claims` are then that ancestor's, and `alsoClaimed` lists what the resource's own
metadata (and that of objects in between) names, each `on` its object: commonly inherited from a template, not disproven.
When nothing settles it and the resource makes no claim that isn't copied, the nearest ancestor whose metadata names a manager answers as `via`, with `claimed`. Claims of
objects the walk passed appear in `alsoClaimed` with or without `via`. `get_resource` and `diagnose` then leave out the
metadata-only warning naming an Argo CD, Flux or Helm manager, which this answer weighs; metadata the answer doesn't
weigh keeps its warning: another controller's `app.kubernetes.io/managed-by` label, or Helm named by labels without
its release annotation. A tracking ID copied from another object raises no warning anywhere. `lead` is where to look first, not a verdict. `narrowed: true` means you may read only part of
what could name a manager. `managedBy` keeps its own meaning: the topmost owner the relationship graph links.
`get_resource` and `diagnose` add `resourceContext.manager`: which Argo CD or Flux manager to look at, without picking
a winner the evidence doesn't support ([full rules](ownership.md#what-manages-an-object)).

- `listedBy`: managers whose status lists name the resource. `claims`: managers its labels and annotations name, each
with a `check` (`recorded`, `omitted`, `missing`, `withheld`, …).
- `state`: `agree` (a list and the metadata name the same manager — the strongest answer), `listed` (a list alone,
which isn't proof the manager applies it), `disagree` (look at both), `shared` (several lists), `claimed` (metadata
only), `unknown` (Radar couldn't look everywhere) or `none` (nothing Radar can see — **not** "unmanaged").
- `via`: the controller ancestor whose evidence answers (a Pod's Deployment); `listedBy`/`claims` are then its.
`alsoClaimed` lists other claims on the way, each `on` its object.
- `lead` is where to look first, not a verdict. `narrowed: true` means you may read only part of the evidence.

Helm annotations beside an Argo CD or Kustomization manager mean an adopted Helm install: its next upgrade can still
overwrite a change. With this answer present, the metadata-only manager warning is left out for the claims it weighs.
`managedBy` keeps its own meaning: the topmost owner the relationship graph links.

### Node placement context

Expand Down
Loading
Loading