T-CW4K-scaffold-dashboard-vite-vue
Status: closed/done · Impact: high · Complexity: medium
The dashboard UI is a ~180-line HTML/CSS/JS string (INDEX_HTML) in server.ts — past the
limit of maintainability. D-0013-dashboard-app picks Vite + Vue 3 to replace it. This task
lays the first stone: an apps/dashboard Vite + Vue 3 SPA shell plus the one-command dev loop
that brings the Bun/Hono backend and the Vite dev server up together. It delivers D-0013’s first
v1 success criterion — easy backend+frontend dev startup — and unblocks the build-into-plugin
pipeline (T-JDEV), the generated client (T-JZL4), and the four-source port (T-UUMK).
| Location | Role today |
|---|---|
plugin/lib/services/dashboard/server.ts#makeApp | Hono app: GET / serves INDEX_HTML; GET /api/state and POST /api/refresh are the JSON API |
plugin/lib/services/dashboard/server.ts#INDEX_HTML | The entire UI as an embedded HTML string — fetches /api/state, renders tables, polls on an interval |
plugin/lib/services/dashboard/server.ts#buildState | Assembles the /api/state payload (tasks, leases, milestones, summary) |
plugin/lib/services/dashboard/dashboard-service.ts | defineService wiring sdlc dashboard {start,stop,list}; start detaches a backgrounded server on a chosen port |
plugin/lib/services/dashboard/dashboard-service-main.ts | Detached-child entry: parses args and runs the foreground server loop |
justfile | Task-runner recipes (fmt-markdown, check-markdown); no dev recipe yet |
package.json | Substrate package; Hono is a dependency; no Vite/Vue toolchain |
apps/ does not exist yet — T-RVMG creates the workspace and the
apps//packages/ trees this app lands in.
Proposed
Section titled “Proposed”A Vite + Vue 3 SPA shell lives at apps/dashboard/: vue-router for routing, a single root view
that fetches GET /api/state through a relative /api base and renders a minimal
proof-of-life (e.g. project root + ticket count from summary.task_total). The Vite dev server
proxies /api to the running Bun/Hono backend, so the relative base works identically in dev and
(later) when the built SPA is served same-origin by the existing server.
One command starts both processes together: a dev task (a moon task in apps/dashboard/moon.yml
and a just dev recipe that invokes it) runs sdlc dashboard start (backend) and vite (dev
server) concurrently. vite build produces a static apps/dashboard/dist/. The existing
INDEX_HTML and GET / route are untouched — retiring them is T-UUMK/T-JDEV work.
Approach
Section titled “Approach”- Confirm the
apps/tree and moon are present (fromT-RVMG). Createapps/dashboard/. - Scaffold the Vite + Vue 3 SPA. Add
apps/dashboard/package.json(devDeps:vite,@vitejs/plugin-vue,vue,vue-router,typescript; scripts:dev,build,preview),apps/dashboard/index.html(Vite entry mounting#app),apps/dashboard/src/main.ts(creates the Vue app + router, mounts to#app), andapps/dashboard/src/App.vue(root component). Optionally addunplugin-auto-importper D-0013 — record the decision inline if adopted; the shell does not require it. - Configure the dev proxy. In
apps/dashboard/vite.config.tsregister@vitejs/plugin-vueand aserver.proxyentry mapping/apito the backend origin. The backend binds an ephemeral port (server.tspickFreePort), so the proxy target must be resolvable — pin a fixed dev port by passing--porttosdlc dashboard startin thedevtask and point the proxy athttp://127.0.0.1:<that-port>. - Wire the proof-of-life fetch. In
App.vue, on mountfetch('/api/state')(relative base) and renderproject_rootplussummary.task_total. No four-source view — that isT-UUMK. - Add the one-command dev loop. Create
apps/dashboard/moon.ymlwith adevtask that runs the backend (sdlc dashboard start --port <fixed>) andviteconcurrently. Add ajust devrecipe to the rootjustfilethat invokes the moondevtask (moon run dashboard:dev), so bothjust devand the moon task are valid single-command entry points. - Verify the build. Confirm
vite build(viamoon run dashboard:build) emitsapps/dashboard/dist/index.htmland assets.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
apps/dashboard/package.json | new | SPA package manifest: Vite/Vue/vue-router devDeps; dev/build/preview scripts |
apps/dashboard/vite.config.ts | new | Vite config: @vitejs/plugin-vue + server.proxy mapping /api to the dev backend port |
apps/dashboard/index.html | new | Vite HTML entry mounting #app, loading src/main.ts |
apps/dashboard/src/main.ts | new | Creates the Vue app + vue-router, mounts to #app |
apps/dashboard/src/App.vue | new | Root component: fetches /api/state and renders proof-of-life |
apps/dashboard/moon.yml | new | moon project: dev task (backend + vite concurrently), build task (vite build) |
justfile | modify | Add a dev recipe invoking moon run dashboard:dev |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: One command (
just dev, which runsmoon run dashboard:dev) starts BOTH the Bun/Hono backend (sdlc dashboard start) and the Vite dev server together. - AC-2: Loading the Vite dev server URL renders the SPA, which fetches
GET /api/statethrough the relative/apibase and displays at least the project root and ticket count from the live payload. - AC-3: The relative
/apibase resolves in dev via the Viteserver.proxyentry — no hardcoded backend host/port inApp.vue/main.ts. - AC-4:
moon run dashboard:build(i.e.vite build) producesapps/dashboard/dist/index.htmlplus bundled assets.
Out of scope
Section titled “Out of scope”- Building the SPA into the plugin and the
web-dist/drift gate →T-JDEV. - The generated typed API client →
T-JZL4; this shell uses a plain relativefetch. - Porting the full four-source dashboard view (working state, GitHub PRs) →
T-UUMK; only a minimal proof-of-life fetch of/api/statehere. - Retiring
INDEX_HTMLand repointingGET /inserver.ts— left intact; later milestone work.
Dependencies
Section titled “Dependencies”T-RVMG— needs theapps/tree and the moon workspace runner in place beforeapps/dashboard/and itsmoon.ymlcan be added.
Post-mortem
Section titled “Post-mortem”Captured by /sdlc:task-work on 2026-06-19. PR: pending.
Acceptance criteria coverage
Section titled “Acceptance criteria coverage”- AC-1: agent-manual — ran
moon run dashboard:dev(the targetjust devwraps); the dev log showed BOTH up: backend on127.0.0.1:6174and Vite ready on:5173. - AC-2: agent-manual (data path) / deferred-user (visual render) —
curlthrough the Vite proxy returned the live/api/statepayload (project_root+summary.task_total=318);App.vuecompiles and fetches on mount. Eyeballing the two rendered values in a browser athttp://localhost:5173/is the remaining human step. - AC-3: agent-manual —
curl http://localhost:5173/api/state(relative base via Viteserver.proxy) returned the backend payload;App.vue/main.tscarry no host/port literals. - AC-4: agent-manual —
moon run dashboard:buildproducedapps/dashboard/dist/index.htmlplusdist/assets/index-*.{js,css}.
What worked
Section titled “What worked”- The moon
apps/*discovery glob auto-registeredapps/dashboardas projectdashboard— no.moon/workspace.ymledit needed. sdlc dashboard startdetaching and returning made the one-command dev loop a simple sequence (start backend, thenvitein the foreground) — no concurrency helper required.- The baseline-diff gate correctly classified all 9 pre-existing rumdl MD013 findings in an untouched file as pre-existing.
Friction and automation gaps
Section titled “Friction and automation gaps”- moon version mismatch blocked every moon task — the proto shim resolved a
global
moon 2.0.0-rc.0, which fails the workspaceversionConstraint: '>=1.41.0'(a-rcprerelease doesn’t satisfy a stable range). Fixed by adding a root.prototoolspinningmoon = "1.41.8". The pin arguably belongs in T-RVMG / D-0012-monorepo-tooling workspace setup rather than in this app task. → T-RVMG-adopt-moon-workspace-runner - moon
systemtoolchain doesn’t addnode_modules/.binto PATH — a barevitetask command fails with exit 127; had to invokebunx vite. A note in the moon-workspace conventions (or a toolchain that injects.bin) would prevent rediscovery. → T-RVMG-adopt-moon-workspace-runner sdlc dashboard start --helpomits the--port/--host/--interval/--no-networkflags thatparseStartArgsaccepts —--portwas confirmed working only by reading the parser. The generated help is a docs gap. →dashboard-start-help-lists-flags- The baseline-diff quality gate treats rumdl’s summary line
(
Found N issues in X/Y files) as a finding; when the branch base moves under parallel WIP the issue-count changes (11→9) and surfaces a spuriousnew-drift=1even though the branch touches nodocs//plugin/files.normalizeFindingmasks timing/port/PID tokens but not the issue-count token in a summary line. → T-BCNP-quality-gate-ignores-summary-and-corpus-lines - The
lint-state-originpre-commit hook false-positives whenorigin/mainlags localmain: parallel sessions’ state-onlyverify/startcommits that sit on localmain(ahead oforigin/main) appear as task-branch violations, because_lint_state_origin_core.tscompares eachtask/*branch againstorigin/mainonly — not against localmaintoo, contrary to its own docstring. Required pushingmainmid-flow to clear it before any commit-on-main could land. → T-6R73-pre-commit-drift-hooks-gate-unconditionally-forcing-no
Spawned follow-up tasks
Section titled “Spawned follow-up tasks”dashboard-start-help-lists-flags(https://github.com/sksizer/dev/pull/466) —sdlc dashboard start --helpshould enumerate every flagparseStartArgsaccepts; spawned (Upstream-plugin/sdlc, degenerate-local,sdlc-meta).- T-RVMG-adopt-moon-workspace-runner — moon version pin (
.prototools) belongs in the moon-workspace foundation task; linked (existing, in-progress). - T-RVMG-adopt-moon-workspace-runner — moon
systemtoolchain.bin-on-PATH gap belongs in the same workspace foundation task; linked (existing, in-progress). - T-BCNP-quality-gate-ignores-summary-and-corpus-lines — baseline-diff gate treats the rumdl summary line’s issue-count as a finding; linked (existing, third independent observation of this gap).
- T-6R73-pre-commit-drift-hooks-gate-unconditionally-forcing-no —
project-check-task-state-originfalse-positives whenorigin/mainlags localmain; linked (existing tracker, promoted from B-8YI7 the same day).
Depends on
Section titled “Depends on”T-RVMG