Skip to content

T-JDEV-dashboard-build-into-plugin

Status: closed/superseded · Impact: high · Complexity: large

Ship the built dashboard SPA inside the plugin as a committed, generated, drift-gated artifact at plugin/lib/services/dashboard/web-dist/, so consumers get a working dashboard with no build step (D-0013-dashboard-app §Decision.2; preserves the D-0006-typescript-substrate run-from-source model). This task wires the build-and-ship pipeline — a moon build task, a sdlc dashboard build --check drift gate mirroring sdlc docs generate site --check (D-0010-deterministic-site-assembly), and repointing GET / at the committed bundle — retiring the ~180-line INDEX_HTML string that has reached its maintainability limit.

LocationRole today
plugin/lib/services/dashboard/server.ts#INDEX_HTML~180-line HTML/CSS/JS string (lines 805–982); the entire UI, parameterized via __INTERVAL__.
plugin/lib/services/dashboard/server.ts#makeAppHono app factory (lines 403–452); GET / returns INDEX_HTML (410–415), GET /api/state + POST /api/refresh serve JSON, app.all("*") 404s.
plugin/lib/services/dashboard/Bun/Hono service: server.ts (body), dashboard-service.ts (defineService ["dashboard"]{start,stop,list}), no ops/ dir, no web-dist/.
plugin/lib/services/docs/ops/generate.ts#handlerThe --check drift-gate model: checkArtifacts re-renders without writing, throws NAMESPACE_CONFLICT (exit 7) naming drifted targets; clean tree exits 0.
plugin/lib/services/docs/generate.ts#checkArtifactsCompares fresh-rendered bytes against the committed file; emits a DriftFinding per changed target. The byte-equality pattern the build gate mirrors.
lefthook.ymlPre-commit hooks; sibling PR chore/docs-drift-gate adds project-check-docs-drift running bun plugin/cli/sdlc.ts docs generate --check. No web-dist gate.
sdlc.yaml#quality_checksCI gate list; sibling PR adds bun run plugin/cli/sdlc.ts docs generate --check. No web-dist gate.

No repo-root .gitattributes exists yet (no linguist-generated convention). apps/dashboard/ is scaffolded by T-CW4K (dependency); its vite build output (apps/dashboard/dist) is this task’s input.

  • plugin/lib/services/dashboard/web-dist/ exists and is committed: the static vite build output of apps/dashboard (index.html + hashed assets/), copied verbatim into the plugin tree.
  • A moon build task on apps/dashboard runs vite build then copies dist/ → the plugin web-dist/ (clean-replace, deterministic).
  • sdlc dashboard build regenerates web-dist/ from a fresh build; sdlc dashboard build --check performs a fresh build into a temp dir, compares it byte-for-byte against the committed web-dist/, and exits non-zero (naming drifted files) when they differ — mirroring sdlc docs generate site --check. A clean tree exits 0.
  • lefthook.yml runs the --check gate at pre-commit (a project-check-dashboard-drift hook, paralleling the sibling project-check-docs-drift); sdlc.yaml#quality_checks runs it in CI.
  • web-dist/** is marked linguist-generated in a repo-root .gitattributes.
  • INDEX_HTML and the __INTERVAL__ substitution are gone from server.ts. GET / serves web-dist/index.html; asset requests serve hashed files from web-dist/; any other unmatched non-/api GET falls back to web-dist/index.html (client-side routing). The JSON API (GET /api/state, POST /api/refresh) and the app.all("*") 404 for non-GET / /api misses are unchanged.
  • A consumer who installs by symlink and never builds gets a working dashboard from the committed web-dist/.
  1. Add a dashboard build op module at plugin/lib/services/dashboard/ops/build.ts (path ["dashboard","build"], defineOp). The handler resolves apps/dashboard and the plugin web-dist/ under ctx.projectRoot. Default behavior: run vite build (via the moon task, see step 2), then clean-replace web-dist/ with the fresh dist/ (clear stale files, copy new). Add a --check boolean input.
  2. Add the moon build task to apps/dashboard/moon.yml (scaffolded by T-CW4K): vite builddist, then copy dist/ into plugin/lib/services/dashboard/web-dist/. The op shells out to the moon task so the build command lives in one place (the deterministic-tail pattern, like the fmt-markdown justfile recipe). Decision deferred to implementation: whether the copy-into-plugin step is a moon task action or lives in the op — pick whichever keeps the build command single-sourced.
  3. Implement --check mirroring docs/ops/generate.ts#handler and checkArtifacts: build into a temp dir, walk the produced file set and the committed web-dist/ file set, compare bytes per file, collect a drift finding per created/updated/deleted file. On non-empty drift, throw OpError("NAMESPACE_CONFLICT", …) (exit 7) naming the drifted files and the fix (run sdlc dashboard build and commit); clean exits 0. Write nothing under --check.
  4. Retire INDEX_HTML: delete the export const INDEX_HTML block (server.ts 805–982) and the embedded __INTERVAL__ token. Repoint makeApp’s GET / (server.ts 410–415) to read and serve web-dist/index.html. Add an asset route serving files under web-dist/ (correct content types), and an SPA fallback: any unmatched GET whose path is not under /api serves web-dist/index.html with 200. Keep GET /api/state, POST /api/refresh, and the app.all("*") 404 (now reached only by non-GET requests and /api misses). The interval param no longer threads through the served HTML — the SPA owns its own polling cadence (T-UUMK); drop it from the / path only.
  5. Run the moon build task once to populate web-dist/; commit the bundle. Add the repo-root .gitattributes entry plugin/lib/services/dashboard/web-dist/** linguist-generated.
  6. Wire the gate: add project-check-dashboard-drift to lefthook.yml pre-commit running bun plugin/cli/sdlc.ts dashboard build --check, and append the same to sdlc.yaml#quality_checks (paralleling the sibling chore/docs-drift-gate wiring).
LocationKindChange
plugin/lib/services/dashboard/ops/build.tsnewdashboard build op + --check drift gate (mirrors docs generate).
plugin/lib/services/dashboard/web-dist/newCommitted vite build output (index.html + assets/).
.gitattributesnewplugin/lib/services/dashboard/web-dist/** linguist-generated.
plugin/lib/services/dashboard/server.ts#INDEX_HTMLdeleteRemove the ~180-line UI string and __INTERVAL__ token.
plugin/lib/services/dashboard/server.ts#makeAppmodifyGET / + asset routes serve web-dist/; SPA fallback for unmatched non-/api GETs; JSON API + 404 unchanged.
lefthook.ymlmodifyAdd project-check-dashboard-drift running dashboard build --check.
sdlc.yamlmodifyAppend bun run plugin/cli/sdlc.ts dashboard build --check to quality_checks.
  • AC-1: GET / on a running dashboard serves the built SPA from plugin/lib/services/dashboard/web-dist/index.html (not the old INDEX_HTML).
  • AC-2: A deep-link GET (e.g. /tasks/T-JDEV, no matching asset, not under /api) returns 200 with web-dist/index.html (SPA fallback); a hashed asset GET serves the matching web-dist/assets/* file.
  • AC-3: grep -n INDEX_HTML plugin/lib/services/dashboard/server.ts returns no match (the string and __INTERVAL__ token are gone).
  • AC-4: GET /api/state and POST /api/refresh behave exactly as before (JSON bodies, status codes, --no-network 409 on refresh).
  • AC-5: sdlc dashboard build --check exits non-zero (exit 7) and names the drifted files when committed web-dist/ differs from a fresh build; exits 0 when they match.
  • AC-6: git check-attr linguist-generated plugin/lib/services/dashboard/web-dist/index.html reports set (the bundle is marked generated).
  • AC-7: From a clean checkout with no build run, starting the dashboard serves a working UI from the committed web-dist/ (no consumer build step required).
  • AC-8: lefthook.yml and sdlc.yaml#quality_checks both invoke dashboard build --check.
  • The SPA’s features and the four-source view — Vite/Vue scaffold is T-CW4K; porting the dashboard view (active leases, tickets, milestones, local working state, GitHub PRs) to the SPA is T-UUMK.
  • Runtime bundle-vs-API version drift (a web-dist/ built against an older API than the server serving it) — the commit-time --check gate cannot catch it; deferred per D-0013-dashboard-app §Open questions.
  • Localhost-bind hardening and a POST /api/refresh guard — D-0013-dashboard-app §Open questions.
  • Net-new JSON API sources (local working state, GitHub PR status) — added with the view in T-UUMK.
  • T-CW4K — scaffolds apps/dashboard (Vite + Vue 3). This task’s build task and web-dist/ have nothing to build until that app exists; hard dependency, recorded in frontmatter depends_on:.

T-CW4K


← Back to Tasks