Skip to content

Add Vite live reload for representative local redesign work #165

Description

@alexeygrigorev

Add Vite live reload for representative local redesign work

Status: blocked — Architect/Tester/PM accepted and committed locally as 4b0b348; unpushed and unshipped behind the coordinated #166 rollout
Tags: enhancement, portal, frontend, backend, testing, P1
Depends on: #166 rollout sequencing for publication only; implementation is complete; preserve the separate local-replica owner handoff below
Blocks: None
Next owner: #166 orchestrator and credentialed operator for ordered publication; On-Call after repaired Phase C reaches origin
Resume condition: accepted aws-infra prerequisite bc27d07 is applied and #166 repaired Phases A and B have production PASS evidence; then advance only repaired Phase C b6b1ce9, which contains this exact commit, run source CI/On-Call, and keep the preserved private-replica invocation handoff with its existing owner without inspecting or copying that data

Product outcome

npm run dev starts one local development stack. The human opens exactly http://localhost:3000; Vite serves the canonical top-level frontend/ with CSS HMR and automatic page reload, while the real TypeScript backend runs only on a separate loopback port and receives all non-frontend traffic through the Vite origin.

The same stack works with the normal seeded local Dynalite database and with the existing private production-shaped local-replica launcher. Browser writes always terminate in local storage. Production frontend packaging, Lambda routing, and the canonical source/SAM artifact contract remain byte-for-byte unchanged.

Current contract and constraints

  • make dev currently runs tsx watch backend/scripts/dev-server.ts on port 3000. That process serves the frontend, docs/search/content, /work/*, and /api/* from one origin, so backend code reloads but browser CSS/JS does not update automatically.
  • backend/scripts/dev-server.ts adapts HTTP requests to the real Lambda handler, preserves multipart bodies, and decodes binary/download responses. Normal local mode starts persistent Dynalite internally and seeds users/templates.
  • The canonical browser calls same-origin /work/api/* plus docs/search/git/content paths. Portal auth also owns /login, /auth/callback, /auth/error, and /logout with opaque HttpOnly cookies and redirects.
  • Production static serving intentionally exposes only index.html, src/app.js, and src/styles.css; extensionless and canonical .md deep links receive the app shell, while unknown static-looking files and forbidden legacy namespaces fail explicitly.
  • A preserved private worktree at .tmp/worktrees/local-replica-preserved contains uncommitted user work in .gitignore, Makefile, README.md, scripts/local-replica.cjs, and scripts/local-replica.sh. It runs a private persistent Dynalite replica on loopback and currently invokes the backend directly. Those changes and data belong to the user and must not be copied, reverted, exposed, or independently reimplemented in this issue.

Runtime and command contract

  • Add Vite as a root development-only dependency and make root npm run dev the single foreground supervisor command. It starts Vite at http://localhost:3000 and the real backend/scripts/dev-server.ts at http://127.0.0.1:3001 by default.
  • Both listeners bind loopback only. Vite uses strict port behavior and prints the exact human URL. The backend accepts an explicit loopback host and never binds 0.0.0.0 in this development command.
  • Support validated overrides DATAOPS_DEV_FRONTEND_PORT and DATAOPS_DEV_BACKEND_PORT for isolated tests/worktrees. Values must be numeric TCP ports, distinct from each other, and must not change the default human URL.
  • The supervisor checks both ports before starting either child. If either is occupied, it exits nonzero with the exact occupied port and starts no partial stack. A bind race or early child exit also stops the sibling and exits nonzero.
  • SIGINT, SIGTERM, normal child exit, failed readiness, and test cancellation stop and reap Vite, the tsx watch process group, and all descendants. A bounded escalation may use SIGKILL; the command leaves no PID file, listener, watcher, or orphan process.
  • Backend watch restarts may cause a brief proxied 502/unavailable response but do not terminate Vite; the proxy recovers when backend health is ready again. Frontend edits do not restart or reseed the backend.
  • Normal mode preserves today’s local behavior: IS_LOCAL=true, local/offline Docs, dark external-provider flags, in-process/persistent Dynalite, and synthetic seeds. Add an explicit seed mode so the private-replica launcher can select none; default local development still seeds.

Exact Vite/proxy contract

Vite’s document root is the repository’s canonical top-level frontend/. Do not create a second frontend, copy it to a Vite-owned directory, or change frontend/index.html, frontend/src/app.js, or frontend/src/styles.css merely to enable Vite.

The following prefixes are proxied verbatim to the internal backend. A prefix matches the exact path and descendants where the production router does:

Family Paths
Work/API /api, /api/*, /work, /work/* including /work/api/* and /work/health
Docs/content /docs, /docs/*, /images, /images/*, /folders, /folders/*, /lint, /lint/*, /parse, /parse/*, /health, /health/*, /search, /search/*, /git, /git/*, /content, /content/*
Browser auth /login, /logout, /auth, /auth/*
  • The proxy preserves method, path, query, request headers, cookies, JSON, URL-encoded data, multipart boundaries/bytes, and raw upload bodies. It preserves response status, content type, cache directives, Location, every Set-Cookie, content disposition, and binary/download bytes. It does not convert backend JSON 4xx/5xx responses into HTML or a Vite success.
  • The browser-facing host/origin remains localhost:3000. Relative auth redirects and cookie paths work through that origin; the proxy must not leak the internal port into browser Location, callback, cookie, or generated same-origin URLs. HMR WebSocket traffic remains owned by Vite and is never forwarded as application traffic.
  • /, /index.html, /src/app.js, and /src/styles.css are Vite-served development assets. Vite’s own HMR client endpoints exist only in development and are never included in backend/SAM artifacts.
  • Extensionless browser deep links and canonical repository .md document paths load the app shell, preserving hash fragments and query strings across automatic reload. Unknown static-looking paths return a real 404. The forbidden production namespaces assets, frontend, pages, public, static, and ui never fall through to the SPA. Unknown /src/* files also return 404.
  • A backend unavailable state is a concise proxy failure (non-200) and recovers without requiring Vite restart. It is never a false SPA page or a request to a deployed endpoint.

Normal and private-replica data modes

  • The dev stack accepts all existing local backend environment variables. With no external endpoint it uses the current local Dynalite path and synthetic seeds.
  • When DYNAMODB_ENDPOINT is supplied, the supervisor requires an http: loopback URL (localhost, 127.0.0.1, or ::1) and local credentials. It rejects AWS, HTTPS, non-loopback hosts, and missing local-mode flags before starting the browser origin.
  • Representative mode uses the already-downloaded private Dynalite replica, disables seeds, sessions, scheduled workers, provider calls, and all conversational/external execution. It must not call AWS, Secrets Manager, GitHub, SES, S3, Telegram, Typefully, OAuth providers, or any production endpoint while serving or mutating browser requests.
  • Browser mutations in representative mode write only to that local replica and local upload/cache directories. The UI and logs must label the mode local representative replica; they must never imply that a local mutation changed production.
  • The stack does not print, snapshot, attach, or copy representative records, table contents, user/contact data, private paths, or credentials. Test fixtures are independently synthetic and public-safe.
  • Add Vite live reload for representative local redesign work #165 owns only the shared Vite/supervisor/config/tests/package contract. It does not edit the preserved local-replica files. After both streams are accepted, the local-replica owner changes its final backend invocation to call root npm run dev with its existing local environment and seed mode none; that one-line composition is reviewed before either preserved work is discarded.

Production and dependency boundary

  • Vite is absent from backend/package.json runtime dependencies, Lambda code, backend dist, Docker runtime layers, and every SAM function artifact. Root lockfile changes remain development-only under the existing npm ci --omit=dev packaging path.
  • npm --prefix backend run build continues to copy the three canonical frontend files without Vite transformation. Both backend/dist and .aws-sam/build/BackendFunction pass the exact frontend artifact verifier and match top-level source bytes.
  • Production serveCanonicalFrontend route/static allowlists, portal cookies/auth, API behavior, SAM templates, deployment workflow, and CI ordering do not change. This issue adds no production environment variable, listener, route, bundle, or asset.
  • No edits are made to canonical frontend source. Finalize the canonical scan-first Operations Home #161 may continue in parallel and owns frontend/index.html, frontend/src/app.js, frontend/src/styles.css, Home behavior, screenshots, and its capability/parity updates.

Automated real-browser and lifecycle coverage

Add focused tests that own isolated loopback ports and temporary frontend/data/upload directories, and clean up only their own processes. Browser request interception or response mocking does not count.

  • Unit/contract tests cover config validation, exact proxy-family matching, forbidden/static fallback classification, production dependency exclusion, occupied frontend port, occupied backend port, child startup failure, signal shutdown, bounded escalation, and zero surviving owned listeners/processes.
  • A normal real-server Playwright journey starts the supervisor on isolated ports, opens the canonical Home and at least one direct hash/deep-link route, then edits a temporary copy of canonical CSS and observes HMR without manual reload. It edits a temporary copy of canonical JS or HTML and observes automatic reload while preserving the route/hash. The tracked frontend/ files remain untouched.
  • A real proxy journey proves /work/api/me, task and workflow JSON, /docs, /search, read-only /git/status, /content/*, a JSON 401/404, a relative redirect/opaque cookie clear or refresh, multipart task-file upload, and byte-identical download through the Vite origin. Assert the browser never contacts the internal port or a non-loopback/external host.
  • A synthetic representative-mode journey starts an isolated local Dynalite fixture with seeds disabled, reads a known production-shaped-but-public-safe task, performs a reversible local mutation, reloads through Vite, and proves the change exists only in that local fixture. It makes no AWS/provider call and includes no private-replica content.
  • Tests prove an unknown extension/static path and each forbidden namespace return 404 while extensionless and .md deep links load the canonical shell. API/proxy failures retain JSON/status/content type and never fall through to index.html.
  • The new tests may use suggested paths scripts/dev-portal.test.mjs and backend/e2e/vite-dev-portal.spec.js, but their exact passing titles must be recorded in the handoff.

No issue-scoped screenshots or Designer gate are required because this changes developer tooling, not product presentation. The Tester must inspect browser assertions and logs rather than accept a server-start smoke test alone.

Run in this order and report exact commands, exit codes, test counts, chosen isolated ports, and the post-test listener/process inventory:

npm ci
node --test scripts/dev-portal.test.mjs
npx playwright test backend/e2e/vite-dev-portal.spec.js --reporter=line
npm --prefix backend test
npm --prefix backend run typecheck
npm --prefix backend run build
node backend/scripts/verify-frontend-artifact.mjs --source frontend --artifact backend/dist
npm --prefix backend run test:e2e
npm --prefix backend run test:e2e:frontend-parity
make sam-validate
make sam-build
node backend/scripts/verify-frontend-artifact.mjs --source frontend --artifact .aws-sam/build/BackendFunction
make ci
git diff --check

Sequencing and ownership

#165 may be implemented and reviewed in a fresh worktree from current clean main while #161 is active because it must not edit canonical frontend source or #161’s behavior/parity specs. #165 owns root package.json/package-lock.json, its new Vite config and supervisor, the development-only backend server seam, and new issue-specific tests.

The preserved local-replica stream owns .gitignore, README.md, its Makefile targets, and scripts/local-replica.cjs/.sh. Do not copy or edit those files from the #165 worktree. If #165 needs a discoverable Make target, defer that small composition until the preserved stream is integrated; npm run dev is the required issue-owned command and must already be complete without it.

If #161 reaches commit first, rebase #165 on that clean main and rerun the HMR/deep-link/full regression sequence. If #165 reaches acceptance first, it may be integrated independently; #161 then consumes the updated lockfile but does not change the dev tooling. In either order, the orchestrator verifies the final combined tree and the local-replica owner performs the one-line invocation handoff before representative use.

Lifecycle gates

  • Software Engineer implementation, uncommitted and limited to dev tooling/tests/package files
  • Architect PASS on loopback/proxy/process/data-safety and production-package isolation
  • Tester PASS with HMR, proxy, lifecycle, representative-mode, full regression, and artifact/SAM evidence
  • PM ACCEPTED for the localhost:3000 developer journey
  • Preserved local-replica owner confirms the final npm run dev invocation composition without exposing private data
  • Software Engineer commit with Closes #165
  • Orchestrator local merge and push
  • On-Call source CI/CD result

Out of scope

  • Any Home or product redesign, canonical frontend source edit, UI screenshot change, framework/router migration, second frontend, or production Vite bundle.
  • Copying, committing, attaching, logging, sanitizing, or inspecting the private production-shaped replica as issue evidence; downloading/refreshing live data; changing its table inventory or AWS read boundary.
  • Live AWS/DynamoDB/S3/SES/Secrets Manager/GitHub/provider access, live OAuth, production write, external upload/download, manual deploy, workflow dispatch/rerun, IAM/CloudFormation mutation, or [HUMAN] verification.
  • Changing production auth/session semantics, API routes, docs mutation behavior, static allowlists, SAM/CI/deploy behavior, or frontend artifact contents.
  • Editing or reverting Finalize the canonical scan-first Operations Home #161 work, the preserved local-replica files, source repositories, private knowledge, or AWS infrastructure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1ImportantbackendBackend/APIenhancementNew or improved functionalityfrontendFrontend UIportalShared portal shell and UXtestingTests and QA

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions