Skip to content

Add the namespace switcher, chips and quota-held label to the Console (identity-and-access W7-δ) - #690

Merged
rocketbitz merged 8 commits into
masterfrom
worktree-agent-a58192148caa6ccc4
Oct 28, 2026
Merged

rocketbitz merged 8 commits into
masterfrom
worktree-agent-a58192148caa6ccc4

Conversation

@rocketbitz

Copy link
Copy Markdown
Contributor

Summary

  • F2, Console. Header namespace switcher (hidden at one or fewer visible namespaces), a view filter kept in 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.
  • F2, server. Optional ?namespace= on GET /v1/jobs, /v1/incidents and /v1/triggers (spec section 14). It narrows within the caller's allowed set: an unreadable namespace is an empty 200 (no existence oracle), * or an omitted value does not narrow (byte-identical), a malformed value is 400. authz.NamespaceFilter / MarkFiltered and the collection guards are untouched; the narrowing is an extra WHERE after them (authz.NarrowToNamespace). Triggers belong to a namespace through their live jobs (EXISTS), on top of the existing readable-every-namespace rule.
  • F2, external nodes. The new real-renderer leak tests found that an external contract node still emitted 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.
  • F4. Queue rows with held_reason: namespace_quota show "Held by namespace quota" (text and badge).
  • One QueryClient per 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 requests GET /v1/auth/namespaces only for an unscoped cluster viewer or higher.

Decisions

  • D-a (orchestrator, chips always on) implemented; no visual baseline changed: every toHaveScreenshot is an element screenshot (dialog, DAG section, partition table), none contains a job row or header.
  • D-b: cluster principals see declared namespaces plus loaded jobs' namespaces; scoped keys never request the declared route (tested).
  • Foreign deep link covers the run query too (moved job: job 200, run 403), with a fixed message (the 403 body can name namespaces).
  • Judgment: a malformed ?namespace= is a 400 (the audit route's rule); * means "no narrowing" rather than the cluster-shared namespace, which has no meaning for these lists.
  • Judgment: datasets, holds and lineage keep their OpenLineage namespace (a different concept); atoms and the contract graph are not narrowed. The guide says so.
  • Judgment: coverage floors untouched (auth_min_pass stays 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, TestAuthListNamespaceNarrow ran (--- PASS): marketing viewer gets its rows for ?namespace=marketing, an empty 200 for finance, 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 in scripts/coverage-journeys.sh + pins (test_coverage_named_journeys.py passes).
  • just ui-ci (lint, 692 Vitest tests, build): pass. just ui-e2e-auth: 22 passed, including the new ui/e2e/auth/namespaces.spec.ts (narrowing sends ?namespace=marketing and 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.
  • Vitest highlights: switcher options and gating (scoped login never requests /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 real useQuery/useMutation consumers (mutation-checked).

Pre-PR review

  • r1 (b76bc111): 3 P2 + 1 P3, all fixed: command menu now follows the selection; activity feed re-filtered on selection change; the live scenario asserts real incident ids per namespace; incident cache key no longer conflates a namespace named "all" with no selection.
  • r2 (aab8b882): P2 declared namespaces bled across credentials, fixed (consumed only for the current principal).
  • r3 (56333967): P1 late mutation callbacks across an identity change after a naive cache clear. Replaced the clear with a per-identity client.
  • r4 (56ffbde0): P1 a swapped provider client leaves mounted observers on the old client. Fixed by keying the boundary by identity so the subtree remounts; the new test uses real observers and fails if the key is removed. Cap of 4 rounds reached: this last fix was verified by that test and by ui-ci / ui-e2e-auth but has not had a fifth Codex pass.

Follow-ups

  • G2 (W8, Keycloak Playwright lane, serves a policy file): the real policy-backed browser journey for the quota label (two real starts, the label from the actual queue response). Here the label is covered by Vitest and a page.route rendering test; the wire shape is pinned by the Go queue tests.
  • The incidents analytics widgets and the contract graph are not narrowed by the switcher (documented in the guide).

🤖 Generated with Claude Code

https://claude.ai/code/session_01ANYa3orFM83hWHyfzdtkAL

@rocketbitz rocketbitz left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adversarial review — PR #690 (round 1)

Reviewed head fae34100bbbb. Verdict: request_changes — P1 0 · P2 1 · P3 0.

Found one P2 defect at fae3410. Static review only; runtime suites were not rerun.

Comment thread ui/src/features/auth/namespaceView.ts Outdated
@greptile-apps

greptile-apps Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; the latest change fixes the outstanding held-event memory growth.

Summary

The 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.

  • The job, trigger, and incident lists can narrow to one namespace.
  • The Console switcher filters its lists and keeps the choice as a view preference.
  • Each signed-in identity gets its own Console query cache.
  • Job links show an access-denied page, and jobs show their namespace.
  • External graph nodes hide names and unknown endpoint identifiers.
  • The run queue labels jobs held by a namespace quota.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Run event for an unknown job] --> B[Queue event]
  B --> C[Cap queued events at 100]
  C --> D[Refresh selected job list]
  D -->|Success| E[Show matching activity and release keys]
  D -->|Failure| F[Return batch to queue]
  F --> G[Apply bounds and schedule retry]
  G --> D
Loading

Reviews (9) · Last reviewed commit: "Bound held activity events in total (ide..." · Reviewed by Greptile

Comment thread ui/src/features/jobs/useJobsView.ts
Comment thread ui/src/features/auth/namespaceView.ts Outdated

@rocketbitz rocketbitz left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread ui/src/features/jobs/useJobsView.ts
Comment thread ui/src/features/auth/namespaceView.ts Outdated

@rocketbitz rocketbitz left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adversarial review — PR #690 (round 3)

Reviewed head dec88d0f702b. Verdict: request_changes — P1 0 · P2 1 · P3 0.

Reviewed dec88d0. One P2 remains in the claimed activity fix. Reproduced with the locked TanStack Query version; full suites were not run.

Comment thread ui/src/features/jobs/useJobsView.ts Outdated
Comment thread ui/src/features/jobs/useJobsView.ts Outdated

@rocketbitz rocketbitz left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adversarial review — PR #690 (round 4)

Reviewed head 1a884d576bf2. Verdict: request_changes — P1 0 · P2 1 · P3 0.

Reviewed 1a884d5. One P2 remains, confirmed with TanStack Query 5.100.6. Full suites were not rerun.

Comment thread ui/src/features/jobs/useJobsView.ts Outdated

@rocketbitz rocketbitz left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread ui/src/features/jobs/useJobsView.ts
Comment thread ui/src/features/jobs/useJobsView.ts
rocketbitz and others added 6 commits October 28, 2026 11:05
… (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
@rocketbitz
rocketbitz force-pushed the worktree-agent-a58192148caa6ccc4 branch from ed91aea to f0a28c2 Compare October 28, 2026 15:10
Comment thread ui/src/features/jobs/useJobsView.ts Outdated
…-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>
Comment thread ui/src/features/jobs/useJobsView.ts Outdated
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>
@rocketbitz
rocketbitz merged commit 94b4b62 into master Oct 28, 2026
52 checks passed
@rocketbitz
rocketbitz deleted the worktree-agent-a58192148caa6ccc4 branch October 28, 2026 22:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant