592 lines
19 KiB
Markdown
592 lines
19 KiB
Markdown
# React Runtime Migration Implementation Plan
|
|
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
|
**Goal:** Replace the SvelteKit dashboard app with a React-based Vite app and Bun production server while preserving model-driven dashboard behavior and reusable presentation boundaries.
|
|
|
|
**Architecture:** The browser runtime becomes React mounted from `src/main.tsx`. A Bun server at `src/server/index.ts` serves the static React build and exposes JSON API routes for dashboard reads and agent dashboard mutations. Existing model, persistence, datasource, and agent-config modules remain framework-agnostic TypeScript with import path updates as needed.
|
|
|
|
**Tech Stack:** Bun, Vite, React, React DOM, TypeScript, Tailwind CSS v4, shadcn/ui, Vitest, Storybook React Vite, Playwright, Drizzle ORM, Bun SQLite, MSW, uPlot, Iconify React.
|
|
|
|
---
|
|
|
|
## File Structure
|
|
|
|
- Create `index.html`: Vite app shell with `<div id="root"></div>` and `/src/main.tsx`.
|
|
- Create `src/main.tsx`: React DOM bootstrap and global CSS import.
|
|
- Create `src/App.tsx`: Dashboard fetch, refresh interval, ready/empty/loading/invalid rendering.
|
|
- Create `src/App.test.tsx`: React server-rendering and hook behavior tests for dashboard states.
|
|
- Create `src/server/index.ts`: Bun HTTP server, static asset serving, API router, production entry.
|
|
- Create `src/server/routes/dashboard.ts`: `GET /api/dashboard` runtime loader and datasource resolver.
|
|
- Create `src/server/routes/agent-dashboard.ts`: `POST /api/agent/dashboard` adapter for `handleAgentDashboardRequest`.
|
|
- Create `src/server/routes/dashboard.test.ts`: dashboard API state and datasource-disable tests.
|
|
- Create `src/server/routes/agent-dashboard.test.ts`: agent API delegation/auth tests.
|
|
- Create `src/lib/ui/components/*.tsx`: React ports of current reusable Svelte components.
|
|
- Create `src/lib/ui/components/styles.css`: component CSS migrated from Svelte style blocks.
|
|
- Create `components.json`: shadcn/ui configuration for Vite, Radix, Nova, Tailwind v4, and `$lib` aliases.
|
|
- Create `src/lib/components/ui/*.tsx`: selected shadcn primitives, not the full registry.
|
|
- Create `src/lib/utils.ts`: `cn()` helper for shadcn and dashboard components.
|
|
- Replace `src/lib/ui/components/render.test.ts`: React `renderToString` component tests.
|
|
- Replace `src/lib/ui/stories/*.stories.svelte`: React `.stories.tsx` stories.
|
|
- Modify `.storybook/main.ts`: use `@storybook/react-vite` and React story globs.
|
|
- Modify `.storybook/preview.ts`: use React Storybook types and keep MSW setup/global CSS.
|
|
- Modify `vite.config.ts`: use React and Tailwind plugins, alias `$lib` to `src/lib`, build client, and keep Vitest config.
|
|
- Modify `tsconfig.json`: remove `.svelte-kit` inheritance, enable JSX, and define path aliases.
|
|
- Modify `package.json`: swap Svelte/SvelteKit dependencies for React/Tailwind/shadcn tooling and update scripts.
|
|
- Modify `playwright.config.ts`: build React client and Bun server before e2e.
|
|
- Modify `Containerfile`: copy React/Bun build artifacts and keep `bun build/index.js` command.
|
|
- Modify `README.md`: document React runtime, Bun server, scripts, QA gate, deployment.
|
|
- Remove `svelte.config.js`, `src/app.html`, `src/routes/**`, and all `.svelte` files after replacements pass.
|
|
|
|
## Task 1: React Toolchain And Typecheck Scaffold
|
|
|
|
**Files:**
|
|
- Modify: `package.json`
|
|
- Modify: `bun.lock`
|
|
- Modify: `tsconfig.json`
|
|
- Modify: `vite.config.ts`
|
|
- Create: `index.html`
|
|
- Create: `src/main.tsx`
|
|
- Create: `src/App.tsx`
|
|
- Create: `src/App.test.tsx`
|
|
|
|
- [ ] **Step 1: Write the failing React scaffold test**
|
|
|
|
Create `src/App.test.tsx`:
|
|
|
|
```tsx
|
|
import { renderToString } from "react-dom/server";
|
|
import { describe, expect, test } from "vitest";
|
|
import { AppStateView } from "./App";
|
|
|
|
describe("React app dashboard state view", () => {
|
|
test("renders loading dashboard state", () => {
|
|
const html = renderToString(
|
|
<AppStateView
|
|
dashboard={{
|
|
state: "loading",
|
|
title: "Loading Dashboard",
|
|
subtitle: "Fetching active model",
|
|
message: "Waiting for the active dashboard document.",
|
|
}}
|
|
/>,
|
|
);
|
|
|
|
expect(html).toContain("Loading Dashboard");
|
|
expect(html).toContain("Fetching active model");
|
|
});
|
|
});
|
|
```
|
|
|
|
- [ ] **Step 2: Run the test to verify it fails**
|
|
|
|
Run: `bun run test:unit src/App.test.tsx`
|
|
|
|
Expected: FAIL because React dependencies and `src/App.tsx` do not exist.
|
|
|
|
- [ ] **Step 3: Add React dependencies and scaffold files**
|
|
|
|
Update `package.json` scripts and dependencies:
|
|
|
|
```json
|
|
{
|
|
"scripts": {
|
|
"dev": "vite --host 0.0.0.0",
|
|
"build": "vite build && bun build src/server/index.ts --target bun --outdir build",
|
|
"preview": "HOST=0.0.0.0 PORT=4173 bun build/index.js",
|
|
"storybook": "storybook dev -p 6006 --host 0.0.0.0",
|
|
"build-storybook": "storybook build",
|
|
"check": "tsc --noEmit",
|
|
"test": "vitest run",
|
|
"test:unit": "vitest run",
|
|
"test:e2e": "env -u NO_COLOR playwright test",
|
|
"test:qa": "bun run check && bun run test:unit && bun run build && bun run build-storybook && bun run test:e2e",
|
|
"db:generate": "drizzle-kit generate",
|
|
"db:check": "drizzle-kit check"
|
|
}
|
|
}
|
|
```
|
|
|
|
Install React, Tailwind, and shadcn packages with Bun so `bun.lock` updates:
|
|
|
|
```sh
|
|
bun add @iconify/react @vitejs/plugin-react react react-dom
|
|
bun add class-variance-authority clsx lucide-react radix-ui tailwind-merge tw-animate-css @fontsource-variable/geist
|
|
bun add -d @storybook/react-vite @tailwindcss/vite @types/react @types/react-dom shadcn tailwindcss
|
|
```
|
|
|
|
Create `index.html`:
|
|
|
|
```html
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Dimension Lab</title>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="module" src="/src/main.tsx"></script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
Create minimal `src/App.tsx`:
|
|
|
|
```tsx
|
|
import type { DashboardRuntimeState } from "$lib/server/dashboard";
|
|
|
|
export function AppStateView({ dashboard }: { dashboard: DashboardRuntimeState }) {
|
|
if (dashboard.state === "ready") {
|
|
return <main>{dashboard.document.metadata.title}</main>;
|
|
}
|
|
|
|
return (
|
|
<main className="state-shell" data-dashboard-state={dashboard.state}>
|
|
<h1>{dashboard.title}</h1>
|
|
<p>{dashboard.subtitle}</p>
|
|
<p>{dashboard.message}</p>
|
|
</main>
|
|
);
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
<AppStateView
|
|
dashboard={{
|
|
state: "loading",
|
|
title: "Loading Dashboard",
|
|
subtitle: "Fetching active model",
|
|
message: "Waiting for the active dashboard document.",
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
```
|
|
|
|
Create `src/main.tsx`:
|
|
|
|
```tsx
|
|
import { StrictMode } from "react";
|
|
import { createRoot } from "react-dom/client";
|
|
import App from "./App";
|
|
import "./app.css";
|
|
|
|
const root = document.getElementById("root");
|
|
if (!root) throw new Error("Missing React root element");
|
|
|
|
createRoot(root).render(
|
|
<StrictMode>
|
|
<App />
|
|
</StrictMode>,
|
|
);
|
|
```
|
|
|
|
Update `tsconfig.json` with React JSX and path aliases.
|
|
|
|
Update `vite.config.ts` to use `@vitejs/plugin-react`, `@tailwindcss/vite`,
|
|
and the `$lib` alias. Initialize shadcn with:
|
|
|
|
```sh
|
|
bunx --bun shadcn@latest init --template vite --base radix --preset nova --yes --css-variables
|
|
bunx --bun shadcn@latest add badge card progress separator skeleton alert
|
|
```
|
|
|
|
Do not run `bunx --bun shadcn@latest add --all`; the dashboard should only
|
|
vendor primitives it actually uses.
|
|
|
|
- [ ] **Step 4: Run checks for the scaffold**
|
|
|
|
Run: `bun run test:unit src/App.test.tsx && bun run check`
|
|
|
|
Expected: PASS.
|
|
|
|
- [ ] **Step 5: Commit**
|
|
|
|
```sh
|
|
git add package.json bun.lock tsconfig.json vite.config.ts index.html src/main.tsx src/App.tsx src/App.test.tsx
|
|
git commit -m "build: add react vite scaffold"
|
|
```
|
|
|
|
## Task 2: Bun Server And Dashboard API
|
|
|
|
**Files:**
|
|
- Create: `src/server/index.ts`
|
|
- Create: `src/server/routes/dashboard.ts`
|
|
- Create: `src/server/routes/dashboard.test.ts`
|
|
- Create: `src/server/routes/agent-dashboard.ts`
|
|
- Create: `src/server/routes/agent-dashboard.test.ts`
|
|
- Modify: `src/App.tsx`
|
|
- Modify: `playwright.config.ts`
|
|
|
|
- [ ] **Step 1: Write failing API route tests**
|
|
|
|
Create `src/server/routes/dashboard.test.ts`:
|
|
|
|
```ts
|
|
import { describe, expect, test } from "vitest";
|
|
import { loadDashboardResponse } from "./dashboard";
|
|
|
|
describe("dashboard API route", () => {
|
|
test("returns ready dashboard runtime state from the existing model loader", async () => {
|
|
const response = await loadDashboardResponse({
|
|
disableLiveDatasources: true,
|
|
refreshSeedDocument: true,
|
|
seedIfEmpty: true,
|
|
});
|
|
|
|
expect(response.state).toBe("ready");
|
|
if (response.state !== "ready") throw new Error("expected ready dashboard");
|
|
expect(response.document.metadata.title).toContain("Dimension Lab");
|
|
});
|
|
});
|
|
```
|
|
|
|
Create `src/server/routes/agent-dashboard.test.ts`:
|
|
|
|
```ts
|
|
import { describe, expect, test } from "vitest";
|
|
import { handleAgentDashboardRoute } from "./agent-dashboard";
|
|
|
|
describe("agent dashboard API route", () => {
|
|
test("delegates unauthorized requests to the existing agent handler", async () => {
|
|
const response = await handleAgentDashboardRoute(
|
|
new Request("http://localhost/api/agent/dashboard", { method: "POST" }),
|
|
);
|
|
|
|
expect(response.status).toBe(401);
|
|
});
|
|
});
|
|
```
|
|
|
|
- [ ] **Step 2: Run tests to verify they fail**
|
|
|
|
Run: `bun run test:unit src/server/routes/dashboard.test.ts src/server/routes/agent-dashboard.test.ts`
|
|
|
|
Expected: FAIL because the route modules do not exist.
|
|
|
|
- [ ] **Step 3: Implement server route modules and server entry**
|
|
|
|
Implement `loadDashboardResponse()` by calling `loadDashboardRuntime()` and
|
|
`resolveDashboardDatasources()` exactly like the current SvelteKit load
|
|
function. Implement `handleAgentDashboardRoute()` by returning
|
|
`handleAgentDashboardRequest(request)`. Implement `src/server/index.ts` with
|
|
Bun.serve routes for `/api/dashboard`, `/api/agent/dashboard`, static Vite
|
|
assets, and SPA fallback to `index.html`.
|
|
|
|
- [ ] **Step 4: Update React app to fetch `/api/dashboard`**
|
|
|
|
`src/App.tsx` should export `AppStateView` for tests and make the default
|
|
`App` fetch dashboard state with `useEffect`. It should clear refresh timers
|
|
when state changes and on unmount.
|
|
|
|
- [ ] **Step 5: Run route and app tests**
|
|
|
|
Run:
|
|
|
|
```sh
|
|
bun run test:unit src/server/routes/dashboard.test.ts src/server/routes/agent-dashboard.test.ts src/App.test.tsx
|
|
```
|
|
|
|
Expected: PASS.
|
|
|
|
- [ ] **Step 6: Commit**
|
|
|
|
```sh
|
|
git add src/server src/App.tsx src/App.test.tsx playwright.config.ts
|
|
git commit -m "feat(server): add bun dashboard api"
|
|
```
|
|
|
|
## Task 3: React UI Component Library
|
|
|
|
**Files:**
|
|
- Create: `src/lib/ui/components/*.tsx`
|
|
- Create: `src/lib/ui/components/styles.css`
|
|
- Modify: `src/lib/ui/components/render.test.ts`
|
|
- Modify: `src/lib/ui/index.ts`
|
|
- Keep: `src/lib/ui/types.ts`
|
|
- Keep: `src/lib/ui/model-renderer.ts`
|
|
- Keep: `src/lib/ui/fixtures.ts`
|
|
|
|
- [ ] **Step 1: Replace Svelte SSR tests with failing React render tests**
|
|
|
|
Rewrite `src/lib/ui/components/render.test.ts` to import React components and
|
|
`renderToString` from `react-dom/server`. Keep the current assertions for:
|
|
|
|
- dashboard fixture content
|
|
- generic secondary fixture content
|
|
- optional service/status links
|
|
- stable model IDs
|
|
- progress bar behavior
|
|
- uPlot chart surface marker
|
|
- native attributes on Button and IconButton
|
|
|
|
Run: `bun run test:unit src/lib/ui/components/render.test.ts`
|
|
|
|
Expected: FAIL because React component files do not exist yet.
|
|
|
|
- [ ] **Step 2: Port atomic components**
|
|
|
|
Create React equivalents for `Badge`, `Button`, `IconGlyph`, `IconButton`,
|
|
`ProgressMeter`, `Separator`, `Sparkline`, `SignalTrace`, `LineChart`, and
|
|
`StatusBadge`. Preserve class names and `data-*` attributes from Svelte.
|
|
|
|
Run: `bun run test:unit src/lib/ui/components/render.test.ts`
|
|
|
|
Expected: remaining FAILs only for dashboard composite components.
|
|
|
|
- [ ] **Step 3: Port layout and card components**
|
|
|
|
Create React equivalents for `Panel`, `CornerBracketFrame`, `GridFrame`,
|
|
`DiagonalStripeField`, `ModuleCard`, `TelemetryCard`, `TelemetryGrid`,
|
|
`FooterCell`, `FooterStatusCell`, and `StatusStrip`.
|
|
|
|
Run: `bun run test:unit src/lib/ui/components/render.test.ts`
|
|
|
|
Expected: remaining FAILs only for service/dashboard shell components.
|
|
|
|
- [ ] **Step 4: Port service and dashboard shell components**
|
|
|
|
Create React equivalents for `ServiceRow`, `ServicePanel`,
|
|
`ServiceGroupPanel`, `SystemState`, `DashboardHeader`, `DashboardFrame`,
|
|
`TelemetryStrip`, and `WeatherModule`.
|
|
|
|
Run: `bun run test:unit src/lib/ui/components/render.test.ts`
|
|
|
|
Expected: PASS.
|
|
|
|
- [ ] **Step 5: Export React components**
|
|
|
|
Update `src/lib/ui/index.ts` to export `.tsx` React components and continue
|
|
exporting fixtures, renderer, and UI types.
|
|
|
|
Run:
|
|
|
|
```sh
|
|
bun run test:unit src/lib/ui/components/render.test.ts src/lib/ui/model-renderer.test.ts src/lib/ui/content-boundary.test.ts
|
|
```
|
|
|
|
Expected: PASS.
|
|
|
|
- [ ] **Step 6: Commit**
|
|
|
|
```sh
|
|
git add src/lib/ui/components src/lib/ui/index.ts
|
|
git commit -m "feat(ui): port dashboard components to react"
|
|
```
|
|
|
|
## Task 4: React Dashboard App Rendering
|
|
|
|
**Files:**
|
|
- Modify: `src/App.tsx`
|
|
- Modify: `src/App.test.tsx`
|
|
- Modify: `src/app.css`
|
|
- Delete after `src/page.test.tsx` passes: `src/routes/page.test.ts`
|
|
- Create: `src/page.test.tsx`
|
|
|
|
- [ ] **Step 1: Write failing React page tests**
|
|
|
|
Create `src/page.test.tsx` with React `renderToString` assertions equivalent to
|
|
the current Svelte `src/routes/page.test.ts`:
|
|
|
|
- ready dashboard renders model content
|
|
- invalid model state renders validation errors
|
|
- empty and loading states render without crashing
|
|
|
|
Run: `bun run test:unit src/page.test.tsx`
|
|
|
|
Expected: FAIL until `AppStateView` uses the React `DashboardFrame` and
|
|
`SystemState` components.
|
|
|
|
- [ ] **Step 2: Implement app state rendering**
|
|
|
|
Use `dashboardDocumentToUiDashboard()` and `DashboardFrame` for ready state.
|
|
Use `SystemState` for empty/loading/invalid states. Preserve state shell CSS
|
|
and validation error list markup.
|
|
|
|
- [ ] **Step 3: Run page tests**
|
|
|
|
Run: `bun run test:unit src/page.test.tsx src/App.test.tsx`
|
|
|
|
Expected: PASS.
|
|
|
|
- [ ] **Step 4: Commit**
|
|
|
|
```sh
|
|
git add src/App.tsx src/App.test.tsx src/page.test.tsx src/app.css
|
|
git commit -m "feat(app): render dashboard with react"
|
|
```
|
|
|
|
## Task 5: React Storybook
|
|
|
|
**Files:**
|
|
- Modify: `.storybook/main.ts`
|
|
- Modify: `.storybook/preview.ts`
|
|
- Create: `src/lib/ui/stories/*.stories.tsx`
|
|
- Delete after replacement: `src/lib/ui/stories/*.stories.svelte`
|
|
- Delete after replacement: `src/lib/ui/stories/FocusPreview.svelte`
|
|
- Modify: `src/lib/ui/storybook.test.ts`
|
|
|
|
- [ ] **Step 1: Update storybook boundary test first**
|
|
|
|
Change `src/lib/ui/storybook.test.ts` so it requires React `.stories.tsx`
|
|
files and rejects `.stories.svelte` files.
|
|
|
|
Run: `bun run test:unit src/lib/ui/storybook.test.ts`
|
|
|
|
Expected: FAIL while Svelte stories still exist.
|
|
|
|
- [ ] **Step 2: Configure React Storybook**
|
|
|
|
Update `.storybook/main.ts` to use `@storybook/react-vite`, React story globs,
|
|
and the existing addons. Update `.storybook/preview.ts` type imports to React
|
|
Storybook while preserving global CSS, MSW setup, backgrounds, controls, and
|
|
fullscreen layout.
|
|
|
|
- [ ] **Step 3: Port stories to React**
|
|
|
|
Create `.stories.tsx` files for each existing Svelte story. Import React
|
|
components from `src/lib/ui` and generic story data from
|
|
`src/lib/ui/stories/story-data.ts`.
|
|
|
|
- [ ] **Step 4: Remove Svelte stories and run Storybook checks**
|
|
|
|
Run:
|
|
|
|
```sh
|
|
bun run test:unit src/lib/ui/storybook.test.ts
|
|
bun run build-storybook
|
|
```
|
|
|
|
Expected: PASS.
|
|
|
|
- [ ] **Step 5: Commit**
|
|
|
|
```sh
|
|
git add .storybook src/lib/ui/stories src/lib/ui/storybook.test.ts
|
|
git commit -m "feat(storybook): migrate stories to react"
|
|
```
|
|
|
|
## Task 6: Remove SvelteKit Runtime
|
|
|
|
**Files:**
|
|
- Delete: `svelte.config.js`
|
|
- Delete: `src/app.html`
|
|
- Delete: `src/routes/**`
|
|
- Delete: all remaining `*.svelte`
|
|
- Modify: `package.json`
|
|
- Modify: `bun.lock`
|
|
- Modify: `README.md`
|
|
- Modify: `Containerfile`
|
|
- Modify: `playwright.config.ts`
|
|
- Modify: `src/lib/presentation-boundary.test.ts`
|
|
|
|
- [ ] **Step 1: Write/adjust cleanup tests**
|
|
|
|
Add assertions to presentation or storybook boundary tests that no `.svelte`
|
|
files remain under `src/`.
|
|
|
|
Run: `bun run test:unit src/lib/presentation-boundary.test.ts src/lib/ui/storybook.test.ts`
|
|
|
|
Expected: FAIL while Svelte files remain.
|
|
|
|
- [ ] **Step 2: Delete Svelte runtime and dependencies**
|
|
|
|
Remove all Svelte files and Svelte dependencies. Run `bun install
|
|
--frozen-lockfile` only after `package.json` and `bun.lock` are consistent, or
|
|
run `bun remove` commands to update both together:
|
|
|
|
```sh
|
|
bun remove @iconify/svelte @storybook/addon-svelte-csf @storybook/sveltekit @sveltejs/adapter-node @sveltejs/kit @sveltejs/vite-plugin-svelte svelte svelte-check
|
|
```
|
|
|
|
- [ ] **Step 3: Update docs, container, and e2e build command**
|
|
|
|
README should describe React, Vite, Bun server, and unchanged persistence.
|
|
`Containerfile` should copy the Vite client output and Bun server output.
|
|
`playwright.config.ts` should build and start the Bun server.
|
|
|
|
- [ ] **Step 4: Run cleanup checks**
|
|
|
|
Run:
|
|
|
|
```sh
|
|
rg -n "\\.svelte|svelte" package.json src .storybook vite.config.ts tsconfig.json README.md Containerfile
|
|
bun run check
|
|
bun run test:unit
|
|
```
|
|
|
|
Expected: `rg` finds no Svelte app/runtime references except historical docs in
|
|
the committed design/plan, and checks pass.
|
|
|
|
- [ ] **Step 5: Commit**
|
|
|
|
```sh
|
|
git add -A
|
|
git commit -m "refactor: remove svelte runtime"
|
|
```
|
|
|
|
## Task 7: QA Gate, PR, Review, And Merge
|
|
|
|
**Files:**
|
|
- Modify only files needed to fix failures found by this task.
|
|
|
|
- [ ] **Step 1: Run full QA gate**
|
|
|
|
Run: `bun run test:qa`
|
|
|
|
Expected: PASS for check, unit tests, production build, Storybook build, and
|
|
Playwright desktop/mobile tests.
|
|
|
|
- [ ] **Step 2: Inspect current diff**
|
|
|
|
Run:
|
|
|
|
```sh
|
|
git status --short
|
|
git diff --stat main...HEAD
|
|
git diff --name-only main...HEAD
|
|
```
|
|
|
|
Expected: only React migration files and docs changed.
|
|
|
|
- [ ] **Step 3: Push and open ready PR**
|
|
|
|
Run:
|
|
|
|
```sh
|
|
git push -u origin codex/react-migration
|
|
```
|
|
|
|
Open a ready PR against `main` with title:
|
|
|
|
```text
|
|
refactor: migrate dashboard runtime to react
|
|
```
|
|
|
|
- [ ] **Step 4: Independent review**
|
|
|
|
Dispatch an independent reviewer to inspect the issue goal, spec, plan, and PR
|
|
diff in code-review mode. Blocking findings must be fixed on the same branch.
|
|
|
|
- [ ] **Step 5: Fix review findings and re-run QA**
|
|
|
|
For each blocking finding, write or update the relevant failing test first,
|
|
make the minimal fix, and run the focused test plus `bun run test:qa`.
|
|
|
|
- [ ] **Step 6: Merge only after green checks and no blocking review findings**
|
|
|
|
Merge the PR into `main`, sync the worktree back to `main`, and mark the goal
|
|
complete only after the completion criteria in the spec are proven by current
|
|
state.
|
|
|
|
## Plan Self-Review
|
|
|
|
- Spec coverage: Tasks cover React scaffold, Bun API server, UI component
|
|
migration, app rendering, Storybook migration, Svelte removal, QA, PR,
|
|
independent review, and merge.
|
|
- Red-flag scan: The plan has no incomplete-work markers and no unspecified
|
|
acceptance gates.
|
|
- Type consistency: Public names used across tasks are `AppStateView`,
|
|
`loadDashboardResponse`, and `handleAgentDashboardRoute`.
|