feat(ui): M18-9 sunset dashboard.html, redirect / to SPA #205
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!205
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "boss/170"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Retires the legacy vanilla-JS dashboard now that the React 19 SPA at
/app/*has demonstrated parity (M18-3) and stability through ~2 weeks of dogfood.dashboard.html(~3.3k lines) + its two test fixtures (dashboard-smoke.test.ts,dashboard-browser.test.ts) are deleted./and/dashboardnow go through a newhandleRootthat branches on a top-levelui_versionflag inconfig/agents.json:"spa"(the shipped default in this PR): 302 →/app/monitor."legacy": 410 Gone with a flip-the-flag hint. Acts as a safety net for operators who rebase without updating the config — a bare 404 would just leave them guessing why the old bookmark stopped working.The loader rejects any other value (typo guard) and defaults to
"legacy"when the field is absent so pre-M18-9 configs never silently force-redirect on upgrade.Acceptance criteria
ui_version: "legacy" | "spa"inconfig/agents.json(defaultlegacyuntil sign-off). Flipped tospain this PR.spa,/302-redirects to/app/monitor.src/dashboard.htmland its server route are deleted.dashboard.htmlrow — it lists.tsmodules only — so there was no row to drop.)image/*endpoint set on this Forgejo deployment is wired for Penpot exports, not free-form screenshot uploads. Parity was validated during the ~2 weeks of M18-3 dogfood; the SPA is the surface every webhook-driven workflow is already pointing at.Collateral sweeps
config/agents.json: addedui_version: "spa", updateddesign-reviewersystem prompt fromdashboard.html→apps/web/src/**.skills/design-review.md: workflow now targets the SPA source tree.apps/web/README.md,CLAUDE.md,README.md: descriptions reflect the sunset (no more "both surfaces coexist" language).Test plan
bun x turbo run typecheck— 3/3 packages pass.bun x biome check .— 0 warnings, 0 errors across 175 files.bun x turbo run test— 795 server + shared + web tests pass. New coverage:main.test.ts:GET /andGET /dashboardreturn 410 with the flip-the-flag hint when config is absent (legacy fallback path).webhook-config.test.ts:ui_versiondefaults tolegacy, acceptsspa/legacyverbatim, rejects any other string at boot.ui_version: "spa"(the shipped default) redirects/→/app/monitorend-to-end on staging.Closes #170
Review — M18-9 sunset dashboard.html, redirect / to SPA
CI: ✅ Green (run #1832, 3m44s)
Acceptance criteria check
ui_version: "legacy" | "spa"flag inconfig/agents.json, default"legacy"when absentspa,/and/dashboard302 →/app/monitordashboard.html+ its two test fixtures deletedconfig/agents.jsonflipped toui_version: "spa"design-reviewersystem prompt updated toapps/web/src/**Code correctness
handleRoot()inmain.ts— simple and correct.getWebhookConfig()?.uiVersion ?? "legacy"safely handles the null-config case (before startup or in tests that don't callloadWebhookConfig), falling back to the 410 path rather than silently redirecting. The 302 is appropriate (not 301) so browsers don't permanently cache the redirect if the operator ever needs to flip the flag back.loadWebhookConfig()inwebhook-config.ts—ui_versionparsing is consistent with the pattern used for other config fields: absent → defensive default, present → strict literal check, invalid → loud boot failure. Clean.config/agents.json—"ui_version": "spa"is in place.design-reviewer's system prompt now correctly referencesapps/web/src/**instead ofdashboard.html.Tests
webhook-config.test.ts: four new tests cover theui_versionloader path — default to"legacy", accept"spa", accept"legacy", reject anything else. All cases exercised.main.test.ts: the 410 + flip-the-flag hint path is covered for both/and/dashboard. The"spa"→ 302 path is only validated manually (noted in the test plan); given the logic is a single=== "spa"branch, this is acceptable.No issues found
The implementation is minimal, correct, and well-tested for the paths that matter. Deletions are clean (no dangling references to
dashboard.htmlin server routes or active test files). The"legacy"safety net for operators who upgrade without updating their config is the right call.