Repository navigation
Add the namespace switcher, chips and quota-held label to the Console (identity-and-access W7-δ) - #690
Conversation
[Medium impact] The PR appears safe to merge; the latest change fixes the outstanding held-event memory growth. SummaryThe PR adds namespace selection across the Console, namespace chips, fixed access-denied pages, and quota-held labels. The server narrows list results within the caller’s existing access.
|
rocketbitz
left a comment
There was a problem hiding this comment.
Adversarial review — PR #690 (round 2)
Reviewed head 3766aea594f6. Verdict: request_changes — P1 0 · P2 2 · P3 0.
The switcher-clearing fix is present, but two prior P2 defects remain. Source-level probes reproduced both; full suites were not run.
rocketbitz
left a comment
There was a problem hiding this comment.
Adversarial review — PR #690 (round 5)
Reviewed head 1bc8c33fa5b5. Verdict: request_changes — P1 0 · P2 1 · P3 0.
Reviewed 1bc8c33 against the spec, callers, and prior fixes. Found one remaining P2 activity-loss race, reproduced with the extracted batching function and a mocked query client. Full suites were not run.
… (identity-and-access W7-δ) The Console gains a header namespace switcher (a view filter kept in localStorage), namespace chips on jobs, a fixed-message access-denied page for foreign deep links, and a "Held by namespace quota" queue label. GET /v1/jobs, /v1/incidents and /v1/triggers accept an optional ?namespace= that narrows within the caller's allowed set (an unreadable namespace is an empty 200). External lineage and contract nodes no longer expose data attributes or raw endpoint ids. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ANYa3orFM83hWHyfzdtkAL
…ss W7-δ review) A stored selection stayed applied after the principal's options shrank to one namespace, although the switcher (the only control that clears it) was hidden. The view now applies and persists no selection while the switcher is hidden. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ANYa3orFM83hWHyfzdtkAL
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ANYa3orFM83hWHyfzdtkAL
…ccess W7-δ review) With a namespace selected, an activity event for a job missing from the narrowed list is now held until a refetch started after it says whether the job belongs, instead of being dropped. Jobs with an empty namespace are offered as default. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ANYa3orFM83hWHyfzdtkAL
…ity-and-access W7-δ review) cancelRefetch only cancels when the query holds data, so an event arriving during the first scoped load was answered by a request that may predate it. Cancel explicitly first. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ANYa3orFM83hWHyfzdtkAL
…cess W7-δ review) A failed list refresh now keeps the held batch and leaves its activity keys unseen, retrying on the next event or after a short delay; keys are remembered only once an event is applied or proven foreign. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ANYa3orFM83hWHyfzdtkAL
ed91aea to
f0a28c2
Compare
…-access W7-δ review) With a namespace selected, events for other namespaces' jobs are held alongside the selected one's until a list refresh decides membership. A failed refresh trimmed the whole held batch to the newest 20, so 20 newer foreign events could discard a selected job's event. Cap each job's held events at the feed limit instead: membership is per job, so no other namespace can displace them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The per-job cap kept another namespace's events from displacing the selected one's, but left the held queue growing with every job that emitted during a long list outage. Cap the queue at 100 events as well, on each failed refresh and as events arrive during a slow one. Over the cap, drop the events whose run payload reports another namespace first, oldest first; a moved job's earlier runs keep their old namespace, so they are demoted rather than dropped on sight. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
localStorage(never the credential, validated against what the principal can see). Namespace chips are always on for job rows and job detail. A deep link to a job (or to a run of a moved job) that the server answers 403 renders a fixed-message access-denied page instead of "Job not found". Sidebar counts, the command menu, the activity feed and the Triggers and Incidents pages follow the selection.?namespace=onGET /v1/jobs,/v1/incidentsand/v1/triggers(spec section 14). It narrows within the caller's allowed set: an unreadable namespace is an empty200(no existence oracle),*or an omitted value does not narrow (byte-identical), a malformed value is400.authz.NamespaceFilter/MarkFilteredand the collection guards are untouched; the narrowing is an extraWHEREafter them (authz.NarrowToNamespace). Triggers belong to a namespace through their live jobs (EXISTS), on top of the existing readable-every-namespace rule.data-job-alias/data-dataset-*attributes and that an edge to an unknown endpoint showed the raw id. Both removed; the placeholder is now leak-free by construction.held_reason: namespace_quotashow "Held by namespace quota" (text and badge).QueryClientper signed-in identity (SessionQueryProvider, remounts the subtree) so one principal's cache or late mutation response can never reach the next in the same tab. The switcher requestsGET /v1/auth/namespacesonly for an unscoped cluster viewer or higher.Decisions
toHaveScreenshotis an element screenshot (dialog, DAG section, partition table), none contains a job row or header.?namespace=is a400(the audit route's rule);*means "no narrowing" rather than the cluster-shared namespace, which has no meaning for these lists.namespace(a different concept); atoms and the contract graph are not narrowed. The guide says so.auth_min_passstays 21; the journey list grew by one).Test plan
just lint: 0 issues.just unit-test: pass (new:TestJobListNamespaceNarrow,TestTriggerListNamespaceNarrow,TestIncidentListNamespaceNarrow,TestParseNamespaceNarrow).just integration-test: pass (new scenario skips off the auth lane).just integration-test-agent: pass,TestAuthListNamespaceNarrowran (--- PASS): marketing viewer gets its rows for?namespace=marketing, an empty 200 forfinance, unchanged without the parameter, a 400 for a malformed value; incidents are seeded in both namespaces and asserted by id (viewer, a two-namespace viewer, the admin). It needs no policy file and is inscripts/coverage-journeys.sh+ pins (test_coverage_named_journeys.pypasses).just ui-ci(lint, 692 Vitest tests, build): pass.just ui-e2e-auth: 22 passed, including the newui/e2e/auth/namespaces.spec.ts(narrowing sends?namespace=marketingand survives a reload with a fresh sign-in and no localStorage clearing, chips, no switcher for one namespace, foreign deep link, quota label). No test asserts the full list of declared namespaces./auth/namespaces), real external node and edge renderers mounted for the leak checks (contract graph and lineage), job and run 403 pages, command menu and activity feed following the selection, identity-change isolation with realuseQuery/useMutationconsumers (mutation-checked).Pre-PR review
ui-ci/ui-e2e-authbut has not had a fifth Codex pass.Follow-ups
page.routerendering test; the wire shape is pinned by the Go queue tests.🤖 Generated with Claude Code
https://claude.ai/code/session_01ANYa3orFM83hWHyfzdtkAL