Files
kandev/docs/specs/executors/system-design/kubernetes-retained-compute.md
T
2026-09-10 11:05:05 +01:00

145 lines
7.7 KiB
Markdown

---
status: current
system: executors
requirements:
- REQ-EXECUTORS-K8S-RETAINED-001
- REQ-EXECUTORS-K8S-RETAINED-002
---
# Kubernetes Retained Compute Visibility System Design
## Purpose and boundaries
The [requirements](../requirements/kubernetes-retained-compute.md) extend the
existing executor session projection. The feature does not introduce a resource
inventory or infer process activity from Kubernetes readiness.
## Requirement mapping
| Requirement | Design section |
| --- | --- |
| REQ-EXECUTORS-K8S-RETAINED-001 | Backend projection and classification |
| REQ-EXECUTORS-K8S-RETAINED-002 | Permissions and responsive presentation |
## Backend projection and classification
`internal/kubernetes/sessions.go` already retrieves the task session, checks
task access, and gets the exact recorded Pod. Extend `SessionRow` at that seam.
The HTTP sessions route and `kubernetes.sessions.list` share the same projection.
Current fields retain their meaning. New fields are additive:
| Field | Meaning |
| --- | --- |
| `session_state` | Canonical known `TaskSession.State`, otherwise `unknown` |
| `retention_state` | `active`, `retained`, `terminating`, `terminal`, `missing`, or `unknown` |
| `main_container_requests.cpu` | Optional canonical Kubernetes quantity string |
| `main_container_requests.memory` | Optional canonical Kubernetes quantity string |
The request object appears only after exact identity verification and a matching
main container. It contains only CPU and memory, never a complete resource map.
The UI labels these fields as main-container requests. It does not aggregate
them or call them usage, reserved cluster capacity, or cost.
Classification follows this precedence:
1. Invalid inventory, failed access, malformed states, a missing main container,
or identity mismatch yields `unknown`.
2. An exact recorded-Pod GET returning NotFound yields `missing`.
3. A verified Pod with a deletion timestamp yields `terminating`.
4. A verified Pod in Succeeded or Failed phase yields `terminal`.
5. A Pending or Running Pod plus session `CANCELLED`, `COMPLETED`, `FAILED`, or `IDLE` yields `retained`.
6. A Pending or Running Pod plus session `CREATED`, `STARTING`, `RUNNING`, or `WAITING_FOR_INPUT` yields `active`.
7. Any remaining combination yields `unknown`.
`CANCELLED` maps to stopped-session wording. Other retained session states keep
their distinct session labels. `WAITING_FOR_INPUT` is never labeled stopped.
The term `active` denotes the recorded session category, not verified agent connectivity.
An Office `IDLE` session can still retain Kubernetes resources under the current executor policy.
Neither category establishes whether a terminal or background process exists.
Derivation uses `TaskSession.State`, not `executors_running.Status`, task workflow
columns, or container readiness. The existing endpoint enumerates inventory rows
without discarding them solely because the agent session stopped.
Keep that behavior so ordinary Stop cannot hide its retained Pod.
Task state and Pod state are separate observations. The projection does not promise
an atomic cross-system snapshot. The next refresh resolves concurrent lifecycle changes.
No new observation timestamp uses `TaskSession.UpdatedAt` as a false stopped-since clock.
## Permissions and failure behavior
Existing task authorization and identity checks remain before Pod projection.
New fields do not bypass executor/task/session filters or expand Kubernetes RBAC.
The backend performs no additional Pod GET, list, metrics, log, or Event request.
The admin-only `session-impact` response remains count-only with its existing semantics.
This feature adds no global count, including for administrators.
An access or identity error omits resource requests and uses `unknown`.
An exact NotFound uses `missing` while preserving the existing sanitized failure field.
Raw session errors and Pod spec content never enter the response.
An unavailable client remains an endpoint error rather than an empty success.
## Responsive presentation
Extend `KubernetesSession` in `lib/types/http-kubernetes.ts` and the existing API tests.
New fields are optional in TypeScript for old responses and fixture compatibility.
The backend owns classification. Frontend helpers select translated labels and
fallbacks, without recreating the state machine.
`KubernetesSessionsCard` remains the only changed product surface.
Desktop keeps the table. Each default row uses at most two status lines. The
first line shows session and retention badges. The second line shows distinct
Pod phase and main-container state values. The Pod cell also shows the workspace
mode, and a separate column shows the main-container requests. A disclosure
opens one labeled diagnostic row without changing another session row. The task
identity becomes a routing-adapter link to `/t/<encoded-task-id>`. It does not
select or resume a session automatically.
Phone presentation reuses the shipped `MobileSessionList` cards in this file and
the direct-navigation pattern from `components/kanban-with-preview.tsx`.
Each card groups Pod identity, task and session identity, compact status, requests,
workspace mode, and creation time. The status uses the same two-line hierarchy as
the desktop row. The phone card has no diagnostic disclosure.
The card body navigates to its task because there are no competing inline actions.
The whole card is a semantic link with at least a 44px touch height.
The page remains the single vertical scroll owner. Long identifiers wrap.
No drawer, extra navigation step, nested vertical scroller, or fixed footer is needed.
Desktop link density remains unchanged. Focus indicators and accessible names
identify the task and session on both viewports.
Visible card-level guidance explains Stop retention and Archive/Delete workspace
consequences. It also states that requests exclude sidecars and do not show actual use.
No direct destructive action is added. Existing task routes own those actions and confirmations.
`useKubernetesSessions` retains its 90-second poll and manual refresh behavior.
Refresh failures must remain visible. A stale prior row must not appear as current
confirmed retention. Existing executor-scope generation guards discard late responses.
Missing new fields use unknown/unspecified labels, not inferred defaults.
New copy uses `executors` translation keys in English, Portuguese, and Chinese catalogs.
Traditional Chinese derives through the existing converter. Pseudo-locale tests
cover expansion without translating state discriminants or Kubernetes quantities.
## Verification and persistence
Backend tables cover every session state, Pod phase, deletion, NotFound, access
error, identity mismatch, missing request, explicit zero request, and admission-mutated request.
Actual Pod values win over current profile values and the original template.
HTTP and WebSocket responses share field tests. Fake-client action counts prove
unauthorized rows trigger no Pod read and visible rows require one exact GET.
Component tests cover optional fields, distinct labels, links, loading, and errors.
Desktop and mobile E2E extend the existing Kubernetes configuration specs.
They verify navigation, safe guidance, responsive geometry, and post-refresh changes.
A Kind lifecycle scenario proves Stop retains the Pod and refresh shows it,
then Resume clears the retained category without replacing the Pod.
No schema, lifecycle, poller, feature-toggle, or credential change is required.
Public documentation changes describe the new fields and retained-resource wording.
## Related decisions and delivery
- [Kubernetes resource ownership](../../../decisions/2026-08-24-kubernetes-executor-resource-ownership.md)
- [Kubernetes foundation](../../kubernetes-executor/spec.md)
- [Retained compute plan](../../../plans/kubernetes-retained-compute/plan.md)