From 6e8ab4cd64c7cf9c0b27ba4b15e478a026c48e95 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Tue, 24 Mar 2026 14:57:12 +0000 Subject: [PATCH] Unify frontend dev entrypoints with managed runtime --- CONTRIBUTING.md | 11 +++++-- .../subsystems/deployment-installability.md | 11 ++++++- .../v6/internal/subsystems/registry.json | 24 ++++++++++++++ frontend-modern/package.json | 6 +++- scripts/dev-check.sh | 9 ++++-- scripts/tests/test-hot-dev-bg.sh | 31 +++++++++++++++++++ 6 files changed, 85 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1af290bb85..88a436f075 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,10 +39,14 @@ cd .. ### Hot Reload Dev Loop ```bash -./scripts/hot-dev.sh # Backend on :7656, frontend on :7655 +npm run dev # Frontend shell on :5173, backend on :7655 npm run mock:on # Optional: enable mock data ``` +Use `http://127.0.0.1:5173` in the browser for local frontend development. The +frontend dev shell proxies `/api` and `/ws` to the backend on `:7655`; do not +switch your browser to `:7655` unless you are debugging the backend directly. + Backend-only hot reload (requires `air`): ```bash @@ -74,7 +78,10 @@ examples where possible. ## Frontend Workflow -- Dev server: `cd frontend-modern && npm run dev` +- Managed dev runtime: `npm run dev` +- Runtime status: `npm run dev:status` +- Browser recovery proof: `npm run dev:verify` +- Frontend-only escape hatch: `cd frontend-modern && npm run dev:frontend-only` - Tests: `npm run test` - Lint: `npm run lint` - Format: `npm run format` diff --git a/docs/release-control/v6/internal/subsystems/deployment-installability.md b/docs/release-control/v6/internal/subsystems/deployment-installability.md index 43e520bd5e..3a82bb7d54 100644 --- a/docs/release-control/v6/internal/subsystems/deployment-installability.md +++ b/docs/release-control/v6/internal/subsystems/deployment-installability.md @@ -33,6 +33,8 @@ server-side update execution surfaces. 11. `scripts/hot-dev-bg.sh` 12. `tests/integration/scripts/managed-dev-runtime.mjs` 13. `package.json` +14. `frontend-modern/package.json` +15. `scripts/dev-check.sh` ## Shared Boundaries @@ -47,7 +49,7 @@ server-side update execution surfaces. 2. Add or change release-build metadata injection, release artifact assembly, or governed promotion metadata resolution through `scripts/build-release.sh`, `scripts/release_ldflags.sh`, and `scripts/release_control/resolve_release_promotion.py`, plus the container Dockerfile and governed release workflows that consume those same contracts 3. Add or change shell installer, Windows installer, container-agent installer, or auto-update script behavior through `scripts/install.sh`, `scripts/install.ps1`, `scripts/install-container-agent.sh`, and `scripts/pulse-auto-update.sh` 4. Add or change server update transport through `internal/api/updates.go` and `frontend-modern/src/api/updates.ts` -5. Add or change local dev-runtime orchestration, managed ownership, browser-runtime proof wiring, frontend/backend coherence diagnostics, or the canonical root dev-entry wrappers through `scripts/hot-dev.sh`, `scripts/hot-dev-bg.sh`, `tests/integration/scripts/managed-dev-runtime.mjs`, and `package.json` +5. Add or change local dev-runtime orchestration, managed ownership, browser-runtime proof wiring, frontend/backend coherence diagnostics, or the canonical developer entry wrappers through `scripts/hot-dev.sh`, `scripts/hot-dev-bg.sh`, `tests/integration/scripts/managed-dev-runtime.mjs`, `package.json`, `frontend-modern/package.json`, and `scripts/dev-check.sh` ## Forbidden Paths @@ -174,6 +176,13 @@ local dev path, including explicit status, log, stop, backend-restart, and verification wrappers, instead of requiring developers to remember lane-specific shell paths or continue discovering the runtime through a stale unmanaged `5173` process by accident. +That same canonical dev-entry boundary also includes the frontend workspace +package and developer health helper. `frontend-modern/package.json` may not +advertise raw `vite` as the default `dev` command, and `scripts/dev-check.sh` +must route operators back to the managed runtime entrypoint before falling back +to process-killing folklore, otherwise the repo keeps reintroducing the same +split-ownership `5173` versus `7655` confusion through secondary entry +surfaces. That shared `scripts/install.sh` boundary must also keep one canonical service argument builder for the runtime flags it persists. Token-bearing install paths, token-file systemd paths, wrapper-script launches, and later service diff --git a/docs/release-control/v6/internal/subsystems/registry.json b/docs/release-control/v6/internal/subsystems/registry.json index 0522569d11..9810579ded 100644 --- a/docs/release-control/v6/internal/subsystems/registry.json +++ b/docs/release-control/v6/internal/subsystems/registry.json @@ -853,6 +853,8 @@ "owned_files": [ "frontend-modern/src/api/ai.ts", "frontend-modern/src/api/patrol.ts", + "frontend-modern/src/components/AI/AICostDashboard.tsx", + "frontend-modern/src/utils/aiCostPresentation.ts", "internal/api/ai_handler.go", "internal/api/ai_handlers.go", "internal/api/ai_intelligence_handlers.go" @@ -884,6 +886,22 @@ "internal/api/ai_intelligence_handlers_test.go" ] }, + { + "id": "ai-cost-dashboard-surface", + "label": "AI cost dashboard proof", + "match_prefixes": [], + "match_files": [ + "frontend-modern/src/components/AI/AICostDashboard.tsx", + "frontend-modern/src/utils/aiCostPresentation.ts" + ], + "allow_same_subsystem_tests": false, + "test_prefixes": [], + "exact_files": [ + "frontend-modern/src/components/AI/__tests__/AICostDashboard.test.tsx", + "frontend-modern/src/utils/__tests__/aiCostPresentation.test.ts", + "frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts" + ] + }, { "id": "ai-runtime-engine", "label": "AI runtime engine proof", @@ -1984,9 +2002,12 @@ ".github/workflows/deploy-demo-server.yml", ".github/workflows/release-dry-run.yml", "Dockerfile", + "frontend-modern/package.json", "frontend-modern/src/api/updates.ts", "internal/api/updates.go", + "package.json", "scripts/build-release.sh", + "scripts/dev-check.sh", "scripts/hot-dev-bg.sh", "scripts/hot-dev.sh", "scripts/install-container-agent.sh", @@ -2086,6 +2107,9 @@ "label": "dev runtime orchestration proof", "match_prefixes": [], "match_files": [ + "frontend-modern/package.json", + "package.json", + "scripts/dev-check.sh", "scripts/hot-dev-bg.sh", "scripts/hot-dev.sh", "tests/integration/scripts/managed-dev-runtime.mjs" diff --git a/frontend-modern/package.json b/frontend-modern/package.json index 6869b72264..08acbb799a 100644 --- a/frontend-modern/package.json +++ b/frontend-modern/package.json @@ -14,7 +14,11 @@ }, "homepage": "https://github.com/rcourtman/Pulse", "scripts": { - "dev": "vite", + "dev": "../scripts/hot-dev-bg.sh start --takeover", + "dev:status": "../scripts/hot-dev-bg.sh status", + "dev:stop": "../scripts/hot-dev-bg.sh stop", + "dev:verify": "../scripts/hot-dev-bg.sh verify --takeover", + "dev:frontend-only": "vite", "build": "node scripts/build-embed-assets.mjs", "preview": "vite preview", "generate-types": "cd ../scripts && go run generate-types.go", diff --git a/scripts/dev-check.sh b/scripts/dev-check.sh index 50c7d3ccc1..056a9de9cd 100755 --- a/scripts/dev-check.sh +++ b/scripts/dev-check.sh @@ -14,6 +14,9 @@ NC='\033[0m' # Handle --kill flag if [[ "${1:-}" == "--kill" ]]; then echo "Stopping all dev processes..." + if [[ -x "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hot-dev-bg.sh" ]]; then + "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hot-dev-bg.sh" stop >/dev/null 2>&1 || true + fi pkill -9 -f "bin/pulse$" 2>/dev/null || true pkill -9 -f "^\./pulse$" 2>/dev/null || true pkill -f "node.*vite" 2>/dev/null || true @@ -54,13 +57,13 @@ else echo -e "${RED}✗ NOT RESPONDING${NC}" fi -# Check Vite frontend -echo -n "Vite frontend (port 5173): " +# Check frontend shell +echo -n "Frontend shell (port 5173): " if curl -s -o /dev/null http://127.0.0.1:5173/ 2>/dev/null; then echo -e "${GREEN}✓ Running${NC}" else echo -e "${RED}✗ NOT RUNNING${NC}" - echo " Fix: cd \${PULSE_REPOS_DIR:-\$HOME/pulse/repos}/pulse/frontend-modern && npm run dev" + echo " Fix: cd \${PULSE_REPOS_DIR:-\$HOME/pulse/repos}/pulse && npm run dev" fi # Check AI status diff --git a/scripts/tests/test-hot-dev-bg.sh b/scripts/tests/test-hot-dev-bg.sh index f7356b8faf..300254a58f 100755 --- a/scripts/tests/test-hot-dev-bg.sh +++ b/scripts/tests/test-hot-dev-bg.sh @@ -7,6 +7,7 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" HOT_DEV_BG="${ROOT_DIR}/scripts/hot-dev-bg.sh" PACKAGE_JSON="${ROOT_DIR}/package.json" +FRONTEND_PACKAGE_JSON="${ROOT_DIR}/frontend-modern/package.json" if [[ ! -x "${HOT_DEV_BG}" ]]; then echo "hot-dev-bg.sh not found or not executable at ${HOT_DEV_BG}" >&2 @@ -179,6 +180,35 @@ PY assert_contains "root package keeps foreground escape hatch" "${output}" "dev:foreground=./scripts/hot-dev.sh" } +test_frontend_package_exposes_managed_runtime_entrypoints() { + local output + output="$( + PACKAGE_JSON_PATH="${FRONTEND_PACKAGE_JSON}" \ + python3 - <<'PY' +import json +import os + +with open(os.environ["PACKAGE_JSON_PATH"], "r", encoding="utf-8") as fh: + scripts = json.load(fh)["scripts"] + +for key in [ + "dev", + "dev:status", + "dev:stop", + "dev:verify", + "dev:frontend-only", +]: + print(f"{key}={scripts.get(key, '')}") +PY + )" + + assert_contains "frontend package exposes managed dev start" "${output}" "dev=../scripts/hot-dev-bg.sh start --takeover" + assert_contains "frontend package exposes managed dev status" "${output}" "dev:status=../scripts/hot-dev-bg.sh status" + assert_contains "frontend package exposes managed dev stop" "${output}" "dev:stop=../scripts/hot-dev-bg.sh stop" + assert_contains "frontend package exposes managed dev verify" "${output}" "dev:verify=../scripts/hot-dev-bg.sh verify --takeover" + assert_contains "frontend package keeps explicit frontend-only escape hatch" "${output}" "dev:frontend-only=vite" +} + test_backend_restart_requires_managed_runtime() { local frontend_port backend_port output local state_dir @@ -255,6 +285,7 @@ main() { test_cli_parses_takeover_flag test_verify_command_injects_managed_runtime_env test_root_package_exposes_managed_runtime_entrypoints + test_frontend_package_exposes_managed_runtime_entrypoints test_backend_restart_requires_managed_runtime test_status_without_runtime test_detects_unmanaged_listeners