agent: add deterministic Service endpoint diagnosis - #212
Closed
hellices wants to merge 12 commits into
Closed
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- summary_for gains optional keyword-only group: str | None = None.
For EndpointSlice, when group is provided it takes precedence over
apiVersion, so native LIST items that omit TypeMeta dispatch correctly
to EndpointSliceSummary when group == 'discovery.k8s.io'. A provided
non-discovery group stays GenericSummary even if apiVersion claims
discovery. When group is absent the existing apiVersion-prefix fallback
is unchanged (direct callers / tests unaffected).
- KubeClient._object_summary passes meta.group so every list_objects /
watch path uses the authoritative group.
- fake_kube.py and test_executor.py's inline fake list_objects both pass
meta.group, mirroring production.
- _meta_for_kind_name: for kinds in _DIAGNOSE_BUILTIN_METAS, only
considers discovered aliases whose group equals the builtin's group.
A same-kind CRD from a different group can no longer shadow the stable
builtin (fixes false no_endpoint_slices when a CRD is named
EndpointSlice in a different API group).
- test_diagnose_service_result_remains_bounded_yaml: slices are now
current (owner_uids='svc-1'); asserts outcome==healthy, size bound
respected, and elision marker present.
- New RED->GREEN tests:
* summary_for TypeMeta-less EndpointSlice + group='discovery.k8s.io'
-> EndpointSliceSummary
* summary_for group='example.io' -> GenericSummary even with
apiVersion='discovery.k8s.io/v1'
* KubeClient.list_objects with no-apiVersion raw item -> typed summary
* _meta_for_kind_name ignores wrong-group CRD alias
* End-to-end: raw TypeMeta-less list items -> healthy diagnosis
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds deterministic Service-to-EndpointSlice diagnosis across agent and MCP surfaces.
Changes:
- Introduces versioned Service endpoint analysis with structured evidence and gaps.
- Adds EndpointSlice projection, tool registration, and follow-mode support.
- Expands regression tests and documentation.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/korvid/core/service_analysis.py |
Implements deterministic analysis. |
src/korvid/tools/executor.py |
Executes Service diagnosis. |
src/korvid/tools/registry.py |
Registers the shared tool. |
src/korvid/tools/follow.py |
Opens Service describe views. |
src/korvid/k8s/models.py |
Adds EndpointSlice summaries. |
src/korvid/k8s/client.py |
Passes authoritative API groups. |
src/korvid/evals/fake_kube.py |
Aligns evaluation projections. |
tests/core/test_service_analysis.py |
Tests analysis rules. |
tests/tools/test_executor.py |
Tests tool execution and bounds. |
tests/tools/test_registry.py |
Tests registration and format. |
tests/tools/test_follow.py |
Tests follow behavior. |
tests/tools/test_list_resources.py |
Tests EndpointSlice rendering. |
tests/k8s/test_models.py |
Tests summary projection. |
tests/k8s/test_client.py |
Tests TypeMeta-less list items. |
tests/agent/test_runtime.py |
Derives request ceilings from schemas. |
docs/agent.md |
Documents agent diagnosis. |
docs/mcp.md |
Documents MCP exposure. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
An EndpointSlice owned only by a custom CRD controller was incorrectly flagged stale because _partition_current checked owner_uids (all owner refs) against the Service UID. Changes: - Add _service_owner_uids() to models.py: extracts UIDs only from ownerReferences where kind=='Service' and apiVersion=='v1'. - Add EndpointSliceSummary.service_owner_uids: tuple[str, ...] populated by the new helper; generic owner_uids is preserved for relation logic. - Rename EndpointSliceSnapshot.owner_uids -> service_owner_uids so the snapshot field clearly carries only Service UIDs. - Update _partition_current, _confidence_for_healthy, _current_evidence and _endpoint_slice_snapshot to use service_owner_uids throughout. - Update all test helpers and call sites consistently. Tested: - Unrelated custom-controller owner + ready endpoints → healthy - Mismatching service_owner_uids → incomplete (stale-owner gap) - Mixed custom+Service refs: generic owner_uids has all UIDs; service_owner_uids has only the Service UID - Wrong apiVersion 'Service' ref excluded from service_owner_uids - Full gate: 4113 passed, 21 skipped Fixes PR #212 review finding. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/korvid/tools/executor.py:1595
- This fetches every EndpointSlice in the namespace and only filters by
kubernetes.io/service-nameafter all response objects have been transferred and summarized. In namespaces with many Services/endpoints, a diagnosis becomes O(total namespace slices) in network, memory, and CPU even though Kubernetes supports filtering this LIST by the Service-name label. Please extend the read boundary/client to accept a server-side label selector and requestkubernetes.io/service-name=<service>here.
summaries = await self._kube.list_objects(slice_meta, namespace)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/korvid/tools/executor.py:1601
- EndpointSlice LIST failures are not limited to
ApiStatusError: transport and response-decoding failures can escape_request_jsonas other exceptions. Those currently turn this structured diagnostic into anERROR:result instead of the promisedoutcome: incompleteevidence gap for unavailable slice evidence. Convert any LIST failure to a gap while continuing to let the Service GET fail normally.
except ApiStatusError as exc:
report = analyze_service_endpoints(
service,
(),
EvidenceGap("endpointslices", _api_gap_reason(exc)),
)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
diagnose_serviceas one shared read-only structured-YAML tool for the full agent, small agent, and MCP, with follow mode opening the Service describe viewWhy
Pod and Deployment diagnosis was already deterministic, but Service connectivity still depended on model inference. This first vertical slice of #191 identifies missing/no-ready endpoints without per-object GET fan-out and never reports healthy when EndpointSlice evidence is denied, unavailable, or stale.
Verification
make check: Ruff, mypy, pytest (4106 passed, 21 skipped), tachScope
This implements the Service/EndpointSlice phase of #191. PVC, PDB, node/scheduling, and missing-reference analyzer families remain follow-up work.