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 + + + | Environment | + Hosting model | + Runtime expectation | + Database model | + Storage folder | + State | + Status | +
+ + + | Environment comparison | Reference-only | Waiting for safe API status | Reference-only | Reference-only | Unavailable | PENDING |
+ +
+
Environment Capabilities 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");