Repository navigation
feat: multi-namespace scope and the live rollout detail view - #45
Merged
Merged
Conversation
The picker becomes multi-select (Base UI Combobox multiple mode). The selected set is the list scope: - exactly one namespace keeps the existing live-watch path byte-for-byte; - the empty selection stays the All-namespaces cluster-wide snapshot; - two or more namespaces fan out as ordinary single-namespace snapshot requests (limit 100 each) merged in selection order — no watch streams, so "at most one watch per list view" and the no-global-informer boundary are untouched. The cap is 32 (renderer-only constant). New pure seam lib/namespace-scope.ts owns toggle/cap/summary/canonical scope-key serialization plus the merged-response helpers. useNamespaces holds namespaceScope: string[] (bail-out on equal contents so a context switch cannot churn array identity); useResourceList/useHelm key their effects on the order-insensitive scope key and read the scope through a ref. Pod usage and Helm releases fan out per selected namespace; usage is keyed by namespace/name so same-named pods cannot trade rows. Picker UX: click applies immediately, direct-Enter appends (IME guard kept), the All-namespaces row clears the selection, the trigger summarizes (a, b +N with a full-list tooltip), and the 32-cap rejection shows a footer message. Palette/context-switch/create-dialog/reset flows pin, replace or clear the scope as before. Covers #31
While a detail is open, the view keeps itself live instead of freezing at the moment the object was fetched — the frozen case being every snapshot-only scope (All namespaces), where the table's disabled watch could never bump the selection. - Single-object live channel in useResourceDetail: one lazy watch with fieldSelector metadata.name (plumbed end-to-end already), keyed on the object's identity so resourceVersion bumps adopt in place instead of resubscribing. A bump re-gets the object once, so vitals, conditions and the YAML tab converge the same way an explicit refresh does; a deleted event closes the detail like the list-follow path. Closed on leave; never cluster-scoped; no informers. - Events go live: the object's events re-fetch when its resourceVersion bumps (the watch's adopt) plus a 15s backstop, and a new usePodEvents poll surfaces the rollout's child pod events (Pulling / Created / Started) — one namespaced events list filtered server-side with involvedObject.kind=Pod and joined client-side to the workload's own pod names. No watch stream, no per-pod fan-out. The Events tab merges both halves, newest first, capped at 100. - Rollout indicator: the identity header gains a chip derived from the Progressing condition plus the ready/updated/desired counters — "Rollout in progress / Rolled out / Rollout stuck" — so a stuck rollout (ProgressDeadlineExceeded) is visible at a glance. - Core: Event rows now project involvedObject.name (the only field the client-side join needs); no validation or RPC surface changes. Covers #39
Integrates both features into one branch: the multi-namespace picker's useResourceDetail now keys on the canonical scope key (namespaceKey) and gains the coreReady-gated single-object live channel; ResourceDetailView keeps the scoped pods watch on namespaceScope while adding the pod-events join and rollout chip.
This was referenced Sep 15, 2026
…edup)
Spec fixes:
- rolloutStatus: settled replica counters now complete a non-terminal
Progressing=True (StatefulSet no longer reads "Rollout in progress"
forever); lagging updated replicas keep it honest mid-rollout
- trigger summary overflows from the third name on ("a, b +1" per #31);
cap message matches the spec text
- create dialog falls back to the context default namespace for All/multi
scopes instead of a hardcoded "default" (#31 story 24)
- the single-object live watch and event polls are scoped to rollout
workload kinds (#39): no watch ever leaves with an empty namespace
- usePodEvents keys on the empty/non-empty name transition, so a live
pod list refresh no longer restarts the poll
Standards cleanup:
- shared namespaceScopeKey / SLOW_POLL_MS / newestEventsFirst seams
replace the inlined copies
- useHelm unifies the single- and multi-namespace stream paths, fixing
multi-scope load-more replacing pages and page errors wiping rows
- scopeLabel/scopeKey renames; RolloutBadge renders from one map
Refs #45
Competitor-parity interaction pass on PR #45's surfaces: - Sidebar favorites: hover a resource row and star it; starred kinds pin into a Favorites group at the top of the navigation. The star is a sibling overlay of the row button, so the a11y tree keeps both controls separate; the fill uses the interactive token, not a semantic color. - Most used: every kind open feeds a usage counter (localStorage, like the fold prefs); the top three unstarred kinds render in a Most used group. Both groups resolve entries against the full catalog so icon, label, and enabled state match the home rows, and carry distinct testids so quick-access rows never shadow the originals. - Namespace picker: a multi selection (2+) grows an x button beside the trigger that clears back to All namespaces in one click — the in-popup All row stays as the two-click path. Single selections keep no undo control: one namespace is the ordinary scope, not a selection. - Playwright: the smoke preview port is overridable via ASTER_SMOKE_PORT so a local service on 4173 cannot win reuseExistingServer and serve the wrong app to the suite. Refs #45
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.
Closes #31
Closes #39
Combined PR for both issues (the two features touch adjacent wiring in
App.tsx/ResourceDetailView.tsx, so they ship together instead of as two conflicting PRs). Supersedes #43 and #44.#31 — multi-namespace selection in the toolbar namespace picker
The picker becomes multi-select (Base UI Combobox
multiple). The selected set is the list scope:[]= All namespaces (cluster-wide snapshot, unchanged); one name = the existing single-namespace live-watch path, byte-for-byte; two or more = bounded per-namespace snapshot fan-out (limit 100 each, parallel, merged in selection order). No watch streams for multi-scope — "at most one watch per list view" and the no-global-informer boundary are untouched. Cap = 32 (renderer-only constant).lib/namespace-scope.ts(vitest-covered): toggle/cap, trigger summary (a, b +N), canonical order-insensitive scope key, merged-response helpers.useNamespacesholdsnamespaceScope: string[](content-equality bail-out so a context switch cannot churn array identity);useResourceList/useHelmkey effects on the scope key and read the scope through a ref; Pod usage and Helm releases fan out per selected namespace; usage is keyed by namespace/name so same-named pods cannot trade rows.#39 — the workload detail stays live during a rollout
While an object is open, the detail keeps itself live instead of freezing at fetch time (the frozen case being every snapshot-only scope, where the table's disabled watch could never bump the selection):
useResourceDetail: one lazy watch withfieldSelector: metadata.name=<name>(already plumbed end-to-end), keyed on the object's identity so resourceVersion bumps adopt in place and re-get the object once — vitals, conditions and the YAML tab converge like an explicit refresh. Adeletedevent closes the detail. Lazy, one stream, closed on leave, never cluster-scoped.usePodEventspoll surfaces child pod events (Pulling / Created / Started) — one namespaced events list filtered server-side withinvolvedObject.kind=Pod, joined client-side to the workload's own pods. The Events tab merges both, newest first, capped at 100.Rollout in progress / Rolled out / Rollout stuck(purerolloutStatus()with vitest coverage).involvedObject.name; no validation caps, RPC surface, or shell changes.Verification
pnpm checkgreen (typecheck, vitest 139, build, cargo test + clippy);go -C core test -race ./...+go vetgreen.pnpm --dir apps/desktop smoke:visual58/58 with screenshots and overflow checks, including the new end-to-end cases: multi-select merge with per-namespace request recording (no watch), 32-cap rejection, Helm merged fan-out, pod usage across namespaces, and the live rollout (scripted snapshot → delta → get drives vitals/conditions/events without refresh; leaving the detail cancels the watch).