feat(SFLW-47): React 18 → 19 ecosystem upgrade (dashboard frontend)#31
Conversation
…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)
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (4)
WalkthroughThe 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. ChangesE2E Smoke Testing Infrastructure and Frontend Modernization
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | ✅ 35 (≤ 100 complexity) |
| Duplication | ✅ 2 (≤ 5 duplication) |
AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
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.jsonplusnpm run typecheck:dashboardto 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.
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (13)
CHANGELOG.mdis excluded by!CHANGELOG.mdpackage-lock.jsonis excluded by!**/package-lock.json,!**/package-lock.jsonsrc/dashboard_frontend/src/vendor/react-text-annotate-blend/LICENSEis excluded by!**/vendor/**src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/Annotator.tsxis excluded by!**/vendor/**src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/Mark.tsxis excluded by!**/vendor/**src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/SplitTag.tsxis excluded by!**/vendor/**src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/TextAnnotate.tsxis excluded by!**/vendor/**src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/TextAnnotateBlend.tsxis excluded by!**/vendor/**src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/utils/annotation-utils.tsis excluded by!**/vendor/**src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/utils/blend.tsis excluded by!**/vendor/**src/dashboard_frontend/src/vendor/react-text-annotate-blend/components/utils/utils.tsis excluded by!**/vendor/**src/dashboard_frontend/src/vendor/react-text-annotate-blend/index.tsis excluded by!**/vendor/**src/dashboard_frontend/src/vendor/react-text-annotate-blend/types/annotate-types.tsis excluded by!**/vendor/**
📒 Files selected for processing (12)
e2e/dashboard-route-sweep.spec.tse2e/helpers/dashboard-smoke-harness.tse2e/spec-viewer-mdx.spec.tspackage.jsonplaywright.smoke.config.tssrc/dashboard_frontend/src/components/I18nErrorBoundary.tsxsrc/dashboard_frontend/src/modules/approvals/ApprovalsAnnotator.tsxsrc/dashboard_frontend/src/modules/approvals/SideBySideView.tsxsrc/dashboard_frontend/src/modules/approvals/hooks/useScrollSync.tssrc/dashboard_frontend/src/modules/pages/ApprovalsPage.tsxsrc/dashboard_frontend/src/modules/pages/LogsPage.tsxsrc/dashboard_frontend/tsconfig.json
…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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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-react5.2.0 specifically requires^20.19.0 || >=22.12.0(see package-lock). The repo currently doesn’t advertise this inpackage.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"
There was a problem hiding this comment.
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 winScope the first readiness gate to
wt-aonly.Line 189 always includes both basenames, so
waitForProjects(1, 45000)will accept eitherwt-aorwt-b. If a leaked MCP from another run still haswt-bregistered,startMcpServers()can advance before the newly startedwt-ais actually ready, which weakens the isolation ordering this helper is trying to enforce.As per coding guidelines, `e2e/**`: "Playwright E2E tests. Verify batch-approvals and worktree-isolation semantics if either is touched."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);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
📒 Files selected for processing (11)
.codacy.yml.codacy/codacy.config.jsone2e/dashboard-route-sweep.spec.tse2e/helpers/dashboard-smoke-harness.tse2e/helpers/dual-server-config.tse2e/helpers/process-utils.tse2e/helpers/worktree-harness.tse2e/spec-viewer-mdx.spec.tse2e/worktree-no-shared.spec.tsplaywright.smoke.config.tsplaywright.worktree.config.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.
…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.
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.
…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).
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:
Also in this PR:
src/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.npm 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-blendremoved — upstream is dead (final release 1.2.0, peers React ≤18); it was the sole peer blocker. Its MIT TypeScript source is vendored atsrc/dashboard_frontend/src/vendor/react-text-annotate-blend/(LICENSE included; transitivelodash.sortbyreplaced with a typed local sortBy). Net runtime deps: −1.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)
Review gates already run
Post-merge handoff
npm publish --access publicis passkey-gated and must be run by the user after merge, thennpm view @lbruton/specflow version+ dashboard verification per the Post-Publish checklist.🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Chores
Tests