Skip to content

agent: add deterministic Service endpoint diagnosis - #212

Closed
hellices wants to merge 12 commits into
mainfrom
agents/deterministic-service-diagnosis-191
Closed

agent: add deterministic Service endpoint diagnosis#212
hellices wants to merge 12 commits into
mainfrom
agents/deterministic-service-diagnosis-191

Conversation

@hellices

@hellices hellices commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • add a pure, versioned Service-to-EndpointSlice analyzer with structured findings, evidence, and explicit evidence gaps
  • project native EndpointSlice readiness safely even when Kubernetes LIST items omit TypeMeta, while protecting same-kind CRDs by authoritative API group
  • expose diagnose_service as one shared read-only structured-YAML tool for the full agent, small agent, and MCP, with follow mode opening the Service describe view
  • make request-ceiling regression tests derive their budget from the registry tool schema

Why

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), tach
  • task-by-task spec and quality reviews
  • final whole-branch review: READY

Scope

This implements the Service/EndpointSlice phase of #191. PVC, PDB, node/scheduling, and missing-reference analyzer families remain follow-up work.

hellices and others added 9 commits August 7, 2026 01:39
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>
Copilot AI balanced review requested due to automatic review settings August 6, 2026 18:40

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Comment thread src/korvid/core/service_analysis.py Outdated
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>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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-name after 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 request kubernetes.io/service-name=<service> here.
            summaries = await self._kube.list_objects(slice_meta, namespace)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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_json as other exceptions. Those currently turn this structured diagnostic into an ERROR: result instead of the promised outcome: incomplete evidence 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>
@hellices hellices closed this Aug 6, 2026
@hellices hellices reopened this Aug 6, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants