Skip to content

Hub page/tab switch motion — live-DOM fade; View Transitions API rejected on evidence (#521) - #524

Merged
richard-devbot merged 2 commits into
mainfrom
claude/view-transitions-521
Aug 1, 2026
Merged

Hub page/tab switch motion — live-DOM fade; View Transitions API rejected on evidence (#521)#524
richard-devbot merged 2 commits into
mainfrom
claude/view-transitions-521

Conversation

@richard-devbot

@richard-devbot richard-devbot commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Closes #521 · third implementation child of epic #499.

The interesting part: what got rejected, and why

The View Transitions API version was fully implemented and its own tests were green — then the full browser suite caught the #495 click-straddle and #494 dedup journeys going intermittent. An elementFromPoint probe (sampled every 40ms post-navigation) showed hit-testing returning <html> for the entire animation window (~160–250ms): a root capture suppresses painting and hit-testing of the live page, so every click right after a page switch vanished. That is the #495 swallowed-click class reintroduced by the platform itself — and ::view-transition { pointer-events: none; } cannot fix it (it stops the overlay capturing; it cannot restore hit-testing to suppressed content). Per the epic's iron rules, the no-swallowed-clicks contract wins. Full probe data on #521.

What shipped instead — same visual, zero interactivity cost

  • .page.active and .run-workspace-panel:not([hidden]) fade in via page-enter at the fast tier — on the live DOM, interactive every frame.
  • Only navigation toggles those selectors, so live snapshot repaints structurally cannot re-trigger the fade (pinned: one .active toggle site in the bundle, in navigation).
  • A normal CSS animation ⇒ the pinned global reduced-motion guard covers it; no JS gate.
  • The rejection is itself pinned: startViewTransition must appear nowhere in the bundle.
  • Rider hardening: showPage owns the workspace-section render, the history write derives from resolvedPage, and the Approvals projection: one decision renders as two pending items — dedup by (run, task, artifact), not raw queue id #494 journey's hand-rolled navigations wait for the page to land (correct under any swap timing).

The defining test

a click lands immediately after navigation, mid-fade — the exact contract the API could never pass, now a permanent browser journey.

Verification

Core 1829/1829 · browser 23/23 (including the previously-intermittent #494/#495 journeys, stable again) · lint 0 · typecheck 0 · validate 196 · security green · whitespace clean. TDD throughout — every new pin watched failing first, including the pointer-events intermediate attempt that the probe then invalidated.

Merging after green checks + reviewer bodies read, per the current working protocol.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Enhancements

    • Improved dashboard navigation with faster, smoother page and workspace-panel transitions.
    • Added subtle fade-in animations when switching views.
    • Navigation now preserves the correct workspace section and resets scroll position appropriately.
    • View changes remain responsive during rapid clicks and live updates.
    • Reduced-motion preferences are respected for accessibility.
  • Bug Fixes

    • Improved view loading behavior so content is ready before its details are inspected or displayed.

…cted (#521)

The incoming page (and workspace tab panel) fades in through the motion
tokens as a PLAIN animation on the live DOM. The View Transitions API was
implemented first and rejected on evidence: a root capture suppresses
painting and hit-testing of the live page for the whole animation window
(~160-250ms measured by elementFromPoint probing after navigation), so
every click right after a page switch vanished — the #495 swallowed-click
class reintroduced by the platform; pointer-events: none on the overlay
cannot restore suppressed content. The full browser suite caught it: the
#495 click-straddle and #494 dedup journeys went intermittent under the
API build. Evidence and probe data on #521.

The fade keeps the page interactive for every frame — pinned by the
defining browser contract the API could never pass: a click landing
immediately after navigation, mid-fade, works. Only navigation toggles
.active/the panel hidden attribute, so live repaints structurally cannot
re-trigger the entry animation (pinned); the global reduced-motion guard
covers it like any other animation (no JS gate); the rejection is pinned
by asserting startViewTransition appears nowhere in the bundle.

Also: showPage owns the workspace-section render (deduped from
showRunWorkspaceSection), the history write derives from resolvedPage,
and the #494 journey's hand-rolled navigations now wait for the page to
land — correct under any swap timing.

Closes #521. Part of epic #499.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@strix-security

strix-security Bot commented Aug 1, 2026

Copy link
Copy Markdown

Strix is installed on this repository, but we couldn't run this PR security review because this workspace's trial has ended. Add a card to resume code reviews here.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

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

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 21542698-29a3-463a-90ec-28f3f44c7c8e

📥 Commits

Reviewing files that changed from the base of the PR and between 83148fc and cbf26a2.

📒 Files selected for processing (2)
  • tests/browser/dashboard-494-approval-dedup.test.js
  • tests/browser/dashboard-521-transitions.test.js
📝 Walkthrough

Walkthrough

Dashboard navigation now uses synchronous live-DOM page switching with CSS fade-in animation. Workspace sections render through the same path. Route history uses the resolved page and active section. Tests cover timing, reduced motion, repaint behavior, and visibility waits.

Changes

Dashboard navigation fades

Layer / File(s) Summary
Synchronous navigation flow
src/observability/dashboard/ui/navigation.js
showPage renders the resolved destination before updating history and resetting scroll. Workspace section navigation delegates to showPage.
Live-DOM entry animation
src/observability/dashboard/ui/styles.js
Active pages and visible workspace panels use the page-enter fade animation with existing motion tokens.
Navigation behavior validation
tests/browser/dashboard-521-transitions.test.js, tests/hub-view-transitions-521.test.js, tests/browser/dashboard-494-approval-dedup.test.js
Tests cover immediate interaction, animation timing, reduced motion, repaint behavior, resolved route history, and waits for visible pages.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant DashboardNavigation
  participant LiveDOM
  participant BrowserHistory
  User->>DashboardNavigation: select page or workspace section
  DashboardNavigation->>LiveDOM: render resolved destination
  LiveDOM-->>User: show page-enter fade
  DashboardNavigation->>BrowserHistory: write resolved route
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: richardsongunde

Poem

A rabbit hops through pages bright,
Live fades bloom in gentle light.
Routes resolve, then history stays,
Reduced motion clears the haze.
No repaint repeats the entry glow.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR meets the motion objective but does not implement the native View Transitions API required by [#521]. Update [#521] to approve the live-DOM alternative, or implement View Transitions without swallowed clicks while preserving reduced-motion and repaint requirements.
✅ Passed checks (4 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 live-DOM fade implementation and its relationship to View Transitions API rejection.
Out of Scope Changes check ✅ Passed The navigation, animation, and browser-test changes directly support the motion and interaction requirements in [#521].
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/view-transitions-521

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.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Dashboard page/tab switch motion via live-DOM fade (reject View Transitions API)

✨ Enhancement 🧪 Tests 🐞 Bug fix 🕐 40+ Minutes

Grey Divider

AI Description

• Fade in the active dashboard page and workspace panel using live-DOM CSS animation tokens.
• Harden navigation ordering: render workspace section in showPage and route from resolvedPage.
• Add hub + browser tests pinning interactivity mid-fade and banning View Transitions API usage.
Diagram

graph TD
  U([User navigation click]) --> N["navigation.js: showPage/syncNavigationState"] --> D["DOM: .page.active & panel hidden"] --> C["styles.js: page-enter fade"]
  N --> H["History: writeDashboardRoute"]
  T["Tests: hub + browser"] --> N --> C
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use View Transitions API (startViewTransition)
2. JS-driven overlay animation on a wrapper element
  • ➕ Can avoid View Transitions root-capture behavior
  • ➕ Could allow more complex motion choreography
  • ➖ More moving parts (timers, cleanup, reduced-motion gating)
  • ➖ Higher risk of reintroducing hit-testing/stacking-context bugs during animation
3. CSS transition on opacity instead of keyframes animation
  • ➕ Simpler mental model in some cases (state-driven transitions)
  • ➕ May be easier to control with class toggles
  • ➖ Still requires careful control to avoid retrigger on repaints/state reapplication
  • ➖ PR already pins retrigger behavior with current animation approach

Recommendation: Keep the shipped live-DOM CSS fade. Given the explicit “no swallowed clicks” contract, View Transitions is a non-starter under observed root-capture hit-testing suppression, and the PR appropriately pins this rejection (bundle must not contain startViewTransition). The current approach stays synchronous, leverages the existing reduced-motion global guard, and is protected by browser journeys that validate mid-fade interactivity and non-retriggering on snapshot repaints.

Files changed (5) +163 / -3

Enhancement (1) +11 / -0
styles.jsAdd live-DOM fade-in animation for active pages and workspace panel +11/-0

Add live-DOM fade-in animation for active pages and workspace panel

• Applies a fast-tier opacity-only entry animation to .page.active and the visible run-workspace panel. Relies on the existing prefers-reduced-motion global guard to collapse animation when requested.

src/observability/dashboard/ui/styles.js

Bug fix (1) +16 / -3
navigation.jsRender workspace section in showPage; derive history writes from resolvedPage +16/-3

Render workspace section in showPage; derive history writes from resolvedPage

• Documents the rejection of the View Transitions API and the rationale for live-DOM motion. Moves workspace-section rendering into showPage and makes the history update derive from resolvedPage to avoid ordering dependence on ACTIVE_PAGE mutation.

src/observability/dashboard/ui/navigation.js

Tests (3) +136 / -0
dashboard-494-approval-dedup.test.jsWait for navigated pages to land before asserting DOM contents +10/-0

Wait for navigated pages to land before asserting DOM contents

• Adds explicit waits for the target page section to become visible after navigation before reading page content. Stabilizes the #494 journey against any navigation swap timing differences.

tests/browser/dashboard-494-approval-dedup.test.js

dashboard-521-transitions.test.jsAdd browser contracts for mid-fade clickability, token timing, reduced-motion, and no retrigger +73/-0

Add browser contracts for mid-fade clickability, token timing, reduced-motion, and no retrigger

• Introduces Playwright tests that assert clicks immediately after navigation still land mid-fade, the animation uses the fast motion token, reduced-motion collapses animation, and snapshot repaints do not restart the entry fade.

tests/browser/dashboard-521-transitions.test.js

hub-view-transitions-521.test.jsPin live-DOM fade mechanism and forbid startViewTransition in the bundle +53/-0

Pin live-DOM fade mechanism and forbid startViewTransition in the bundle

• Adds hub-level tests that assert the CSS selectors/keyframes exist, ensure only navigation toggles the active state, and verify the generated client bundle contains no startViewTransition call sites.

tests/hub-view-transitions-521.test.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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/observability/dashboard/ui/navigation.js`:
- Around line 241-245: Remove the redundant renderRunWorkspaceSection call from
the popstate history-navigation handler, while retaining the call in showPage
for resolved run-workspace routes. Ensure back and forward navigation performs
only the single render triggered by showPage.

In `@tests/browser/dashboard-494-approval-dedup.test.js`:
- Around line 45-46: Update the navigation comment near the visibility wait to
describe the current synchronous live-DOM CSS fade mechanism instead of
attributing it to a View Transitions callback or issue `#521`. Preserve the
existing wait behavior unchanged.

In `@tests/browser/dashboard-521-transitions.test.js`:
- Around line 23-30: Add a browser journey alongside the existing test in
dashboard-521-transitions.test.js that clicks a visible workspace tab, verifies
showRunWorkspaceSection, and targets the corresponding run-workspace-* panel
while its animation is active. Assert the panel is visible and immediately
interactive without a settling wait, preserving the same mid-fade interaction
coverage as the existing navigation test.
🪄 Autofix (Beta)

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bed91a37-e190-47a1-8d56-422d8a3588bf

📥 Commits

Reviewing files that changed from the base of the PR and between 893e2ee and 83148fc.

📒 Files selected for processing (5)
  • src/observability/dashboard/ui/navigation.js
  • src/observability/dashboard/ui/styles.js
  • tests/browser/dashboard-494-approval-dedup.test.js
  • tests/browser/dashboard-521-transitions.test.js
  • tests/hub-view-transitions-521.test.js

Comment on lines 241 to +245
if (resolvedPage === 'run-workspace') {
ACTIVE_RUN_SECTION = RUN_WORKSPACE_SECTIONS.indexOf(options.section) >= 0 ? options.section : (ACTIVE_PAGE === 'run-workspace' ? ACTIVE_RUN_SECTION : 'summary');
}
syncNavigationState(resolvedPage);
if (options.history !== false) writeDashboardRoute(ACTIVE_PAGE, SCOPE.run, ACTIVE_PAGE === 'run-workspace' ? ACTIVE_RUN_SECTION : '', 'push');
if (options.closeMobile !== false) closeMobileNavigation();
if (resolvedPage === 'run-workspace' && typeof renderRunWorkspaceSection === 'function') renderRunWorkspaceSection();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Remove the duplicate workspace render on history navigation.

When a popstate route resolves to run-workspace, showPage renders the section at Line 245. The popstate handler then renders it again at Line 382. Render the section only through showPage so back and forward navigation performs one DOM update.

Proposed fix
 window.addEventListener('popstate', function() {
   var route = readDashboardRoute();
   showPage(route.page || DEFAULT_PAGE, { history: false, closeMobile: false, section: route.section });
   restoreScopeFromRoute(route.run);
-  if (typeof renderRunWorkspaceSection === 'function') renderRunWorkspaceSection();
 });
🤖 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/observability/dashboard/ui/navigation.js` around lines 241 - 245, Remove
the redundant renderRunWorkspaceSection call from the popstate
history-navigation handler, while retaining the call in showPage for resolved
run-workspace routes. Ensure back and forward navigation performs only the
single render triggered by showPage.

Comment on lines +45 to +46
// #521 made the visual swap asynchronous (view-transition callback) —
// wait for the page to land before reading it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the navigation comment.

Line 45 states that #521 uses a View Transitions callback. This PR uses a synchronous live-DOM CSS fade. Keep the visibility wait, but describe the current mechanism.

🤖 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 `@tests/browser/dashboard-494-approval-dedup.test.js` around lines 45 - 46,
Update the navigation comment near the visibility wait to describe the current
synchronous live-DOM CSS fade mechanism instead of attributing it to a View
Transitions callback or issue `#521`. Preserve the existing wait behavior
unchanged.

Comment on lines +23 to +30
function browserTest(name, contextOptions, body) {
suite.browserTest(name, { seed: seedRichProject, contextOptions, tmpPrefix: 'rstack-browser-521-' }, body);
}

browserTest('a click lands immediately after navigation, mid-fade (#521)', {}, async ({ page, server }) => {
await gotoDashboard(page, server);
await navigate(page, 'traceability');
// No settling wait — this click arrives while the entry fade is running.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -0 -e js . tests | xargs -0 rg -n -C 3 \
  'run-workspace-tab|run-workspace-panel|showRunWorkspaceSection'

Repository: richard-devbot/SDLC-rstack

Length of output: 2413


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate test files =="
git ls-files | rg '(^|/)dashboard-521-transitions\.test\.js$|hub-view-transitions-521\.test\.js$|dashboard-run-workspace\.test\.js$' || true

echo
echo "== dashboard-521-transitions outline/content =="
if [ -f tests/browser/dashboard-521-transitions.test.js ]; then
  wc -l tests/browser/dashboard-521-transitions.test.js
  cat -n tests/browser/dashboard-521-transitions.test.js
fi

echo
echo "== hub-view-transitions snippet =="
if [ -f tests/hub-view-transitions-521.test.js ]; then
  wc -l tests/hub-view-transitions-521.test.js
  sed -n '1,80p' tests/hub-view-transitions-521.test.js | cat -n
fi

echo
echo "== dashboard-run-workspace relevant snippets =="
if [ -f tests/dashboard-run-workspace.test.js ]; then
  wc -l tests/dashboard-run-workspace.test.js
  sed -n '1,130p' tests/dashboard-run-workspace.test.js | cat -n
fi

echo
echo "== source references for workspace section implementation =="
rg -n -C 4 'showRunWorkspaceSection|run-workspace-tab|run-workspace-panel|workspace.*tab|run-workspace-tabs' src . --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' || true

echo
echo "== browser test helpers definitions =="
rg -n -C 4 'function browserTest|suite\.browserTest|gotoDashboard|navigate|page-enter|showRunWorkspaceSection' tests src --glob '*.{js,mjs,ts,tsx}' || true

Repository: richard-devbot/SDLC-rstack

Length of output: 50382


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== precise browser test symbols =="
rg -n -C 3 'browserTest|suite\.browserTest|gotoDashboard|navigate|showRunWorkspaceSection|run-workspace-section|run-workspace-tabs|showRunWorkspace' tests src --glob '*.js' --glob '*.mjs' --glob '*.ts' --glob '*.tsx' || true

echo
echo "== workspace section-related selectors =="
rg -n 'run-workspace-section|run-workspace-panel|run-workspace-tab|openDrawer|showRunWorkspaceSection' src tests --glob '*.{js,mjs,ts,tsx}' || true

Repository: richard-devbot/SDLC-rstack

Length of output: 43254


Add a browser journey for workspace-tab motion.

tests/browser/dashboard-521-transitions.test.js only covers /page-traceability page fades. It does not click a visible workspace tab, verify showRunWorkspaceSection, or check the target run-workspace-* panel animation and immediate interaction. Add a browser test for workspace-tab switching to cover the same mid-fade interactivity contract.

🤖 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 `@tests/browser/dashboard-521-transitions.test.js` around lines 23 - 30, Add a
browser journey alongside the existing test in dashboard-521-transitions.test.js
that clicks a visible workspace tab, verifies showRunWorkspaceSection, and
targets the corresponding run-workspace-* panel while its animation is active.
Assert the panel is visible and immediately interactive without a settling wait,
preserving the same mid-fade interaction coverage as the existing navigation
test.

Source: Coding guidelines

@qodo-code-review

qodo-code-review Bot commented Aug 1, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (2)

Context used
✅ Compliance rules (platform): 300 rules
✅ Skills: 17 invoked
  code-review-pr
  claude-api
  documentation-writing
  pptx
  docx
  performance-monitoring
  security-compliance
  cso
  plan-eng-review
  design-review
  prompt-engineering
  mcp-builder
  qa-testing
  code-patterns
  xlsx
  security-owasp
  testing-qa

Grey Divider


Remediation recommended

1. Reduced-motion duration misparsed ✓ Resolved 🐞 Bug ☼ Reliability
Description
The reduced-motion browser test uses parseFloat(animationDuration) and compares it as seconds, but
the reduced-motion CSS sets durations in milliseconds (.01ms), making the assertion unit-dependent
and potentially wrong/flaky across serialization engines.
Code

tests/browser/dashboard-521-transitions.test.js[R54-58]

+  const anim = await page.evaluate(() => {
+    const style = getComputedStyle(document.getElementById('page-traceability'));
+    return parseFloat(style.animationDuration);
+  });
+  assert.ok(anim < 0.001, `the global guard freezes the fade — got ${anim}s`);
Relevance

●●● Strong

Unit-aware parsing for animationDuration vs parseFloat() was previously accepted for reduced-motion
tests (PR #514).

PR-#514

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The reduced-motion stylesheet forces animation durations to .01ms, but the new test drops units
via parseFloat() and compares the result as seconds; an existing motion test already demonstrates
the unit-aware approach used elsewhere in the repo.

tests/browser/dashboard-521-transitions.test.js[51-61]
src/observability/dashboard/ui/styles.js[1734-1740]
tests/browser/dashboard-512-motion.test.js[45-53]
PR-#514

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The reduced-motion test in `tests/browser/dashboard-521-transitions.test.js` converts `getComputedStyle(...).animationDuration` using `parseFloat()` and treats the result as seconds. Under reduced motion the stylesheet forces `animation-duration: .01ms`, so a unitless parse can misinterpret `.01ms` as `0.01` seconds.

### Issue Context
The repo already has a unit-aware duration parser in the browser motion suite and the reduced-motion CSS explicitly uses `ms` units.

### Fix Focus Areas
- tests/browser/dashboard-521-transitions.test.js[51-61]
- src/observability/dashboard/ui/styles.js[1734-1740]
- tests/browser/dashboard-512-motion.test.js[45-53]

### Suggested fix
In the #521 reduced-motion test, return the raw `style.animationDuration` string from the page context, then normalize to seconds with a helper similar to `toSeconds()` in `dashboard-512-motion.test.js` (handle `ms` vs `s`, and comma-separated lists by taking the first value). Assert against the normalized seconds value.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Hardcoded timeouts and port ✓ Resolved 📜 Skill insight ⚙ Maintainability
Description
New tests introduce magic-number timeouts and a hardcoded port value, reducing clarity and making
future adjustments error-prone. These values should be extracted into named constants to document
intent and centralize configuration.
Code

tests/browser/dashboard-494-approval-dedup.test.js[R47-50]

+  await page.waitForFunction(() => {
+    const el = document.getElementById('page-approvals');
+    return el && el.offsetParent !== null;
+  }, null, { timeout: 5000 });
Relevance

●●● Strong

Team has accepted extracting hardcoded timeouts to named constants in browser tests (accepted in PR
#509).

PR-#509

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1400588 requires replacing magic numbers with named constants. The PR adds
timeout: 5000 and timeout: 3000 in browser tests, and uses clientScript(3008) with a hardcoded
port, all of which are configuration-like literals suitable for extraction into constants.

tests/browser/dashboard-494-approval-dedup.test.js[47-50]
tests/browser/dashboard-494-approval-dedup.test.js[69-72]
tests/browser/dashboard-521-transitions.test.js[67-67]
tests/hub-view-transitions-521.test.js[33-33]
tests/hub-view-transitions-521.test.js[43-43]
Skill: code-patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several new/modified tests use literal numeric values for timeouts and configuration (e.g., `5000`, `3000`, `3008`). Per the guideline, these should be replaced with descriptive named constants.

## Issue Context
These values are used as timeouts for `page.waitForFunction(...)` and as an argument to `clientScript(3008)`. Keeping them as literals obscures intent and makes coordinated changes harder.

## Fix Focus Areas
- tests/browser/dashboard-494-approval-dedup.test.js[47-50]
- tests/browser/dashboard-494-approval-dedup.test.js[69-72]
- tests/browser/dashboard-521-transitions.test.js[67-67]
- tests/hub-view-transitions-521.test.js[33-33]
- tests/hub-view-transitions-521.test.js[43-43]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Double render on popstate 🐞 Bug ➹ Performance
Description
showPage() now calls renderRunWorkspaceSection() when resolving to run-workspace, but the popstate
handler still calls renderRunWorkspaceSection() after showPage(), causing redundant DOM writes on
history navigations to run-workspace.
Code

src/observability/dashboard/ui/navigation.js[245]

+  if (resolvedPage === 'run-workspace' && typeof renderRunWorkspaceSection === 'function') renderRunWorkspaceSection();
Relevance

●● Moderate

No clear historical evidence on removing redundant render calls in navigation/popstate; no similar
accepted/rejected precedent found.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR-added call in showPage makes the existing popstate call redundant; the renderer performs DOM
mutations, so invoking it twice adds unnecessary work.

src/observability/dashboard/ui/navigation.js[238-251]
src/observability/dashboard/ui/navigation.js[378-383]
src/observability/dashboard/ui/pages/run-workspace.js[140-149]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`showPage()` now renders the run-workspace section by calling `renderRunWorkspaceSection()`. The `popstate` handler also calls `renderRunWorkspaceSection()` after invoking `showPage()`, resulting in a duplicate render on history navigation to `run-workspace`.

### Issue Context
`renderRunWorkspaceSection()` writes `aria-selected`, `tabindex`, and toggles `panel.hidden` across workspace panels, so calling it twice is redundant work.

### Fix Focus Areas
- src/observability/dashboard/ui/navigation.js[238-251]
- src/observability/dashboard/ui/navigation.js[378-383]
- src/observability/dashboard/ui/pages/run-workspace.js[140-149]

### Suggested fix
Delete the unconditional `renderRunWorkspaceSection()` call inside the `popstate` handler (or guard it so it only runs when `showPage()` did not already handle it).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. el variable name too generic 📜 Skill insight ⚙ Maintainability
Description
The new Playwright waits use a generic variable name (el), which reduces readability and makes the
code’s intent less clear. More descriptive names (e.g., approvalsPage, studioPage) improve
maintainability and reduce misinterpretation during future changes.
Code

tests/browser/dashboard-494-approval-dedup.test.js[R48-49]

+    const el = document.getElementById('page-approvals');
+    return el && el.offsetParent !== null;
Relevance

● Weak

Naming/“too generic” variable refactors are often rejected as low-value churn (e.g., rejected rename
helper in PR #511).

PR-#511

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1399635 requires descriptive variable names and flags generic names like
x/data/temp; the added waits introduce const el = ... for specific page sections, which is
non-descriptive.

tests/browser/dashboard-494-approval-dedup.test.js[45-50]
tests/browser/dashboard-494-approval-dedup.test.js[69-72]
Skill: code-patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The test introduces generic variable names (`el`) that are not descriptive.

## Issue Context
Per naming guidelines, variables should convey intent; in these waits, the variable represents a specific page section element.

## Fix Focus Areas
- tests/browser/dashboard-494-approval-dedup.test.js[48-49]
- tests/browser/dashboard-494-approval-dedup.test.js[70-71]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Missing regression attribution block 📜 Skill insight ⚙ Maintainability
Description
The new regression tests added for #521 (both the browser journey test and the unit-style guard
test) are missing required attribution details—specifically the date found and the QA report
path—reducing traceability and making it harder to audit and maintain why these tests exist
long-term.
Code

tests/browser/dashboard-521-transitions.test.js[R1-9]

+/**
+ * #521 — page-switch motion in the real DOM (epic #499, child 3).
+ *
+ * The live-DOM fade must never cost interactivity: the defining contract —
+ * the one that got the View Transitions API rejected — is that a click
+ * landing IMMEDIATELY after a navigation still works, mid-fade.
+ *
+ * owner: RStack developed by Richardson Gunde
+ */
Relevance

● Weak

Regression-test attribution metadata (date found/QA path) additions have been explicitly rejected in
multiple PRs (e.g., PR #508/#509).

PR-#508
PR-#509

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1400067 requires regression tests to include an attribution block with issue ID,
description, date found, and QA report path. In both newly added #521 test files, the header comment
includes the issue reference (#521) and narrative description of what broke, but does not include
either the date found or any QA report reference path, demonstrating non-compliance with the
standard.

tests/browser/dashboard-521-transitions.test.js[1-9]
tests/hub-view-transitions-521.test.js[1-14]
Skill: qa-testing

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Regression-style tests must include an attribution comment block containing: issue ID, what broke, date found, and QA report path. The newly added #521 regression tests (journey and unit/guard) include the issue ID and narrative context, but do not include the date found or a QA report reference path.

## Issue Context
These tests act as durable regression/contract coverage for #521 behavior (including the browser journey and the guard behavior around rejecting `startViewTransition` and related contracts), so they should be fully attributable per the regression-test documentation standard to support long-term auditing and maintenance.

## Fix Focus Areas
- tests/browser/dashboard-521-transitions.test.js[1-9]
- tests/hub-view-transitions-521.test.js[1-14]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread tests/browser/dashboard-494-approval-dedup.test.js Outdated
Comment thread tests/browser/dashboard-521-transitions.test.js
syncNavigationState(resolvedPage);
if (options.history !== false) writeDashboardRoute(ACTIVE_PAGE, SCOPE.run, ACTIVE_PAGE === 'run-workspace' ? ACTIVE_RUN_SECTION : '', 'push');
if (options.closeMobile !== false) closeMobileNavigation();
if (resolvedPage === 'run-workspace' && typeof renderRunWorkspaceSection === 'function') renderRunWorkspaceSection();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Informational

5. Double render on popstate 🐞 Bug ➹ Performance

showPage() now calls renderRunWorkspaceSection() when resolving to run-workspace, but the popstate
handler still calls renderRunWorkspaceSection() after showPage(), causing redundant DOM writes on
history navigations to run-workspace.
Agent Prompt
### Issue description
`showPage()` now renders the run-workspace section by calling `renderRunWorkspaceSection()`. The `popstate` handler also calls `renderRunWorkspaceSection()` after invoking `showPage()`, resulting in a duplicate render on history navigation to `run-workspace`.

### Issue Context
`renderRunWorkspaceSection()` writes `aria-selected`, `tabindex`, and toggles `panel.hidden` across workspace panels, so calling it twice is redundant work.

### Fix Focus Areas
- src/observability/dashboard/ui/navigation.js[238-251]
- src/observability/dashboard/ui/navigation.js[378-383]
- src/observability/dashboard/ui/pages/run-workspace.js[140-149]

### Suggested fix
Delete the unconditional `renderRunWorkspaceSection()` call inside the `popstate` handler (or guard it so it only runs when `showPage()` did not already handle it).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Qodo (both my own accepted precedents): the #494 journey's navigation
waits use the harness's PAGE_VISIBLE_TIMEOUT_MS (#509 convention), and
the reduced-motion fade assertion parses durations unit-aware (#514
pattern) instead of assuming seconds serialization.

Refs #521 (PR #524 review follow-up).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@richard-devbot

Copy link
Copy Markdown
Owner Author

Both Qodo findings addressed (each is a previously-accepted pattern of ours): the #494 waits use the harness's PAGE_VISIBLE_TIMEOUT_MS, and the reduced-motion assertion parses durations unit-aware per the #514 pattern. 5/5 affected tests green, lint clean. — Claude Main

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.

Hub page/tab switches via the View Transitions API (epic #499, child 3)

2 participants