diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints.md b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints.md new file mode 100644 index 000000000..7f1f8d884 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints.md @@ -0,0 +1,30 @@ +# PR_26177_CHARLIE_032-runtime-health-json-endpoints + +Team: Charlie +Branch: pr/26177-CHARLIE-032-runtime-health-json-endpoints +Base: pr/26177-CHARLIE-031-environment-health-comparison +Lifecycle: Build / Validation +Repair: Rebased onto repaired PR_26177_CHARLIE_031 branch on 2026-06-25. + +## Scope +- Added GET /api/runtime/health as a structured Local API JSON health endpoint. +- Endpoint includes environment, API status, database status, storage status, and timestamp. +- Endpoint is server-owned, does not expose secrets, and does not give the browser direct database ownership. + +## Changed Files +- src/dev-runtime/server/local-api-router.mjs +- tests/api/admin-system-health/contract.test.mjs +- tests/dev-runtime/AdminHealthOperations.test.mjs +- tests/playwright/tools/AdminHealthOperationsPage.spec.mjs +- docs_build/dev/reports/coverage_changed_js_guardrail.txt +- docs_build/dev/reports/playwright_v8_coverage_report.txt + +## Validation +- PASS: node --check src/dev-runtime/server/local-api-router.mjs +- PASS: node --test tests/api/admin-system-health/contract.test.mjs +- PASS: node --test tests/dev-runtime/AdminHealthOperations.test.mjs +- PASS: npx playwright test tests/playwright/tools/AdminHealthOperationsPage.spec.mjs --workers=1 --reporter=line +- PASS: git diff --check + +## ZIP +- Generated after repair: C:\Users\DavidQ\Documents\GitHub\HTML-JavaScript-Gaming\tmp\PR_26177_CHARLIE_032-runtime-health-json-endpoints_delta.zip diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_branch-validation.md b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_branch-validation.md new file mode 100644 index 000000000..ffee36713 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_branch-validation.md @@ -0,0 +1,17 @@ +# PR_26177_CHARLIE_032-runtime-health-json-endpoints Branch Validation + +Branch: pr/26177-CHARLIE-032-runtime-health-json-endpoints +Expected stack base: pr/26177-CHARLIE-031-environment-health-comparison +Current status at validation: +## pr/26177-CHARLIE-032-runtime-health-json-endpoints +Updated onto repaired PR_26177_CHARLIE_031 stack base. + +Result: PASS + +Checks: +- PASS: Branch created from PR 031 branch for the approved stacked chain. +- PASS: Active branch matches PR identity. +- PASS: Worktree contained only scoped PR changes and generated validation reports. +- PASS: Branch is based on repaired PR 031 branch. +- PASS: Rebase conflict scope was generated report artifacts only. +- PASS: No start_of_day files modified. diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_manual-validation-notes.md b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_manual-validation-notes.md new file mode 100644 index 000000000..ed6174f57 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_manual-validation-notes.md @@ -0,0 +1,8 @@ +# PR_26177_CHARLIE_032-runtime-health-json-endpoints Manual Validation Notes + +- Confirmed GET /api/runtime/health returns JSON through the Local API router. +- Confirmed payload includes environment, API, database, storage, and timestamp fields. +- Confirmed payload excludes configured API/site credentials and secret values. +- Confirmed System Health API Contract/Admin API Registry list the runtime health endpoint. +- Confirmed branch repair conflict was limited to generated report artifacts. +- Confirmed no start_of_day files changed. diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_requirements-checklist.md b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_requirements-checklist.md new file mode 100644 index 000000000..6a3f730a9 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_requirements-checklist.md @@ -0,0 +1,13 @@ +# PR_26177_CHARLIE_032-runtime-health-json-endpoints Requirement Checklist + +- PASS: Added structured Local API JSON health endpoint. +- PASS: Includes environment details. +- PASS: Includes API status. +- PASS: Includes DB status when available. +- PASS: Includes storage status when available. +- PASS: Includes timestamp. +- PASS: Does not expose secrets or URL credentials. +- PASS: Browser UI remains API/service-contract based. +- PASS: No direct browser database access added. +- PASS: No unrelated files or start_of_day files modified. +- PASS: Rebased onto repaired PR_26177_CHARLIE_031 branch. diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_validation-lane.md b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_validation-lane.md new file mode 100644 index 000000000..0aa4db67f --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_validation-lane.md @@ -0,0 +1,20 @@ +# PR_26177_CHARLIE_032-runtime-health-json-endpoints Validation Lane Report + +Impacted lanes: +- runtime: Local API route contract. +- contract: Admin System Health API contract tests. +- UI-adjacent: Admin System Health API registry display. +- Playwright: targeted Admin System Health page spec. + +Commands: +- PASS: node --check src/dev-runtime/server/local-api-router.mjs +- PASS: node --test tests/api/admin-system-health/contract.test.mjs +- PASS: node --test tests/dev-runtime/AdminHealthOperations.test.mjs +- PASS: npx playwright test tests/playwright/tools/AdminHealthOperationsPage.spec.mjs --workers=1 --reporter=line +- PASS: git diff --check + +Skipped lanes: +- Full samples smoke skipped; no samples/runtime game code changed. +- Full Project Workspace suite skipped; endpoint and Admin registry were covered by targeted tests. + +Result: PASS diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txt index fe0c7b785..d23702138 100644 --- a/docs_build/dev/reports/codex_changed_files.txt +++ b/docs_build/dev/reports/codex_changed_files.txt @@ -1,11 +1,9 @@ -# git diff --name-only pr/26177-CHARLIE-030-r2-storage-health-expanded-validation -- -admin/system-health.html -assets/theme-v2/js/admin-system-health.js -docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison.md -docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_branch-validation.md -docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_manual-validation-notes.md -docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_requirements-checklist.md -docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_validation-lane.md +# git diff --name-only pr/26177-CHARLIE-031-environment-health-comparison -- +docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints.md +docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_branch-validation.md +docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_manual-validation-notes.md +docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_requirements-checklist.md +docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_validation-lane.md docs_build/dev/reports/codex_changed_files.txt docs_build/dev/reports/codex_review.diff docs_build/dev/reports/coverage_changed_js_guardrail.txt @@ -16,29 +14,20 @@ tests/dev-runtime/AdminHealthOperations.test.mjs tests/playwright/tools/AdminHealthOperationsPage.spec.mjs # git status --short - M docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison.md - M docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_branch-validation.md - M docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_manual-validation-notes.md - M docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_requirements-checklist.md - M docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_validation-lane.md - M docs_build/dev/reports/codex_review.diff - M docs_build/dev/reports/coverage_changed_js_guardrail.txt - M docs_build/dev/reports/playwright_v8_coverage_report.txt +(no output) -# git diff --stat pr/26177-CHARLIE-030-r2-storage-health-expanded-validation -- - admin/system-health.html | 20 + - assets/theme-v2/js/admin-system-health.js | 50 ++ - ...77_CHARLIE_031-environment-health-comparison.md | 33 + - ...ironment-health-comparison_branch-validation.md | 17 + - ...nt-health-comparison_manual-validation-notes.md | 9 + - ...ent-health-comparison_requirements-checklist.md | 12 + - ...nvironment-health-comparison_validation-lane.md | 21 + - docs_build/dev/reports/codex_changed_files.txt | 71 +-- - docs_build/dev/reports/codex_review.diff | 663 +-------------------- - .../dev/reports/coverage_changed_js_guardrail.txt | 3 +- - .../dev/reports/playwright_v8_coverage_report.txt | 8 +- - src/dev-runtime/server/local-api-router.mjs | 76 +++ - tests/api/admin-system-health/contract.test.mjs | 13 + - tests/dev-runtime/AdminHealthOperations.test.mjs | 9 + - .../tools/AdminHealthOperationsPage.spec.mjs | 11 + - 15 files changed, 305 insertions(+), 711 deletions(-) +# git diff --stat pr/26177-CHARLIE-031-environment-health-comparison -- + ...77_CHARLIE_032-runtime-health-json-endpoints.md | 30 ++++++++++ + ...time-health-json-endpoints_branch-validation.md | 17 ++++++ + ...ealth-json-endpoints_manual-validation-notes.md | 8 +++ + ...health-json-endpoints_requirements-checklist.md | 13 +++++ + ...untime-health-json-endpoints_validation-lane.md | 20 +++++++ + docs_build/dev/reports/codex_changed_files.txt | 66 ++++++++-------------- + docs_build/dev/reports/codex_review.diff | 2 +- + .../dev/reports/coverage_changed_js_guardrail.txt | 1 - + .../dev/reports/playwright_v8_coverage_report.txt | 2 - + src/dev-runtime/server/local-api-router.mjs | 48 ++++++++++++++++ + tests/api/admin-system-health/contract.test.mjs | 9 +++ + tests/dev-runtime/AdminHealthOperations.test.mjs | 12 ++++ + .../tools/AdminHealthOperationsPage.spec.mjs | 2 + + 13 files changed, 185 insertions(+), 45 deletions(-) diff --git a/docs_build/dev/reports/codex_review.diff b/docs_build/dev/reports/codex_review.diff index 9445ae236..d90693b0c 100644 --- a/docs_build/dev/reports/codex_review.diff +++ b/docs_build/dev/reports/codex_review.diff @@ -1 +1 @@ -diff --git a/admin/system-health.html b/admin/system-health.html index b62f232d7..5294d7d6e 100644 --- a/admin/system-health.html +++ b/admin/system-health.html @@ -40,6 +40,7 @@

Environment Identity

Environment Map

+

Environment Health Comparison

Environment Capabilities

Health API Contract

Admin API Registry

@@ -110,6 +111,25 @@
+
+ + + + + + + + + + + + + + + + +
Environment Health Comparison
EnvironmentHosting modelRuntime expectationDatabase modelStorage folderStateStatus
Environment comparisonReference-onlyWaiting for safe API statusReference-onlyReference-onlyUnavailablePENDING
+
diff --git a/assets/theme-v2/js/admin-system-health.js b/assets/theme-v2/js/admin-system-health.js index 21bc50a63..49e0593b1 100644 --- a/assets/theme-v2/js/admin-system-health.js +++ b/assets/theme-v2/js/admin-system-health.js @@ -69,6 +69,7 @@ class AdminSystemHealthController { this.historyRows = root.querySelector("[data-admin-system-health-history-rows]"); this.apiContractRows = root.querySelector("[data-admin-system-health-api-contract-rows]"); this.apiRegistryRows = root.querySelector("[data-admin-system-health-api-registry-rows]"); + this.environmentComparisonRows = root.querySelector("[data-admin-system-health-environment-comparison-rows]"); this.capabilityRows = root.querySelector("[data-admin-system-health-capability-rows]"); this.featureFlagRows = root.querySelector("[data-admin-system-health-feature-flag-rows]"); this.actionRows = root.querySelector("[data-admin-system-health-action-rows]"); @@ -160,6 +161,7 @@ class AdminSystemHealthController { this.renderPostgresMetricsPending(reason); this.renderStoragePending(reason); this.renderRuntimeHealthPending(reason); + this.renderEnvironmentComparisonPending(reason); this.renderEnvironmentCapabilitiesPending(reason); this.renderApiContractPending(reason); this.renderAdminApiRegistryPending(reason); @@ -203,6 +205,53 @@ class AdminSystemHealthController { this.setEnvironmentStatus("lastHealthCheck", environmentIdentity.lastHealthCheck ? "PASS" : "WARN", reason); } + renderEnvironmentComparisonPending(reason) { + if (!this.environmentComparisonRows) { + return; + } + const row = document.createElement("tr"); + row.append( + this.createCell("Environment comparison"), + this.createCell("Reference-only"), + this.createCell("not available"), + this.createCell("Reference-only"), + this.createCell("Reference-only"), + this.createCell("Unavailable"), + this.createStatusCell("PENDING", reason), + ); + this.environmentComparisonRows.replaceChildren(row); + } + + renderEnvironmentComparison(environmentComparison = {}) { + if (!this.environmentComparisonRows) { + return; + } + if (environmentComparison?.secretsExposed === true || environmentComparison?.secretEditingAllowed === true) { + this.renderEnvironmentComparisonPending("Safe environment comparison response was blocked because it exposed secret controls."); + return; + } + const rows = Array.isArray(environmentComparison.rows) ? environmentComparison.rows : []; + if (!rows.length) { + this.renderEnvironmentComparisonPending("Safe Admin System Health API returned no environment comparison rows."); + return; + } + const fragment = document.createDocumentFragment(); + rows.forEach((comparisonRow) => { + const row = document.createElement("tr"); + row.append( + this.createCell(comparisonRow.displayName), + this.createCell(comparisonRow.hostingModel), + this.createCell(comparisonRow.runtimeExpectation), + this.createCell(comparisonRow.databaseModel), + this.createCell(comparisonRow.storageFolder), + this.createCell(comparisonRow.state), + this.createStatusCell(comparisonRow.status, comparisonRow.summary || environmentComparison.message), + ); + fragment.append(row); + }); + this.environmentComparisonRows.replaceChildren(fragment); + } + renderStoragePending(reason) { ["bucket", "list", "upload", "read", "delete", "lastChecked"].forEach((key) => { this.setStorageStatus(key, "PENDING", reason); @@ -893,6 +942,7 @@ class AdminSystemHealthController { return; } this.renderEnvironmentIdentity(data?.environmentIdentity || {}); + this.renderEnvironmentComparison(data?.environmentComparison || {}); this.renderPostgresStatus(data?.databaseStatus || {}); this.renderStartupDiagnostics(data?.localApiStartup || {}); this.renderStorageStatus(data?.storageStatus || {}); diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison.md b/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison.md new file mode 100644 index 000000000..a21a4baff --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison.md @@ -0,0 +1,33 @@ +# PR_26177_CHARLIE_031-environment-health-comparison + +Team: Charlie +Branch: pr/26177-CHARLIE-031-environment-health-comparison +Base: pr/26177-CHARLIE-030-r2-storage-health-expanded-validation +Lifecycle: Build / Validation +Repair: Rebased onto repaired PR_26177_CHARLIE_030 branch on 2026-06-25. + +## Scope +- Added a server-owned Environment Health Comparison payload and System Health table. +- Shows Local (VS Code), DEV, IST, UAT, and PROD as a reference comparison view. +- Marks only the current deployment as actively checked; peer environments are reference-only Not Configured or Unavailable. + +## Changed Files +- admin/system-health.html +- assets/theme-v2/js/admin-system-health.js +- src/dev-runtime/server/local-api-router.mjs +- tests/api/admin-system-health/contract.test.mjs +- tests/dev-runtime/AdminHealthOperations.test.mjs +- tests/playwright/tools/AdminHealthOperationsPage.spec.mjs +- docs_build/dev/reports/coverage_changed_js_guardrail.txt +- docs_build/dev/reports/playwright_v8_coverage_report.txt + +## Validation +- PASS: node --check src/dev-runtime/server/local-api-router.mjs +- PASS: node --check assets/theme-v2/js/admin-system-health.js +- PASS: node --test tests/api/admin-system-health/contract.test.mjs +- PASS: node --test tests/dev-runtime/AdminHealthOperations.test.mjs +- PASS: npx playwright test tests/playwright/tools/AdminHealthOperationsPage.spec.mjs --workers=1 --reporter=line +- PASS: git diff --check + +## ZIP +- Generated after repair: C:\Users\DavidQ\Documents\GitHub\HTML-JavaScript-Gaming\tmp\PR_26177_CHARLIE_031-environment-health-comparison_delta.zip diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_branch-validation.md b/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_branch-validation.md new file mode 100644 index 000000000..c6b7a27f6 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_branch-validation.md @@ -0,0 +1,17 @@ +# PR_26177_CHARLIE_031-environment-health-comparison Branch Validation + +Branch: pr/26177-CHARLIE-031-environment-health-comparison +Expected stack base: pr/26177-CHARLIE-030-r2-storage-health-expanded-validation +Current status at validation: +## pr/26177-CHARLIE-031-environment-health-comparison +Updated onto repaired PR_26177_CHARLIE_030 stack base. + +Result: PASS + +Checks: +- PASS: Branch created from PR 030 branch for the approved stacked chain. +- PASS: Active branch matches PR identity. +- PASS: Worktree contained only scoped PR changes and generated validation reports. +- PASS: Branch is based on repaired PR 030 branch. +- PASS: Rebase conflict scope was generated report artifacts only. +- PASS: No start_of_day files modified. diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_manual-validation-notes.md b/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_manual-validation-notes.md new file mode 100644 index 000000000..0b516491f --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_manual-validation-notes.md @@ -0,0 +1,9 @@ +# PR_26177_CHARLIE_031-environment-health-comparison Manual Validation Notes + +- Confirmed Environment Health Comparison appears as a reference-only table. +- Confirmed DEV current deployment shows Current in the Playwright environment. +- Confirmed peer rows show Not Configured or Unavailable and are not actively checked. +- Confirmed PROD displays with R2 /prd without changing the existing PRD Environment Map. +- Confirmed no secrets or peer-environment health probes are exposed. +- Confirmed branch repair conflict was limited to generated report artifacts. +- Confirmed no start_of_day files changed. diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_requirements-checklist.md b/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_requirements-checklist.md new file mode 100644 index 000000000..4ca4e8f53 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_requirements-checklist.md @@ -0,0 +1,12 @@ +# PR_26177_CHARLIE_031-environment-health-comparison Requirement Checklist + +- PASS: Added a single System Health comparison view. +- PASS: Includes Local (VS Code), DEV, IST, UAT, and PROD. +- PASS: Shows clear state/status indicators per environment. +- PASS: Local (VS Code) represents local static server/API/developer runtime expectations. +- PASS: Unavailable or non-current environments do not have to pass. +- PASS: No silent defaults; peer rows explicitly show Not Configured or Unavailable. +- PASS: No cross-environment active checks added. +- PASS: Browser UI consumes API/service contract. +- PASS: No unrelated files or start_of_day files modified. +- PASS: Rebased onto repaired PR_26177_CHARLIE_030 branch. diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_validation-lane.md b/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_validation-lane.md new file mode 100644 index 000000000..3730a9234 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_031-environment-health-comparison_validation-lane.md @@ -0,0 +1,21 @@ +# PR_26177_CHARLIE_031-environment-health-comparison Validation Lane Report + +Impacted lanes: +- runtime: Local API Admin System Health status payload. +- contract: Admin System Health API contract tests. +- UI: Admin System Health Theme V2 table rendering. +- Playwright: targeted Admin System Health page spec. + +Commands: +- PASS: node --check src/dev-runtime/server/local-api-router.mjs +- PASS: node --check assets/theme-v2/js/admin-system-health.js +- PASS: node --test tests/api/admin-system-health/contract.test.mjs +- PASS: node --test tests/dev-runtime/AdminHealthOperations.test.mjs +- PASS: npx playwright test tests/playwright/tools/AdminHealthOperationsPage.spec.mjs --workers=1 --reporter=line +- PASS: git diff --check + +Skipped lanes: +- Full samples smoke skipped; comparison view is Admin System Health only. +- Full Project Workspace suite skipped; targeted Admin/System Health coverage was sufficient. + +Result: PASS diff --git a/docs_build/dev/reports/coverage_changed_js_guardrail.txt b/docs_build/dev/reports/coverage_changed_js_guardrail.txt index 7211f2375..fe02b5ecf 100644 --- a/docs_build/dev/reports/coverage_changed_js_guardrail.txt +++ b/docs_build/dev/reports/coverage_changed_js_guardrail.txt @@ -7,8 +7,7 @@ Source: Playwright/Chromium built-in V8 coverage from the active Playwright run. Changed runtime JS files considered: (0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only -(81%) assets/theme-v2/js/admin-system-health.js - executed lines 872/872; executed functions 72/89 -(100%) src/api/admin-system-health-api-client.js - executed lines 31/31; executed functions 5/5 +(80%) assets/theme-v2/js/admin-system-health.js - executed lines 920/920; executed functions 74/92 Guardrail warnings: (0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file missing from coverage; advisory only diff --git a/docs_build/dev/reports/playwright_v8_coverage_report.txt b/docs_build/dev/reports/playwright_v8_coverage_report.txt index 2a2982e6a..c6e25e167 100644 --- a/docs_build/dev/reports/playwright_v8_coverage_report.txt +++ b/docs_build/dev/reports/playwright_v8_coverage_report.txt @@ -18,8 +18,7 @@ Exercised tool entry points detected: Changed runtime JS files covered: (0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only -(81%) assets/theme-v2/js/admin-system-health.js - executed lines 872/872; executed functions 72/89 -(100%) src/api/admin-system-health-api-client.js - executed lines 31/31; executed functions 5/5 +(80%) assets/theme-v2/js/admin-system-health.js - executed lines 920/920; executed functions 74/92 Files with executed line/function counts where available: (36%) src/api/server-api-client.js - executed lines 168/168; executed functions 5/14 @@ -27,9 +26,9 @@ Files with executed line/function counts where available: (65%) src/api/public-config-client.js - executed lines 209/209; executed functions 17/26 (75%) assets/theme-v2/js/gamefoundry-partials.js - executed lines 1046/1046; executed functions 76/102 (77%) assets/theme-v2/js/tool-display-mode.js - executed lines 304/304; executed functions 23/30 +(80%) assets/theme-v2/js/admin-system-health.js - executed lines 920/920; executed functions 74/92 (80%) assets/theme-v2/js/theme-icons.js - executed lines 69/69; executed functions 4/5 (80%) src/api/admin-owner-navigation.js - executed lines 42/42; executed functions 4/5 -(81%) assets/theme-v2/js/admin-system-health.js - executed lines 872/872; executed functions 72/89 (83%) assets/js/shared/status.js - executed lines 37/37; executed functions 5/6 (91%) assets/theme-v2/js/admin-owner-navigation.js - executed lines 58/58; executed functions 10/11 (100%) src/api/admin-system-health-api-client.js - executed lines 31/31; executed functions 5/5 @@ -42,5 +41,4 @@ Changed JS files considered: (0%) tests/api/admin-system-health/contract.test.mjs - changed JS file not collected as browser runtime coverage (0%) tests/dev-runtime/AdminHealthOperations.test.mjs - changed JS file not collected as browser runtime coverage (0%) tests/playwright/tools/AdminHealthOperationsPage.spec.mjs - changed JS file not collected as browser runtime coverage -(81%) assets/theme-v2/js/admin-system-health.js - changed JS file with browser V8 coverage -(100%) src/api/admin-system-health-api-client.js - changed JS file with browser V8 coverage +(80%) assets/theme-v2/js/admin-system-health.js - changed JS file with browser V8 coverage diff --git a/src/dev-runtime/server/local-api-router.mjs b/src/dev-runtime/server/local-api-router.mjs index 6dbb8a106..01702c505 100644 --- a/src/dev-runtime/server/local-api-router.mjs +++ b/src/dev-runtime/server/local-api-router.mjs @@ -983,6 +983,80 @@ function systemHealthEnvironmentMap() { return SYSTEM_HEALTH_ENVIRONMENT_MODELS.map((model) => ({ ...model })); } +function systemHealthEnvironmentComparison({ checkedAt = new Date().toISOString(), environmentIdentity = {} } = {}) { + const currentEnvironmentName = normalizeEnvironmentName(environmentIdentity.name); + const comparisonModels = [ + { + canonicalName: "Local", + databaseModel: "Local Docker PostgreSQL", + displayName: "Local (VS Code)", + hostingModel: "VS Code + Local API", + runtimeExpectation: "Local static server + Local API + developer workstation", + storageFolder: "/local", + }, + { + canonicalName: "DEV", + databaseModel: "Local Docker PostgreSQL", + displayName: "DEV", + hostingModel: "Local Docker", + runtimeExpectation: "Local Docker runtime + Local API", + storageFolder: "/dev", + }, + { + canonicalName: "IST", + databaseModel: "Local Docker PostgreSQL", + displayName: "IST", + hostingModel: "Local Docker", + runtimeExpectation: "Local Docker runtime + Local API", + storageFolder: "/ist", + }, + { + canonicalName: "UAT", + databaseModel: "Supabase PostgreSQL", + displayName: "UAT", + hostingModel: "Cloudflare", + runtimeExpectation: "Cloudflare deployment + server API contract", + storageFolder: "/uat", + }, + { + canonicalName: "PRD", + databaseModel: "Supabase PostgreSQL", + displayName: "PROD", + hostingModel: "Cloudflare", + runtimeExpectation: "Cloudflare deployment + server API contract", + storageFolder: "/prd", + }, + ]; + const rows = comparisonModels.map((model) => { + const current = normalizeEnvironmentName(model.canonicalName) === currentEnvironmentName; + const state = current + ? "Current" + : model.canonicalName === "Local" + ? "Unavailable" + : "Not Configured"; + return { + ...model, + activeCheck: current, + checkedAt: current ? checkedAt : "", + state, + status: current ? environmentIdentity.status || "PASS" : "WARN", + summary: current + ? `This deployment actively checks only ${environmentIdentity.name || model.displayName}.` + : `${model.displayName} is reference-only from this deployment; no active health check was run.`, + }; + }); + return { + currentEnvironment: environmentIdentity.name || "Unknown", + lastChecked: checkedAt, + message: "Environment Health Comparison is reference-only for peer environments; only the current deployment is actively checked.", + noCrossEnvironmentChecks: true, + rows, + secretEditingAllowed: false, + secretsExposed: false, + status: rows.some((row) => row.activeCheck) ? "PASS" : "WARN", + }; +} + function topLevelStorageFolder(value) { const segment = String(value || "") .trim() @@ -4595,6 +4669,7 @@ SELECT pg_database_size(current_database()) AS database_size_bytes, const runtimeFeatureFlags = systemHealthRuntimeFeatureFlags(checkedAt); const environmentIdentity = systemHealthEnvironmentIdentity(process.env, checkedAt); const environmentMap = systemHealthEnvironmentMap(); + const environmentComparison = systemHealthEnvironmentComparison({ checkedAt, environmentIdentity }); const databaseStatus = await this.ownerDatabaseStatus(environmentIdentity); const postgresMetrics = databaseStatus.postgresMetrics || systemHealthPostgresMetrics(databaseStatus, checkedAt); const storageStatus = this.ownerStorageStatus(); @@ -4763,6 +4838,7 @@ SELECT pg_database_size(current_database()) AS database_size_bytes, message: "Admin System Health loaded safe status only.", environmentIdentity, environmentMap, + environmentComparison, environmentCapabilities, healthCheckHistory, notificationsFoundation, diff --git a/tests/api/admin-system-health/contract.test.mjs b/tests/api/admin-system-health/contract.test.mjs index 54e821f13..9d54111f2 100644 --- a/tests/api/admin-system-health/contract.test.mjs +++ b/tests/api/admin-system-health/contract.test.mjs @@ -107,6 +107,7 @@ test("Admin System Health completion contract remains server-owned and current-e [ "apiContract", "adminApiRegistry", + "environmentComparison", "environmentCapabilities", "runtimeFeatureFlags", "serviceHealth", @@ -118,6 +119,7 @@ test("Admin System Health completion contract remains server-owned and current-e [ "apiContract", "adminApiRegistry", + "environmentComparison", "environmentCapabilities", "runtimeFeatureFlags", "serviceHealth", @@ -127,6 +129,17 @@ test("Admin System Health completion contract remains server-owned and current-e "notificationsFoundation", ], ); + assert.equal(health.environmentComparison.noCrossEnvironmentChecks, true); + assert.deepEqual( + health.environmentComparison.rows.map((row) => `${row.displayName}:${row.state}`), + [ + "Local (VS Code):Unavailable", + "DEV:Current", + "IST:Not Configured", + "UAT:Not Configured", + "PROD:Not Configured", + ], + ); assert.deepEqual( health.postgresMetrics.rows.map((row) => row.metric), [ diff --git a/tests/dev-runtime/AdminHealthOperations.test.mjs b/tests/dev-runtime/AdminHealthOperations.test.mjs index 9ea40988a..0c413cfda 100644 --- a/tests/dev-runtime/AdminHealthOperations.test.mjs +++ b/tests/dev-runtime/AdminHealthOperations.test.mjs @@ -226,6 +226,15 @@ test("Admin can view operational health while Creator sessions are blocked", asy health.environmentMap.map((row) => row.name), ["Local", "DEV", "IST", "UAT", "PRD"], ); + assert.equal(health.environmentComparison.noCrossEnvironmentChecks, true); + assert.deepEqual( + health.environmentComparison.rows.map((row) => row.displayName), + ["Local (VS Code)", "DEV", "IST", "UAT", "PROD"], + ); + assert.equal(health.environmentComparison.rows.filter((row) => row.activeCheck === true).length, 1); + assert.equal(health.environmentComparison.rows.find((row) => row.displayName === "Local (VS Code)").state, "Current"); + assert.equal(health.environmentComparison.rows.find((row) => row.displayName === "DEV").state, "Not Configured"); + assert.equal(health.environmentComparison.rows.find((row) => row.displayName === "PROD").storageFolder, "/prd"); assert.equal(health.apiContract.contractVersion, "2026-06-24.system-health.v1"); assert.equal(health.apiContract.currentDeploymentOnly, true); assert.equal(health.apiContract.noCrossEnvironmentChecks, true); diff --git a/tests/playwright/tools/AdminHealthOperationsPage.spec.mjs b/tests/playwright/tools/AdminHealthOperationsPage.spec.mjs index 713344ac0..f7f2cb66c 100644 --- a/tests/playwright/tools/AdminHealthOperationsPage.spec.mjs +++ b/tests/playwright/tools/AdminHealthOperationsPage.spec.mjs @@ -145,6 +145,16 @@ test("Admin System Health renders Postgres diagnostics through the safe status A await expect(page.getByRole("table", { name: "Environment map" })).toContainText("UAT"); await expect(page.getByRole("table", { name: "Environment map" })).toContainText("PRD"); await expect(page.getByRole("table", { name: "Environment map" }).locator("[data-health-status]")).toHaveCount(0); + const comparisonTable = page.getByRole("table", { name: "Environment health comparison" }); + await expect(comparisonTable).toContainText("Local (VS Code)"); + await expect(comparisonTable).toContainText("Local static server + Local API"); + await expect(comparisonTable).toContainText("DEV"); + await expect(comparisonTable).toContainText("Current"); + await expect(comparisonTable).toContainText("IST"); + await expect(comparisonTable).toContainText("UAT"); + await expect(comparisonTable).toContainText("PROD"); + await expect(comparisonTable).toContainText("Not Configured"); + await expect(comparisonTable).toContainText("Unavailable"); const capabilitiesTable = page.getByRole("table", { name: "Environment capabilities" }); await expect(capabilitiesTable).toContainText("Hosting"); await expect(capabilitiesTable).toContainText("API"); @@ -335,6 +345,7 @@ test("Admin System Health operations page keeps scripts and styles external", as expect(pageSource).not.toContain("SQLite"); expect(pageSource).toContain("Environment Identity"); expect(pageSource).toContain("Environment Map"); + expect(pageSource).toContain("Environment Health Comparison"); expect(pageSource).toContain("Environment Capabilities"); expect(pageSource).toContain("Health API Contract"); expect(pageSource).toContain("Admin API Registry"); +diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints.md b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints.md new file mode 100644 index 000000000..7f1f8d884 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints.md @@ -0,0 +1,30 @@ +# PR_26177_CHARLIE_032-runtime-health-json-endpoints + +Team: Charlie +Branch: pr/26177-CHARLIE-032-runtime-health-json-endpoints +Base: pr/26177-CHARLIE-031-environment-health-comparison +Lifecycle: Build / Validation +Repair: Rebased onto repaired PR_26177_CHARLIE_031 branch on 2026-06-25. + +## Scope +- Added GET /api/runtime/health as a structured Local API JSON health endpoint. +- Endpoint includes environment, API status, database status, storage status, and timestamp. +- Endpoint is server-owned, does not expose secrets, and does not give the browser direct database ownership. + +## Changed Files +- src/dev-runtime/server/local-api-router.mjs +- tests/api/admin-system-health/contract.test.mjs +- tests/dev-runtime/AdminHealthOperations.test.mjs +- tests/playwright/tools/AdminHealthOperationsPage.spec.mjs +- docs_build/dev/reports/coverage_changed_js_guardrail.txt +- docs_build/dev/reports/playwright_v8_coverage_report.txt + +## Validation +- PASS: node --check src/dev-runtime/server/local-api-router.mjs +- PASS: node --test tests/api/admin-system-health/contract.test.mjs +- PASS: node --test tests/dev-runtime/AdminHealthOperations.test.mjs +- PASS: npx playwright test tests/playwright/tools/AdminHealthOperationsPage.spec.mjs --workers=1 --reporter=line +- PASS: git diff --check + +## ZIP +- Generated after repair: C:\Users\DavidQ\Documents\GitHub\HTML-JavaScript-Gaming\tmp\PR_26177_CHARLIE_032-runtime-health-json-endpoints_delta.zip diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_branch-validation.md b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_branch-validation.md new file mode 100644 index 000000000..ffee36713 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_branch-validation.md @@ -0,0 +1,17 @@ +# PR_26177_CHARLIE_032-runtime-health-json-endpoints Branch Validation + +Branch: pr/26177-CHARLIE-032-runtime-health-json-endpoints +Expected stack base: pr/26177-CHARLIE-031-environment-health-comparison +Current status at validation: +## pr/26177-CHARLIE-032-runtime-health-json-endpoints +Updated onto repaired PR_26177_CHARLIE_031 stack base. + +Result: PASS + +Checks: +- PASS: Branch created from PR 031 branch for the approved stacked chain. +- PASS: Active branch matches PR identity. +- PASS: Worktree contained only scoped PR changes and generated validation reports. +- PASS: Branch is based on repaired PR 031 branch. +- PASS: Rebase conflict scope was generated report artifacts only. +- PASS: No start_of_day files modified. diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_manual-validation-notes.md b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_manual-validation-notes.md new file mode 100644 index 000000000..ed6174f57 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_manual-validation-notes.md @@ -0,0 +1,8 @@ +# PR_26177_CHARLIE_032-runtime-health-json-endpoints Manual Validation Notes + +- Confirmed GET /api/runtime/health returns JSON through the Local API router. +- Confirmed payload includes environment, API, database, storage, and timestamp fields. +- Confirmed payload excludes configured API/site credentials and secret values. +- Confirmed System Health API Contract/Admin API Registry list the runtime health endpoint. +- Confirmed branch repair conflict was limited to generated report artifacts. +- Confirmed no start_of_day files changed. diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_requirements-checklist.md b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_requirements-checklist.md new file mode 100644 index 000000000..6a3f730a9 --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_requirements-checklist.md @@ -0,0 +1,13 @@ +# PR_26177_CHARLIE_032-runtime-health-json-endpoints Requirement Checklist + +- PASS: Added structured Local API JSON health endpoint. +- PASS: Includes environment details. +- PASS: Includes API status. +- PASS: Includes DB status when available. +- PASS: Includes storage status when available. +- PASS: Includes timestamp. +- PASS: Does not expose secrets or URL credentials. +- PASS: Browser UI remains API/service-contract based. +- PASS: No direct browser database access added. +- PASS: No unrelated files or start_of_day files modified. +- PASS: Rebased onto repaired PR_26177_CHARLIE_031 branch. diff --git a/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_validation-lane.md b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_validation-lane.md new file mode 100644 index 000000000..0aa4db67f --- /dev/null +++ b/docs_build/dev/reports/PR_26177_CHARLIE_032-runtime-health-json-endpoints_validation-lane.md @@ -0,0 +1,20 @@ +# PR_26177_CHARLIE_032-runtime-health-json-endpoints Validation Lane Report + +Impacted lanes: +- runtime: Local API route contract. +- contract: Admin System Health API contract tests. +- UI-adjacent: Admin System Health API registry display. +- Playwright: targeted Admin System Health page spec. + +Commands: +- PASS: node --check src/dev-runtime/server/local-api-router.mjs +- PASS: node --test tests/api/admin-system-health/contract.test.mjs +- PASS: node --test tests/dev-runtime/AdminHealthOperations.test.mjs +- PASS: npx playwright test tests/playwright/tools/AdminHealthOperationsPage.spec.mjs --workers=1 --reporter=line +- PASS: git diff --check + +Skipped lanes: +- Full samples smoke skipped; no samples/runtime game code changed. +- Full Project Workspace suite skipped; endpoint and Admin registry were covered by targeted tests. + +Result: PASS diff --git a/docs_build/dev/reports/coverage_changed_js_guardrail.txt b/docs_build/dev/reports/coverage_changed_js_guardrail.txt index fe02b5ecf..74a29674c 100644 --- a/docs_build/dev/reports/coverage_changed_js_guardrail.txt +++ b/docs_build/dev/reports/coverage_changed_js_guardrail.txt @@ -7,7 +7,6 @@ Source: Playwright/Chromium built-in V8 coverage from the active Playwright run. Changed runtime JS files considered: (0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only -(80%) assets/theme-v2/js/admin-system-health.js - executed lines 920/920; executed functions 74/92 Guardrail warnings: (0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file missing from coverage; advisory only diff --git a/docs_build/dev/reports/playwright_v8_coverage_report.txt b/docs_build/dev/reports/playwright_v8_coverage_report.txt index c6e25e167..e539a3bb1 100644 --- a/docs_build/dev/reports/playwright_v8_coverage_report.txt +++ b/docs_build/dev/reports/playwright_v8_coverage_report.txt @@ -18,7 +18,6 @@ Exercised tool entry points detected: Changed runtime JS files covered: (0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only -(80%) assets/theme-v2/js/admin-system-health.js - executed lines 920/920; executed functions 74/92 Files with executed line/function counts where available: (36%) src/api/server-api-client.js - executed lines 168/168; executed functions 5/14 @@ -41,4 +40,3 @@ Changed JS files considered: (0%) tests/api/admin-system-health/contract.test.mjs - changed JS file not collected as browser runtime coverage (0%) tests/dev-runtime/AdminHealthOperations.test.mjs - changed JS file not collected as browser runtime coverage (0%) tests/playwright/tools/AdminHealthOperationsPage.spec.mjs - changed JS file not collected as browser runtime coverage -(80%) assets/theme-v2/js/admin-system-health.js - changed JS file with browser V8 coverage diff --git a/src/dev-runtime/server/local-api-router.mjs b/src/dev-runtime/server/local-api-router.mjs index 01702c505..9ffc20905 100644 --- a/src/dev-runtime/server/local-api-router.mjs +++ b/src/dev-runtime/server/local-api-router.mjs @@ -365,11 +365,13 @@ const SYSTEM_HEALTH_MANUAL_ACTION_LABELS = Object.freeze({ "storage-check": "Run Storage Check", }); const SYSTEM_HEALTH_API_ENDPOINTS = Object.freeze([ + Object.freeze({ method: "GET", path: "/api/runtime/health", purpose: "Read public-safe Local API runtime health JSON." }), Object.freeze({ method: "GET", path: "/api/admin/system-health/status", purpose: "Read current deployment System Health status." }), Object.freeze({ method: "POST", path: "/api/admin/system-health/action", purpose: "Run current deployment manual health actions." }), Object.freeze({ method: "POST", path: "/api/admin/system-health/storage-connectivity-action", purpose: "Run current deployment R2 folder diagnostics." }), ]); const ADMIN_API_REGISTRY_ENTRIES = Object.freeze([ + Object.freeze({ method: "GET", owner: "Team Charlie", path: "/api/runtime/health", purpose: "Runtime health JSON contract" }), Object.freeze({ method: "GET", owner: "Team Charlie", path: "/api/admin/system-health/status", purpose: "System Health status contract" }), Object.freeze({ method: "POST", owner: "Team Charlie", path: "/api/admin/system-health/action", purpose: "System Health manual actions" }), Object.freeze({ method: "POST", owner: "Team Charlie", path: "/api/admin/system-health/storage-connectivity-action", purpose: "System Health R2 diagnostics" }), @@ -4865,6 +4867,48 @@ SELECT pg_database_size(current_database()) AS database_size_bytes, }; } + async runtimeHealthJsonStatus() { + const checkedAt = new Date().toISOString(); + const environmentIdentity = systemHealthEnvironmentIdentity(process.env, checkedAt); + const runtimeHealth = systemHealthRuntimeHealth(environmentIdentity, checkedAt); + const databaseStatus = await this.ownerDatabaseStatus(environmentIdentity); + const storageStatus = this.ownerStorageStatus(); + return { + api: { + status: runtimeHealth.status || "WARN", + version: runtimeHealth.apiVersion || "not available", + }, + database: { + connectivity: databaseStatus.connectivity || "not configured", + databaseType: databaseStatus.databaseType || environmentIdentity.databaseModel || "PostgreSQL", + lastChecked: databaseStatus.lastChecked || checkedAt, + responseTimeMs: Number.isFinite(databaseStatus.responseTimeMs) ? databaseStatus.responseTimeMs : null, + status: databaseStatus.connectivityStatus || databaseStatus.status || "WARN", + version: databaseStatus.version || "not available", + }, + environment: { + hostingModel: environmentIdentity.hostingModel || "not configured", + name: environmentIdentity.name || "Unknown", + storageFolder: environmentIdentity.storageFolder || "not configured", + }, + message: "Runtime health JSON is server-owned and safe for Local API status consumers.", + secretEditingAllowed: false, + secretsExposed: false, + status: overallHealthStatus([ + { status: runtimeHealth.status || "WARN" }, + { status: databaseStatus.connectivityStatus || databaseStatus.status || "WARN" }, + { status: storageStatus.status || "WARN" }, + ]), + storage: { + configured: storageStatus.configured === true, + environmentFolder: storageStatus.environmentFolder || environmentIdentity.storageFolder || "not configured", + lastChecked: storageStatus.lastChecked || checkedAt, + status: storageStatus.status || "WARN", + }, + timestamp: checkedAt, + }; + } + projectWorkspaceProjectsForRoute() { const activeProject = this.gameWorkspaceRepository.getActiveGame(); const records = gameWorkspaceProjectRecords(this.gameWorkspaceRepository); @@ -6725,6 +6769,10 @@ export function createLocalApiRouter({ if (request.method === "GET" && await handleAdminNotesDirectoryApiRequest(requestUrl, response, { repoRoot })) { return true; } + if (parts[1] === "runtime" && request.method === "GET" && parts[2] === "health") { + ok(response, await dataSource.runtimeHealthJsonStatus()); + return true; + } if (parts[1] === "session") { if (request.method === "HEAD" && ["current", "modes", "users"].includes(parts[2])) { sendNoContent(response, 200); diff --git a/tests/api/admin-system-health/contract.test.mjs b/tests/api/admin-system-health/contract.test.mjs index 9d54111f2..c82496a7b 100644 --- a/tests/api/admin-system-health/contract.test.mjs +++ b/tests/api/admin-system-health/contract.test.mjs @@ -93,6 +93,15 @@ test("Admin System Health completion contract remains server-owned and current-e }, async () => { const server = await startApiServer(); try { + const runtimeJson = await apiJson(server.baseUrl, "/api/runtime/health"); + assert.equal(runtimeJson.environment.name, "DEV"); + assert.equal(runtimeJson.environment.storageFolder, "/dev"); + assert.equal(runtimeJson.api.status, "PASS"); + assert.equal(Object.hasOwn(runtimeJson, "timestamp"), true); + assert.equal(runtimeJson.secretEditingAllowed, false); + assert.equal(runtimeJson.secretsExposed, false); + assert.equal(JSON.stringify(runtimeJson).includes("api-secret"), false); + assert.equal(JSON.stringify(runtimeJson).includes("site-secret"), false); await apiJson(server.baseUrl, "/api/session/user", { body: { userKey: SEED_DB_KEYS.users.admin }, method: "POST", diff --git a/tests/dev-runtime/AdminHealthOperations.test.mjs b/tests/dev-runtime/AdminHealthOperations.test.mjs index 0c413cfda..8d37ad5c3 100644 --- a/tests/dev-runtime/AdminHealthOperations.test.mjs +++ b/tests/dev-runtime/AdminHealthOperations.test.mjs @@ -242,6 +242,7 @@ test("Admin can view operational health while Creator sessions are blocked", asy assert.deepEqual( health.apiContract.endpoints.map((endpoint) => `${endpoint.method} ${endpoint.path}`), [ + "GET /api/runtime/health", "GET /api/admin/system-health/status", "POST /api/admin/system-health/action", "POST /api/admin/system-health/storage-connectivity-action", @@ -250,6 +251,7 @@ test("Admin can view operational health while Creator sessions are blocked", asy assert.deepEqual( health.adminApiRegistry.rows.map((row) => `${row.method} ${row.path}`), [ + "GET /api/runtime/health", "GET /api/admin/system-health/status", "POST /api/admin/system-health/action", "POST /api/admin/system-health/storage-connectivity-action", @@ -260,6 +262,16 @@ test("Admin can view operational health while Creator sessions are blocked", asy "GET /api/navigation/admin-menu", ], ); + const runtimeJson = await apiJson(server.baseUrl, "/api/runtime/health"); + assert.equal(runtimeJson.environment.name, "Local"); + assert.equal(runtimeJson.api.status, "PASS"); + assert.ok(["PASS", "WARN", "FAIL"].includes(runtimeJson.database.status)); + assert.ok(["PASS", "WARN", "FAIL"].includes(runtimeJson.storage.status)); + assert.equal(typeof runtimeJson.timestamp, "string"); + assert.equal(runtimeJson.secretEditingAllowed, false); + assert.equal(runtimeJson.secretsExposed, false); + assert.equal(JSON.stringify(runtimeJson).includes("api-secret"), false); + assert.equal(JSON.stringify(runtimeJson).includes("site-secret"), false); assert.deepEqual( health.runtimeFeatureFlags.rows.map((row) => `${row.flag}:${row.value}`), [ diff --git a/tests/playwright/tools/AdminHealthOperationsPage.spec.mjs b/tests/playwright/tools/AdminHealthOperationsPage.spec.mjs index f7f2cb66c..45d704c3e 100644 --- a/tests/playwright/tools/AdminHealthOperationsPage.spec.mjs +++ b/tests/playwright/tools/AdminHealthOperationsPage.spec.mjs @@ -166,8 +166,10 @@ test("Admin System Health renders Postgres diagnostics through the safe status A await expect(apiContractTable).toContainText("2026-06-24.system-health.v1"); await expect(apiContractTable).toContainText("Current deployment only"); await expect(apiContractTable).toContainText("Reference only"); + await expect(apiContractTable).toContainText("GET /api/runtime/health"); await expect(apiContractTable).toContainText("GET /api/admin/system-health/status"); const apiRegistryTable = page.getByRole("table", { name: "Admin API registry" }); + await expect(apiRegistryTable).toContainText("/api/runtime/health"); await expect(apiRegistryTable).toContainText("/api/admin/system-health/status"); await expect(apiRegistryTable).toContainText("/api/admin/system-health/action"); await expect(apiRegistryTable).toContainText("/api/admin/infrastructure/storage-path-status"); diff --git a/docs_build/dev/reports/coverage_changed_js_guardrail.txt b/docs_build/dev/reports/coverage_changed_js_guardrail.txt index fe02b5ecf..74a29674c 100644 --- a/docs_build/dev/reports/coverage_changed_js_guardrail.txt +++ b/docs_build/dev/reports/coverage_changed_js_guardrail.txt @@ -7,7 +7,6 @@ Source: Playwright/Chromium built-in V8 coverage from the active Playwright run. Changed runtime JS files considered: (0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only -(80%) assets/theme-v2/js/admin-system-health.js - executed lines 920/920; executed functions 74/92 Guardrail warnings: (0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file missing from coverage; advisory only diff --git a/docs_build/dev/reports/playwright_v8_coverage_report.txt b/docs_build/dev/reports/playwright_v8_coverage_report.txt index c6e25e167..e539a3bb1 100644 --- a/docs_build/dev/reports/playwright_v8_coverage_report.txt +++ b/docs_build/dev/reports/playwright_v8_coverage_report.txt @@ -18,7 +18,6 @@ Exercised tool entry points detected: Changed runtime JS files covered: (0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only -(80%) assets/theme-v2/js/admin-system-health.js - executed lines 920/920; executed functions 74/92 Files with executed line/function counts where available: (36%) src/api/server-api-client.js - executed lines 168/168; executed functions 5/14 @@ -41,4 +40,3 @@ Changed JS files considered: (0%) tests/api/admin-system-health/contract.test.mjs - changed JS file not collected as browser runtime coverage (0%) tests/dev-runtime/AdminHealthOperations.test.mjs - changed JS file not collected as browser runtime coverage (0%) tests/playwright/tools/AdminHealthOperationsPage.spec.mjs - changed JS file not collected as browser runtime coverage -(80%) assets/theme-v2/js/admin-system-health.js - changed JS file with browser V8 coverage diff --git a/src/dev-runtime/server/local-api-router.mjs b/src/dev-runtime/server/local-api-router.mjs index 01702c505..9ffc20905 100644 --- a/src/dev-runtime/server/local-api-router.mjs +++ b/src/dev-runtime/server/local-api-router.mjs @@ -365,11 +365,13 @@ const SYSTEM_HEALTH_MANUAL_ACTION_LABELS = Object.freeze({ "storage-check": "Run Storage Check", }); const SYSTEM_HEALTH_API_ENDPOINTS = Object.freeze([ + Object.freeze({ method: "GET", path: "/api/runtime/health", purpose: "Read public-safe Local API runtime health JSON." }), Object.freeze({ method: "GET", path: "/api/admin/system-health/status", purpose: "Read current deployment System Health status." }), Object.freeze({ method: "POST", path: "/api/admin/system-health/action", purpose: "Run current deployment manual health actions." }), Object.freeze({ method: "POST", path: "/api/admin/system-health/storage-connectivity-action", purpose: "Run current deployment R2 folder diagnostics." }), ]); const ADMIN_API_REGISTRY_ENTRIES = Object.freeze([ + Object.freeze({ method: "GET", owner: "Team Charlie", path: "/api/runtime/health", purpose: "Runtime health JSON contract" }), Object.freeze({ method: "GET", owner: "Team Charlie", path: "/api/admin/system-health/status", purpose: "System Health status contract" }), Object.freeze({ method: "POST", owner: "Team Charlie", path: "/api/admin/system-health/action", purpose: "System Health manual actions" }), Object.freeze({ method: "POST", owner: "Team Charlie", path: "/api/admin/system-health/storage-connectivity-action", purpose: "System Health R2 diagnostics" }), @@ -4865,6 +4867,48 @@ SELECT pg_database_size(current_database()) AS database_size_bytes, }; } + async runtimeHealthJsonStatus() { + const checkedAt = new Date().toISOString(); + const environmentIdentity = systemHealthEnvironmentIdentity(process.env, checkedAt); + const runtimeHealth = systemHealthRuntimeHealth(environmentIdentity, checkedAt); + const databaseStatus = await this.ownerDatabaseStatus(environmentIdentity); + const storageStatus = this.ownerStorageStatus(); + return { + api: { + status: runtimeHealth.status || "WARN", + version: runtimeHealth.apiVersion || "not available", + }, + database: { + connectivity: databaseStatus.connectivity || "not configured", + databaseType: databaseStatus.databaseType || environmentIdentity.databaseModel || "PostgreSQL", + lastChecked: databaseStatus.lastChecked || checkedAt, + responseTimeMs: Number.isFinite(databaseStatus.responseTimeMs) ? databaseStatus.responseTimeMs : null, + status: databaseStatus.connectivityStatus || databaseStatus.status || "WARN", + version: databaseStatus.version || "not available", + }, + environment: { + hostingModel: environmentIdentity.hostingModel || "not configured", + name: environmentIdentity.name || "Unknown", + storageFolder: environmentIdentity.storageFolder || "not configured", + }, + message: "Runtime health JSON is server-owned and safe for Local API status consumers.", + secretEditingAllowed: false, + secretsExposed: false, + status: overallHealthStatus([ + { status: runtimeHealth.status || "WARN" }, + { status: databaseStatus.connectivityStatus || databaseStatus.status || "WARN" }, + { status: storageStatus.status || "WARN" }, + ]), + storage: { + configured: storageStatus.configured === true, + environmentFolder: storageStatus.environmentFolder || environmentIdentity.storageFolder || "not configured", + lastChecked: storageStatus.lastChecked || checkedAt, + status: storageStatus.status || "WARN", + }, + timestamp: checkedAt, + }; + } + projectWorkspaceProjectsForRoute() { const activeProject = this.gameWorkspaceRepository.getActiveGame(); const records = gameWorkspaceProjectRecords(this.gameWorkspaceRepository); @@ -6725,6 +6769,10 @@ export function createLocalApiRouter({ if (request.method === "GET" && await handleAdminNotesDirectoryApiRequest(requestUrl, response, { repoRoot })) { return true; } + if (parts[1] === "runtime" && request.method === "GET" && parts[2] === "health") { + ok(response, await dataSource.runtimeHealthJsonStatus()); + return true; + } if (parts[1] === "session") { if (request.method === "HEAD" && ["current", "modes", "users"].includes(parts[2])) { sendNoContent(response, 200); diff --git a/tests/api/admin-system-health/contract.test.mjs b/tests/api/admin-system-health/contract.test.mjs index 9d54111f2..c82496a7b 100644 --- a/tests/api/admin-system-health/contract.test.mjs +++ b/tests/api/admin-system-health/contract.test.mjs @@ -93,6 +93,15 @@ test("Admin System Health completion contract remains server-owned and current-e }, async () => { const server = await startApiServer(); try { + const runtimeJson = await apiJson(server.baseUrl, "/api/runtime/health"); + assert.equal(runtimeJson.environment.name, "DEV"); + assert.equal(runtimeJson.environment.storageFolder, "/dev"); + assert.equal(runtimeJson.api.status, "PASS"); + assert.equal(Object.hasOwn(runtimeJson, "timestamp"), true); + assert.equal(runtimeJson.secretEditingAllowed, false); + assert.equal(runtimeJson.secretsExposed, false); + assert.equal(JSON.stringify(runtimeJson).includes("api-secret"), false); + assert.equal(JSON.stringify(runtimeJson).includes("site-secret"), false); await apiJson(server.baseUrl, "/api/session/user", { body: { userKey: SEED_DB_KEYS.users.admin }, method: "POST", diff --git a/tests/dev-runtime/AdminHealthOperations.test.mjs b/tests/dev-runtime/AdminHealthOperations.test.mjs index 0c413cfda..8d37ad5c3 100644 --- a/tests/dev-runtime/AdminHealthOperations.test.mjs +++ b/tests/dev-runtime/AdminHealthOperations.test.mjs @@ -242,6 +242,7 @@ test("Admin can view operational health while Creator sessions are blocked", asy assert.deepEqual( health.apiContract.endpoints.map((endpoint) => `${endpoint.method} ${endpoint.path}`), [ + "GET /api/runtime/health", "GET /api/admin/system-health/status", "POST /api/admin/system-health/action", "POST /api/admin/system-health/storage-connectivity-action", @@ -250,6 +251,7 @@ test("Admin can view operational health while Creator sessions are blocked", asy assert.deepEqual( health.adminApiRegistry.rows.map((row) => `${row.method} ${row.path}`), [ + "GET /api/runtime/health", "GET /api/admin/system-health/status", "POST /api/admin/system-health/action", "POST /api/admin/system-health/storage-connectivity-action", @@ -260,6 +262,16 @@ test("Admin can view operational health while Creator sessions are blocked", asy "GET /api/navigation/admin-menu", ], ); + const runtimeJson = await apiJson(server.baseUrl, "/api/runtime/health"); + assert.equal(runtimeJson.environment.name, "Local"); + assert.equal(runtimeJson.api.status, "PASS"); + assert.ok(["PASS", "WARN", "FAIL"].includes(runtimeJson.database.status)); + assert.ok(["PASS", "WARN", "FAIL"].includes(runtimeJson.storage.status)); + assert.equal(typeof runtimeJson.timestamp, "string"); + assert.equal(runtimeJson.secretEditingAllowed, false); + assert.equal(runtimeJson.secretsExposed, false); + assert.equal(JSON.stringify(runtimeJson).includes("api-secret"), false); + assert.equal(JSON.stringify(runtimeJson).includes("site-secret"), false); assert.deepEqual( health.runtimeFeatureFlags.rows.map((row) => `${row.flag}:${row.value}`), [ diff --git a/tests/playwright/tools/AdminHealthOperationsPage.spec.mjs b/tests/playwright/tools/AdminHealthOperationsPage.spec.mjs index f7f2cb66c..45d704c3e 100644 --- a/tests/playwright/tools/AdminHealthOperationsPage.spec.mjs +++ b/tests/playwright/tools/AdminHealthOperationsPage.spec.mjs @@ -166,8 +166,10 @@ test("Admin System Health renders Postgres diagnostics through the safe status A await expect(apiContractTable).toContainText("2026-06-24.system-health.v1"); await expect(apiContractTable).toContainText("Current deployment only"); await expect(apiContractTable).toContainText("Reference only"); + await expect(apiContractTable).toContainText("GET /api/runtime/health"); await expect(apiContractTable).toContainText("GET /api/admin/system-health/status"); const apiRegistryTable = page.getByRole("table", { name: "Admin API registry" }); + await expect(apiRegistryTable).toContainText("/api/runtime/health"); await expect(apiRegistryTable).toContainText("/api/admin/system-health/status"); await expect(apiRegistryTable).toContainText("/api/admin/system-health/action"); await expect(apiRegistryTable).toContainText("/api/admin/infrastructure/storage-path-status");
Environment Capabilities