Skip to content

feat(SFLW-47): React 18 → 19 ecosystem upgrade (dashboard frontend)#31

Merged
lbruton merged 17 commits into
mainfrom
feat/SFLW-47-react-19-upgrade
Jun 11, 2026
Merged

feat(SFLW-47): React 18 → 19 ecosystem upgrade (dashboard frontend)#31
lbruton merged 17 commits into
mainfrom
feat/SFLW-47-react-19-upgrade

Conversation

@lbruton

@lbruton lbruton commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

Coordinated React 19 migration of the dashboard frontend — nine packages bumped together as four sequenced, individually-green commits (bisect-clean), plus the safety infrastructure to prove parity:

Package From → To
react / react-dom 18.3.1 → 19.2.7
@types/react / @types/react-dom 18.3.x → 19.2.x
@vitejs/plugin-react 4.x → 5.2.0
react-router-dom 6.26 → 7.17.0 (library mode)
i18next 25.5 → 26.3.1 (induced by react-i18next 17)
react-i18next 15.7 → 17.0.8
@mdxeditor/editor 3.x → 4.0.2
vite held at ^7.2.7 (Vite 8 is a separate migration)

Also in this PR:

  • New dashboard type-check gatesrc/dashboard_frontend/tsconfig.json + npm run typecheck:dashboard. The frontend was never type-checked before; 7 React-type-adjacent errors fixed here, 18 pre-existing errors baselined → SFLW-52.
  • New seeded smoke E2E suitenpm run test:e2e:smoke: real backend + seeded project, 8-route render sweep with console-error assertions, MDX + mermaid render test. Written and green on React 18 before any bump (behavior pinning), green through every bump.
  • react-text-annotate-blend removed — upstream is dead (final release 1.2.0, peers React ≤18); it was the sole peer blocker. Its MIT TypeScript source is vendored at src/dashboard_frontend/src/vendor/react-text-annotate-blend/ (LICENSE included; transitive lodash.sortby replaced with a typed local sortBy). Net runtime deps: −1.
  • Version 3.9.0 → 3.10.0 + CHANGELOG entry.

Linked issue

Closes SFLW-47 (Plane — already set Done). New issues filed during the spec: SFLW-51 (vite dev-proxy WS localhost/127.0.0.1 mismatch, pre-existing), SFLW-52 (clear 18 baselined type-debt errors, then wire typecheck into build).

Test results (final tree, task 7)

  • vitest: 333/333 (= React 18 baseline)
  • typecheck:dashboard: 18 errors = documented pre-existing baseline, zero new
  • validate:i18n: clean
  • smoke E2E: 2/2; worktree E2E (SFLW-50): 3/3
  • default E2E: unchanged vs baseline (9 pre-existing failures in the unseeded legacy spec; new smoke specs self-skip outside their config)
  • npm audit (final lockfile): 0 across all severities; bundle +0.75% (budget ≤10%)
  • Live parity pass on a prod React 19 build: approval review→annotate→side-by-side→approve, language switch EN↔DE, single persistent WS connection

Review gates already run

  • Codacy CLI: 24 Warning-only Lizard findings — 19 baseline-verified pre-existing, 5 accepted-use in vendored source; 0 Critical/High
  • CodeRabbit: 3 rounds; all major findings fixed (helper dedup, isolatedModules); 1 suggestion rejected with in-code justification

Post-merge handoff

npm publish --access public is passkey-gated and must be run by the user after merge, then npm view @lbruton/specflow version + dashboard verification per the Post-Publish checklist.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved type safety for DOM references and timeout IDs
  • Chores

    • Bumped version to 3.10.0
    • Upgraded React to v19 and React Router to v7
    • Updated MDX editor dependency
    • Updated Node engine requirements to ^20.19.0 || >=22.12.0
  • Tests

    • Added dashboard smoke testing infrastructure

lbruton added 11 commits June 10, 2026 23:21
…E2E specs

Green on React 18 before any dependency bump (TDD parity baseline):
- playwright.smoke.config.ts: dual webServer (backend 5085 + frontend 5185),
  isolated SPEC_WORKFLOW_HOME under tmpdir
- dashboard-smoke-harness.ts: single-project seed (markdown + mermaid fixture)
- dashboard-route-sweep.spec.ts: all 8 routes render seeded content, no
  error boundary/blank frame, console-error assertions
- spec-viewer-mdx.spec.ts: MDX body + fenced mermaid render as SVG
- known pre-existing dev-proxy WS handshake failure filtered, tracked as SFLW-51
…ard)

The frontend was never type-checked (excluded from both root tsconfigs;
vite build only transpiles). Adds src/dashboard_frontend/tsconfig.json
(options mirrored from vscode-extension/tsconfig.app.json, React 19-proven)
and the typecheck:dashboard script.

React 18 baseline: 25 errors surfaced; fixed the 7 React-type-adjacent ones
(LogsPage heroicons icon prop typing, CSS-module declarations via vite/client
types). Remaining 18 are pre-existing generic type debt — documented in the
spec test-baseline.json and tracked as SFLW-52. Build wiring deferred until
the baseline is clean (SFLW-52); tasks 2-5 gate on no-new-errors vs baseline.
react-text-annotate-blend@1.2.0 is the final upstream release and peers
react ^16.8||^17||^18 only - the sole peer blocker for React 19 (confirmed
via combined dry-run). User-approved resolution: vendor the MIT-licensed
TypeScript source into src/dashboard_frontend/src/vendor/ (LICENSE included),
switch ApprovalsAnnotator + SideBySideView imports, and remove the npm
dependency (also drops transitive lodash.sortby - replaced with a 15-line
typed stable sortBy). Net runtime deps: -1. Vendored code passes the strict
dashboard type-check; gates green on React 18 (unit 333/333, build, smoke 2/2).
react@19.2.7, react-dom@19.2.7, @types/react@^19.2.17,
@types/react-dom@^19.2.3, @vitejs/plugin-react@^5.2.0. Vite HELD at ^7.2.7.

Clean peer resolution (no overrides, no legacy-peer-deps) - possible after
the react-text-annotate-blend vendoring in the previous commit.

Type fallout: exactly 2 new errors, both useScrollSync.ts - React 19's
useRef(null) now returns RefObject<T | null>; interface updated to match.
typecheck:dashboard back at the documented 18-error baseline (SFLW-52).
main.tsx confirmed zero changes (createRoot + StrictMode already in use).

Gates: install clean, typecheck at baseline, build green, vitest 333/333,
smoke E2E 2/2 (route sweep + MDX render passing under React 19).
Library mode only - no framework-mode patterns adopted. All 7 consumer
files (HashRouter in main.tsx; Routes/Route/Navigate in App; NavLink in
PageNavigationSidebar; useSearchParams in Logs/Approvals/Tasks/SpecViewer)
compile and run unchanged: v6 signatures preserved in v7, import paths valid.

Gates: install clean, typecheck at 18-error baseline (zero new), build
green, vitest 333/333, smoke E2E 2/2 - route sweep covers all 8 routes
with seeded data under router 7.
The induced pair: react-i18next@17 peers i18next >= 26.2.0. Lockfile-only
change - src/i18n.ts init options (resources, fallbackLng, detection,
interpolation.escapeValue: true, react.useSuspense) are all stable v26
options; escapeValue retained for defense-in-depth.
i18next-browser-languagedetector@8.2.1 dedupes cleanly against v26.

Gates: install clean, validate:i18n no missing keys, typecheck at 18-error
baseline (zero new), build green, vitest 333/333, smoke E2E 2/2.
Highest-risk integration lands last and isolated. All 29 imported symbols
(plus MDXEditorMethods/CodeBlockEditorDescriptor types) exist unchanged in
v4 - zero edits to MDXEditorWrapper.tsx / mermaidPlugin.tsx.

Two incidental type fixups: v4 dropped a transitive @types/node, exposing
Node globals in frontend code. Replaced with browser idioms:
- I18nErrorBoundary: process.env.NODE_ENV === 'development' -> import.meta.env.DEV
- ApprovalsPage: NodeJS.Timeout -> ReturnType<typeof setTimeout>

Gates: install clean, typecheck at 18-error baseline (zero new), build
green, vitest 333/333, smoke E2E 2/2 - spec-viewer-mdx confirms markdown
body + fenced mermaid render as SVG under @MDXEditor 4.
- selectProject deduplicated into dashboard-smoke-harness.ts, imported by
  both smoke specs (CodeRabbit major finding)
- documented WHY the MCP child keeps stdin as 'pipe' (stdio MCP exits on
  stdin EOF) - CodeRabbit's 'ignore' suggestion would kill the server at
  boot; rejected with in-code justification
- smoke suite re-verified green (2/2)
…it r2)

- DASHBOARD_API_BASE_URL, PRE_EXISTING_WS_PROXY_ERROR, and
  collectConsoleErrors moved into dashboard-smoke-harness.ts; both specs
  import the shared symbols
- waitForProject now records non-ok HTTP responses and fetch errors in
  lastBody so registration timeouts are debuggable
- smoke suite re-verified green (2/2)
isolatedModules: true (esbuild/vite transpiles file-by-file - tsc now
flags patterns esbuild cannot handle) and
forceConsistentCasingInFileNames: true. Typecheck unchanged at the
18-error documented baseline; build + smoke green.
- package.json + package-lock.json 3.9.0 -> 3.10.0 (minor, per spec)
- CHANGELOG.md: [3.10.0] entry covering the React 19 nine-package bump,
  the new typecheck:dashboard gate, the seeded smoke E2E suite, and the
  react-text-annotate-blend removal-by-vendoring; prior [Unreleased]
  items folded in (they ship with this release)
- grep-verified: zero stale 3.9.0 in version-bearing files;
  vscode-extension independently versioned (1.1.7, sync-checked)
Copilot AI review requested due to automatic review settings June 11, 2026 05:28
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ed82cd61-f2c0-4d4c-a175-451cf48a8513

📥 Commits

Reviewing files that changed from the base of the PR and between 752f23e and a010afc.

📒 Files selected for processing (4)
  • e2e/helpers/dashboard-smoke-harness.ts
  • e2e/helpers/process-utils.ts
  • e2e/helpers/worktree-harness.ts
  • package.json

Walkthrough

The PR adds comprehensive E2E smoke testing infrastructure, modernizes the frontend stack to React 19 and Router v7, and refactors test harnesses to share common subprocess and Playwright configuration utilities. It introduces deterministic seeded smoke tests that validate dashboard routes and MDX spec rendering.

Changes

E2E Smoke Testing Infrastructure and Frontend Modernization

Layer / File(s) Summary
Package version, scripts, and dependency upgrades
package.json
Bumps version to 3.10.0, adds smoke and typecheck scripts, upgrades React/Router ecosystem and MDX editor, updates matching type packages and Vite plugin.
Frontend vendor imports and type refinements
src/dashboard_frontend/src/components/I18nErrorBoundary.tsx, src/dashboard_frontend/src/modules/approvals/{ApprovalsAnnotator,SideBySideView}.tsx, src/dashboard_frontend/src/modules/approvals/hooks/useScrollSync.ts, src/dashboard_frontend/src/modules/pages/ApprovalsPage.tsx, src/dashboard_frontend/tsconfig.json
Redirects TextAnnotate to vendored copy, switches to import.meta.env.DEV, makes React refs nullable, aligns setTimeout types, and adds dashboard TypeScript config.
Shared process and subprocess utilities
e2e/helpers/process-utils.ts
Introduces RegisteredProject/CommandResult types, platform-specific npm/git constants, runCommand/killProcess with graceful escalation, spawnMcpProcess with bounded logging, pollProjectsList with retry/diagnostics, and cleanupProcessesAndTemp lifecycle management.
Dual-server Playwright configuration helper
e2e/helpers/dual-server-config.ts
Adds createDualServerConfig that accepts ports/workflow-home/testMatch and returns unified Playwright config with dual webServers and environment wiring.
Dashboard smoke harness helpers and UI automation
e2e/helpers/dashboard-smoke-harness.ts (helpers section)
Defines smoke constants (spec name, API base, markers), implements collectConsoleErrors with known-error filtering, and provides selectProject and openSeededDashboard UI automation helpers.
Smoke harness fixtures and DashboardSmokeHarness lifecycle
e2e/helpers/dashboard-smoke-harness.ts (harness section)
Provides deterministic fixture templates, implements DashboardSmokeHarness lifecycle (setup temp repo/git, seedProject spec/approval files, startMcpServer spawn/poll, cleanup), and bootSmokeHarness orchestrator that manages SPEC_WORKFLOW_HOME state.
Smoke Playwright config and E2E smoke test specs
playwright.smoke.config.ts, e2e/dashboard-route-sweep.spec.ts, e2e/spec-viewer-mdx.spec.ts
Creates smoke config with SPECFLOW_SMOKE_CONFIG guard and dual-server setup; adds route-sweep spec asserting 8 HashRouter routes render without console errors; adds spec-viewer-mdx spec verifying seeded markdown and mermaid SVG rendering.
Worktree harness refactoring to use shared utilities
playwright.worktree.config.ts, e2e/helpers/worktree-harness.ts
Refactors worktree config to use createDualServerConfig, updates WorktreeHarness to delegate to spawnMcpProcess/pollProjectsList, removes local command/process helpers, adjusts startup sequencing, adds expectedProjectNames filtering, and simplifies cleanup.
Test helper consolidation
e2e/worktree-no-shared.spec.ts
Removes duplicate local selectProject and imports shared helper from dashboard-smoke-harness.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • lbruton/SpecFlow#29: Modifies e2e/helpers/worktree-harness.ts project matching logic to use projectName instead of projectPath, aligning with the refactor in this PR that migrates to the same naming convention for flexible project filtering.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 and specifically describes the main change: a React 18 to React 19 upgrade for the dashboard frontend, with the JIRA ticket reference.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feat/SFLW-47-react-19-upgrade

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

@codacy-production

codacy-production Bot commented Jun 11, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 35 complexity · 2 duplication

Metric Results
Complexity 35 (≤ 100 complexity)
Duplication 2 (≤ 5 duplication)

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

Copilot AI 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.

Pull request overview

This PR upgrades the dashboard frontend to the React 19 ecosystem, adds a dedicated dashboard TypeScript typecheck gate, and introduces a seeded “smoke” Playwright E2E suite to pin UI parity across the migration. It also removes the unmaintained react-text-annotate-blend dependency by vendoring its MIT TypeScript source into the dashboard codebase.

Changes:

  • Upgrade React/react-dom and key frontend ecosystem packages (router, i18n, Vite React plugin, MDX editor) and remove the dead peer-blocking dependency by vendoring it.
  • Add src/dashboard_frontend/tsconfig.json plus npm run typecheck:dashboard to typecheck the dashboard frontend.
  • Add seeded Playwright smoke configuration + tests to validate core dashboard routes and MDX/mermaid rendering against real seeded backend data.

Reviewed changes

Copilot reviewed 13 out of 25 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/dashboard_frontend/tsconfig.json Introduces a dedicated strict TS config for dashboard typechecking (bundler mode, noEmit).
src/dashboard_frontend/src/vendor/react-text-annotate-blend/** Vendors react-text-annotate-blend source and replaces lodash.sortby with a local sortBy.
src/dashboard_frontend/src/modules/pages/LogsPage.tsx Updates artifact icon typing to align with SVG icon component signatures.
src/dashboard_frontend/src/modules/pages/ApprovalsPage.tsx Fixes timeout handle typing for browser builds (ReturnType<typeof setTimeout>).
src/dashboard_frontend/src/modules/approvals/SideBySideView.tsx Switches annotate import to the vendored implementation.
src/dashboard_frontend/src/modules/approvals/hooks/useScrollSync.ts Adjusts ref typings for React 19 type definitions.
src/dashboard_frontend/src/modules/approvals/ApprovalsAnnotator.tsx Switches annotate import to the vendored implementation.
src/dashboard_frontend/src/components/I18nErrorBoundary.tsx Uses Vite’s import.meta.env.DEV for dev-only error details rendering.
playwright.smoke.config.ts Adds a dedicated Playwright config that boots seeded backend + frontend for smoke specs.
e2e/helpers/dashboard-smoke-harness.ts Adds a harness to seed a real git project/spec/approval and boot MCP for smoke testing.
e2e/dashboard-route-sweep.spec.ts Adds an 8-route render sweep asserting non-empty content + no unexpected console errors.
e2e/spec-viewer-mdx.spec.ts Adds an MDX/mermaid rendering smoke test with console error assertions.
package.json Bumps version to 3.10.0; upgrades deps; adds test:e2e:smoke and typecheck:dashboard.
package-lock.json Locks upgraded package graph corresponding to React 19 ecosystem changes.
CHANGELOG.md Documents the React 19 migration, new typecheck gate, smoke E2E suite, and dependency removal.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread package.json
@lbruton lbruton marked this pull request as ready for review June 11, 2026 13:10

@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: 1

🤖 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 `@e2e/dashboard-route-sweep.spec.ts`:
- Around line 35-55: Extract the duplicated beforeAll setup into a shared helper
function (e.g., setupSmokeTest) that accepts projectDirName and returns {
harness, project }; move the common logic (reading SPEC_WORKFLOW_HOME, rm/mkdir
specWorkflowHome, creating new DashboardSmokeHarness with serverRoot,
dashboardApiBaseUrl, specWorkflowHome and projectDirName, then await
harness.setup() and const project = await harness.startMcpServer()) into that
helper; update each test file's test.beforeAll to call
setupSmokeTest('smoke-routes') or setupSmokeTest('smoke-mdx') and assign the
returned harness and project to the existing variables so beforeAll only sets
timeout and delegates the setup.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2859b3ec-3add-4375-b3b4-09f87ba37085

📥 Commits

Reviewing files that changed from the base of the PR and between 368eb9d and bf83a26.

⛔ Files ignored due to path filters (13)
  • CHANGELOG.md is excluded by !CHANGELOG.md
  • package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
  • src/dashboard_frontend/src/vendor/react-text-annotate-blend/LICENSE is excluded by !**/vendor/**
  • src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/Annotator.tsx is excluded by !**/vendor/**
  • src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/Mark.tsx is excluded by !**/vendor/**
  • src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/SplitTag.tsx is excluded by !**/vendor/**
  • src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/TextAnnotate.tsx is excluded by !**/vendor/**
  • src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/TextAnnotateBlend.tsx is excluded by !**/vendor/**
  • src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/utils/annotation-utils.ts is excluded by !**/vendor/**
  • src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/utils/blend.ts is excluded by !**/vendor/**
  • src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/utils/utils.ts is excluded by !**/vendor/**
  • src/dashboard_frontend/src/vendor/react-text-annotate-blend/index.ts is excluded by !**/vendor/**
  • src/dashboard_frontend/src/vendor/react-text-annotate-blend/types/annotate-types.ts is excluded by !**/vendor/**
📒 Files selected for processing (12)
  • e2e/dashboard-route-sweep.spec.ts
  • e2e/helpers/dashboard-smoke-harness.ts
  • e2e/spec-viewer-mdx.spec.ts
  • package.json
  • playwright.smoke.config.ts
  • src/dashboard_frontend/src/components/I18nErrorBoundary.tsx
  • src/dashboard_frontend/src/modules/approvals/ApprovalsAnnotator.tsx
  • src/dashboard_frontend/src/modules/approvals/SideBySideView.tsx
  • src/dashboard_frontend/src/modules/approvals/hooks/useScrollSync.ts
  • src/dashboard_frontend/src/modules/pages/ApprovalsPage.tsx
  • src/dashboard_frontend/src/modules/pages/LogsPage.tsx
  • src/dashboard_frontend/tsconfig.json

Comment thread e2e/dashboard-route-sweep.spec.ts
lbruton added 2 commits June 11, 2026 08:32
…lication gate)

The smoke harness/config/specs were modeled on the worktree equivalents by
copying - jscpd measured 12 new clone pairs. Extracted the shared
infrastructure instead:

- e2e/helpers/process-utils.ts: runCommand, killProcess, spawnMcpProcess
  (stdin-pipe contract documented), pollProjectsList (with non-ok/fetch-error
  capture), cleanupProcessesAndTemp, RegisteredProject - consumed by BOTH
  worktree-harness.ts and dashboard-smoke-harness.ts (worktree harness sheds
  ~120 duplicated lines)
- e2e/helpers/dual-server-config.ts: shared dual-webServer Playwright config
  factory; playwright.worktree.config.ts and playwright.smoke.config.ts are
  now ~10-line parameterizations
- bootSmokeHarness + openSeededDashboard helpers kill the twin beforeAll/test
  prologues in the two smoke specs
- worktree-no-shared.spec.ts now imports the shared selectProject instead of
  carrying its own copy

New-clone count vs main: 12 -> ~2 (remaining: import block + describe shell).
Suites re-verified: smoke 2/2, worktree 3/3, vitest 333/333.
src/dashboard_frontend/src/vendor/** added to .codacy.yml exclude_paths
(and the local config mirror). The vendored react-text-annotate-blend copy
is upstream MIT code kept verbatim (minimal-divergence policy) - its ~190
total CCN and internal splitWithOffsets twins are upstream-authored and
should not be graded as first-party code. Standard ExternalCode
classification, consistent with the existing vscode-extension/webview-dist
and dist excludes. No gate thresholds changed.

@codacy-production codacy-production 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.

Pull Request Overview

This PR completes the migration of the dashboard frontend to React 19, including upgrades for React Router v7 and MDX Editor v4. It introduces essential quality gates, such as a dedicated type-checking script and a seeded E2E smoke test suite. However, the overall quality is currently rated as not up to standards by Codacy, primarily due to pre-existing complexity and newly introduced logic issues in vendored components.

The most critical findings concern the 'react-text-annotate-blend' utility. A bug in the 'isNumber' check will cause index 0 to be dropped during text splitting, and the 'blender' function currently mutates React state objects directly. Additionally, the 'LogsPage' component has surfaced as a high-risk area; it is both highly complex (cyclomatic complexity of 17) and lacks test coverage, which may lead to regressions under React 19's new rendering behavior. These issues should be addressed before merging to ensure a stable transition.

About this PR

  • The PR baselines 18 pre-existing type errors (SFLW-52). While this prevents new regressions from entering the dashboard, the CI gate will not be fully clean until this technical debt is resolved.

Test suggestions

  • Verify 8 dashboard routes render successfully with seeded data and no console errors
  • Validate MDX and Mermaid diagram rendering in the spec viewer
  • Execute dashboard-specific type-checking (noEmit)
  • Verify dual-server Playwright configuration (backend + vite frontend ports)
  • Implement unit tests for LogEntryCard to cover complex status logic and artifact grouping
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Implement unit tests for LogEntryCard to cover complex status logic and artifact grouping

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment thread src/dashboard_frontend/src/modules/approvals/hooks/useScrollSync.ts
Comment thread e2e/dashboard-route-sweep.spec.ts

Copilot AI 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.

Pull request overview

Copilot reviewed 20 out of 32 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

package.json:8

  • Several upgraded frontend deps now declare Node engines >=20 (e.g. react-router 7.x) and @vitejs/plugin-react 5.2.0 specifically requires ^20.19.0 || >=22.12.0 (see package-lock). The repo currently doesn’t advertise this in package.json, which can lead to confusing installs/build failures for users still on the documented Node 18+ baseline.
  "name": "@lbruton/specflow",
  "version": "3.10.0",
  "description": "MCP server for spec-driven development workflow with real-time web dashboard",
  "main": "dist/index.js",
  "type": "module",
  "bin": {
    "specflow": "dist/index.js"

Comment thread e2e/helpers/dashboard-smoke-harness.ts

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
e2e/helpers/worktree-harness.ts (1)

161-165: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope the first readiness gate to wt-a only.

Line 189 always includes both basenames, so waitForProjects(1, 45000) will accept either wt-a or wt-b. If a leaked MCP from another run still has wt-b registered, startMcpServers() can advance before the newly started wt-a is actually ready, which weakens the isolation ordering this helper is trying to enforce.

Proposed fix
   async startMcpServers(): Promise<void> {
     await this.startMcpForPath(this.wtAPath);
-    await this.waitForProjects(1, 45000);
+    await this.waitForProjects(1, 45000, [basename(this.wtAPath)]);
     await this.startMcpForPath(this.wtBPath);
   }
@@
-  async waitForProjects(expectedCount = 2, timeoutMs = 60000): Promise<RegisteredProject[]> {
+  async waitForProjects(
+    expectedCount = 2,
+    timeoutMs = 60000,
+    expectedProjectNames = [basename(this.wtAPath), basename(this.wtBPath)],
+  ): Promise<RegisteredProject[]> {
@@
-    const expectedNames = new Set([basename(this.wtAPath), basename(this.wtBPath)]);
+    const expectedNames = new Set(expectedProjectNames);
As per coding guidelines, `e2e/**`: "Playwright E2E tests. Verify batch-approvals and worktree-isolation semantics if either is touched."

Also applies to: 183-203

🤖 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 `@e2e/helpers/worktree-harness.ts` around lines 161 - 165, startMcpServers()
advances readiness too broadly because waitForProjects(1, 45000) will accept
either wt-a or wt-b; change the first readiness gate so it only accepts the wt-a
instance by scoping the wait to this.wtAPath's basename (e.g., add or use an
optional name filter on waitForProjects to assert presence of the wt-a project
after startMcpForPath(this.wtAPath)), then start wt-b and perform the subsequent
wait as before; apply the same scoping fix to the other readiness checks in the
same block (the related code around startMcpServers / startMcpForPath /
waitForProjects in the 183-203 region).

Source: Coding guidelines

🤖 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 `@e2e/helpers/dashboard-smoke-harness.ts`:
- Around line 66-73: In openSeededDashboard, start collecting console errors
before the initial navigation so early errors aren't missed: create the
consoleErrors array and call collectConsoleErrors(page, consoleErrors) before
page.goto('/') (and before calling selectProject), then proceed with
page.goto('/') and selectProject(page, projectId) as before and finally return
consoleErrors; reference functions/identifiers: openSeededDashboard,
collectConsoleErrors, page.goto, selectProject, and consoleErrors.

In `@e2e/helpers/process-utils.ts`:
- Around line 103-114: The logs for child processes are missing the worktree
identifier; update the appendLog helper so every entry includes logLabel (e.g.,
change the pushed string inside appendLog to include logLabel along with
source), ensuring child.stdout/on('data') and child.stderr/on('data') still call
appendLog and the existing child.on('error') message remains unchanged; update
the string formatting in appendLog to something like a prefixed
"[{logLabel}][{source}] ..." so all logs in the logs array are attributable to
the correct worktree.
- Around line 137-156: pollProjectsList currently calls fetch(url) inside the
loop without a per-request timeout so a single hung request can block
re-evaluating the overall deadline; fix it by creating an AbortController for
each iteration, compute remainingMs = timeoutMs - (Date.now() - startedAt), use
controller.signal passed to fetch and set a per-request timer that calls
controller.abort() after remainingMs (or a minimum of 0), ensure you clear that
timer and handle AbortError in the existing catch (updating lastBody
appropriately), and then proceed with the existing matcher/response logic;
reference the pollProjectsList function and the variables startedAt, timeoutMs,
url, lastBody, and matcher when making these changes.

---

Outside diff comments:
In `@e2e/helpers/worktree-harness.ts`:
- Around line 161-165: startMcpServers() advances readiness too broadly because
waitForProjects(1, 45000) will accept either wt-a or wt-b; change the first
readiness gate so it only accepts the wt-a instance by scoping the wait to
this.wtAPath's basename (e.g., add or use an optional name filter on
waitForProjects to assert presence of the wt-a project after
startMcpForPath(this.wtAPath)), then start wt-b and perform the subsequent wait
as before; apply the same scoping fix to the other readiness checks in the same
block (the related code around startMcpServers / startMcpForPath /
waitForProjects in the 183-203 region).
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1acf8d4a-2978-468f-88c1-34b96b756c26

📥 Commits

Reviewing files that changed from the base of the PR and between bf83a26 and 3df0292.

📒 Files selected for processing (11)
  • .codacy.yml
  • .codacy/codacy.config.json
  • e2e/dashboard-route-sweep.spec.ts
  • e2e/helpers/dashboard-smoke-harness.ts
  • e2e/helpers/dual-server-config.ts
  • e2e/helpers/process-utils.ts
  • e2e/helpers/worktree-harness.ts
  • e2e/spec-viewer-mdx.spec.ts
  • e2e/worktree-no-shared.spec.ts
  • playwright.smoke.config.ts
  • playwright.worktree.config.ts

Comment thread e2e/helpers/dashboard-smoke-harness.ts
Comment thread e2e/helpers/process-utils.ts
Comment thread e2e/helpers/process-utils.ts
Top-level exclude_paths only covers tool/issue analysis - the cyclomatic
complexity ('metric') and duplication engines read their own
engines.<name>.exclude_paths lists (per Codacy configuration-file docs).
Observed on PR #31: vendor issues were correctly excluded while
deltaComplexity still counted the vendored files. The UI Ignored-files
setting is inapplicable when a config file exists, so this is the only
correct mechanism.

Copilot AI 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.

Pull request overview

Copilot reviewed 20 out of 32 changed files in this pull request and generated no new comments.

lbruton added a commit that referenced this pull request Jun 11, 2026
…es (#32)

* chore: exclude vendored third-party source from Codacy analysis engines

Adds src/dashboard_frontend/src/vendor/** (the MIT-licensed vendored copy
of the dead react-text-annotate-blend package, landing in SFLW-47 PR #31)
to .codacy.yml:
- top-level exclude_paths (tool/issue analysis)
- engines.metric.exclude_paths (cyclomatic-complexity deltas)
- engines.duplication.exclude_paths (clone deltas)
plus the local config mirror.

Must land on main first: Codacy reads metrics-engine configuration from
the default branch, so PR #31 cannot apply this exclusion to its own
quality-gate computation (observed: vendor issues excluded per-PR, but
deltaComplexity still counted vendored files). Standard ExternalCode
classification; no gate thresholds changed.

* chore: mirror artifact/generated paths into metric+duplication engine excludes

Per review suggestion on #32: the engine-level exclusion lists don't
inherit top-level exclude_paths, so build artifacts (dist, webview-dist,
coverage, node_modules, assets) are mirrored there too to keep metrics
first-party-only.
lbruton added 2 commits June 11, 2026 10:26
Resolves .codacy.yml conflict by taking main's version (the superset from
PR #32: engine-level excludes incl. build-artifact paths).
- package.json: declare engines.node ^20.19.0 || >=22.12.0 (matches vite 7.3
  and @vitejs/plugin-react 5.2 requirements - makes the toolchain's Node
  baseline discoverable instead of failing mid-install)
- openSeededDashboard: attach console/pageerror collectors BEFORE the first
  page.goto so app-boot errors are captured (no false-green smoke runs)
- process-utils: per-attempt AbortController in pollProjectsList so a hung
  fetch cannot outlive the overall deadline; capped final sleep; MCP log
  entries now prefixed with logLabel so interleaved multi-server logs are
  attributable
- worktree-harness: first readiness gate scoped to wt-a's name so a leaked
  wt-b registration from a prior run cannot satisfy the wait

Gates: typecheck 18-baseline, vitest 333/333, smoke 2/2, worktree 3/3.

Copilot AI 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.

Pull request overview

Copilot reviewed 18 out of 30 changed files in this pull request and generated no new comments.

…er React 19

The ArtifactSection icon-prop widening (ComponentType<{className: string}> ->
ComponentType<SVGProps>) was added in task 1 to clear 6 heroicons
propTypes-variance errors under @types/react 18 - BEFORE the bump.
@types/react 19 removed propTypes from component typing, so main's original
line typechecks clean on the upgraded tree (verified: 18-error baseline holds).

Reverting makes LogsPage.tsx byte-identical to main, removing it from the PR
diff. This also clears the phantom +93 complexity delta Codacy attributed to
the file (its entire pre-existing complexity was counted as 'introduced'
because a one-line type edit pulled it into the diff).

Copilot AI 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.

Pull request overview

Copilot reviewed 17 out of 29 changed files in this pull request and generated no new comments.

@lbruton lbruton merged commit 070a6e4 into main Jun 11, 2026
6 checks passed
@lbruton lbruton deleted the feat/SFLW-47-react-19-upgrade branch June 11, 2026 16:40
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.

2 participants