13 KiB
spec, created, status
| spec | created | status |
|---|---|---|
| docs/specs/kubernetes-executor/spec.md | 2026-09-01 | complete |
Implementation Plan: Kubernetes Executor Task Status Preview Repair
Overview
The Kanban and sidebar executor glyph is rendered by
RemoteCloudTooltip, but useLiveRemoteStatus returns before doing any work
unless its local open state is true. A newly mounted card therefore has no
remote_checked_at, getCloudState deliberately classifies it as stale, and
the glyph remains muted until the first hover opens the Tooltip. The focused
test currently reinforces that behavior by calling openTooltip() before it
expects getKubernetesTaskSession or the compatibility WebSocket request.
The same component renders its disclosure as an unstructured
space-y-0.5 list inside an uncontrolled Tooltip and wraps the glyph in a
non-focusable span. By contrast, PRTaskIcon uses a controlled, focusable
trigger, useChangeRequestTaskTooltipState, a viewport-bounded 320 px surface,
and ChangeRequestTaskStatusSummary with an identity header and aligned
semantic rows. This explains both reported symptoms without involving the
Kubernetes backend, Kanban mapping, or missing task identities:
KanbanCardBody and TaskItemContent already pass the exact task, primary
session, executor ID, executor type, and fallback name.
The repair moves exact remote-task status into a small shared frontend resource
that eagerly hydrates valid rendered scopes, deduplicates simultaneous Kanban
and sidebar consumers, and keeps hover/focus as an explicit refresh. It then
gives the executor glyph the same visual and interaction quality bar as the PR
glyph, with a coarse-pointer Drawer rather than a hover-only path. Kubernetes
continues to use the exact session inventory endpoint; other remote executors
continue to use task.session.status.
Root-Cause Evidence
apps/web/components/task/remote-cloud-tooltip.tsxpassesopenintouseLiveRemoteStatus; its effect exits on!open.statusis consequentlynullon mount andgetCloudState(null)isstale.apps/web/components/task/remote-cloud-tooltip.test.tsxopens the mocked Tooltip before asserting either status request. There is no eager-mount or duplicate-consumer regression.apps/web/components/kanban-card-content.tsxalready suppliesprimarySessionId,primaryExecutorId,primaryExecutorType, and task ID.apps/web/components/task/task-item.tsxreceives the same exact identities throughtask-session-sidebar-item.ts; prop projection is not the defect.- The executor trigger is
span.cursor-defaultand the content is plain text.apps/web/components/github/pr-task-icon.tsxis the requested in-product comparison: it supplies keyboard focus/Escape behavior and renders aw-80 max-w-[calc(100vw-1rem)] p-3structured summary. apps/web/lib/types/http-kubernetes.ts#KubernetesSessionalready exposes Pod name, phase/container state, restarts, workspace kind, creation time, and a sanitized failure reason. No backend or wire-shape change is needed.
Frontend
Eager, shared exact-status resource
- Create a focused resource under
apps/web/hooks/domains/session/remote-executor-status-resource.tsand auseRemoteExecutorStatushook beside it. Model a request with executor type, optional executor ID, task ID, and primary session ID; derive a collision-safe scope key from all four identities. - Keep source selection exactly where it exists today:
getKubernetesTaskSession(executorId, taskId, sessionId)fork8s, andgetWebSocketClient().request("task.session.status", ...)for compatible remote executors. ProjectKubernetesSessioninto a display snapshot that retainsrestartsandworkspace_kindinstead of discarding those facts. - Follow the external-resource pattern in
apps/web/hooks/domains/github/pr-commits-resource.ts: stable snapshots,useSyncExternalStore, one promise per exact scope, listener notification, last-used tracking, and bounded LRU eviction. Successful results remain fresh for 90 seconds; failures remain visible but retry on the next mount or disclosure open rather than being suppressed for the full freshness window. - On first mount of a valid scope, call
loadwithout waiting for the Tooltip to open. Simultaneous Kanban/sidebar instances subscribe to and join that one request. A forcedrefreshused by hover or keyboard focus also joins an existing request and retains the previous snapshot whileloadingis true. - Do not request with an empty task/session or with a Kubernetes scope lacking
its executor ID. Scope changes subscribe to the new entry immediately, and a
late response remains isolated under its old exact key. Preserve the existing
statusprop as an authoritative no-fetch compatibility path. - Keep errors causal and sanitized. The icon must derive its tone from the
resource snapshot and never promote an absent/malformed scope to
ready.
PR-quality responsive disclosure
- Refactor
RemoteCloudTooltipinto a thin responsive shell over a newRemoteExecutorTaskStatusSummarycomponent. Keep the exported component name for its Kanban, task-sidebar, and mobile-topbar callers, but remove network state ownership from the presentation component. - Extract the pointer/focus/Escape behavior in
use-change-request-task-tooltip-state.tsinto a domain-neutralapps/web/components/task/use-task-icon-tooltip-state.ts. Keep the existing change-request export as a compatibility wrapper so PR and registered-review icons do not change behavior while the executor icon reuses the same interaction contract. - On fine pointers, use a controlled Tooltip, a focusable translated trigger,
sideOffset={6}, and the same boundedw-80/viewport/padding surface asPRTaskIcon. The summary header uses the executor glyph and exact remote or Pod identity; aligned rows show state, Kubernetes restart count and workspace mode when available, created time, last check, and a distinct sanitized error state. State text and semantic icons accompany color. - Preserve the small visual glyph in dense Kanban/sidebar chrome. Loading may
animate a quiet status affordance but must not replace retained facts or shift
task-row layout. Missing identity/result copy says unavailable; it must not
synthesize
Ready. - On coarse pointers, use
useTouchDrawerand the existing Drawer primitives to present the same shared summary. Give the trigger a 44 px effective hit area without increasing the visible glyph or row height, stop its activation from selecting the underlying task card/row, support Enter/Space, constrain the body with internal scrolling, and honor bottom safe-area padding. - Add all new labels through
tasklocale keys in English, pseudo, Portuguese, Simplified Chinese, and both Traditional Chinese catalogs. Generate the Traditional Chinese pair withpnpm run i18n:zh-hant; do not hardcode visible copy.
Tests
- What: a valid Kubernetes scope requests exact status immediately on mount,
before the Tooltip opens, and publishes its semantic icon tone.
File:
apps/web/components/task/remote-cloud-tooltip.test.tsxplus the new resource test. How: render without calling the Tooltip open helper; resolve a deferredKubernetesSession; assert exact arguments, loading transition, Pod fields, and the healthy/error class. - What: two mounted consumers for the same exact scope make one request,
hover/focus joins an eager in-flight request, a later open forces one refresh,
failed reads are retryable, and obsolete scopes cannot publish into the active
view.
File:
apps/web/hooks/domains/session/remote-executor-status-resource.test.tsandremote-cloud-tooltip.test.tsx. How: use deferred requests, fake time for the 90-second freshness boundary, subscription counts, scope changes, and explicit release/remount cases. - What: non-Kubernetes executors remain on
task.session.status, an externalstatusprop performs no request, and invalid identities perform no request or false-ready projection. File:remote-cloud-tooltip.test.tsx. - What: the fine-pointer executor trigger is focusable and translated,
hover/focus/Escape follow the PR icon contract, and the summary has a bounded
identity header plus aligned state/restarts/workspace/timestamp/error rows.
Files:
remote-cloud-tooltip.test.tsxand a focused newremote-executor-task-status-summary.test.tsx. - What: coarse-pointer activation opens a Drawer with the same facts, a 44 px effective target, internal scrolling, safe-area treatment, keyboard activation, and no parent task-card selection. Files: the same component tests plus a focused TaskItem/Kanban integration test where propagation matters.
- What: the existing PR and registered change-request task glyphs retain
their pointer, focus, Escape, color, and summary behavior after extracting the
shared tooltip-state hook.
Files: existing
pr-task-iconand registered-change-request icon tests.
E2E Tests
- Amend
apps/web/e2e/tests/settings/kubernetes-task-environment.spec.tsso Kanban and task-sidebar scenarios assert the exact Kubernetes session request and healthy or failed icon tone before any hover. Then hover/focus and assert the PR-style identity header, aligned Pod rows, and a causal refreshed response. - Amend
apps/web/e2e/tests/settings/mobile-kubernetes-task-environment.spec.tswith a coarse-pointer task-list/card scenario: tap the compact glyph, assert the bottom Drawer contains the same exact Pod facts, dismiss it, and verify the task was not selected as a side effect. Keep the existing task-page executor Drawer scenario intact. - Cover both a successful Pod and a sanitized unauthorized/unavailable response without printing credentials or raw Kubernetes response bodies.
Public Documentation
- Update the task-status sections in
docs/public/k8s.mdanddocs/public/executors.mdto state that Kanban/sidebar executor glyphs hydrate status on render, show a structured exact-session summary on pointer/focus, and use a touch Drawer on coarse-pointer devices. - Keep
k8s.mdprimarily a how-to/reference page andexecutors.mdprimarily an executor reference. Do not add a new page or changedocs/public/meta.json.
Implementation Waves And Parallel Candidates
The default is sequential execution in the primary conversation. The resource, component shell, and E2E selectors share contracts, so no task is marked parallel-safe.
Wave 1:
Wave 2:
Wave 3:
Verification Results
- Focused Vitest: 5 files and 49 tests passed, including eager hydration, exact-scope sharing, retryable failures, LRU eviction, responsive disclosure, mobile projection, and unchanged PR/change-request indicators.
- Frontend
typecheckand full zero-warninglintpassed. - Desktop managed Chromium E2E passed 1/1 first attempt with
--retries=0; mobile Chrome passed 1/1 first attempt with--retries=0after a fresh build. - Localization checks passed for all six catalogs; the i18n reference audit found 7,233 referenced keys and the new-copy ratchet was clean.
- Public-doc validation passed 61 tests and validated 41 published pages.
- The E2E sleep ratchet, focused Prettier checks, and
git diff --checkpassed.
Risks And Constraints
- Eager per-card reads can create a request burst on large Kanban boards. The exact-scope promise cache, 90-second successful-result freshness, bounded LRU, and no-request invalid-scope guard are required acceptance behavior, not later optimization.
- The same task can render in Kanban, a sidebar, and mobile task switchers. Shared status must be keyed by executor type/ID plus task/session, not stored as local component state or keyed by session alone.
- A task can switch primary sessions while an old request is unresolved. Old results may remain cached for their old key but cannot recolor or populate the current indicator.
- The task row itself is interactive. The coarse-pointer status trigger must not cause task selection, drag start, or nested-button markup regressions.
- Error color alone is insufficient. Every status remains available as text and a semantic icon, and loading preserves the last known result.
- The worktree contains the prior Kubernetes executor implementation and repair changes. Implementation must preserve those overlapping edits and avoid broad formatting or cleanup.
Out Of Scope
- Backend changes, database migrations, task-status-summary schema changes, or a new batch endpoint.
- Periodic polling while a tab remains idle. This repair covers eager mount, foreground reuse, bounded freshness, and explicit pointer/focus refresh.
- Changing Kubernetes authorization, exact inventory validation, executor lifecycle, settings hierarchy, or cleanup controls.
- Redesigning PR/MR/change-request status content beyond the behavior-preserving shared trigger-state extraction.