Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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
55 changes: 22 additions & 33 deletions docs_build/dev/reports/codex_changed_files.txt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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(-)
2 changes: 1 addition & 1 deletion docs_build/dev/reports/codex_review.diff

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion docs_build/dev/reports/coverage_changed_js_guardrail.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 0 additions & 2 deletions docs_build/dev/reports/playwright_v8_coverage_report.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
48 changes: 48 additions & 0 deletions src/dev-runtime/server/local-api-router.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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" }),
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions tests/api/admin-system-health/contract.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
12 changes: 12 additions & 0 deletions tests/dev-runtime/AdminHealthOperations.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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}`),
[
Expand Down
2 changes: 2 additions & 0 deletions tests/playwright/tools/AdminHealthOperationsPage.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Loading