9.9 KiB
spec, created, status
| spec | created | status |
|---|---|---|
| docs/specs/kubernetes-executor/spec.md | 2026-08-25 | complete |
Implementation Plan: Kubernetes Task Runtime Disclosure
Overview
The task-header disclosure currently treats Kubernetes as an untyped remote
environment: EXECUTOR_ICON_MAP.k8s selects IconCloudComputing,
GET /api/v1/tasks/:id/environment/live returns no Kubernetes block, and
buildFields has no k8s branch. The live seeded Kind task therefore renders
ready from TaskEnvironment.status beside “No resource details available”
even though the existing Kubernetes session API can verify the exact Pod and
return its live state. The repair first adds an exact task/session filter to
that existing API, then makes one shared desktop/touch disclosure consume it
and replaces every Kubernetes cloud glyph with a Pod-shaped cube.
No new persistence, cluster mutation, or lifecycle operation is introduced. The existing session Stop/Resume and terminal Archive/Delete paths remain the only Pod/PVC lifecycle controls.
Backend
Exact task/session status selection
- Extend
apps/backend/internal/kubernetes/sessions.gowith a small typedSessionFilteraccepted byHandler.listSessions. Applytask_idand optionalsession_idtoexecutors_runningrows beforesessionRowperforms task authorization or an exact Pod GET. Preserve the existing unfiltered settings-page behavior. - Update
apps/backend/internal/kubernetes/handlers.gosohttpListSessionsreadstask_id/session_idquery parameters andwsListSessionsaccepts the same optional fields. Rejectsession_idwithouttask_id; a well-formed mismatch returns an empty list without a Kubernetes API call. - Keep
sessionRow,validateSessionInventory, andmatchesSessionIdentityas the sole trust boundary. The filter narrows which authoritative rows are evaluated; it must not permit a caller-supplied Pod name, namespace, or UID.
No database migration or new route is required.
Frontend
Kubernetes status client and polling state
- Add
getKubernetesTaskSessiontoapps/web/lib/api/domains/kubernetes-api.ts. It calls the existing session list route with encoded task/session filters, normalizes the response throughnormalizeKubernetesSessions, and returns the exact row ornull. - Extend
apps/web/hooks/domains/session/use-task-environment.tsto accept the activesessionId, load the filtered Kubernetes row after the environment identifiesexecutor_type: "k8s", and exposekubernetes, a sanitized status error, and an explicitrefreshaction. Guard the chained responses by the current task/session generation so a late result cannot overwrite a newly selected session. - Extend
resolveExecutorEnvironmentStatusinapps/web/components/task/executor-environment-status.tswith an explicit Kubernetes input.container_state/pod_phaseandfailure_reasontake precedence over the recorded environment state. A completed filtered lookup with no row or a lookup failure is unavailable/error, neverready.
Pod details and controls
- Extend
apps/web/components/task/executor-environment-info.tsxso thek8sbranch renders Pod name, phase/container state, restart count, workspace mode, created time, and sanitized failure reason. Reuse the existingexecutors:*Kubernetes labels/status/workspace translations. - Extract the disclosure body/actions from
apps/web/components/task/executor-settings-button.tsxinto a focused shared component if needed to keep both files below the web subtree limits. The action row contains Refresh and a link built withkubernetesExecutorSettingsPath(env.executor_id). The generic Reset environment action stays available to its existing executor types but is not rendered for Kubernetes, where it cannot own exact Pod/PVC cleanup. - On fine pointers, preserve the compact task-topbar trigger and desktop
popover. On coarse pointers, use
useTouchDrawerand a shadcnDrawerwith the same status/details/actions, an accessible title/description, safe-area padding, internal overflow, and a visible named trigger with a 44 px hit target. - In
apps/web/components/task/mobile/session-mobile-top-bar.tsx, render that Kubernetes disclosure button instead of the tooltip-onlyRemoteCloudTooltip. Other remote-executor indicators remain unchanged.
Pod icon
- Change the central
k8smapping and status mapping inapps/web/lib/executor-icons.tsfromIconCloudComputing/IconCloudOfftoIconCube/IconCubeOff, retaining the Kubernetes test ID. - Make the Kubernetes create header, executor header, and profile connection
action consume the same central mapping instead of importing
IconCloudComputingdirectly:apps/web/app/settings/executors/new/[type]/kubernetes-create-page.tsx,apps/web/app/settings/executors/k8s/[executorId]/page.tsx, andapps/web/components/settings/profile-edit/profile-connection-settings-action.tsx. - Add only genuinely new copy to all six locale catalogs and regenerate the Traditional Chinese and pseudo catalogs with the repository scripts.
Tests
- What: filtered status reads touch only the requested authoritative
task/session row, still enforce task authorization and Pod UID/label checks,
and reject a session-only filter.
File:
apps/backend/internal/kubernetes/sessions_test.goandapps/backend/internal/kubernetes/handlers_test.go. How: fake repository/client reactors that fail if an unrelated Pod GET or authorization occurs. - What: the client encodes the executor/task/session IDs and returns zero or
one normalized Kubernetes session.
File:
apps/web/lib/api/domains/kubernetes-api.test.ts. How: mocked HTTP client assertions for matching, missing, and malformed response rows. - What: Kubernetes running/pending/failed/missing/error states override the
recorded
TaskEnvironment.status, while Docker behavior is unchanged. File:apps/web/components/task/executor-environment-status.test.ts. How: table-driven pure-function tests, including the reportedenvironment.ready + no Kubernetes rowregression. - What: Pod fields replace “No resource details available,” Refresh and
Executor settings are visible, Kubernetes does not show Reset environment,
and the cube icon is used in normal/error states.
File:
apps/web/components/task/executor-environment-info.test.tsx,apps/web/components/task/executor-settings-button.test.tsx, andapps/web/lib/executor-icons.test.ts. How: component tests with a running Kubernetes row and a failed row. - What: a coarse-pointer Kubernetes trigger opens a Drawer containing the
same details/actions and is reachable by accessible name.
File:
apps/web/components/task/executor-settings-button.test.tsxand a focused mobile-topbar component test besideapps/web/components/task/mobile/session-mobile-top-bar.tsx. How: mockuseTouchDrawer()totrue, tap the trigger, and assert Drawer semantics plus action target sizes.
E2E Tests
- Scenario: GIVEN a real running Kind-backed task, WHEN the desktop
executor disclosure opens, THEN it shows the exact Pod name, live Running
state, zero restarts, workspace mode, Pod glyph, Refresh, and canonical
executor-settings link without the generic empty-resource text.
File:
apps/web/e2e/tests/kubernetes/kubernetes-executor.spec.ts. What to verify: extend the existing kubeconfig launch case after it has the exact created Pod identity; do not add a thirteenth Kind lifecycle case. - Scenario: GIVEN a Kubernetes task at the
mobile-chromecoarse-pointer viewport, WHEN the executor control is tapped, THEN a Drawer exposes the same Pod status/actions with no hover dependency or document overflow. File:apps/web/e2e/tests/settings/mobile-kubernetes-task-environment.spec.ts. What to verify: use isolated backend fixture data and intercept only the filtered read-only session-status response; no real cluster is required.
Verification Results
- Backend exact-filter tests and the full
internal/kubernetespackage passed. - Frontend focused verification passed 7 files and 36 tests; full lint,
typecheck, locale generation,
i18n:check, andi18n:ratchetpassed. - The fresh mobile E2E passed 1/1 and the focused real Kind E2E passed 1/1, both on their first attempt with retries disabled. Task 03 records timings and teardown evidence.
- Public-document validation and
git diff --checkpassed after the task-header disclosure documentation was synchronized.
Implementation Order
All tasks are sequential. The frontend consumes the backend filter contract, and E2E follows the completed UI. No subagent delegation is implied.
Risks And Considerations
- The settings session list can cover many Pods; task chrome must always use the new pre-Pod-lookup filter and must not poll the unfiltered list every three seconds.
- A selected task/session can change while HTTP is in flight. Generation guarding is required to avoid showing the prior Pod under the next task.
- A missing row is not evidence that a Pod is absent; it can also mean retained inventory is unavailable. The UI should say unavailable and preserve the sanitized error instead of inventing a cluster state.
- The existing generic Reset environment endpoint does not own Kubernetes inventory cleanup. Hiding it for Kubernetes prevents misleading destructive UI while leaving the accepted lifecycle authority unchanged.
- The seeded Tailscale/Kind lab is user-requested and remains live during this design checkpoint. E2E teardown may remove only its own fixture cluster, not that lab or the main Kandev process on port 9998.