Skip to content

feat: governance tags - #372

Merged
v-kessler merged 12 commits into
mainfrom
feat/governance-tags
Aug 7, 2026
Merged

feat: governance tags#372
v-kessler merged 12 commits into
mainfrom
feat/governance-tags

Conversation

@v-kessler

@v-kessler v-kessler commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Wires the new @lakekeeper/console-components governance tags feature into the app: Identities gains a Tags tab (project-scoped tag vocabulary), warehouse/namespace/table/view pages gain entity tag management via a "Manage tags" cog action, and a routed tag detail page at /governance/tags/:id.
  • Governance page gains a Permissions tab (shown on OpenFGA backends) alongside the existing Tags tab, with consistent page titles.
  • Fix: tab-switch animation on namespace/table/view pages was stalled by the tab→URL sync calling router.replace() (runs the full navigation-guard pipeline, including an awaited getServerInfo()) on every click — switched to history.replaceState().
  • Fix: facet-style v-tabs-window groups (independent facets, not a wizard sequence) now use crossfade instead of a directional slide.

⚠️ Depends on lakekeeper/console-components#269 — this branch is pinned to that PR's commit SHA as an interim measure (see the chore(ui) commit). Needs re-pinning to the released version tag once that PR merges and release-please cuts it.

Test plan

  • just reviewable (format + lint + build) passes
  • npx vue-tsc --noEmit passes after npm run unlink
  • New Playwright e2e coverage in console-e2e (specs/flows/governance-tags.spec.ts) — companion, verified passing against a from-source backend build; blocked on a published backend image with the tag-authorization actions before it can run in CI

BEGIN_COMMIT_OVERRIDE
feat(ui): governance tags — Tags tab on Identities, entity tag management, tag detail route
feat(ui): governance permissions tab + consistent page titles
fix(ui): tab-switch animation stalled by router.replace's navigation-guard pipeline; use history.replaceState
fix(ui): facet-style tab groups use crossfade instead of a directional slide
chore(ui): bump console-components to the governance-tags branch commit (interim, pending release)
END_COMMIT_OVERRIDE

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a Governance section with tag browsing and tag detail views.
    • Added a Permissions tab and explorer when supported by the authorization configuration.
    • Added namespace details and improved generic table tag-management support.
  • Improvements

    • Added clearer headings, icons, spacing, and scrolling across Warehouses, Identities, and Server Settings.
    • Improved tab transitions and preserved selected tabs in bookmarkable URLs.
    • Reordered generic table tabs to show Files first.

v-kessler and others added 8 commits July 29, 2026 12:00
Adds a Tags tab to warehouse/namespace/table/view/generic-table pages
(table page also gets column tags), a new /governance route hosting the
Tag Definition manager, and a Governance nav item.

Depends on @lakekeeper/console-components governance-tags release; dep bump
pending.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a centralized Permissions tab to the Governance page (gated to OpenFGA
backends), and standardize main page titles (Identities, Governance,
Warehouses, Server settings) on a text-h6 heading with pa-4 padding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Gate the tab on the authz backend being OpenFGA instead of server-level
grant-read, so project admins (who lack server read_assignments) still
see it; per-scope access is handled inside the explorer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Namespace page: add Details tab (last, default stays namespaces) rendering
NamespaceDetails; remove inline tags card; tag management via cog menu.
Entity pages (table/view/generic-table): thread can-manage-tags; tags now live
in Details/cog per the console-components redesign.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the tag detail page rendering TagDetail from console-components.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The tab bar's URL-sync watcher called router.replace() on every single tab click,
which runs Vue Router's full navigation-guard pipeline — including an awaited
getServerInfo() network call — on every switch. That async work was interrupting
the v-tabs-window slide transition (confirmed via its lifecycle hooks: leave
completed, enter never fired). Sync the tab into the URL via
window.history.replaceState() instead, which updates the address bar without
running navigation guards; route.query.tab is only ever read once on mount, so a
reactive route update isn't needed.

Also give the namespace page the same pinned header+tabs layout (flex column
content, v-card as the scrolling region) that table/view/warehouse already had,
so it behaves consistently and the v-tabs-window has a stable height to animate
within.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A directional left/right slide implies the tabs are a spatial sequence, which
doesn't hold for independent facets of the same object (Details/Permissions/
Health/etc). Add Vuetify's built-in crossfade prop to every v-tabs-window that
switches between independent views, matching the pattern used by professional
dense-data tools.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Interim pin to lakekeeper/console-components@014d152 (its feat/governance-tags
PR, not yet released) so this branch builds against the real feature instead
of the local npm-link copy. Re-pin to the released version tag once that PR
merges and release-please cuts it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@v-kessler, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 581eb0ec-cc0e-4b2b-831b-4fe61581ced8

📥 Commits

Reviewing files that changed from the base of the PR and between c8b1d64 and 8adc25f.

📒 Files selected for processing (4)
  • src/assets/dependencies.json
  • src/pages/governance/index.vue
  • src/pages/warehouse/[id].namespace.[nsid].table.[tid].vue
  • src/pages/warehouse/[id].namespace.[nsid].view.[vid].vue

Walkthrough

The console updates its components reference, adds Governance pages and typed routes, and revises administrative and warehouse layouts. Warehouse tab changes now update bookmarkable URLs through browser history without router navigation guards.

Changes

Governance and navigation

Layer / File(s) Summary
Component version and generated declarations
package.json, src/assets/dependencies.json, src/auto-imports.d.ts
The console-components dependency now uses tag v0.19.0. Generated declarations disable Oxlint and Oxfmt.
Governance navigation and routes
src/components/NavigationBar.vue, src/typed-router.d.ts, src/pages/governance/index.vue, src/pages/governance/tags/[id].vue
The navigation bar links to Governance. Typed routes and pages support tag management, tag details, and OpenFGA-specific permissions.
Administrative and warehouse page layouts
src/pages/identities.vue, src/pages/server-settings.vue, src/pages/warehouse/index.vue, src/pages/warehouse/[id].vue, src/pages/warehouse/[id].namespace.[nsid].vue
Pages receive updated headings, spacing, scrolling, details content, and crossfade tab transitions.
Warehouse tab state and generic-table integration
src/pages/warehouse/[id].namespace.[nsid].generic-table.[tid].vue, src/pages/warehouse/[id].namespace.[nsid].table.[tid].vue, src/pages/warehouse/[id].namespace.[nsid].view.[vid].vue
Warehouse tab changes update the URL with browser history. Generic-table pages default to files and pass tag-management state to the overview and permissions logic.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant NavigationBar
  participant Router
  participant GovernancePage
  participant OpenFGA
  NavigationBar->>Router: Navigate to /governance
  Router->>GovernancePage: Render governance route
  GovernancePage->>OpenFGA: Read authorization backend
  OpenFGA-->>GovernancePage: Return backend type
  GovernancePage-->>Router: Synchronize selected tab with URL query
Loading

Possibly related PRs

Poem

A rabbit hops through tabs with care,
Governance blooms beside the stair.
Tags and permissions find their way,
While history keeps the URL.
Crossfades softly guide the view—
“A tidy console!” says Bun too.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: integrating governance tags and related governance functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch feat/governance-tags
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/governance-tags

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/governance/index.vue`:
- Around line 35-42: Update the tab initialization and synchronization around
tab and showPermissionsTab so only tags, or permissions when showPermissionsTab
is true, can remain selected; normalize invalid or unavailable query values to
tags. Watch route.query.tab and showPermissionsTab, update the local tab
accordingly, and preserve the existing router query synchronization without
leaving the page with an unrendered tab.

In `@src/pages/warehouse/`[id].namespace.[nsid].table.[tid].vue:
- Line 92: Update the tab content rendered inside the v-tabs-window associated
with tab so previously visited v-tabs-window-item bodies remain mounted during
crossfade. Replace current-tab-only v-if rendering with v-show or an equivalent
visited-tab cache, preserving the existing tab content and transitions.

In `@src/pages/warehouse/`[id].namespace.[nsid].view.[vid].vue:
- Line 89: Update the tab body rendering under v-tabs-window so the previous tab
component remains mounted during crossfade transitions; replace the current-tab
v-if with v-show, or otherwise cache visited tab bodies, while preserving the
existing active-tab behavior.

In `@src/pages/warehouse/`[id].namespace.[nsid].vue:
- Around line 358-364: Keep Vue Router’s current query.tab synchronized while
preserving the lightweight tab URL update and avoiding the full navigation-guard
pipeline. Update the tab watcher/helper at
src/pages/warehouse/[id].namespace.[nsid].vue lines 358-364,
src/pages/warehouse/[id].namespace.[nsid].table.[tid].vue lines 344-350, and
src/pages/warehouse/[id].namespace.[nsid].view.[vid].vue lines 309-315
consistently, using a router-safe helper or tab-only guard bypass, and verify
Back/Forward navigation retains the selected tab.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a81a4de5-62bd-415d-93d3-41e6d81e4309

📥 Commits

Reviewing files that changed from the base of the PR and between e8b94fd and 3f50fa2.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (15)
  • package.json
  • src/assets/dependencies.json
  • src/auto-imports.d.ts
  • src/components/NavigationBar.vue
  • src/pages/governance/index.vue
  • src/pages/governance/tags/[id].vue
  • src/pages/identities.vue
  • src/pages/server-settings.vue
  • src/pages/warehouse/[id].namespace.[nsid].generic-table.[tid].vue
  • src/pages/warehouse/[id].namespace.[nsid].table.[tid].vue
  • src/pages/warehouse/[id].namespace.[nsid].view.[vid].vue
  • src/pages/warehouse/[id].namespace.[nsid].vue
  • src/pages/warehouse/[id].vue
  • src/pages/warehouse/index.vue
  • src/typed-router.d.ts

Comment on lines +35 to +42
const tab = ref((route.query.tab as string) || 'tags');

// Permission assignments are an OpenFGA concept — show the tab whenever the
// backend is OpenFGA (per-scope visibility is handled inside the explorer).
// Cedar surfaces policies instead; allow-all has no permission management.
const showPermissionsTab = computed(() => visual.getServerInfo()['authz-backend'] === 'openfga');

watch(tab, (t) => router.replace({ query: { ...route.query, tab: t } }));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Normalize and synchronize the selected tab.

Line 35 accepts any tab query value. ?tab=permissions on a non-OpenFGA backend, or ?tab=other, selects no rendered v-tabs-window-item and leaves the page blank. Normalize the query value to tags unless Permissions is available and selected. Also update tab when the route query or showPermissionsTab changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/governance/index.vue` around lines 35 - 42, Update the tab
initialization and synchronization around tab and showPermissionsTab so only
tags, or permissions when showPermissionsTab is true, can remain selected;
normalize invalid or unavailable query values to tags. Watch route.query.tab and
showPermissionsTab, update the local tab accordingly, and preserve the existing
router query synchronization without leaving the page with an unrendered tab.

Comment thread src/pages/warehouse/[id].namespace.[nsid].table.[tid].vue
Comment thread src/pages/warehouse/[id].namespace.[nsid].view.[vid].vue
Comment on lines +358 to +364
// Update the URL bar without going through router.replace(): that runs the full
// navigation-guard pipeline (including an awaited getServerInfo() network call)
// on every tab click, which was stalling/interrupting this page's tab transition.
// route.query.tab is only ever read once on mount, so a reactive route update
// isn't needed here — just keep the URL bookmarkable/shareable.
const href = router.resolve({ query: { ...route.query, tab: newTab } }).href;
window.history.replaceState(window.history.state, '', href);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

files=(
  'src/pages/warehouse/[id].namespace.[nsid].vue'
  'src/pages/warehouse/[id].namespace.[nsid].table.[tid].vue'
  'src/pages/warehouse/[id].namespace.[nsid].view.[vid].vue'
)

for file in "${files[@]}"; do
  printf '\n== %s ==\n' "$file"
  rg -n -C 5 'window\.history\.replaceState|router\.(push|replace)|watch\(tab' "$file"
done

Repository: lakekeeper/console

Length of output: 7615


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '== package deps ==\n'
if [ -f package.json ]; then
  node -e 'const p=require("./package.json"); console.log({ "vue-router": p.dependencies?.["vue-router"] || p.devDependencies?.["vue-router"], vuecli: p.dependencies?.["`@vue/cli-service`"] || p.devDependencies?.["`@vue/cli-service`"] });'
fi

printf '\n== route/tab guards and getServerInfo ==\n'
rg -n "getServerInfo|beforeEach|beforeResolve|tab|history\.state|replaceState|pushState" src -S

printf '\n== navigation files ==\n'
git ls-files 'src/**/*.vue' | rg 'warehouse/\[id\].namespace' | sed -n '1,40p'

Repository: lakekeeper/console

Length of output: 36171


🌐 Web query:

Vue Router 5.0.4 history.replaceState update history state current location source html5 pushState state

💡 Result:

Vue Router 5.0.4, released on March 19, 2026, continues to manage the browser's history state internally to track navigation context, such as scroll position and navigation direction [1][2][3]. When using HTML5 history mode, the router internally utilizes the window.history.pushState and window.history.replaceState APIs [4][5]. Developers interacting with history states should be aware of the following technical details: 1. Internal State Management: Vue Router relies on window.history.state to store its own metadata [6]. If you manually invoke window.history.replaceState or window.history.pushState, you risk overwriting this internal data [7][6]. 2. Preserving State: To modify the history state without breaking Vue Router's functionality, you must merge your custom state with the existing state [7][6]. The recommended pattern for replacing state is: history.replaceState({...history.state,...newState }, '') [6] 3. Router API Integration: Vue Router provides internal mechanisms to update history entries. While developers can use router.push and router.replace, they are designed to manage navigation. When custom state persistence is required alongside navigation, developers often combine router navigation with manual history state adjustments or rely on the router's internal state handling [8][9]. 4. Security and Browser Quirks: Vue Router wraps the browser's history methods in a try-catch block [5]. If an error occurs (such as Safari's security limitation on frequent state updates), the router may fallback to location.replace or location.assign to ensure the navigation completes [5]. Version 5.0.4 focuses on bug fixes—such as resolving iterator helper compatibility for Node 20 and fixing issues with named views and auto routes—and does not introduce changes to the core HTML5 history state handling logic [2][10].

Citations:


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re, urllib.request as u

router_html5 = Path('/tmp/vue-router-5.0.4-h5.ts')
url = 'https://raw.githubusercontent.com/vuejs/router/v5.0.4/packages/router/src/history/html5.ts'
u.urlretrieve(url, router_html5)
text = router_html5.read_text()

for name in ['_setHistoryStateWhenAvailable', '_updateCurrentLocation', 'replaceState']:
    idx = text.find(name)
    print(f'\n== {name} around index {idx} ==')
    print(text[max(0, idx-900):idx+1400])

print('\n== replaceState implementation references ==')
for m in re.finditer(r'(\n\s*replaceState[\s\S]{0,800}?\n\s*(?:_setHistoryStateWhenAvailable|\n\s*else)\n)', text):
    print(m.group(1))
PY

printf '\n== router.beforeEach excerpt ==\n'
sed -n '60,155p' src/router/index.ts

Repository: lakekeeper/console

Length of output: 2571


🌐 Web query:

Vue Router history state replaceState currentLocation router.push replace state.history.state source

💡 Result:

In Vue Router, navigation methods like router.push and router.replace are designed to mirror the native browser History API, specifically window.history.pushState and window.history.replaceState [1][2]. Key Concepts regarding History State and Navigation: Navigation Methods: router.push adds a new entry to the browser's history stack, while router.replace replaces the current history entry [1][2]. You can also perform a replacement by passing { replace: true } within the navigation object to router.push [1][2]. Managing History State: Vue Router internally utilizes the browser's history.state to persist information such as scroll position and navigation metadata [3][4]. Because the router relies on this object, direct manual manipulation of history.state can cause conflicts [3]. Adding Custom State: If you need to attach custom data to a history entry, you can pass a state property to router.push or router.replace [5][4]. Example usage: router.push({ path: '/home', state: { myCustomKey: 'value' } }) Updating State After Navigation: If you need to modify the history state after a navigation has already occurred, you can use the native history.replaceState API to merge new data with the existing state, which preserves internal router data [3][6]. Example: await router.push('/new-path') history.replaceState({...history.state, myCustomKey: 'newValue' }, '') Important Considerations: - Serializability: Any state passed must be serializable (e.g., no functions, Symbols, or non-plain objects) [5][4]. - Preservation: When using native history.replaceState, always spread existing state (history.state) to avoid overwriting internal router data, such as scroll position or key tracking [3][6]. - Consistency: These navigation methods work consistently across different router history modes (e.g., HTML5 History, Hash) [2][7].

Citations:


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import ssl, urllib.request as u, re

ctx = ssl._create_unverified_context()
router_html5 = Path('/tmp/vue-router-5.0.4-h5.ts')
url = 'https://raw.githubusercontent.com/vuejs/router/v5.0.4/packages/router/src/history/html5.ts'
u.urlretrieve(url, router_html5)
text = router_html5.read_text()

for symbol in ['currentLocation', 'replaceState', '_setHistoryStateWhenAvailable', '_updateCurrentLocation']:
    idx = text.find(symbol)
    print(f'\n== {symbol} ==')
    if idx == -1:
        continue
    print(text[max(0, idx-800):idx+1800])

print('\n== deterministic excerpt around replaceState/currentLocation usages ==')
matches = list(re.finditer(r'\n\s*(?:function|const|async)?\s*(?:replaceState|currentLocation|_updateCurrentLocation|_setHistoryStateWhenAvailable)\s*[\(:{]', text))
for mm in matches:
    print(f'{text[mm.start():mm.start()+120].strip()}')
PY

printf '\n== router guard relevant section ==\n'
sed -n '1,130p' src/router/index.ts

Repository: lakekeeper/console

Length of output: 2571


🌐 Web query:

Vue Router html5.ts replaceState call _setHistoryStateWhenAvailable currentLocation.value

💡 Result:

In Vue Router (specifically the Vue Router 4 html5.ts history implementation), replace is a method that updates the browser's history entry without adding a new one [1][2]. When replace is called, it performs the following sequence of operations: 1. State Construction: It creates a new state object by merging the existing history.state with a new buildState object (which preserves navigation details like forward/back pointers and the current position) and any additional user-provided data [1]. 2. changeLocation Execution: It calls changeLocation(to, state, true) [1]. - Inside changeLocation, the browser's history.replaceState(state, '', url) is executed [1]. - A try/catch block wraps this call to handle browser quirks (such as SecurityError exceptions) by falling back to location.replace(url) if necessary [1][3]. - The internal historyState.value is updated to reflect the new state [1]. 3. currentLocation Update: After the browser history is successfully updated, the currentLocation.value is updated to the new destination [1]. While _setHistoryStateWhenAvailable does not exist as a standard method in current Vue Router 4 source code, internal state management relies on tracking historyState.value (a Ref or reactive container) to ensure Vue Router's navigation system remains in sync with the actual window history [1][4]. This mechanism ensures that data like scroll positions and navigation direction are correctly maintained across push and replace operations [1][5].

Citations:


Keep query.tab in sync with Vue Router when updating the URL.

watch(tab, ...) changes the URL with history.replaceState, but Vue Router’s current location can still carry the previous tab during later navigation to the same page. For the affected warehouse pages, use a router-safe helper or a tab-only guard bypass and test Back/Forward behavior.

  • src/pages/warehouse/[id].namespace.[nsid].vue#L358-L364
  • src/pages/warehouse/[id].namespace.[nsid].table.[tid].vue#L344-L350
  • src/pages/warehouse/[id].namespace.[nsid].view.[vid].vue#L309-L315
📍 Affects 3 files
  • src/pages/warehouse/[id].namespace.[nsid].vue#L358-L364 (this comment)
  • src/pages/warehouse/[id].namespace.[nsid].table.[tid].vue#L344-L350
  • src/pages/warehouse/[id].namespace.[nsid].view.[vid].vue#L309-L315
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/warehouse/`[id].namespace.[nsid].vue around lines 358 - 364, Keep
Vue Router’s current query.tab synchronized while preserving the lightweight tab
URL update and avoiding the full navigation-guard pipeline. Update the tab
watcher/helper at src/pages/warehouse/[id].namespace.[nsid].vue lines 358-364,
src/pages/warehouse/[id].namespace.[nsid].table.[tid].vue lines 344-350, and
src/pages/warehouse/[id].namespace.[nsid].view.[vid].vue lines 309-315
consistently, using a router-safe helper or tab-only guard bypass, and verify
Back/Forward navigation retains the selected tab.

v-kessler and others added 4 commits August 7, 2026 13:44
Was pinned to the feat/governance-tags PR's commit SHA as an interim
measure before that PR released; point at the real tag now that it has.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Governance page: fall back to the Tags tab if ?tab=permissions is selected
  but the Permissions tab isn't actually available (non-OpenFGA backend, or
  landing before serverInfo has loaded) — previously left the page with no
  tab body rendered at all.
- Table/view detail pages: once a tab is visited, keep its content mounted
  (Vuetify's own window-item toggling handles show/hide) instead of
  unmounting it the instant you switch away. The immediate v-if swap meant
  the outgoing tab was already blank by the time the crossfade transition
  started, so it faded from nothing rather than from real content.

Skipped (already deliberately designed, re-verified): the tab-sync watcher
across namespace/table/view pages intentionally bypasses router.replace via
history.replaceState (that's what fixed the original tab-switch stutter —
router.replace runs the full navigation-guard pipeline, including an awaited
getServerInfo() call, on every click). route.query.tab is read once on mount,
nothing else depends on it reactively, and this was grepped/confirmed earlier
in the same work. No new information here changes that.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@v-kessler
v-kessler merged commit 9453042 into main Aug 7, 2026
7 checks passed
@v-kessler
v-kessler deleted the feat/governance-tags branch August 7, 2026 12:58
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