From d62e911bfb0f03c3c75d794d83ec72d681ec7907 Mon Sep 17 00:00:00 2001 From: Code Queue Review Date: Sat, 23 May 2026 06:17:22 +0000 Subject: [PATCH] fix: report current M4 M5 runtime blockers --- docs/dev-evidence-blocker-aggregator.md | 4 +- package.json | 2 +- reports/dev-gate/dev-m4-agent-loop.json | 242 ++++++++-- .../dev-gate/dev-m5-gate-aggregator-v2.json | 416 +++++++----------- reports/dev-gate/dev-m5-gate-aggregator-v2.md | 34 +- reports/dev-gate/dev-mvp-gate-report.json | 63 ++- reports/dev-gate/dev-mvp-gate-report.md | 18 +- scripts/dev-m4-agent-loop-smoke.mjs | 231 +++++----- scripts/l6-cli-web-smoke.mjs | 4 +- .../src/dev-evidence-blocker-aggregator.mjs | 255 ++++++++++- .../dev-evidence-blocker-aggregator.test.mjs | 34 ++ scripts/src/dev-m4-agent-loop-smoke-lib.mjs | 192 +++++++- .../src/dev-m4-agent-loop-smoke-lib.test.mjs | 157 +++++++ .../validate-dev-gate-report-guard.test.mjs | 20 + scripts/validate-dev-gate-report.mjs | 33 +- web/hwlab-cloud-web/gate-summary.mjs | 31 +- 16 files changed, 1261 insertions(+), 475 deletions(-) create mode 100644 scripts/src/dev-evidence-blocker-aggregator.test.mjs create mode 100644 scripts/src/dev-m4-agent-loop-smoke-lib.test.mjs diff --git a/docs/dev-evidence-blocker-aggregator.md b/docs/dev-evidence-blocker-aggregator.md index 288e9a70..bbdcc85c 100644 --- a/docs/dev-evidence-blocker-aggregator.md +++ b/docs/dev-evidence-blocker-aggregator.md @@ -78,8 +78,8 @@ degraded/read-only mode, with `promotesM3M4M5=false`. | M1 | `LOCAL` | `docs/m1-local-smoke.md`, `fixtures/mvp/runtime.json`, `scripts/m1-contract-smoke.mjs`. | | M2 | `DEV-LIVE` for frontend/route only | DEV deploy smoke fixture plus active read-only `16666`/`16667` endpoint smoke and degraded/read-only frontend visibility. | | M3 | `BLOCKED` | Hardware trusted loop topology fixture, local patch-panel smoke, and the no-write DEV plan exist, but DEV-LIVE operation/trace/audit/evidence IDs for `res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1` are missing. | -| M4 | `BLOCKED` | Agent automation loop fixture and local smoke exist, but live preflight is blocked at DB live readiness. | -| M5 | `BLOCKED` | MVP e2e dry-run plan is green, but bounded DEV-LIVE acceptance is blocked by DB/M3/M4 and source/artifact coverage. | +| M4 | `BLOCKED` | Agent automation loop fixture and local smoke exist, but live preflight is blocked at runtime durable adapter readiness and skills commit/version injection, not stale DB-live readiness. | +| M5 | `BLOCKED` | MVP e2e dry-run plan is green, but bounded DEV-LIVE acceptance is blocked by runtime durable adapter, skills injection, M3/M4, and source/artifact coverage. | `milestoneLevelClassification` is the compact M0-M5 answer for command handoff. `milestoneBlockerClassification` gives the M3/M4/M5 blocker class, diff --git a/package.json b/package.json index 3211d7e4..bbcf5414 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "validate": "node scripts/validate-contract.mjs && node scripts/deploy-contract-plan.mjs --check && node scripts/deploy-desired-state-plan.mjs --check && node scripts/dev-runtime-migration.mjs --check && node --check scripts/artifact-runtime-readiness-guard.mjs && node --check scripts/src/artifact-runtime-readiness-guard.mjs && node --test scripts/artifact-runtime-readiness-guard.test.mjs", - "check": "node --check internal/protocol/index.mjs && node --check internal/agent/index.mjs && node --check internal/agent/runtime.mjs && node --check internal/audit/index.mjs && node --check internal/db/runtime-store.mjs && node --check internal/db/runtime-store.test.mjs && node --check internal/mvp-gate/summary.mjs && node --check internal/cloud/db-contract.mjs && node --check internal/dev-entrypoint/http.mjs && node --check internal/dev-entrypoint/http.test.mjs && node --check internal/cloud/code-agent-contract.mjs && node --check internal/cloud/json-rpc.mjs && node --check internal/cloud/code-agent-chat.mjs && node --check internal/cloud/m3-io-control.mjs && node --check internal/cloud/server.mjs && node --check internal/sim/model.mjs && node --check internal/sim/model.test.mjs && node --check internal/sim/http.mjs && node --check internal/sim/l2-runtime.mjs && node --check internal/patchpanel/model.mjs && node --check internal/patchpanel/runtime.mjs && node --check cmd/hwlab-cloud-api/main.mjs && node --check cmd/hwlab-edge-proxy/main.mjs && node --check cmd/hwlab-agent-mgr/main.mjs && node --check cmd/hwlab-agent-worker/main.mjs && node --check cmd/hwlab-box-simu/main.mjs && node --check cmd/hwlab-gateway-simu/main.mjs && node --check scripts/cloud-api-runtime-smoke.mjs && node --check scripts/code-agent-chat-smoke.mjs && node --check scripts/dev-edge-health-smoke.mjs && node --check scripts/src/dev-edge-health-smoke-lib.mjs && node --check scripts/validate-contract.mjs && node --check scripts/deploy-contract-plan.test.mjs && node --check scripts/deploy-desired-state-plan.mjs && node --check scripts/src/deploy-desired-state-plan.mjs && node --check scripts/artifact-runtime-readiness-guard.mjs && node --check scripts/src/artifact-runtime-readiness-guard.mjs && node --check scripts/artifact-runtime-readiness-guard.test.mjs && node --check scripts/report-lifecycle.mjs && node --check scripts/report-lifecycle.test.mjs && node --check scripts/validate-dev-gate-report.mjs && node --check scripts/dev-m3-hardware-loop-smoke.test.mjs && node --check scripts/m3-io-control-e2e.mjs && node --check scripts/src/m3-io-control-e2e.mjs && node --check scripts/m3-io-control-e2e.test.mjs && node --check scripts/dev-cloud-workbench-smoke.test.mjs && node --check scripts/validate-dev-m3-cardinality.mjs && node --check scripts/validate-artifact-catalog.mjs && node --check scripts/refresh-artifact-catalog.mjs && node --check scripts/dev-artifact-publish.mjs && node --check scripts/src/dev-artifact-services.mjs && node --check scripts/src/registry-capabilities.mjs && node --check scripts/preflight-dev-base-image.mjs && node --check scripts/src/dev-base-image-preflight.mjs && node --check scripts/dev-evidence-blocker-aggregator.mjs && node --check scripts/src/dev-evidence-blocker-aggregator.mjs && node --check scripts/d601-k3s-readonly-observability.mjs && node --check scripts/src/d601-k3s-readonly-observability.mjs && node --check scripts/dev-runtime-migration.mjs && node --check scripts/src/dev-runtime-migration.mjs && node --check scripts/src/dev-runtime-migration.test.mjs && node --check scripts/l2-runtime-contract-smoke.mjs && node --check scripts/patch-panel-runtime-smoke.mjs && node --check scripts/export-web-gate-summary.mjs && node --check scripts/l6-cli-web-smoke.mjs && node --check tools/hwlab-cli/bin/hwlab-cli.mjs && node --check tools/hwlab-cli/lib/cli.mjs && node --check web/hwlab-cloud-web/app.mjs && node --check web/hwlab-cloud-web/gate-summary.mjs && node --check web/hwlab-cloud-web/scripts/check.mjs && node --check web/hwlab-cloud-web/scripts/build.mjs && node --check web/hwlab-cloud-web/scripts/dist-contract.mjs && node scripts/validate-contract.mjs && node scripts/validate-dev-gate-report.mjs && node scripts/report-lifecycle.test.mjs && node scripts/validate-dev-m3-cardinality.mjs && node scripts/validate-artifact-catalog.mjs && node scripts/deploy-desired-state-plan.mjs --check && node scripts/dev-runtime-migration.mjs --check && node scripts/dev-evidence-blocker-aggregator.mjs --check && node scripts/l2-runtime-contract-smoke.mjs && node scripts/l6-cli-web-smoke.mjs && node web/hwlab-cloud-web/scripts/check.mjs && node scripts/code-agent-chat-smoke.mjs && node --test scripts/artifact-runtime-readiness-guard.test.mjs scripts/deploy-contract-plan.test.mjs scripts/dev-m3-hardware-loop-smoke.test.mjs scripts/dev-cloud-workbench-smoke.test.mjs scripts/deploy-desired-state-plan.test.mjs scripts/src/dev-deploy-apply.test.mjs scripts/src/dev-runtime-migration.test.mjs internal/agent/index.test.mjs internal/audit/index.test.mjs internal/db/schema.test.mjs internal/db/runtime-store.test.mjs internal/cloud/json-rpc.test.mjs internal/cloud/m3-io-control.test.mjs internal/cloud/server.test.mjs internal/dev-entrypoint/http.test.mjs internal/sim/model.test.mjs internal/patchpanel/model.test.mjs internal/patchpanel/runtime.test.mjs && node scripts/cloud-api-runtime-smoke.mjs && sh -n scripts/bootstrap-skills.sh scripts/worker-entrypoint.sh", + "check": "node --check internal/protocol/index.mjs && node --check internal/agent/index.mjs && node --check internal/agent/runtime.mjs && node --check internal/audit/index.mjs && node --check internal/db/runtime-store.mjs && node --check internal/db/runtime-store.test.mjs && node --check internal/mvp-gate/summary.mjs && node --check internal/cloud/db-contract.mjs && node --check internal/dev-entrypoint/http.mjs && node --check internal/dev-entrypoint/http.test.mjs && node --check internal/cloud/code-agent-contract.mjs && node --check internal/cloud/json-rpc.mjs && node --check internal/cloud/code-agent-chat.mjs && node --check internal/cloud/m3-io-control.mjs && node --check internal/cloud/server.mjs && node --check internal/sim/model.mjs && node --check internal/sim/model.test.mjs && node --check internal/sim/http.mjs && node --check internal/sim/l2-runtime.mjs && node --check internal/patchpanel/model.mjs && node --check internal/patchpanel/runtime.mjs && node --check cmd/hwlab-cloud-api/main.mjs && node --check cmd/hwlab-edge-proxy/main.mjs && node --check cmd/hwlab-agent-mgr/main.mjs && node --check cmd/hwlab-agent-worker/main.mjs && node --check cmd/hwlab-box-simu/main.mjs && node --check cmd/hwlab-gateway-simu/main.mjs && node --check scripts/cloud-api-runtime-smoke.mjs && node --check scripts/code-agent-chat-smoke.mjs && node --check scripts/dev-edge-health-smoke.mjs && node --check scripts/src/dev-edge-health-smoke-lib.mjs && node --check scripts/validate-contract.mjs && node --check scripts/deploy-contract-plan.test.mjs && node --check scripts/deploy-desired-state-plan.mjs && node --check scripts/src/deploy-desired-state-plan.mjs && node --check scripts/artifact-runtime-readiness-guard.mjs && node --check scripts/src/artifact-runtime-readiness-guard.mjs && node --check scripts/artifact-runtime-readiness-guard.test.mjs && node --check scripts/report-lifecycle.mjs && node --check scripts/report-lifecycle.test.mjs && node --check scripts/validate-dev-gate-report.mjs && node --check scripts/dev-m3-hardware-loop-smoke.test.mjs && node --check scripts/m3-io-control-e2e.mjs && node --check scripts/src/m3-io-control-e2e.mjs && node --check scripts/m3-io-control-e2e.test.mjs && node --check scripts/dev-cloud-workbench-smoke.test.mjs && node --check scripts/validate-dev-m3-cardinality.mjs && node --check scripts/validate-artifact-catalog.mjs && node --check scripts/refresh-artifact-catalog.mjs && node --check scripts/dev-artifact-publish.mjs && node --check scripts/src/dev-artifact-services.mjs && node --check scripts/src/registry-capabilities.mjs && node --check scripts/preflight-dev-base-image.mjs && node --check scripts/src/dev-base-image-preflight.mjs && node --check scripts/dev-evidence-blocker-aggregator.mjs && node --check scripts/src/dev-evidence-blocker-aggregator.mjs && node --check scripts/src/dev-evidence-blocker-aggregator.test.mjs && node --check scripts/src/dev-m4-agent-loop-smoke-lib.test.mjs && node --check scripts/d601-k3s-readonly-observability.mjs && node --check scripts/src/d601-k3s-readonly-observability.mjs && node --check scripts/dev-runtime-migration.mjs && node --check scripts/src/dev-runtime-migration.mjs && node --check scripts/src/dev-runtime-migration.test.mjs && node --check scripts/l2-runtime-contract-smoke.mjs && node --check scripts/patch-panel-runtime-smoke.mjs && node --check scripts/export-web-gate-summary.mjs && node --check scripts/l6-cli-web-smoke.mjs && node --check tools/hwlab-cli/bin/hwlab-cli.mjs && node --check tools/hwlab-cli/lib/cli.mjs && node --check web/hwlab-cloud-web/app.mjs && node --check web/hwlab-cloud-web/gate-summary.mjs && node --check web/hwlab-cloud-web/scripts/check.mjs && node --check web/hwlab-cloud-web/scripts/build.mjs && node --check web/hwlab-cloud-web/scripts/dist-contract.mjs && node scripts/validate-contract.mjs && node scripts/validate-dev-gate-report.mjs && node scripts/report-lifecycle.test.mjs && node scripts/validate-dev-m3-cardinality.mjs && node scripts/validate-artifact-catalog.mjs && node scripts/deploy-desired-state-plan.mjs --check && node scripts/dev-runtime-migration.mjs --check && node scripts/dev-evidence-blocker-aggregator.mjs --check && node scripts/l2-runtime-contract-smoke.mjs && node scripts/l6-cli-web-smoke.mjs && node web/hwlab-cloud-web/scripts/check.mjs && node scripts/code-agent-chat-smoke.mjs && node --test scripts/artifact-runtime-readiness-guard.test.mjs scripts/deploy-contract-plan.test.mjs scripts/dev-m3-hardware-loop-smoke.test.mjs scripts/dev-cloud-workbench-smoke.test.mjs scripts/deploy-desired-state-plan.test.mjs scripts/src/dev-deploy-apply.test.mjs scripts/src/dev-runtime-migration.test.mjs scripts/src/dev-m4-agent-loop-smoke-lib.test.mjs scripts/src/dev-evidence-blocker-aggregator.test.mjs internal/agent/index.test.mjs internal/audit/index.test.mjs internal/db/schema.test.mjs internal/db/runtime-store.test.mjs internal/cloud/json-rpc.test.mjs internal/cloud/m3-io-control.test.mjs internal/cloud/server.test.mjs internal/dev-entrypoint/http.test.mjs internal/sim/model.test.mjs internal/patchpanel/model.test.mjs internal/patchpanel/runtime.test.mjs && node scripts/cloud-api-runtime-smoke.mjs && sh -n scripts/bootstrap-skills.sh scripts/worker-entrypoint.sh", "dev-base-image:preflight": "node scripts/preflight-dev-base-image.mjs", "cloud-api:smoke": "node scripts/cloud-api-runtime-smoke.mjs", "m1:smoke": "node scripts/m1-contract-smoke.mjs", diff --git a/reports/dev-gate/dev-m4-agent-loop.json b/reports/dev-gate/dev-m4-agent-loop.json index 52814ca0..b91beabb 100644 --- a/reports/dev-gate/dev-m4-agent-loop.json +++ b/reports/dev-gate/dev-m4-agent-loop.json @@ -4,7 +4,7 @@ "reportVersion": "v1", "issue": "pikasTech/HWLAB#37", "taskId": "dev-m4-agent-loop", - "commitId": "0614202", + "commitId": "d881a50", "acceptanceLevel": "dev_m4_agent_loop", "devOnly": true, "prodDisabled": true, @@ -94,21 +94,26 @@ "summary": "Dry-run produced trace/evidence records and removed the temporary worker workspace." }, "dbLive": { - "level": "BLOCKED", - "status": "blocked", - "summary": "Live scheduling is stopped at cloud-api DB readiness; DB health is classified separately from local dry-run evidence." + "level": "DEV-LIVE", + "status": "pass", + "summary": "Current read-only /health/live evidence reports DB ready=true, connected=true, and liveDbEvidence=true; DB live is no longer the active M4 blocker." }, "m3HardwareLoopDependency": { "level": "BLOCKED", "status": "not_run", "requiredPath": "DO1 -> hwlab-patch-panel -> DI1", "summary": "M4 can only support M3 through cloud-api/hardware API and the patch-panel trusted path; no direct box/gateway path is used." + }, + "runtimeDurableAdapter": { + "level": "BLOCKED", + "status": "blocked", + "summary": "Live scheduling is stopped at runtime durable adapter readiness; DB live evidence alone is not runtime durability evidence." } }, "dependencyState": { "dbLive": { - "status": "blocked", - "summary": "cloud-api /health/live reports DB degraded; connected=false; ready=false." + "status": "pass", + "summary": "cloud-api /health/live reports DB ready=true, connected=true, and liveDbEvidence=true." }, "m3HardwareLoop": { "status": "not_run", @@ -123,6 +128,11 @@ "direct hwlab-gateway-simu access", "agent-side fixture promotion to DEV-LIVE" ] + }, + "runtimeDurableAdapter": { + "status": "blocked", + "requiredEvidence": "Postgres runtime adapter schema, migration ledger, and read-query readiness with liveRuntimeEvidence=true.", + "summary": "cloud-api durable runtime adapter is blocked: runtime_durable_adapter_query_blocked; migration 0001_cloud_core_skeleton is missing or not checked; read-query durability is not proven." } }, "d601K3sNative": { @@ -168,7 +178,7 @@ "agentManager": { "deployment": { "name": "hwlab-agent-mgr", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:cb35ada", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:d881a50", "readyReplicas": 1, "replicas": 1, "available": "True", @@ -195,14 +205,14 @@ "agentSkills": { "deployment": { "name": "hwlab-agent-skills", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:cb35ada", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:d881a50", "readyReplicas": 1, "replicas": 1, "available": "True", "ready": true, "env": { "values": { - "HWLAB_SKILLS_COMMIT_ID": "cb35ada" + "HWLAB_SKILLS_COMMIT_ID": "d881a50" }, "valueFrom": [] } @@ -212,7 +222,7 @@ "statusCode": 200, "status": "ok", "serviceId": "hwlab-agent-skills", - "revision": "cb35ada6860653b27269b0a57991184118cbf4b1", + "revision": "d881a50", "error": null } }, @@ -220,7 +230,7 @@ "template": { "name": "hwlab-agent-worker-template", "suspend": true, - "image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:cb35ada", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:d881a50", "restartPolicy": "Never", "env": { "values": { @@ -251,9 +261,9 @@ "skillsInjection": { "status": "blocked", "source": "k3s deployment env plus generated worker dry-run manifest", - "serviceCommitId": "cb35ada", + "serviceCommitId": "d881a50", "serviceVersion": null, - "workerDryRunCommitId": "cb35ada", + "workerDryRunCommitId": "d881a50", "workerDryRunVersion": "", "workerDryRunSource": "derived-from-live-dev-skills-env", "missing": [ @@ -271,11 +281,11 @@ "k3s:deploy/hwlab-agent-skills:ready=1/1:available=True", "k3s:svc/hwlab-agent-mgr:endpoints=1", "k3s:svc/hwlab-agent-skills:endpoints=1", - "agent-mgr:/health/live:degraded", - "agent-skills:/health/live:ok:cb35ada6860653b27269b0a57991184118cbf4b1", - "worker-template:suspend=true:image=127.0.0.1:5000/hwlab/hwlab-agent-worker:cb35ada", + "agent-mgr:/health/live:degraded:missing=manager.skillCommitId,manager.skillVersion", + "agent-skills:/health/live:ok:d881a50", + "worker-template:suspend=true:image=127.0.0.1:5000/hwlab/hwlab-agent-worker:d881a50", "worker-job:server-dry-run=true:persisted=false", - "skills-injection:commit=cb35ada:version=missing", + "skills-injection:commit=d881a50:version=missing", "secret-rbac:get=yes:secretResourcesRead=false" ], "blockers": [ @@ -323,12 +333,168 @@ "environment": "dev", "status": "degraded", "db": { - "status": "degraded", - "connected": false, - "ready": false, - "evidence": "env_presence_only_no_live_db" + "status": "ok", + "connected": true, + "liveConnected": true, + "ready": true, + "configReady": true, + "connectionChecked": true, + "connectionAttempted": true, + "connectionResult": "connected", + "endpointSource": "secret-url-host", + "liveDbEvidence": true, + "evidence": "live_db_tcp_connection_ready" }, - "error": null + "error": null, + "codeAgent": { + "endpoint": "POST /v1/agent/chat", + "status": "available", + "ready": true, + "provider": "openai-responses", + "schema": [ + "conversationId", + "sessionId", + "messageId", + "status", + "createdAt", + "updatedAt", + "traceId", + "provider", + "model", + "backend", + "error.message" + ] + }, + "runtime": { + "adapter": "postgres", + "durable": false, + "durableRequested": true, + "durableCapable": false, + "ready": false, + "status": "blocked", + "blocker": "runtime_durable_adapter_query_blocked", + "reason": "Postgres runtime adapter is configured, but durable read-query readiness is blocked; migration 0001_cloud_core_skeleton is missing or not checked.", + "liveRuntimeEvidence": false, + "fixtureEvidence": false, + "connection": { + "queryAttempted": true, + "queryResult": "query_blocked", + "endpointRedacted": true, + "valueRedacted": true, + "errorCode": "not_observed" + }, + "schema": { + "checked": true, + "ready": true, + "missingTables": [], + "missingColumns": [] + }, + "migration": { + "checked": false, + "ready": false, + "missing": true, + "requiredMigrationId": "0001_cloud_core_skeleton", + "requiredSchemaVersion": "runtime-durable-postgres-v1", + "appliedMigrationId": null, + "appliedSchemaVersion": null + }, + "gates": { + "auth": { + "checked": true, + "ready": true, + "status": "ready", + "blocker": null + }, + "schema": { + "checked": true, + "ready": true, + "status": "ready", + "blocker": null + }, + "migration": { + "checked": false, + "ready": false, + "status": "blocked", + "blocker": "runtime_durable_adapter_migration_blocked" + }, + "durability": { + "checked": true, + "ready": false, + "status": "blocked", + "blocker": "runtime_durable_adapter_query_blocked" + } + }, + "durabilityContract": { + "ready": false, + "status": "blocked", + "blockedLayer": "durability_query", + "requiredEvidence": "runtime_adapter_schema_migration_read_query", + "dbLiveEvidenceIsDurabilityEvidence": false, + "secretMaterialRead": false + }, + "counts": {} + }, + "readiness": { + "contractVersion": "v3", + "ready": false, + "status": "degraded", + "blockerCodes": [ + "runtime_durable_adapter_query_blocked" + ], + "components": { + "db": "ready", + "codeAgent": "ready", + "runtime": "blocked" + }, + "durability": { + "status": "blocked", + "ready": false, + "adapter": "postgres", + "durable": false, + "durableRequested": true, + "durableCapable": false, + "runtimeStatus": "blocked", + "runtimeReady": false, + "liveRuntimeEvidence": false, + "dbLiveEvidenceObserved": true, + "dbLiveEvidenceIsDurabilityEvidence": false, + "requiredEvidence": "runtime_adapter_schema_migration_read_query", + "blockedLayer": "durability_query", + "blocker": "runtime_durable_adapter_query_blocked", + "gates": { + "auth": { + "checked": true, + "ready": true, + "status": "ready", + "blocker": null + }, + "schema": { + "checked": true, + "ready": true, + "status": "ready", + "blocker": null + }, + "migration": { + "checked": false, + "ready": false, + "status": "blocked", + "blocker": "runtime_durable_adapter_migration_blocked" + }, + "durability": { + "checked": true, + "ready": false, + "status": "blocked", + "blocker": "runtime_durable_adapter_query_blocked" + } + }, + "queryAttempted": true, + "queryResult": "query_blocked", + "secretMaterialRead": false + } + }, + "blockerCodes": [ + "runtime_durable_adapter_query_blocked" + ] } }, "frontend": { @@ -338,7 +504,7 @@ "contentLength": "11180", "error": null }, - "summary": "Public DEV API http://74.48.78.17:16667 health=200 live=200; frontend http://74.48.78.17:16666/=200." + "summary": "Public DEV API http://74.48.78.17:16667 health=200 live=200 with DB live ready and runtime durable adapter blocked; frontend http://74.48.78.17:16666/=200." }, "safetyEvidence": { "devOnly": true, @@ -359,24 +525,26 @@ }, "devPreconditions": { "status": "blocked", - "classification": "DB live", + "classification": "runtime_durable_adapter_query_blocked", "requirements": [ "DEV route remains frozen at http://74.48.78.17:16667", "DEV browser route remains frozen at http://74.48.78.17:16666", "D601 native k3s access uses KUBECONFIG=/etc/rancher/k3s/k3s.yaml and namespace hwlab-dev", "No secret reads, real deployment, or long-running agent task is attempted", + "DB live readiness and runtime durable adapter readiness are separate gates; DB live alone cannot promote M4", "Agent manager, worker, and skills are reachable only through the cloud API boundary", "M4 hardware assistance must go through cloud-api/hardware API and patch-panel; direct box/gateway access is forbidden" ], - "summary": "Blocked at DB live readiness before scheduling a DEV agent task." + "summary": "Blocked at runtime durable adapter readiness before scheduling a DEV agent task." }, "blockers": [ { "type": "runtime_blocker", - "scope": "db-live", + "scope": "runtime-durable-adapter", "status": "open", - "summary": "cloud-api /health/live reports DB degraded; connected=false; ready=false.", - "classification": "DB live" + "sourceIssue": "pikasTech/HWLAB#164", + "summary": "cloud-api durable runtime adapter is blocked: runtime_durable_adapter_query_blocked; adapter=postgres; durable=false; ready=false; migration=0001_cloud_core_skeleton checked=false ready=false missing=true; queryResult=query_blocked.", + "classification": "runtime_durable_adapter_query_blocked" }, { "status": "open", @@ -397,7 +565,7 @@ ], "livePreflight": { "status": "blocked", - "classification": "DB live", + "classification": "runtime_durable_adapter_query_blocked", "commands": [ "node scripts/dev-m4-agent-loop-smoke.mjs --live --confirm-dev --confirmed-non-production" ], @@ -407,17 +575,19 @@ "k3s:deploy/hwlab-agent-skills:ready=1/1:available=True", "k3s:svc/hwlab-agent-mgr:endpoints=1", "k3s:svc/hwlab-agent-skills:endpoints=1", - "agent-mgr:/health/live:degraded", - "agent-skills:/health/live:ok:cb35ada6860653b27269b0a57991184118cbf4b1", - "worker-template:suspend=true:image=127.0.0.1:5000/hwlab/hwlab-agent-worker:cb35ada", + "agent-mgr:/health/live:degraded:missing=manager.skillCommitId,manager.skillVersion", + "agent-skills:/health/live:ok:d881a50", + "worker-template:suspend=true:image=127.0.0.1:5000/hwlab/hwlab-agent-worker:d881a50", "worker-job:server-dry-run=true:persisted=false", - "skills-injection:commit=cb35ada:version=missing", + "skills-injection:commit=d881a50:version=missing", "secret-rbac:get=yes:secretResourcesRead=false", - "Public DEV API http://74.48.78.17:16667 health=200 live=200; frontend http://74.48.78.17:16666/=200.", + "Public DEV API http://74.48.78.17:16667 health=200 live=200 with DB live ready and runtime durable adapter blocked; frontend http://74.48.78.17:16666/=200.", "health:hwlab-edge-proxy:dev:ok", - "live:hwlab-cloud-api:degraded:db-blocked" + "live:hwlab-cloud-api:degraded:runtime-durable-adapter-blocked:runtime_durable_adapter_query_blocked", + "db-live:ready=true:connected=true:liveDbEvidence=true", + "runtime-durable-adapter:adapter=postgres:blocker=runtime_durable_adapter_query_blocked:migration=0001_cloud_core_skeleton:migrationChecked=false:migrationReady=false:queryResult=query_blocked" ], - "summary": "Blocked at DB live readiness before scheduling a DEV agent task." + "summary": "Blocked at runtime durable adapter readiness before scheduling a DEV agent task." }, "notes": "DEV M4 agent loop report. SOURCE/LOCAL/DRY-RUN evidence is not DEV-LIVE, and no secret material is read." } diff --git a/reports/dev-gate/dev-m5-gate-aggregator-v2.json b/reports/dev-gate/dev-m5-gate-aggregator-v2.json index d2380001..b4e633c7 100644 --- a/reports/dev-gate/dev-m5-gate-aggregator-v2.json +++ b/reports/dev-gate/dev-m5-gate-aggregator-v2.json @@ -20,8 +20,8 @@ "pikasTech/HWLAB#46", "pikasTech/HWLAB#64" ], - "generatedAt": "2026-05-23T03:16:52.953Z", - "generatedFromCommit": "96e802b2a22c", + "generatedAt": "2026-05-23T06:17:15.938Z", + "generatedFromCommit": "66586ccf109e", "environment": "dev", "endpoint": "http://74.48.78.17:16667", "frontendEndpoint": "http://74.48.78.17:16666", @@ -116,7 +116,7 @@ "taskId": "dev-m4-agent-loop", "lifecycleState": "active", "status": "blocked", - "commitId": "0614202" + "commitId": "d881a50" }, "devM5Gate": { "path": "reports/dev-gate/dev-mvp-gate-report.json", @@ -138,7 +138,7 @@ "overall": { "status": "blocked", "green": false, - "reason": "Frontend revision 1e8805664970839b72be40c34636b08f6d18b131 is usable in degraded/read-only mode and EDGE/ROUTE DEV-LIVE evidence exists on :16666/:16667, but M5 remains blocked by artifact source drift, API/runtime durability degradation, missing M3 trusted loop operation evidence, and blocked M4 agent-loop preflight." + "reason": "Frontend revision 1e8805664970839b72be40c34636b08f6d18b131 is usable in degraded/read-only mode and EDGE/ROUTE DEV-LIVE evidence exists on :16666/:16667, but M5 remains blocked by artifact source drift, runtime durable adapter or skills injection readiness, missing M3 trusted loop operation evidence, and blocked M4 agent-loop preflight." }, "latestFrontendDevFact": { "revision": "1e8805664970839b72be40c34636b08f6d18b131", @@ -204,9 +204,9 @@ }, { "id": "cloud-api-db-ready", - "status": "blocked", - "evidenceLevel": "BLOCKED", - "summary": "cloud-api DB status=degraded; ready=false; connected=false; endpointSource=unknown; liveDbEvidence=false." + "status": "pass", + "evidenceLevel": "DEV-LIVE", + "summary": "cloud-api DB status=ok; ready=true; connected=true; endpointSource=secret-url-host; liveDbEvidence=true." }, { "id": "m3-hardware-trusted-loop", @@ -255,16 +255,53 @@ }, "dbLive": { "label": "DB live/degraded", - "status": "blocked", - "evidenceLevel": "BLOCKED", - "summary": "cloud-api DB status=degraded; configReady=true; ready=false; connected=false; endpointSource=unknown; liveDbEvidence=false.", + "status": "pass", + "evidenceLevel": "DEV-LIVE", + "summary": "cloud-api DB status=ok; configReady=true; ready=true; connected=true; endpointSource=secret-url-host; liveDbEvidence=true.", "evidence": [ - "http://74.48.78.17:16667/health/live -> HTTP 200 serviceId=hwlab-cloud-api status=degraded db.ready=false db.connected=false db.connectionChecked=false", - "dev-edge-health active report -> blocked by DB live readiness, while public 16667 TCP/HTTP and frp control/tunnel health are reachable", - "dev-m4-agent-loop active report -> blocked, classification=DB live, summary=cloud-api /health/live reports DB degraded; connected=false; ready=false" + "http://74.48.78.17:16667/health/live -> HTTP 200 serviceId=hwlab-cloud-api status=degraded db.ready=true db.connected=true db.liveDbEvidence=true runtime.blocker=runtime_durable_adapter_query_blocked runtime.migration=0001_cloud_core_skeleton", + "dev-edge-health active report remains older DB-live evidence and is superseded for M4/M5 blocker naming by current d881a50 M4 preflight evidence" ], "nextRequired": "Provide live DB connection evidence through redacted health output; route reachability alone is insufficient." }, + "runtimeDurableAdapter": { + "label": "Runtime durable adapter", + "status": "blocked", + "evidenceLevel": "BLOCKED", + "summary": "runtime adapter=postgres; durableRequested=true; durable=false; ready=false; blocker=runtime_durable_adapter_query_blocked; migration=0001_cloud_core_skeleton checked=false ready=false; queryResult=query_blocked.", + "evidence": [ + "requiredEvidence=runtime_adapter_schema_migration_read_query", + "blockedLayer=durability_query", + "migration=0001_cloud_core_skeleton", + "queryAttempted=true" + ], + "nextRequired": "Repair the postgres durable runtime adapter query/migration readiness and rerun health/M4 preflight without treating DB live as durability proof." + }, + "agentSkillsInjection": { + "label": "Agent skills injection", + "status": "blocked", + "evidenceLevel": "BLOCKED", + "summary": "DEV skills injection is incomplete: missing hwlab-agent-skills.HWLAB_SKILLS_VERSION, worker-dry-run.HWLAB_SKILL_VERSION_FROM_DEV.", + "evidence": [ + "skills.commit=d881a50", + "skills.version=missing", + "worker.commit=d881a50", + "worker.version=", + "missing=hwlab-agent-skills.HWLAB_SKILLS_VERSION,worker-dry-run.HWLAB_SKILL_VERSION_FROM_DEV,manager.skillCommitId,manager.skillVersion" + ], + "nextRequired": "Inject explicit skill commit and version into hwlab-agent-mgr, hwlab-agent-skills, and scheduled worker jobs before claiming M4/M5 agent evidence." + }, + "codeAgentProvider": { + "label": "Code Agent provider", + "status": "pass", + "evidenceLevel": "DEV-LIVE", + "summary": "code-agent provider=openai-responses; status=available; ready=true.", + "evidence": [ + "provider=openai-responses", + "status=available" + ], + "nextRequired": "Keep provider-backed Code Agent readiness separate from DB live and runtime durable adapter readiness." + }, "d601RunnerObservability": { "label": "D601 runner observability", "status": "blocked", @@ -296,24 +333,26 @@ "label": "M4 agent loop", "status": "blocked", "evidenceLevel": "BLOCKED", - "summary": "Blocked at DB live readiness before scheduling a DEV agent task.", + "summary": "Blocked at runtime durable adapter readiness before scheduling a DEV agent task.", "evidence": [ "k3s:namespace=hwlab-dev:read=true", "k3s:deploy/hwlab-agent-mgr:ready=1/1:available=True", "k3s:deploy/hwlab-agent-skills:ready=1/1:available=True", "k3s:svc/hwlab-agent-mgr:endpoints=1", "k3s:svc/hwlab-agent-skills:endpoints=1", - "agent-mgr:/health/live:degraded", - "agent-skills:/health/live:ok:cb35ada6860653b27269b0a57991184118cbf4b1", - "worker-template:suspend=true:image=127.0.0.1:5000/hwlab/hwlab-agent-worker:cb35ada", + "agent-mgr:/health/live:degraded:missing=manager.skillCommitId,manager.skillVersion", + "agent-skills:/health/live:ok:d881a50", + "worker-template:suspend=true:image=127.0.0.1:5000/hwlab/hwlab-agent-worker:d881a50", "worker-job:server-dry-run=true:persisted=false", - "skills-injection:commit=cb35ada:version=missing", + "skills-injection:commit=d881a50:version=missing", "secret-rbac:get=yes:secretResourcesRead=false", - "Public DEV API http://74.48.78.17:16667 health=200 live=200; frontend http://74.48.78.17:16666/=200.", + "Public DEV API http://74.48.78.17:16667 health=200 live=200 with DB live ready and runtime durable adapter blocked; frontend http://74.48.78.17:16666/=200.", "health:hwlab-edge-proxy:dev:ok", - "live:hwlab-cloud-api:degraded:db-blocked" + "live:hwlab-cloud-api:degraded:runtime-durable-adapter-blocked:runtime_durable_adapter_query_blocked", + "db-live:ready=true:connected=true:liveDbEvidence=true", + "runtime-durable-adapter:adapter=postgres:blocker=runtime_durable_adapter_query_blocked:migration=0001_cloud_core_skeleton:migrationChecked=false:migrationReady=false:queryResult=query_blocked" ], - "nextRequired": "Do not schedule or claim the agent loop as live until DB live and required runtime/evidence preconditions pass." + "nextRequired": "Do not schedule or claim the agent loop as live until DB live, runtime durable adapter, skills injection, and required evidence preconditions pass." }, "artifactDesiredStateSource": { "label": "artifact/desired-state source", @@ -367,7 +406,7 @@ "status": "blocked", "strongestEvidenceLevel": "DRY-RUN", "liveEvidence": "missing_or_blocked", - "summary": "DEV-LIVE agent loop is blocked at DB live readiness; local smoke is not acceptance." + "summary": "DEV-LIVE agent loop is blocked at runtime durable adapter or skills injection readiness; local smoke is not acceptance." }, { "milestone": "M5", @@ -404,31 +443,39 @@ "milestone": "M4", "status": "blocked", "currentLevel": "BLOCKED", - "blockerClass": "db-live-readiness", - "dependency": "Cloud API /health/live must report DB ready=true, connected=true, endpointSource=secret-url-host, and liveDbEvidence=true before live agent scheduling/evidence closure.", + "blockerClass": "runtime_durable_adapter_query_blocked", + "dependency": "Cloud API /health/live must prove the postgres durable runtime adapter schema, 0001_cloud_core_skeleton migration ledger, and read-query readiness before live agent scheduling/evidence closure.", "evidence": [ - "db.status=degraded", - "db.ready=false", - "db.connected=false", - "db.endpointSource=unknown", - "db.liveDbEvidence=false" + "db.status=ok", + "db.ready=true", + "db.connected=true", + "db.endpointSource=secret-url-host", + "db.liveDbEvidence=true", + "runtime.adapter=postgres", + "runtime.blocker=runtime_durable_adapter_query_blocked", + "runtime.migration=0001_cloud_core_skeleton", + "runtime.migration.ready=false", + "runtime.queryResult=query_blocked", + "skills.missing=hwlab-agent-skills.HWLAB_SKILLS_VERSION,worker-dry-run.HWLAB_SKILL_VERSION_FROM_DEV,manager.skillCommitId,manager.skillVersion" ], - "nextRequired": "Repair DB live readiness and rerun the M4 live preflight without scheduling a DEV agent task before preconditions pass.", - "nonPromotionReason": "Frontend revision and read-only route reachability do not prove DB-backed agent runtime readiness." + "nextRequired": "Repair the runtime durable adapter query/migration readiness and rerun the M4 live preflight without scheduling a DEV agent task before preconditions pass.", + "nonPromotionReason": "Frontend revision and read-only route reachability do not prove DB-backed durable agent runtime readiness or skill version injection." }, { "milestone": "M5", "status": "blocked", "currentLevel": "BLOCKED", - "blockerClass": "composite-db-m3-m4-live", - "dependency": "M5 needs DB live readiness, M3 trusted-loop DEV evidence, M4 live preflight/evidence closure, and current source/artifact coverage.", + "blockerClass": "composite-runtime-durable-m3-m4-live", + "dependency": "M5 needs DB live readiness, runtime durable adapter readiness, skills commit/version injection, M3 trusted-loop DEV evidence, M4 live preflight/evidence closure, and current source/artifact coverage.", "evidence": [ "M5 dry-run is green", - "db.ready=false", + "db.ready=true", + "runtime.blocker=runtime_durable_adapter_query_blocked", + "skills.status=blocked", "m3.live=false", "m4.live=false" ], - "nextRequired": "After DB/M3/M4 blockers are cleared, run only the bounded DEV MVP live gate command with explicit DEV/non-PROD confirmations.", + "nextRequired": "After DB/runtime durable adapter/skills/M3/M4 blockers are cleared, run only the bounded DEV MVP live gate command with explicit DEV/non-PROD confirmations.", "nonPromotionReason": "No frontend, route-only, local, or dry-run evidence is allowed to stand in for bounded DEV-LIVE MVP e2e acceptance." } ], @@ -457,7 +504,7 @@ "highestVisibleLevel": "DEV-LIVE", "liveEvidence": "pass", "evidenceCount": 7, - "blockerCount": 7, + "blockerCount": 4, "summary": "deploy/runtime readiness is blocked before live DEV; highest visible level is DEV-LIVE; status is blocked." }, { @@ -475,7 +522,7 @@ "highestVisibleLevel": "DRY-RUN", "liveEvidence": "missing_or_blocked", "evidenceCount": 3, - "blockerCount": 2, + "blockerCount": 3, "summary": "agent loop has local smoke but live preflight is blocked; highest visible level is DRY-RUN; status is blocked." }, { @@ -484,7 +531,7 @@ "highestVisibleLevel": "DRY-RUN", "liveEvidence": "missing_or_blocked", "evidenceCount": 2, - "blockerCount": 16, + "blockerCount": 11, "summary": "dry-run is green but live MVP gate is blocked; highest visible level is DRY-RUN; status is blocked." } ], @@ -768,7 +815,7 @@ "issue": "pikasTech/HWLAB#37", "taskId": "dev-m4-agent-loop", "reportPath": "reports/dev-gate/dev-m4-agent-loop.json", - "commitId": "0614202", + "commitId": "d881a50", "lifecycleState": "active", "level": "LOCAL", "status": "pass", @@ -787,7 +834,7 @@ "issue": "pikasTech/HWLAB#37", "taskId": "dev-m4-agent-loop", "reportPath": "reports/dev-gate/dev-m4-agent-loop.json", - "commitId": "0614202", + "commitId": "d881a50", "lifecycleState": "active", "level": "DRY-RUN", "status": "pass", @@ -811,7 +858,7 @@ "issue": "pikasTech/HWLAB#37", "taskId": "dev-m4-agent-loop", "reportPath": "reports/dev-gate/dev-m4-agent-loop.json", - "commitId": "0614202", + "commitId": "d881a50", "lifecycleState": "active", "level": "BLOCKED", "status": "blocked", @@ -825,17 +872,19 @@ "k3s:deploy/hwlab-agent-skills:ready=1/1:available=True", "k3s:svc/hwlab-agent-mgr:endpoints=1", "k3s:svc/hwlab-agent-skills:endpoints=1", - "agent-mgr:/health/live:degraded", - "agent-skills:/health/live:ok:cb35ada6860653b27269b0a57991184118cbf4b1", - "worker-template:suspend=true:image=127.0.0.1:5000/hwlab/hwlab-agent-worker:cb35ada", + "agent-mgr:/health/live:degraded:missing=manager.skillCommitId,manager.skillVersion", + "agent-skills:/health/live:ok:d881a50", + "worker-template:suspend=true:image=127.0.0.1:5000/hwlab/hwlab-agent-worker:d881a50", "worker-job:server-dry-run=true:persisted=false", - "skills-injection:commit=cb35ada:version=missing", + "skills-injection:commit=d881a50:version=missing", "secret-rbac:get=yes:secretResourcesRead=false", - "Public DEV API http://74.48.78.17:16667 health=200 live=200; frontend http://74.48.78.17:16666/=200.", + "Public DEV API http://74.48.78.17:16667 health=200 live=200 with DB live ready and runtime durable adapter blocked; frontend http://74.48.78.17:16666/=200.", "health:hwlab-edge-proxy:dev:ok", - "live:hwlab-cloud-api:degraded:db-blocked" + "live:hwlab-cloud-api:degraded:runtime-durable-adapter-blocked:runtime_durable_adapter_query_blocked", + "db-live:ready=true:connected=true:liveDbEvidence=true", + "runtime-durable-adapter:adapter=postgres:blocker=runtime_durable_adapter_query_blocked:migration=0001_cloud_core_skeleton:migrationChecked=false:migrationReady=false:queryResult=query_blocked" ], - "summary": "Blocked at DB live readiness before scheduling a DEV agent task." + "summary": "Blocked at runtime durable adapter readiness before scheduling a DEV agent task." }, { "milestone": "M5", @@ -878,13 +927,13 @@ ], "evidence": [ "http://74.48.78.17:16666/ -> HTTP 200 text/html, title=HWLAB DEV MVP Gate", - "http://74.48.78.17:16667/health -> HTTP 200 serviceId=hwlab-edge-proxy status=ok commit=cb35ada6860653b27269b0a57991184118cbf4b1", - "http://74.48.78.17:16667/health/live -> HTTP 200 serviceId=hwlab-cloud-api status=degraded db.ready=false db.connected=false db.connectionChecked=false", - "dev-edge-health active report -> blocked by DB live readiness, while public 16667 TCP/HTTP and frp control/tunnel health are reachable", + "http://74.48.78.17:16667/health -> HTTP 200 serviceId=hwlab-edge-proxy status=ok commit=d881a50", + "http://74.48.78.17:16667/health/live -> HTTP 200 serviceId=hwlab-cloud-api status=degraded db.ready=true db.connected=true db.liveDbEvidence=true runtime.blocker=runtime_durable_adapter_query_blocked runtime.migration=0001_cloud_core_skeleton", + "dev-edge-health active report remains older DB-live evidence and is superseded for M4/M5 blocker naming by current d881a50 M4 preflight evidence", "dev-m3-hardware-loop active report -> blocked, classification=hardware loop, summary=patch-panel active DO1 -> DI1 connection missing", - "dev-m4-agent-loop active report -> blocked, classification=DB live, summary=cloud-api /health/live reports DB degraded; connected=false; ready=false" + "dev-m4-agent-loop active report -> blocked, classification=runtime_durable_adapter_query_blocked, summary=runtime durable adapter query/migration readiness is blocked; skills commit/version injection is incomplete" ], - "summary": "Public DEV route/frp is reachable on 16666/16667, but live MVP remains blocked by DB live readiness and the M3 hardware loop topology; no historical 6667 failure is counted as active evidence." + "summary": "Public DEV route/frp is reachable on 16666/16667, but live MVP remains blocked by runtime_durable_adapter_query_blocked, skills commit/version injection, and the M3 hardware loop topology; no DB-live-only blocker is counted as current M4/M5 evidence." } ], "levels": { @@ -1050,7 +1099,7 @@ "status": "blocked", "category": "agent-loop-live-preflight", "reportPath": "reports/dev-gate/dev-m4-agent-loop.json", - "summary": "Blocked at DB live readiness before scheduling a DEV agent task." + "summary": "Blocked at runtime durable adapter readiness before scheduling a DEV agent task." }, { "milestone": "M5", @@ -1060,7 +1109,7 @@ "status": "blocked", "category": "mvp-e2e-live", "reportPath": "reports/dev-gate/dev-mvp-gate-report.json", - "summary": "Public DEV route/frp is reachable on 16666/16667, but live MVP remains blocked by DB live readiness and the M3 hardware loop topology; no historical 6667 failure is counted as active evidence." + "summary": "Public DEV route/frp is reachable on 16666/16667, but live MVP remains blocked by runtime_durable_adapter_query_blocked, skills commit/version injection, and the M3 hardware loop topology; no DB-live-only blocker is counted as current M4/M5 evidence." }, { "priority": "P1", @@ -1105,56 +1154,36 @@ { "priority": "P1", "order": 5, - "type": "runtime_blocker", - "scope": "cloud-api-db", - "sourceIssue": "pikasTech/HWLAB#36", - "source": "reports/dev-gate/dev-edge-health.json", - "summary": "DB endpoint DNS resolution failed from the cloud-api runtime" - }, - { - "priority": "P1", - "order": 5, - "type": "network_blocker", - "scope": "cloud-api-db", - "sourceIssue": "pikasTech/HWLAB#34", - "source": "reports/dev-gate/dev-preflight-report.json", - "summary": "DB endpoint DNS resolution failed from the cloud-api runtime", - "nextTask": "Deploy cloud-api DB runtime readiness probe and/or repair DEV DB connectivity, then rerun the read-only health smoke without reading or printing the DB secret value." - }, - { - "priority": "P1", - "order": 5, - "type": "runtime_blocker", - "scope": "cloud-api-db-health-gate", - "sourceIssue": "pikasTech/HWLAB#34", - "source": "reports/dev-gate/dev-preflight-report.json", - "summary": "cloud-api DB runtime env is not ready; missing HWLAB_CLOUD_DB_URL, HWLAB_CLOUD_DB_SSL_MODE.", - "nextTask": "Configure DEV hwlab-cloud-api with Secret hwlab-cloud-api-dev-db/database-url and HWLAB_CLOUD_DB_SSL_MODE=require, then rerun health/preflight without printing the secret value." - }, - { - "priority": "P1", - "order": 5, - "type": "runtime_blocker", - "scope": "db-live", + "type": "agent_blocker", + "scope": "agent-mgr-health", "sourceIssue": "pikasTech/HWLAB#37", "source": "reports/dev-gate/dev-m4-agent-loop.json", - "summary": "cloud-api /health/live reports DB degraded; connected=false; ready=false." + "summary": "hwlab-agent-mgr /health/live is degraded." }, { "priority": "P1", "order": 5, "type": "runtime_blocker", - "scope": "db-live", - "sourceIssue": "pikasTech/HWLAB#39", - "source": "reports/dev-gate/dev-mvp-gate-report.json", - "summary": "cloud-api /health/live reports DB degraded; db.ready=false, db.connected=false, and live DB evidence is not established." + "scope": "runtime-durable-adapter", + "sourceIssue": "pikasTech/HWLAB#164", + "source": "reports/dev-gate/dev-m4-agent-loop.json", + "summary": "cloud-api durable runtime adapter is blocked: runtime_durable_adapter_query_blocked; adapter=postgres; durable=false; ready=false; migration=0001_cloud_core_skeleton checked=false ready=false missing=true; queryResult=query_blocked." + }, + { + "priority": "P1", + "order": 5, + "type": "agent_blocker", + "scope": "skills-commit-version-injection", + "sourceIssue": "pikasTech/HWLAB#37", + "source": "reports/dev-gate/dev-m4-agent-loop.json", + "summary": "DEV skills injection is incomplete: missing hwlab-agent-skills.HWLAB_SKILLS_VERSION, worker-dry-run.HWLAB_SKILL_VERSION_FROM_DEV." }, { "priority": "P0", "order": 6, "type": "runtime_blocker", "scope": "m3-box-simu-identity", - "sourceIssue": "pikasTech/HWLAB#38", + "sourceIssue": "pikasTech/HWLAB#64", "source": "reports/dev-gate/dev-m3-hardware-loop.json", "summary": "DEV exposes two box-simu endpoints, but live identities are not the required distinct resources res_boxsimu_1 and res_boxsimu_2; observed resources=res_boxsimu_1." }, @@ -1163,7 +1192,7 @@ "order": 6, "type": "runtime_blocker", "scope": "m3-gateway-simu-identity", - "sourceIssue": "pikasTech/HWLAB#38", + "sourceIssue": "pikasTech/HWLAB#64", "source": "reports/dev-gate/dev-m3-hardware-loop.json", "summary": "DEV exposes two gateway-simu endpoints, but live gateway identities are not distinct; observed identities=gateway-a:gws_gateway-a." }, @@ -1172,7 +1201,7 @@ "order": 6, "type": "runtime_blocker", "scope": "m3-patch-panel-wiring", - "sourceIssue": "pikasTech/HWLAB#38", + "sourceIssue": "pikasTech/HWLAB#64", "source": "reports/dev-gate/dev-m3-hardware-loop.json", "summary": "DEV patch-panel is callable, but live wiring does not contain res_boxsimu_1:DO1 -> res_boxsimu_2:DI1; active=res_boxsim_alpha:uart0->res_boxsim_beta:uart0, res_boxsim_alpha:gpio0->res_boxsim_beta:gpio0; configured=res_boxsim_alpha:uart0->res_boxsim_beta:uart0, res_boxsim_alpha:gpio0->res_boxsim_beta:gpio0." }, @@ -1185,15 +1214,6 @@ "source": "reports/dev-gate/dev-mvp-gate-report.json", "summary": "Live M3 smoke reached DEV simulators, but patch-panel active DO1 -> DI1 connection is missing." }, - { - "priority": "P3", - "order": 99, - "type": "agent_blocker", - "scope": "code-agent-provider-secret", - "sourceIssue": "pikasTech/HWLAB#36", - "source": "reports/dev-gate/dev-edge-health.json", - "summary": "Code Agent provider Secret hwlab-code-agent-provider/openai-api-key is not present as key-presence evidence; #143 real provider-backed chat remains blocked" - }, { "priority": "P3", "order": 99, @@ -1202,24 +1222,6 @@ "sourceIssue": "pikasTech/HWLAB#38", "source": "reports/dev-gate/dev-m3-hardware-loop.json", "summary": "Plan-only mode is intentionally non-mutating; run the bounded live smoke only after explicit DEV/non-PROD approval and after read-only preconditions identify the exact HWLAB targets." - }, - { - "priority": "P3", - "order": 99, - "type": "agent_blocker", - "scope": "agent-mgr-health", - "sourceIssue": "pikasTech/HWLAB#37", - "source": "reports/dev-gate/dev-m4-agent-loop.json", - "summary": "hwlab-agent-mgr /health/live is degraded." - }, - { - "priority": "P3", - "order": 99, - "type": "agent_blocker", - "scope": "skills-commit-version-injection", - "sourceIssue": "pikasTech/HWLAB#37", - "source": "reports/dev-gate/dev-m4-agent-loop.json", - "summary": "DEV skills injection is incomplete: missing hwlab-agent-skills.HWLAB_SKILLS_VERSION, worker-dry-run.HWLAB_SKILL_VERSION_FROM_DEV." } ] }, @@ -1300,89 +1302,55 @@ "rationale": "Runner read-only observability must be repaired without treating the runner gap as proof that D601 k3s or public DEV endpoints are unavailable." }, { - "id": "dev-edge-health:cloud-api-db", + "id": "dev-m4-agent-loop:agent-mgr-health", "priority": "P1", - "type": "runtime_blocker", - "scope": "cloud-api-db", - "status": "open", - "source": "reports/dev-gate/dev-edge-health.json", - "sourceIssue": "pikasTech/HWLAB#36", - "summary": "DB endpoint DNS resolution failed from the cloud-api runtime", - "unblockOrder": 5, - "unblocks": [ - "pikasTech/HWLAB#34", - "pikasTech/HWLAB#33", - "pikasTech/HWLAB#39" - ], - "rationale": "Cloud API DB env and health readiness block runtime health and MVP evidence even if ingress starts responding." - }, - { - "id": "dev-gate-preflight:cloud-api-db", - "priority": "P1", - "type": "network_blocker", - "scope": "cloud-api-db", - "status": "open", - "source": "reports/dev-gate/dev-preflight-report.json", - "sourceIssue": "pikasTech/HWLAB#34", - "summary": "DB endpoint DNS resolution failed from the cloud-api runtime", - "nextTask": "Deploy cloud-api DB runtime readiness probe and/or repair DEV DB connectivity, then rerun the read-only health smoke without reading or printing the DB secret value.", - "unblockOrder": 5, - "unblocks": [ - "pikasTech/HWLAB#34", - "pikasTech/HWLAB#33", - "pikasTech/HWLAB#39" - ], - "rationale": "Cloud API DB env and health readiness block runtime health and MVP evidence even if ingress starts responding." - }, - { - "id": "dev-gate-preflight:cloud-api-db-health-gate", - "priority": "P1", - "type": "runtime_blocker", - "scope": "cloud-api-db-health-gate", - "status": "open", - "source": "reports/dev-gate/dev-preflight-report.json", - "sourceIssue": "pikasTech/HWLAB#34", - "summary": "cloud-api DB runtime env is not ready; missing HWLAB_CLOUD_DB_URL, HWLAB_CLOUD_DB_SSL_MODE.", - "nextTask": "Configure DEV hwlab-cloud-api with Secret hwlab-cloud-api-dev-db/database-url and HWLAB_CLOUD_DB_SSL_MODE=require, then rerun health/preflight without printing the secret value.", - "unblockOrder": 5, - "unblocks": [ - "pikasTech/HWLAB#34", - "pikasTech/HWLAB#33", - "pikasTech/HWLAB#39" - ], - "rationale": "Cloud API DB env and health readiness block runtime health and MVP evidence even if ingress starts responding." - }, - { - "id": "dev-m4-agent-loop:db-live", - "priority": "P1", - "type": "runtime_blocker", - "scope": "db-live", + "type": "agent_blocker", + "scope": "agent-mgr-health", "status": "open", "source": "reports/dev-gate/dev-m4-agent-loop.json", "sourceIssue": "pikasTech/HWLAB#37", - "summary": "cloud-api /health/live reports DB degraded; connected=false; ready=false.", + "summary": "hwlab-agent-mgr /health/live is degraded.", "unblockOrder": 5, "unblocks": [ "pikasTech/HWLAB#37", - "pikasTech/HWLAB#39" + "pikasTech/HWLAB#39", + "pikasTech/HWLAB#164" ], - "rationale": "M4 and M5 cannot claim live agent or MVP evidence until cloud-api /health/live proves DB readiness with redacted live evidence." + "rationale": "M4/M5 agent evidence needs deployed manager, skills, and worker paths to expose explicit skills commit and version; session-mode-only worker templates are not enough." }, { - "id": "dev-mvp-gate-report:db-live", + "id": "dev-m4-agent-loop:runtime-durable-adapter", "priority": "P1", "type": "runtime_blocker", - "scope": "db-live", + "scope": "runtime-durable-adapter", "status": "open", - "source": "reports/dev-gate/dev-mvp-gate-report.json", - "sourceIssue": "pikasTech/HWLAB#39", - "summary": "cloud-api /health/live reports DB degraded; db.ready=false, db.connected=false, and live DB evidence is not established.", + "source": "reports/dev-gate/dev-m4-agent-loop.json", + "sourceIssue": "pikasTech/HWLAB#164", + "summary": "cloud-api durable runtime adapter is blocked: runtime_durable_adapter_query_blocked; adapter=postgres; durable=false; ready=false; migration=0001_cloud_core_skeleton checked=false ready=false missing=true; queryResult=query_blocked.", "unblockOrder": 5, "unblocks": [ "pikasTech/HWLAB#37", - "pikasTech/HWLAB#39" + "pikasTech/HWLAB#39", + "pikasTech/HWLAB#164" ], - "rationale": "M4 and M5 cannot claim live agent or MVP evidence until cloud-api /health/live proves DB readiness with redacted live evidence." + "rationale": "M4 and M5 cannot claim live agent or MVP evidence until the cloud-api postgres runtime adapter proves schema, migration, and read-query durability; DB live evidence alone is not durability evidence." + }, + { + "id": "dev-m4-agent-loop:skills-commit-version-injection", + "priority": "P1", + "type": "agent_blocker", + "scope": "skills-commit-version-injection", + "status": "open", + "source": "reports/dev-gate/dev-m4-agent-loop.json", + "sourceIssue": "pikasTech/HWLAB#37", + "summary": "DEV skills injection is incomplete: missing hwlab-agent-skills.HWLAB_SKILLS_VERSION, worker-dry-run.HWLAB_SKILL_VERSION_FROM_DEV.", + "unblockOrder": 5, + "unblocks": [ + "pikasTech/HWLAB#37", + "pikasTech/HWLAB#39", + "pikasTech/HWLAB#164" + ], + "rationale": "M4/M5 agent evidence needs deployed manager, skills, and worker paths to expose explicit skills commit and version; session-mode-only worker templates are not enough." }, { "id": "dev-m3-hardware-loop:m3-box-simu-identity", @@ -1391,7 +1359,7 @@ "scope": "m3-box-simu-identity", "status": "open", "source": "reports/dev-gate/dev-m3-hardware-loop.json", - "sourceIssue": "pikasTech/HWLAB#38", + "sourceIssue": "pikasTech/HWLAB#64", "summary": "DEV exposes two box-simu endpoints, but live identities are not the required distinct resources res_boxsimu_1 and res_boxsimu_2; observed resources=res_boxsimu_1.", "unblockOrder": 6, "unblocks": [ @@ -1408,7 +1376,7 @@ "scope": "m3-gateway-simu-identity", "status": "open", "source": "reports/dev-gate/dev-m3-hardware-loop.json", - "sourceIssue": "pikasTech/HWLAB#38", + "sourceIssue": "pikasTech/HWLAB#64", "summary": "DEV exposes two gateway-simu endpoints, but live gateway identities are not distinct; observed identities=gateway-a:gws_gateway-a.", "unblockOrder": 6, "unblocks": [ @@ -1425,7 +1393,7 @@ "scope": "m3-patch-panel-wiring", "status": "open", "source": "reports/dev-gate/dev-m3-hardware-loop.json", - "sourceIssue": "pikasTech/HWLAB#38", + "sourceIssue": "pikasTech/HWLAB#64", "summary": "DEV patch-panel is callable, but live wiring does not contain res_boxsimu_1:DO1 -> res_boxsimu_2:DI1; active=res_boxsim_alpha:uart0->res_boxsim_beta:uart0, res_boxsim_alpha:gpio0->res_boxsim_beta:gpio0; configured=res_boxsim_alpha:uart0->res_boxsim_beta:uart0, res_boxsim_alpha:gpio0->res_boxsim_beta:gpio0.", "unblockOrder": 6, "unblocks": [ @@ -1452,21 +1420,6 @@ ], "rationale": "M3 remains blocked until the real DEV hardware trusted loop proves DO1 -> patch-panel -> DI1 with operation, trace, audit, and evidence identifiers." }, - { - "id": "dev-edge-health:code-agent-provider-secret", - "priority": "P3", - "type": "agent_blocker", - "scope": "code-agent-provider-secret", - "status": "open", - "source": "reports/dev-gate/dev-edge-health.json", - "sourceIssue": "pikasTech/HWLAB#36", - "summary": "Code Agent provider Secret hwlab-code-agent-provider/openai-api-key is not present as key-presence evidence; #143 real provider-backed chat remains blocked", - "unblockOrder": 99, - "unblocks": [ - "pikasTech/HWLAB#39" - ], - "rationale": "Residual blocker that must be classified before claiming a green DEV gate." - }, { "id": "dev-m3-hardware-loop:m3-live-write-authorization", "priority": "P3", @@ -1481,36 +1434,6 @@ "pikasTech/HWLAB#39" ], "rationale": "Residual blocker that must be classified before claiming a green DEV gate." - }, - { - "id": "dev-m4-agent-loop:agent-mgr-health", - "priority": "P3", - "type": "agent_blocker", - "scope": "agent-mgr-health", - "status": "open", - "source": "reports/dev-gate/dev-m4-agent-loop.json", - "sourceIssue": "pikasTech/HWLAB#37", - "summary": "hwlab-agent-mgr /health/live is degraded.", - "unblockOrder": 99, - "unblocks": [ - "pikasTech/HWLAB#39" - ], - "rationale": "Residual blocker that must be classified before claiming a green DEV gate." - }, - { - "id": "dev-m4-agent-loop:skills-commit-version-injection", - "priority": "P3", - "type": "agent_blocker", - "scope": "skills-commit-version-injection", - "status": "open", - "source": "reports/dev-gate/dev-m4-agent-loop.json", - "sourceIssue": "pikasTech/HWLAB#37", - "summary": "DEV skills injection is incomplete: missing hwlab-agent-skills.HWLAB_SKILLS_VERSION, worker-dry-run.HWLAB_SKILL_VERSION_FROM_DEV.", - "unblockOrder": 99, - "unblocks": [ - "pikasTech/HWLAB#39" - ], - "rationale": "Residual blocker that must be classified before claiming a green DEV gate." } ], "nextSteps": [ @@ -1557,20 +1480,18 @@ "blockerOrder": 5, "priority": "P1", "scopes": [ - "cloud-api-db", - "cloud-api-db-health-gate", - "db-live" + "agent-mgr-health", + "runtime-durable-adapter", + "skills-commit-version-injection" ], "sourceIssues": [ - "pikasTech/HWLAB#33", - "pikasTech/HWLAB#34", - "pikasTech/HWLAB#36", + "pikasTech/HWLAB#164", "pikasTech/HWLAB#37", "pikasTech/HWLAB#39" ], - "rationale": "Cloud API DB env and health readiness block runtime health and MVP evidence even if ingress starts responding.", - "action": "Deploy cloud-api DB runtime readiness probe and/or repair DEV DB connectivity, then rerun the read-only health smoke without reading or printing the DB secret value.", - "evidenceRequired": "Cloud API health/live output showing DB env ready, endpointSource=secret-url-host, redacted secret references, and no secret material." + "rationale": "M4/M5 agent evidence needs deployed manager, skills, and worker paths to expose explicit skills commit and version; session-mode-only worker templates are not enough.", + "action": "Inject explicit skills commit and version into agent manager, skills service, and worker jobs, then rerun the M4 read-only/live preflight.", + "evidenceRequired": "Read-only hwlab-agent-mgr/hwlab-agent-skills health plus worker dry-run manifest showing skillCommitId/skillVersion and HWLAB_SKILL_COMMIT_ID/HWLAB_SKILL_VERSION injected." }, { "order": 4, @@ -1596,14 +1517,9 @@ "blockerOrder": 99, "priority": "P3", "scopes": [ - "code-agent-provider-secret", - "m3-live-write-authorization", - "agent-mgr-health", - "skills-commit-version-injection" + "m3-live-write-authorization" ], "sourceIssues": [ - "pikasTech/HWLAB#36", - "pikasTech/HWLAB#37", "pikasTech/HWLAB#38", "pikasTech/HWLAB#39" ], diff --git a/reports/dev-gate/dev-m5-gate-aggregator-v2.md b/reports/dev-gate/dev-m5-gate-aggregator-v2.md index 437c9874..88e6c96a 100644 --- a/reports/dev-gate/dev-m5-gate-aggregator-v2.md +++ b/reports/dev-gate/dev-m5-gate-aggregator-v2.md @@ -1,7 +1,7 @@ # HWLAB M5 DEV Gate Aggregator v2 Status: blocked -Generated from: `96e802b2a22c` +Generated from: `66586ccf109e` Scope: DEV only, report-only Active frontend: `http://74.48.78.17:16666/` Active API/live: `http://74.48.78.17:16667/health/live` @@ -9,7 +9,7 @@ Deprecated public endpoints: `http://74.48.78.17:6666`, `http://74.48.78.17:6667 ## Summary -Frontend revision 1e8805664970839b72be40c34636b08f6d18b131 is usable in degraded/read-only mode and EDGE/ROUTE DEV-LIVE evidence exists on :16666/:16667, but M5 remains blocked by artifact source drift, API/runtime durability degradation, missing M3 trusted loop operation evidence, and blocked M4 agent-loop preflight. +Frontend revision 1e8805664970839b72be40c34636b08f6d18b131 is usable in degraded/read-only mode and EDGE/ROUTE DEV-LIVE evidence exists on :16666/:16667, but M5 remains blocked by artifact source drift, runtime durable adapter or skills injection readiness, missing M3 trusted loop operation evidence, and blocked M4 agent-loop preflight. ## Frontend DEV Fact @@ -21,10 +21,13 @@ The latest #99/#108 frontend DEV visibility fact is revision `1e8805664970839b72 | --- | --- | --- | --- | --- | | Frontend DEV revision | pass | DEV-LIVE | http://74.48.78.17:16666/ serves the Cloud Workbench frontend revision 1e8805664970839b72be40c34636b08f6d18b131; deployed UI is usable in degraded/read-only mode only and this is browser/frontend evidence only. | Keep frontend revision proof separate from DB live readiness, M3 hardware-loop evidence, M4 agent-loop evidence, and M5 acceptance. | | EDGE/ROUTE live | pass | DEV-LIVE | http://74.48.78.17:16666/, http://74.48.78.17:16667/health, and http://74.48.78.17:16667/health/live returned observed HWLAB DEV route responses in the active M2 read-only smoke. | Keep this separated from DB readiness, M3/M4 loop evidence, and M5 acceptance. | -| DB live/degraded | blocked | BLOCKED | cloud-api DB status=degraded; configReady=true; ready=false; connected=false; endpointSource=unknown; liveDbEvidence=false. | Provide live DB connection evidence through redacted health output; route reachability alone is insufficient. | +| DB live/degraded | pass | DEV-LIVE | cloud-api DB status=ok; configReady=true; ready=true; connected=true; endpointSource=secret-url-host; liveDbEvidence=true. | Provide live DB connection evidence through redacted health output; route reachability alone is insufficient. | +| Runtime durable adapter | blocked | BLOCKED | runtime adapter=postgres; durableRequested=true; durable=false; ready=false; blocker=runtime_durable_adapter_query_blocked; migration=0001_cloud_core_skeleton checked=false ready=false; queryResult=query_blocked. | Repair the postgres durable runtime adapter query/migration readiness and rerun health/M4 preflight without treating DB live as durability proof. | +| Agent skills injection | blocked | BLOCKED | DEV skills injection is incomplete: missing hwlab-agent-skills.HWLAB_SKILLS_VERSION, worker-dry-run.HWLAB_SKILL_VERSION_FROM_DEV. | Inject explicit skill commit and version into hwlab-agent-mgr, hwlab-agent-skills, and scheduled worker jobs before claiming M4/M5 agent evidence. | +| Code Agent provider | pass | DEV-LIVE | code-agent provider=openai-responses; status=available; ready=true. | Keep provider-backed Code Agent readiness separate from DB live and runtime durable adapter readiness. | | D601 runner observability | blocked | DEV-LIVE | D601 public DEV endpoints are reachable, but the runner cannot read /etc/rancher/k3s/k3s.yaml; classify as #46 runner permission/mount or read-only observability gap, not D601 global offline. Alternate read-only cluster probes are readable. runnerKubeconfigReadable=false, runnerKubeconfigProbeExitCode=0, runnerKubeconfigProbeStderr=empty, d601PublicEndpointsReachable=true, d601K3sUnavailable=false. | Treat #46 runner kubeconfig/readonly gaps separately from D601 service health; rerun read-only observability after the mount or permission path is repaired. | | M3 hardware trusted loop | blocked | BLOCKED | No live DEV operation was attempted because read-only direct target checks did not prove the required patch-panel-owned M3 route. Blocker: DEV exposes two box-simu endpoints, but live identities are not the required distinct resources res_boxsimu_1 and res_boxsimu_2; observed resources=res_boxsimu_1. | Only a real DEV res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1 observation with operation/trace/audit/evidence can clear M3. | -| M4 agent loop | blocked | BLOCKED | Blocked at DB live readiness before scheduling a DEV agent task. | Do not schedule or claim the agent loop as live until DB live and required runtime/evidence preconditions pass. | +| M4 agent loop | blocked | BLOCKED | Blocked at runtime durable adapter readiness before scheduling a DEV agent task. | Do not schedule or claim the agent loop as live until DB live, runtime durable adapter, skills injection, and required evidence preconditions pass. | | artifact/desired-state source | blocked | BLOCKED | artifact targetCovered=false; artifactSource=7e29522b65c8; target=f64182b54fe6; desiredApplyMode=dry-run; mutationAttempted=false. | Refresh artifact/source coverage for current origin/main and keep desired-state apply separate from read-only route proof. | ## M0-M5 Level Classification @@ -35,7 +38,7 @@ The latest #99/#108 frontend DEV visibility fact is revision `1e8805664970839b72 | M1 | LOCAL | pass | LOCAL | missing_or_blocked | Local smoke is green; it is not DEV-LIVE. | | M2 | DEV-LIVE | blocked | DEV-LIVE | pass | Current public frontend/API route evidence is DEV-LIVE for route/front-end reachability only. | | M3 | BLOCKED | blocked | LOCAL | missing_or_blocked | DEV-LIVE hardware trusted loop is blocked; local/source shape is not acceptance. | -| M4 | BLOCKED | blocked | DRY-RUN | missing_or_blocked | DEV-LIVE agent loop is blocked at DB live readiness; local smoke is not acceptance. | +| M4 | BLOCKED | blocked | DRY-RUN | missing_or_blocked | DEV-LIVE agent loop is blocked at runtime durable adapter or skills injection readiness; local smoke is not acceptance. | | M5 | BLOCKED | blocked | DRY-RUN | missing_or_blocked | Dry-run is green, but bounded DEV-LIVE MVP e2e is blocked. | ## Milestones @@ -58,7 +61,7 @@ The latest #99/#108 frontend DEV visibility fact is revision `1e8805664970839b72 | artifact-publish-digests | blocked | BLOCKED | artifactState=contract-skeleton, ciPublished=false, registryVerified=false, sha256=0, not_published=13, targetCovered=false | | d601-k3s-observability | blocked | DEV-LIVE | D601 public DEV endpoints are reachable, but the runner cannot read /etc/rancher/k3s/k3s.yaml; classify as #46 runner permission/mount or read-only observability gap, not D601 global offline. Alternate read-only cluster probes are readable. runnerKubeconfigReadable=false, runnerKubeconfigProbeExitCode=0, runnerKubeconfigProbeStderr=empty, d601PublicEndpointsReachable=true, d601K3sUnavailable=false. | | dev-edge-frp-16667 | pass | DEV-LIVE | Committed edge report proves read-only public HTTP on :16667 /health and /health/live; this is route evidence, not DB/M3/M4/M5 acceptance. | -| cloud-api-db-ready | blocked | BLOCKED | cloud-api DB status=degraded; ready=false; connected=false; endpointSource=unknown; liveDbEvidence=false. | +| cloud-api-db-ready | pass | DEV-LIVE | cloud-api DB status=ok; ready=true; connected=true; endpointSource=secret-url-host; liveDbEvidence=true. | | m3-hardware-trusted-loop | blocked | BLOCKED | M3 trusted loop is blocked until res_boxsimu_1:DO1 -> patch-panel -> res_boxsimu_2:DI1 is proven with operation/trace/audit/evidence. | | m4-agent-loop-live | blocked | BLOCKED | M4 agent loop live path is blocked before accepted agent scheduling/evidence closure. | | m5-mvp-dev-live | blocked | BLOCKED | M5 dry-run passed; bounded DEV-LIVE MVP e2e has not passed. | @@ -68,8 +71,8 @@ The latest #99/#108 frontend DEV visibility fact is revision `1e8805664970839b72 | Milestone | Status | Current level | Blocker class | Dependency | Required next proof | | --- | --- | --- | --- | --- | --- | | M3 | blocked | BLOCKED | direct-target-identity-gap | Two distinct live box-simu resources and two distinct gateway-simu identities are required before M3 can run. | Fix DEV simulator instance identity so two box-simu pods report res_boxsimu_1/res_boxsimu_2 and two gateway-simu pods report distinct gateway identities. | -| M4 | blocked | BLOCKED | db-live-readiness | Cloud API /health/live must report DB ready=true, connected=true, endpointSource=secret-url-host, and liveDbEvidence=true before live agent scheduling/evidence closure. | Repair DB live readiness and rerun the M4 live preflight without scheduling a DEV agent task before preconditions pass. | -| M5 | blocked | BLOCKED | composite-db-m3-m4-live | M5 needs DB live readiness, M3 trusted-loop DEV evidence, M4 live preflight/evidence closure, and current source/artifact coverage. | After DB/M3/M4 blockers are cleared, run only the bounded DEV MVP live gate command with explicit DEV/non-PROD confirmations. | +| M4 | blocked | BLOCKED | runtime_durable_adapter_query_blocked | Cloud API /health/live must prove the postgres durable runtime adapter schema, 0001_cloud_core_skeleton migration ledger, and read-query readiness before live agent scheduling/evidence closure. | Repair the runtime durable adapter query/migration readiness and rerun the M4 live preflight without scheduling a DEV agent task before preconditions pass. | +| M5 | blocked | BLOCKED | composite-runtime-durable-m3-m4-live | M5 needs DB live readiness, runtime durable adapter readiness, skills commit/version injection, M3 trusted-loop DEV evidence, M4 live preflight/evidence closure, and current source/artifact coverage. | After DB/runtime durable adapter/skills/M3/M4 blockers are cleared, run only the bounded DEV MVP live gate command with explicit DEV/non-PROD confirmations. | ## Blockers @@ -79,19 +82,14 @@ The latest #99/#108 frontend DEV visibility fact is revision `1e8805664970839b72 | P1 | 3 | contract_blocker | artifact-source-commit | source commit origin/main f64182b is not covered by artifact source 7e29522; target changes since the artifact source touch artifact build inputs. | | P1 | 3 | runtime_blocker | dev-artifact-publish | reports/dev-gate/dev-artifacts.json does not prove all required HWLAB service artifacts for origin/main f64182b; current status is published with 13/13 required services published and source states resolved: 13 source-present, 0 intentionally-disabled. | | P1 | 4 | observability_blocker | runner-kubeconfig-readonly-gap | The runner cannot read /etc/rancher/k3s/k3s.yaml; classify this as a runner permission/mount gap, not a D601 global outage. Alternate read-only cluster probes and public DEV endpoint probes are reported separately. | -| P1 | 5 | runtime_blocker | cloud-api-db | DB endpoint DNS resolution failed from the cloud-api runtime | -| P1 | 5 | network_blocker | cloud-api-db | DB endpoint DNS resolution failed from the cloud-api runtime | -| P1 | 5 | runtime_blocker | cloud-api-db-health-gate | cloud-api DB runtime env is not ready; missing HWLAB_CLOUD_DB_URL, HWLAB_CLOUD_DB_SSL_MODE. | -| P1 | 5 | runtime_blocker | db-live | cloud-api /health/live reports DB degraded; connected=false; ready=false. | -| P1 | 5 | runtime_blocker | db-live | cloud-api /health/live reports DB degraded; db.ready=false, db.connected=false, and live DB evidence is not established. | +| P1 | 5 | agent_blocker | agent-mgr-health | hwlab-agent-mgr /health/live is degraded. | +| P1 | 5 | runtime_blocker | runtime-durable-adapter | cloud-api durable runtime adapter is blocked: runtime_durable_adapter_query_blocked; adapter=postgres; durable=false; ready=false; migration=0001_cloud_core_skeleton checked=false ready=false missing=true; queryResult=query_blocked. | +| P1 | 5 | agent_blocker | skills-commit-version-injection | DEV skills injection is incomplete: missing hwlab-agent-skills.HWLAB_SKILLS_VERSION, worker-dry-run.HWLAB_SKILL_VERSION_FROM_DEV. | | P0 | 6 | runtime_blocker | m3-box-simu-identity | DEV exposes two box-simu endpoints, but live identities are not the required distinct resources res_boxsimu_1 and res_boxsimu_2; observed resources=res_boxsimu_1. | | P0 | 6 | runtime_blocker | m3-gateway-simu-identity | DEV exposes two gateway-simu endpoints, but live gateway identities are not distinct; observed identities=gateway-a:gws_gateway-a. | | P0 | 6 | runtime_blocker | m3-patch-panel-wiring | DEV patch-panel is callable, but live wiring does not contain res_boxsimu_1:DO1 -> res_boxsimu_2:DI1; active=res_boxsim_alpha:uart0->res_boxsim_beta:uart0, res_boxsim_alpha:gpio0->res_boxsim_beta:gpio0; configured=res_boxsim_alpha:uart0->res_boxsim_beta:uart0, res_boxsim_alpha:gpio0->res_boxsim_beta:gpio0. | | P0 | 6 | runtime_blocker | m3-hardware-loop-runtime | Live M3 smoke reached DEV simulators, but patch-panel active DO1 -> DI1 connection is missing. | -| P3 | 99 | agent_blocker | code-agent-provider-secret | Code Agent provider Secret hwlab-code-agent-provider/openai-api-key is not present as key-presence evidence; #143 real provider-backed chat remains blocked | | P3 | 99 | safety_blocker | m3-live-write-authorization | Plan-only mode is intentionally non-mutating; run the bounded live smoke only after explicit DEV/non-PROD approval and after read-only preconditions identify the exact HWLAB targets. | -| P3 | 99 | agent_blocker | agent-mgr-health | hwlab-agent-mgr /health/live is degraded. | -| P3 | 99 | agent_blocker | skills-commit-version-injection | DEV skills injection is incomplete: missing hwlab-agent-skills.HWLAB_SKILLS_VERSION, worker-dry-run.HWLAB_SKILL_VERSION_FROM_DEV. | ## Next Unblock Order @@ -99,8 +97,8 @@ The latest #99/#108 frontend DEV visibility fact is revision `1e8805664970839b72 Evidence required: Artifact publish report with ciPublished=true, registryVerified=true, and sha256 digest for each frozen service ID. 2. Provide the approved read-only runner kubeconfig mount or document the intended alternate KUBECONFIG path, then rerun the read-only report. Evidence required: Read-only report with runnerKubeconfigReadable, runnerKubeconfigProbeExitCode/stderr, d601PublicEndpointsReachable, and d601K3sUnavailable recorded separately, plus direct M3 service target discovery before any DO write. -3. Deploy cloud-api DB runtime readiness probe and/or repair DEV DB connectivity, then rerun the read-only health smoke without reading or printing the DB secret value. - Evidence required: Cloud API health/live output showing DB env ready, endpointSource=secret-url-host, redacted secret references, and no secret material. +3. Inject explicit skills commit and version into agent manager, skills service, and worker jobs, then rerun the M4 read-only/live preflight. + Evidence required: Read-only hwlab-agent-mgr/hwlab-agent-skills health plus worker dry-run manifest showing skillCommitId/skillVersion and HWLAB_SKILL_COMMIT_ID/HWLAB_SKILL_VERSION injected. 4. Fix DEV box-simu instance identity so direct endpoints expose distinct res_boxsimu_1 and res_boxsimu_2 resources. Evidence required: Read-only direct box-simu /health/live and /status output showing distinct res_boxsimu_1 and res_boxsimu_2 resources. 5. Resolve the blocker and attach source/local/dry-run/DEV-live evidence at the correct level. diff --git a/reports/dev-gate/dev-mvp-gate-report.json b/reports/dev-gate/dev-mvp-gate-report.json index 44775d3b..c634dbbb 100644 --- a/reports/dev-gate/dev-mvp-gate-report.json +++ b/reports/dev-gate/dev-mvp-gate-report.json @@ -81,13 +81,14 @@ }, "devPreconditions": { "status": "blocked", - "classification": "DB live; hardware loop", + "classification": "runtime durable adapter; skills injection; hardware loop", "requirements": [ "http://74.48.78.17:16666/ returns the DEV browser entry", "http://74.48.78.17:16667/health returns HWLAB DEV edge JSON", - "http://74.48.78.17:16667/health/live returns HWLAB cloud-api JSON with DB live readiness", + "http://74.48.78.17:16667/health/live returns HWLAB cloud-api JSON with DB live readiness and runtime durability separated", + "Runtime durable adapter proves postgres schema, migration 0001_cloud_core_skeleton, and read-query readiness before M4/M5 can promote", + "Agent manager, skills service, and worker jobs expose explicit skill commit/version before M4/M5 can promote", "M3 hardware loop proves box-simu-1 DO1 -> box-simu-2 DI1 via patch-panel", - "M4 agent runtime/evidence path is only attempted after DB live and M3 readiness are not blocked", "Keep the run DEV-only and avoid PROD, heavy e2e, secrets, restarts, or UniDesk substitution" ], "commands": [ @@ -99,21 +100,30 @@ ], "evidence": [ "http://74.48.78.17:16666/ -> HTTP 200 text/html, title=HWLAB DEV MVP Gate", - "http://74.48.78.17:16667/health -> HTTP 200 serviceId=hwlab-edge-proxy status=ok commit=cb35ada6860653b27269b0a57991184118cbf4b1", - "http://74.48.78.17:16667/health/live -> HTTP 200 serviceId=hwlab-cloud-api status=degraded db.ready=false db.connected=false db.connectionChecked=false", - "dev-edge-health active report -> blocked by DB live readiness, while public 16667 TCP/HTTP and frp control/tunnel health are reachable", + "http://74.48.78.17:16667/health -> HTTP 200 serviceId=hwlab-edge-proxy status=ok commit=d881a50", + "http://74.48.78.17:16667/health/live -> HTTP 200 serviceId=hwlab-cloud-api status=degraded db.ready=true db.connected=true db.liveDbEvidence=true runtime.blocker=runtime_durable_adapter_query_blocked runtime.migration=0001_cloud_core_skeleton", + "dev-edge-health active report remains older DB-live evidence and is superseded for M4/M5 blocker naming by current d881a50 M4 preflight evidence", "dev-m3-hardware-loop active report -> blocked, classification=hardware loop, summary=patch-panel active DO1 -> DI1 connection missing", - "dev-m4-agent-loop active report -> blocked, classification=DB live, summary=cloud-api /health/live reports DB degraded; connected=false; ready=false" + "dev-m4-agent-loop active report -> blocked, classification=runtime_durable_adapter_query_blocked, summary=runtime durable adapter query/migration readiness is blocked; skills commit/version injection is incomplete" ], - "summary": "Public DEV route/frp is reachable on 16666/16667, but live MVP remains blocked by DB live readiness and the M3 hardware loop topology; no historical 6667 failure is counted as active evidence." + "summary": "Public DEV route/frp is reachable on 16666/16667, but live MVP remains blocked by runtime_durable_adapter_query_blocked, skills commit/version injection, and the M3 hardware loop topology; no DB-live-only blocker is counted as current M4/M5 evidence." }, "blockers": [ { "type": "runtime_blocker", - "scope": "db-live", + "scope": "runtime-durable-adapter", "status": "open", - "classification": "DB live", - "summary": "cloud-api /health/live reports DB degraded; db.ready=false, db.connected=false, and live DB evidence is not established." + "sourceIssue": "pikasTech/HWLAB#164", + "classification": "runtime_durable_adapter_query_blocked", + "summary": "cloud-api durable runtime adapter is blocked: runtime_durable_adapter_query_blocked; migration 0001_cloud_core_skeleton is missing or not checked." + }, + { + "type": "agent_blocker", + "scope": "skills-commit-version-injection", + "status": "open", + "sourceIssue": "pikasTech/HWLAB#164", + "classification": "skills injection", + "summary": "DEV skills injection is incomplete: hwlab-agent-skills exposes HWLAB_SKILLS_COMMIT_ID=d881a50 but no HWLAB_SKILLS_VERSION; worker template only exposes session mode unless commit/version are injected." }, { "type": "runtime_blocker", @@ -196,10 +206,10 @@ ], "evidence": [ "[m4-smoke] ok m4-agent-automation-loop-local-contract-smoke", - "Live DEV M4 preflight blocked before agent scheduling: cloud-api /health/live DB degraded, ready=false, connected=false" + "Live DEV M4 preflight blocked before agent scheduling: runtime_durable_adapter_query_blocked; migration 0001_cloud_core_skeleton missing/not checked; skills version injection missing" ], - "summary": "Local M4 remains green, but active DEV-LIVE M4 is blocked at DB live readiness before agent runtime scheduling.", - "blocker": "DB live" + "summary": "Local M4 remains green, but active DEV-LIVE M4 is blocked at runtime durable adapter readiness and skills commit/version injection before agent runtime scheduling.", + "blocker": "runtime_durable_adapter_query_blocked; skills injection" }, { "id": "M5", @@ -213,23 +223,28 @@ "hwlab-cli health -> status: ok, environment: dev, endpoint: http://74.48.78.17:16667, gateStatus: blocked", "hwlab-cli dry-run -> 9 steps, 13 artifacts, 14 health contracts, 2 hardware operations, 2 evidence records, 0 network calls", "M5 MVP E2E dry-run passed: 9 steps, 13 artifacts, 14 health contracts, 2 hardware operations, 2 evidence records, 0 network calls", - "Live MVP remains blocked by DB live and M3 hardware loop active reports" + "Live MVP remains blocked by runtime durable adapter, skills injection, and M3 hardware loop active reports" ], - "summary": "The M5 orchestration is green in dry-run mode, but active DEV-LIVE acceptance is blocked by DB live and hardware loop evidence.", - "blocker": "DB live; hardware loop" + "summary": "The M5 orchestration is green in dry-run mode, but active DEV-LIVE acceptance is blocked by runtime durable adapter, skills injection, and hardware loop evidence.", + "blocker": "runtime durable adapter; skills injection; hardware loop" } ], - "notes": "Active #64 DEV retest report. Public 16666/16667 route is reachable; live MVP remains blocked. No PROD, secret read, runtime restart, long-running agent task, or UniDesk runtime substitution was used.", + "notes": "Active #64 DEV retest report updated for current d881a50 evidence. Public 16666/16667 route and DB live evidence are reachable; live MVP remains blocked. No PROD, secret read, runtime restart, long-running agent task, or UniDesk runtime substitution was used.", "blockedClassification": [ { - "category": "DB live", + "category": "runtime durable adapter", "status": "blocked", - "evidence": "16667/health/live serviceId=hwlab-cloud-api status=degraded db.ready=false db.connected=false" + "evidence": "runtime_durable_adapter_query_blocked; migration 0001_cloud_core_skeleton missing/not checked; db.liveDbEvidence=true is not durability evidence" + }, + { + "category": "skills injection", + "status": "blocked", + "evidence": "hwlab-agent-skills has HWLAB_SKILLS_COMMIT_ID=d881a50 but no HWLAB_SKILLS_VERSION; agent manager health misses skillCommitId/skillVersion" }, { "category": "agent runtime", "status": "not_run", - "evidence": "M4 stopped before agent scheduling because DB live is blocked" + "evidence": "M4 stopped before agent scheduling because runtime durability and skills injection are blocked" }, { "category": "hardware loop", @@ -252,9 +267,9 @@ "evidence": "16666 browser, 16667/health, 16667/health/live, frps 7000, and tunnel health 7402 are reachable" }, { - "category": "other", - "status": "not_observed", - "evidence": "No additional blocker class observed in the short DEV-only retest" + "category": "DB live", + "status": "pass", + "evidence": "Current d881a50 read-only M4 preflight evidence reports db.ready=true, db.connected=true, and liveDbEvidence=true; this does not clear runtime durability" } ], "reportLifecycle": { diff --git a/reports/dev-gate/dev-mvp-gate-report.md b/reports/dev-gate/dev-mvp-gate-report.md index c4895ec5..fc7348bf 100644 --- a/reports/dev-gate/dev-mvp-gate-report.md +++ b/reports/dev-gate/dev-mvp-gate-report.md @@ -7,7 +7,7 @@ Scope: DEV only ## Summary -Public DEV route/frp is reachable on 16666/16667, but live MVP remains blocked by DB live readiness and the M3 hardware loop topology; no historical 6667 failure is counted as active evidence. +Public DEV route/frp is reachable on 16666/16667, and current DB live evidence is ready, but live MVP remains blocked by runtime_durable_adapter_query_blocked, skills commit/version injection, and the M3 hardware loop topology; no historical 6667 failure is counted as active evidence. ## Commands @@ -36,26 +36,28 @@ Public DEV route/frp is reachable on 16666/16667, but live MVP remains blocked b - `M1`: pass - The local M1 contract smoke passes. - `M2`: pass - The deploy smoke remains fixture-only and validates the frozen route phases and artifact metadata. - `M3`: blocked - Local M3 remains green, but active DEV-LIVE M3 is blocked by hardware loop topology. -- `M4`: blocked - Local M4 remains green, but active DEV-LIVE M4 is blocked at DB live readiness before agent runtime scheduling. -- `M5`: blocked - The M5 orchestration is green in dry-run mode, but active DEV-LIVE acceptance is blocked by DB live and hardware loop evidence. +- `M4`: blocked - Local M4 remains green, but active DEV-LIVE M4 is blocked at runtime durable adapter readiness and skills commit/version injection before agent runtime scheduling. +- `M5`: blocked - The M5 orchestration is green in dry-run mode, but active DEV-LIVE acceptance is blocked by runtime durable adapter, skills injection, and hardware loop evidence. ## Blocked Classification | Category | Status | Evidence | | --- | --- | --- | -| DB live | blocked | 16667/health/live serviceId=hwlab-cloud-api status=degraded db.ready=false db.connected=false | -| agent runtime | not_run | M4 stopped before agent scheduling because DB live is blocked | +| runtime durable adapter | blocked | runtime_durable_adapter_query_blocked; migration 0001_cloud_core_skeleton missing/not checked; db.liveDbEvidence=true is not durability evidence | +| skills injection | blocked | hwlab-agent-skills has HWLAB_SKILLS_COMMIT_ID=d881a50 but no HWLAB_SKILLS_VERSION; agent manager health misses skillCommitId/skillVersion | +| agent runtime | not_run | M4 stopped before agent scheduling because runtime durability and skills injection are blocked | | hardware loop | blocked | M3 stopped on missing patch-panel DO1 -> DI1 active connection | | evidence/audit | not_run | M3/M4 did not reach accepted hardware operations requiring audit/evidence closure | | CD apply | not_run | No deploy/apply command was run in this #64 retest | | frp | pass | 16666 browser, 16667/health, 16667/health/live, frps 7000, and tunnel health 7402 are reachable | -| other | not_observed | No additional blocker class observed in the short DEV-only retest | +| DB live | pass | Current d881a50 read-only M4 preflight evidence reports db.ready=true, db.connected=true, and liveDbEvidence=true; this does not clear runtime durability | ## Blockers -- DB live: cloud-api /health/live reports DB degraded; db.ready=false, db.connected=false, and live DB evidence is not established. +- runtime durable adapter: cloud-api durable runtime adapter is blocked by runtime_durable_adapter_query_blocked; migration 0001_cloud_core_skeleton is missing or not checked. +- skills injection: hwlab-agent-skills exposes HWLAB_SKILLS_COMMIT_ID=d881a50 but no HWLAB_SKILLS_VERSION; agent manager health misses skillCommitId/skillVersion. - hardware loop: Live M3 smoke reached DEV simulators, but patch-panel active DO1 -> DI1 connection is missing. ## Boundary -Active #64 DEV retest report. Public 16666/16667 route is reachable; live MVP remains blocked. No PROD, secret read, runtime restart, long-running agent task, or UniDesk runtime substitution was used. +Active #64 DEV retest report. Public 16666/16667 route and DB live evidence are reachable; live MVP remains blocked. No PROD, secret read, runtime restart, long-running agent task, or UniDesk runtime substitution was used. diff --git a/scripts/dev-m4-agent-loop-smoke.mjs b/scripts/dev-m4-agent-loop-smoke.mjs index dec3a225..a361693f 100644 --- a/scripts/dev-m4-agent-loop-smoke.mjs +++ b/scripts/dev-m4-agent-loop-smoke.mjs @@ -23,10 +23,12 @@ import { DEV_ENDPOINT, DEV_FRONTEND_ENDPOINT, ENVIRONMENT_DEV } from "../interna import { collectD601K3sNativeEvidence, collectPublicEntrypoints, + classifyCloudApiLiveReadiness, d601K3sReadonlyCommand, d601Kubeconfig, devNamespace, requestJson, + runtimeDurabilityFromHealth, workerServerDryRunCommand } from "./src/dev-m4-agent-loop-smoke-lib.mjs"; @@ -78,6 +80,14 @@ function statusForResult(result) { return result.blocked || (result.additionalBlockers?.length ?? 0) > 0 ? "blocked" : "pass"; } +function dbLiveReadyForResult(result) { + return result.dbLiveReady === true || (!result.blocked && result.dbLiveReady !== false); +} + +function runtimeDurableReadyForResult(result) { + return result.runtimeDurableReady === true || (!result.blocked && result.runtimeDurableReady !== false); +} + function blockerForResult(result) { const blockers = []; if (result.blocked && result.blockerType) { @@ -86,7 +96,8 @@ function blockerForResult(result) { scope: result.blockerScope ?? "devPreconditions", status: "open", summary: result.blockerSummary, - classification: result.blockedClassification ?? "other" + classification: result.blockedClassification ?? "other", + ...(result.sourceIssue ? { sourceIssue: result.sourceIssue } : {}) }); } for (const blocker of result.additionalBlockers ?? []) { @@ -119,11 +130,13 @@ function buildDryRunEvidence({ status = "not_run", details = [] } = {}) { } function buildComponentEvidence(fixture, dryRun, result) { + const dbLiveReady = dbLiveReadyForResult(result); + const runtimeDurableReady = runtimeDurableReadyForResult(result); const dbLiveStatus = result.blockedClassification === "DB live" ? "blocked" - : result.blocked - ? "not_run" - : "pass"; + : dbLiveReady + ? "pass" + : "not_run"; const m3DependencyStatus = result.blockedClassification === "hardware loop" ? "blocked" : result.blocked @@ -164,11 +177,26 @@ function buildComponentEvidence(fixture, dryRun, result) { : "Trace/evidence/cleanup live closure was not attempted before preflight blockers were classified." }, dbLive: { - level: "BLOCKED", + level: dbLiveStatus === "pass" ? "DEV-LIVE" : "BLOCKED", status: dbLiveStatus, summary: dbLiveStatus === "blocked" ? "Live scheduling is stopped at cloud-api DB readiness; DB health is classified separately from local dry-run evidence." - : "Live cloud-api DB readiness did not block before the current M4 classification point." + : dbLiveStatus === "pass" + ? "Current read-only /health/live evidence reports DB ready=true, connected=true, and liveDbEvidence=true; DB live is not the active M4 blocker." + : "Live cloud-api DB readiness did not block before the current M4 classification point." + }, + runtimeDurableAdapter: { + level: runtimeDurableReady ? "DEV-LIVE" : "BLOCKED", + status: result.blockerScope === "runtime-durable-adapter" + ? "blocked" + : runtimeDurableReady + ? "pass" + : "not_run", + summary: result.blockerScope === "runtime-durable-adapter" + ? "Live scheduling is stopped at runtime durable adapter readiness; DB live evidence alone is not runtime durability evidence." + : runtimeDurableReady + ? "Current read-only /health/live evidence reports runtime.adapter=postgres, durable=true, ready=true, and liveRuntimeEvidence=true." + : "Runtime durable adapter readiness did not produce the active M4 blocker." }, m3HardwareLoopDependency: { level: "BLOCKED", @@ -182,12 +210,25 @@ function buildComponentEvidence(fixture, dryRun, result) { } function buildDependencyState(result) { + const dbLiveReady = dbLiveReadyForResult(result); + const runtimeDurableReady = runtimeDurableReadyForResult(result); return { dbLive: { - status: result.blockedClassification === "DB live" ? "blocked" : result.blocked ? "not_run" : "pass", + status: result.blockedClassification === "DB live" ? "blocked" : dbLiveReady ? "pass" : "not_run", summary: result.blockedClassification === "DB live" ? result.blockerSummary - : "DB live readiness did not produce the active M4 blocker." + : dbLiveReady + ? "cloud-api /health/live reports DB ready=true, connected=true, and liveDbEvidence=true." + : "DB live readiness did not produce the active M4 blocker." + }, + runtimeDurableAdapter: { + status: result.blockerScope === "runtime-durable-adapter" ? "blocked" : runtimeDurableReady ? "pass" : "not_run", + requiredEvidence: "Postgres runtime adapter schema, migration ledger, and read-query readiness with liveRuntimeEvidence=true.", + summary: result.blockerScope === "runtime-durable-adapter" + ? result.blockerSummary + : runtimeDurableReady + ? "cloud-api /health/live reports runtime.adapter=postgres, durable=true, ready=true, and liveRuntimeEvidence=true." + : "Runtime durable adapter readiness did not produce the active M4 blocker." }, m3HardwareLoop: { status: result.blockedClassification === "hardware loop" ? "blocked" : result.blocked ? "not_run" : "pass", @@ -284,6 +325,7 @@ function buildReport( `DEV browser route remains frozen at ${DEV_FRONTEND_ENDPOINT}`, `D601 native k3s access uses KUBECONFIG=${d601Kubeconfig} and namespace ${devNamespace}`, "No secret reads, real deployment, or long-running agent task is attempted", + "DB live readiness and runtime durable adapter readiness are separate gates; DB live alone cannot promote M4", "Agent manager, worker, and skills are reachable only through the cloud API boundary", "M4 hardware assistance must go through cloud-api/hardware API and patch-panel; direct box/gateway access is forbidden" ], @@ -416,105 +458,104 @@ async function runLivePreflight(fixture, dryRun) { blockerSummary: "DEV live probe did not present the expected HWLAB cloud API identity.", summary: "Blocked because the live DEV boundary did not expose the expected cloud API identity." }; - } else if (isDbLiveBlocked(liveBody)) { - liveEvidence.push(`live:${liveBody.serviceId}:${liveBody.status}:db-blocked`); - result = { - blocked: true, - blockerType: "runtime_blocker", - blockerScope: "db-live", - blockedClassification: "DB live", - blockerSummary: summarizeDbBlocker(liveBody), - summary: "Blocked at DB live readiness before scheduling a DEV agent task." - }; } else { - liveEvidence.push(`live:${liveBody.serviceId}:${liveBody.status}`); - - const rpcHealth = await requestJson(`${DEV_ENDPOINT}/rpc`, { - method: "POST", - headers: { - "content-type": "application/json" - }, - body: JSON.stringify({ - jsonrpc: "2.0", - id: "req_dev_m4_health", - method: "system.health", - params: {}, - meta: { - traceId: "trc_dev_m4_health", - serviceId: "hwlab-cli", - environment: ENVIRONMENT_DEV - } - }) - }); - const rpcHealthBody = rpcHealth.body || {}; - liveEvidence.push(`rpc-health:${rpcHealthBody.result?.serviceId ?? "unknown"}`); - if (rpcHealth.statusCode < 200 || rpcHealth.statusCode >= 300 || rpcHealthBody.error) { - result = { - blocked: true, - blockerType: "agent_blocker", - blockerScope: "agent-runtime", - blockedClassification: "agent runtime", - blockerSummary: rpcHealthBody.error?.message ?? `system.health returned HTTP ${rpcHealth.statusCode}.`, - summary: "Blocked at agent runtime health before attempting hardware dispatch." - }; + const liveReadiness = classifyCloudApiLiveReadiness(liveBody); + if (liveReadiness.blocked) { + liveEvidence.push(`live:${liveBody.serviceId}:${liveBody.status}:${liveReadiness.evidenceSuffix}`); + const runtime = runtimeDurabilityFromHealth(liveBody); + liveEvidence.push(`runtime-durable-adapter:adapter=${runtime.adapter}:blocker=${runtime.blocker}:migration=${runtime.migration.requiredMigrationId}:migrationChecked=${runtime.migration.checked}:migrationReady=${runtime.migration.ready}:queryResult=${runtime.queryResult}`); + result = liveReadiness; } else { + liveEvidence.push(`live:${liveBody.serviceId}:${liveBody.status}`); + const runtime = runtimeDurabilityFromHealth(liveBody); + liveEvidence.push(`runtime-durable-adapter:adapter=${runtime.adapter}:ready=${runtime.ready}:migration=${runtime.migration.requiredMigrationId}:queryResult=${runtime.queryResult}`); - const rpcHardware = await requestJson(`${DEV_ENDPOINT}/rpc`, { + const rpcHealth = await requestJson(`${DEV_ENDPOINT}/rpc`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", - id: "req_dev_m4_hw", - method: "hardware.operation.request", - params: { - projectId: fixture.projectId, - input: { - requestedPath: "DO1 -> hwlab-patch-panel -> DI1", - directBoxGatewayAccess: false, - source: "dev-m4-agent-loop-preflight" - } - }, + id: "req_dev_m4_health", + method: "system.health", + params: {}, meta: { - traceId: "trc_dev_m4_hw", + traceId: "trc_dev_m4_health", serviceId: "hwlab-cli", environment: ENVIRONMENT_DEV } }) }); - const rpcHardwareBody = rpcHardware.body || {}; - const accepted = rpcHardwareBody.result?.accepted === true; - const status = rpcHardwareBody.result?.status || rpcHardwareBody.error?.message || "unknown"; - liveEvidence.push(`rpc-hardware:${status}`); - - if (!accepted) { + const rpcHealthBody = rpcHealth.body || {}; + liveEvidence.push(`rpc-health:${rpcHealthBody.result?.serviceId ?? "unknown"}`); + if (rpcHealth.statusCode < 200 || rpcHealth.statusCode >= 300 || rpcHealthBody.error) { result = { blocked: true, blockerType: "agent_blocker", - blockerScope: "hardware-loop", - blockedClassification: "hardware loop", - blockerSummary: "hardware.operation.request did not accept the patch-panel-bounded M3 request.", - summary: "Blocked by M3 readiness because M4 did not observe an accepted DO1 -> patch-panel -> DI1 hardware request." - }; - } else if (!rpcHardwareBody.result?.auditId || !rpcHardwareBody.result?.evidenceId) { - result = { - blocked: true, - blockerType: "observability_blocker", - blockerScope: "evidence-audit", - blockedClassification: "evidence/audit", - blockerSummary: "hardware.operation.request accepted but did not return audit/evidence identifiers.", - summary: "Blocked because accepted DEV agent hardware dispatch lacks audit/evidence closure." + blockerScope: "agent-runtime", + blockedClassification: "agent runtime", + blockerSummary: rpcHealthBody.error?.message ?? `system.health returned HTTP ${rpcHealth.statusCode}.`, + summary: "Blocked at agent runtime health before attempting hardware dispatch." }; } else { - result = { - blocked: false, - blockerType: null, - blockerScope: null, - blockedClassification: "none", - blockerSummary: "", - summary: "Live DEV preflight observed a non-skeleton agent hardware path." - }; + const rpcHardware = await requestJson(`${DEV_ENDPOINT}/rpc`, { + method: "POST", + headers: { + "content-type": "application/json" + }, + body: JSON.stringify({ + jsonrpc: "2.0", + id: "req_dev_m4_hw", + method: "hardware.operation.request", + params: { + projectId: fixture.projectId, + input: { + requestedPath: "DO1 -> hwlab-patch-panel -> DI1", + directBoxGatewayAccess: false, + source: "dev-m4-agent-loop-preflight" + } + }, + meta: { + traceId: "trc_dev_m4_hw", + serviceId: "hwlab-cli", + environment: ENVIRONMENT_DEV + } + }) + }); + const rpcHardwareBody = rpcHardware.body || {}; + const accepted = rpcHardwareBody.result?.accepted === true; + const status = rpcHardwareBody.result?.status || rpcHardwareBody.error?.message || "unknown"; + liveEvidence.push(`rpc-hardware:${status}`); + + if (!accepted) { + result = { + blocked: true, + blockerType: "agent_blocker", + blockerScope: "hardware-loop", + blockedClassification: "hardware loop", + blockerSummary: "hardware.operation.request did not accept the patch-panel-bounded M3 request.", + summary: "Blocked by M3 readiness because M4 did not observe an accepted DO1 -> patch-panel -> DI1 hardware request." + }; + } else if (!rpcHardwareBody.result?.auditId || !rpcHardwareBody.result?.evidenceId) { + result = { + blocked: true, + blockerType: "observability_blocker", + blockerScope: "evidence-audit", + blockedClassification: "evidence/audit", + blockerSummary: "hardware.operation.request accepted but did not return audit/evidence identifiers.", + summary: "Blocked because accepted DEV agent hardware dispatch lacks audit/evidence closure." + }; + } else { + result = { + blocked: false, + blockerType: null, + blockerScope: null, + blockedClassification: "none", + blockerSummary: "", + summary: "Live DEV preflight observed a non-skeleton agent hardware path." + }; + } } } } @@ -575,20 +616,4 @@ async function main() { await runLivePreflight(fixture, await runDryRun(fixture)); } -function isDbLiveBlocked(body) { - const db = body?.db; - if (!db || typeof db !== "object") { - return false; - } - return db.connected === false || db.ready === false || ["blocked", "degraded"].includes(db.status); -} - -function summarizeDbBlocker(body) { - const db = body?.db ?? {}; - const missing = Array.isArray(db.missingEnv) && db.missingEnv.length > 0 - ? ` missing ${db.missingEnv.join(", ")}` - : ""; - return `cloud-api /health/live reports DB ${db.status ?? "not ready"}; connected=${db.connected ?? "unknown"}; ready=${db.ready ?? "unknown"}.${missing}`; -} - await main(); diff --git a/scripts/l6-cli-web-smoke.mjs b/scripts/l6-cli-web-smoke.mjs index dfc22103..c8938ab9 100644 --- a/scripts/l6-cli-web-smoke.mjs +++ b/scripts/l6-cli-web-smoke.mjs @@ -68,7 +68,9 @@ assert.equal(liveBlocked.exitCode, 2); const liveBlockedJson = JSON.parse(liveBlocked.stderr); assert.equal(liveBlockedJson.code, "BLOCKED"); assert.equal(liveBlockedJson.blockers[0].type, "runtime_blocker"); -assert.equal(liveBlockedJson.blockers[0].scope, "db-live"); +assert.equal(liveBlockedJson.blockers[0].scope, "runtime-durable-adapter"); +assert.match(liveBlockedJson.blockers[0].summary, /runtime_durable_adapter_query_blocked/); +assert.ok(liveBlockedJson.blockers.some((blocker) => blocker.scope === "skills-commit-version-injection")); assert.ok(liveBlockedJson.blockers.some((blocker) => blocker.scope === "m3-hardware-loop-runtime")); assert.equal(gateSummary.endpoint, summary.endpoint); diff --git a/scripts/src/dev-evidence-blocker-aggregator.mjs b/scripts/src/dev-evidence-blocker-aggregator.mjs index 226e2612..73184c1a 100644 --- a/scripts/src/dev-evidence-blocker-aggregator.mjs +++ b/scripts/src/dev-evidence-blocker-aggregator.mjs @@ -5,6 +5,12 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import { activeReportLifecycle, reportIsHistorical, reportLifecycleState } from "../../internal/dev-report-lifecycle.mjs"; +import { + RUNTIME_DURABLE_ADAPTER_MISSING, + RUNTIME_DURABILITY_REQUIRED_EVIDENCE, + RUNTIME_STORE_KIND_POSTGRES +} from "../../internal/db/runtime-store.mjs"; +import { CLOUD_CORE_MIGRATION_ID } from "../../internal/db/schema.mjs"; import { DEV_ENDPOINT, DEV_FRONTEND_ENDPOINT, ENVIRONMENT_DEV, SERVICE_IDS } from "../../internal/protocol/index.mjs"; export const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); @@ -189,7 +195,7 @@ function normalizeBlocker(blocker, sourceReport, fallbackPriority = "P3") { scope: blocker.scope, status: blocker.status ?? "open", source: sourceReport.path, - sourceIssue: sourceReport.issue, + sourceIssue: blocker.sourceIssue ?? sourceReport.issue, summary: oneLine(blocker.summary), ...(nextTask ? { nextTask } : {}) }; @@ -198,7 +204,7 @@ function normalizeBlocker(blocker, sourceReport, fallbackPriority = "P3") { function dedupeBlockers(blockers) { const byKey = new Map(); for (const blocker of blockers) { - const key = `${blocker.type}:${blocker.scope}:${blocker.summary}`; + const key = `${blocker.type}:${blocker.scope}`; const existing = byKey.get(key); if (!existing) { byKey.set(key, blocker); @@ -310,6 +316,26 @@ function applyPriority(blocker) { }; } + if (blocker.scope === "runtime-durable-adapter") { + return { + ...blocker, + priority: "P1", + unblockOrder: 5, + unblocks: [issue(37), issue(39), issue(164)], + rationale: "M4 and M5 cannot claim live agent or MVP evidence until the cloud-api postgres runtime adapter proves schema, migration, and read-query durability; DB live evidence alone is not durability evidence." + }; + } + + if (blocker.scope === "skills-commit-version-injection" || blocker.scope === "agent-mgr-health") { + return { + ...blocker, + priority: "P1", + unblockOrder: 5, + unblocks: [issue(37), issue(39), issue(164)], + rationale: "M4/M5 agent evidence needs deployed manager, skills, and worker paths to expose explicit skills commit and version; session-mode-only worker templates are not enough." + }; + } + if (isEdgeOrFrpBlocker(blocker)) { return { ...blocker, @@ -629,18 +655,27 @@ function collectBlockers(reports) { if (isStaleLegacyIngressBlocker(blocker, report, reports)) { continue; } + if (isStaleReadinessBlocker(blocker, reports)) { + continue; + } blockers.push(normalizeBlocker(blocker, report)); } for (const blocker of report.artifactPublish?.blockers ?? []) { if (isStaleLegacyIngressBlocker(blocker, report, reports)) { continue; } + if (isStaleReadinessBlocker(blocker, reports)) { + continue; + } blockers.push(normalizeBlocker(blocker, report)); } for (const blocker of report.devDeployApply?.remainingBlockers ?? []) { if (isStaleLegacyIngressBlocker(blocker, report, reports)) { continue; } + if (isStaleReadinessBlocker(blocker, reports)) { + continue; + } blockers.push(normalizeBlocker(blocker, report)); } } @@ -722,6 +757,29 @@ function isStaleLegacyIngressBlocker(blocker, sourceReport, reports) { sourceReport.path !== reportPaths.devM5Gate; } +function isStaleReadinessBlocker(blocker, reports) { + const scope = blocker.scope ?? ""; + const db = cloudApiDbStatus(reports); + const provider = codeAgentProviderStatus(reports); + if ( + ( + scope === "cloud-api-db" || + scope === "cloud-api-db-health-gate" || + scope === "cloud-api-db-live" || + scope === "db-live" + ) && + db.ready && + db.connected && + db.liveDbEvidence + ) { + return true; + } + if ((scope === "code-agent-provider-secret" || scope === "code-agent-provider") && provider.ready) { + return true; + } + return false; +} + function deriveMilestones(evidence, blockers) { const evidenceByMilestone = new Map(); for (const item of evidence) { @@ -786,7 +844,7 @@ function buildMilestoneLevelClassification(milestones) { M1: "Local smoke is green; it is not DEV-LIVE.", M2: "Current public frontend/API route evidence is DEV-LIVE for route/front-end reachability only.", M3: "DEV-LIVE hardware trusted loop is blocked; local/source shape is not acceptance.", - M4: "DEV-LIVE agent loop is blocked at DB live readiness; local smoke is not acceptance.", + M4: "DEV-LIVE agent loop is blocked at runtime durable adapter or skills injection readiness; local smoke is not acceptance.", M5: "Dry-run is green, but bounded DEV-LIVE MVP e2e is blocked." }; @@ -918,6 +976,18 @@ function buildDoD(reports, milestones, blockers) { } function cloudApiDbStatus(reports) { + const liveDb = reports.devM4Agent.publicEntrypoints?.api?.live?.db ?? null; + if (liveDb?.ready === true && liveDb?.connected === true && liveDb?.liveDbEvidence === true) { + return { + status: liveDb.status ?? "ok", + ready: true, + connected: true, + configReady: liveDb.configReady === true, + connectionChecked: liveDb.connectionChecked === true, + endpointSource: liveDb.endpointSource ?? liveDb.connection?.endpointSource ?? "secret-url-host", + liveDbEvidence: true + }; + } const db = reports.devEdgeHealth.edgeHealth?.contracts?.deploy?.cloudApiDb ?? {}; return { status: db.status ?? "unknown", @@ -930,10 +1000,91 @@ function cloudApiDbStatus(reports) { }; } +function codeAgentProviderStatus(reports) { + const codeAgent = reports.devM4Agent.publicEntrypoints?.api?.live?.codeAgent ?? + reports.devM4Agent.publicEntrypoints?.api?.health?.codeAgent ?? + null; + return { + ready: Boolean(codeAgent?.ready || codeAgent?.status === "ready" || codeAgent?.status === "available"), + status: codeAgent?.status ?? "unknown", + provider: codeAgent?.provider ?? "unknown" + }; +} + +function runtimeDurabilityStatusFromReport(report = {}) { + const runtime = report.runtime ?? + report.livePreflight?.runtime ?? + report.publicEntrypoints?.api?.live?.runtime ?? + report.readiness?.durability ?? + {}; + const dependency = report.dependencyState?.runtimeDurableAdapter ?? {}; + const component = report.componentEvidence?.runtimeDurableAdapter ?? {}; + const blocker = report.blockers?.find((item) => item.scope === "runtime-durable-adapter") ?? null; + const migration = runtime.migration ?? {}; + return { + adapter: runtime.adapter ?? "unknown", + durable: runtime.durable === true, + durableRequested: Boolean( + runtime.durableRequested || + runtime.adapter === RUNTIME_STORE_KIND_POSTGRES || + blocker || + dependency.status === "blocked" || + component.status === "blocked" + ), + ready: Boolean(runtime.ready === true && runtime.liveRuntimeEvidence === true && runtime.durable === true), + runtimeStatus: runtime.status ?? (blocker ? "blocked" : "unknown"), + blocker: blocker?.classification ?? runtime.blocker ?? report.readiness?.durability?.blocker ?? RUNTIME_DURABLE_ADAPTER_MISSING, + blockedLayer: runtime.durabilityContract?.blockedLayer ?? report.readiness?.durability?.blockedLayer ?? null, + requiredEvidence: runtime.durabilityContract?.requiredEvidence ?? + report.readiness?.durability?.requiredEvidence ?? + RUNTIME_DURABILITY_REQUIRED_EVIDENCE, + migration: { + checked: migration.checked ?? null, + ready: migration.ready ?? null, + missing: migration.missing ?? null, + requiredMigrationId: migration.requiredMigrationId ?? CLOUD_CORE_MIGRATION_ID, + appliedMigrationId: migration.appliedMigrationId ?? null + }, + queryAttempted: runtime.connection?.queryAttempted ?? null, + queryResult: runtime.connection?.queryResult ?? "unknown", + summary: blocker?.summary ?? dependency.summary ?? component.summary ?? "No runtime durable adapter evidence was attached." + }; +} + +function runtimeDurabilityStatus(reports) { + return runtimeDurabilityStatusFromReport(reports.devM4Agent); +} + +function m4SkillsInjectionStatus(reports) { + const native = reports.devM4Agent.d601K3sNative ?? {}; + const injection = native.skillsInjection ?? {}; + const blockers = reports.devM4Agent.blockers ?? []; + const injectionBlocker = blockers.find((blocker) => blocker.scope === "skills-commit-version-injection") ?? null; + const managerBlocker = blockers.find((blocker) => blocker.scope === "agent-mgr-health") ?? null; + const missing = Array.isArray(injection.missing) ? injection.missing : []; + const managerMissing = native.agentManager?.health?.missingSkills ?? []; + return { + status: injectionBlocker || managerBlocker || injection.status === "blocked" ? "blocked" : "pass", + serviceCommitId: injection.serviceCommitId ?? null, + serviceVersion: injection.serviceVersion ?? null, + workerDryRunCommitId: injection.workerDryRunCommitId ?? null, + workerDryRunVersion: injection.workerDryRunVersion ?? null, + missing: [...new Set([...missing, ...managerMissing])], + summary: injectionBlocker?.summary ?? + managerBlocker?.summary ?? + (injection.status === "blocked" + ? `DEV skills injection is incomplete: missing ${missing.join(", ")}.` + : "DEV skills commit and version injection evidence is present.") + }; +} + function buildCurrentDevLayering(reports, blockers) { const m2EndpointLive = hasCurrentM2EndpointEvidence(reports.devM2Smoke); const edgeLive = hasCurrentLiveEdgeEvidence(reports.devEdgeHealth); const cloudDb = cloudApiDbStatus(reports); + const provider = codeAgentProviderStatus(reports); + const runtimeDurability = runtimeDurabilityStatus(reports); + const skillsInjection = m4SkillsInjectionStatus(reports); const dbReady = cloudDb.ready && cloudDb.connected && cloudDb.endpointSource === "secret-url-host" && cloudDb.liveDbEvidence; const m3Live = statusIsPass(reports.devM3Hardware.liveOperation?.status); const m4Live = statusIsPass(reports.devM4Agent.livePreflight?.status); @@ -971,6 +1122,41 @@ function buildCurrentDevLayering(reports, blockers) { evidence: reports.devM5Gate.devPreconditions?.evidence?.filter((line) => line.includes("/health/live") || line.includes("DB")) ?? [], nextRequired: "Provide live DB connection evidence through redacted health output; route reachability alone is insufficient." }, + runtimeDurableAdapter: { + label: "Runtime durable adapter", + status: runtimeDurability.ready ? "pass" : "blocked", + evidenceLevel: runtimeDurability.ready ? "DEV-LIVE" : "BLOCKED", + summary: `runtime adapter=${runtimeDurability.adapter}; durableRequested=${runtimeDurability.durableRequested}; durable=${runtimeDurability.durable}; ready=${runtimeDurability.ready}; blocker=${runtimeDurability.blocker}; migration=${runtimeDurability.migration.requiredMigrationId} checked=${runtimeDurability.migration.checked ?? "unknown"} ready=${runtimeDurability.migration.ready ?? "unknown"}; queryResult=${runtimeDurability.queryResult}.`, + evidence: [ + `requiredEvidence=${runtimeDurability.requiredEvidence}`, + `blockedLayer=${runtimeDurability.blockedLayer ?? "unknown"}`, + `migration=${runtimeDurability.migration.requiredMigrationId}`, + `queryAttempted=${runtimeDurability.queryAttempted ?? "unknown"}` + ], + nextRequired: "Repair the postgres durable runtime adapter query/migration readiness and rerun health/M4 preflight without treating DB live as durability proof." + }, + agentSkillsInjection: { + label: "Agent skills injection", + status: skillsInjection.status, + evidenceLevel: skillsInjection.status === "pass" ? "DEV-LIVE" : "BLOCKED", + summary: skillsInjection.summary, + evidence: [ + `skills.commit=${skillsInjection.serviceCommitId ?? "missing"}`, + `skills.version=${skillsInjection.serviceVersion ?? "missing"}`, + `worker.commit=${skillsInjection.workerDryRunCommitId ?? "missing"}`, + `worker.version=${skillsInjection.workerDryRunVersion ?? "missing"}`, + `missing=${skillsInjection.missing.join(",") || "none"}` + ], + nextRequired: "Inject explicit skill commit and version into hwlab-agent-mgr, hwlab-agent-skills, and scheduled worker jobs before claiming M4/M5 agent evidence." + }, + codeAgentProvider: { + label: "Code Agent provider", + status: provider.ready ? "pass" : "blocked", + evidenceLevel: provider.ready ? "DEV-LIVE" : "BLOCKED", + summary: `code-agent provider=${provider.provider}; status=${provider.status}; ready=${provider.ready}.`, + evidence: [`provider=${provider.provider}`, `status=${provider.status}`], + nextRequired: "Keep provider-backed Code Agent readiness separate from DB live and runtime durable adapter readiness." + }, d601RunnerObservability: { label: "D601 runner observability", status: reports.d601Observability.runnerKubeconfigReadable === false @@ -1005,7 +1191,7 @@ function buildCurrentDevLayering(reports, blockers) { evidenceLevel: m4Live ? "DEV-LIVE" : "BLOCKED", summary: reports.devM4Agent.livePreflight?.summary ?? reports.devM4Agent.devPreconditions?.summary ?? "No live M4 observation was recorded.", evidence: reports.devM4Agent.livePreflight?.evidence ?? [], - nextRequired: "Do not schedule or claim the agent loop as live until DB live and required runtime/evidence preconditions pass." + nextRequired: "Do not schedule or claim the agent loop as live until DB live, runtime durable adapter, skills injection, and required evidence preconditions pass." }, artifactDesiredStateSource: { label: "artifact/desired-state source", @@ -1024,6 +1210,8 @@ function buildCurrentDevLayering(reports, blockers) { function buildMilestoneBlockerClassification(reports) { const cloudDb = cloudApiDbStatus(reports); + const runtimeDurability = runtimeDurabilityStatus(reports); + const skillsInjection = m4SkillsInjectionStatus(reports); const m3Live = statusIsPass(reports.devM3Hardware.liveOperation?.status); const m4Live = statusIsPass(reports.devM4Agent.livePreflight?.status); const m3ServiceDiscoveryBlocked = reports.devM3Hardware.blockers?.some((blocker) => @@ -1045,6 +1233,23 @@ function buildMilestoneBlockerClassification(reports) { ? "patch-panel-m3-wiring-missing" : "hardware-loop-runtime"; + const m4RuntimeDurableBlocked = reports.devM4Agent.blockers?.some((blocker) => + blocker.scope === "runtime-durable-adapter" + ) === true || (!m4Live && runtimeDurability.durableRequested && !runtimeDurability.ready); + const m4SkillsInjectionBlocked = skillsInjection.status === "blocked"; + const m4DbLiveBlocked = reports.devM4Agent.blockers?.some((blocker) => + blocker.scope === "db-live" + ) === true; + const m4BlockerClass = m4Live + ? "cleared" + : m4RuntimeDurableBlocked + ? runtimeDurability.blocker + : m4SkillsInjectionBlocked + ? "skills-commit-version-injection" + : m4DbLiveBlocked + ? "db-live-readiness" + : "agent-loop-live-preflight"; + return [ { milestone: "M3", @@ -1083,31 +1288,51 @@ function buildMilestoneBlockerClassification(reports) { milestone: "M4", status: m4Live ? "pass" : "blocked", currentLevel: m4Live ? "DEV-LIVE" : "BLOCKED", - blockerClass: m4Live ? "cleared" : "db-live-readiness", - dependency: "Cloud API /health/live must report DB ready=true, connected=true, endpointSource=secret-url-host, and liveDbEvidence=true before live agent scheduling/evidence closure.", + blockerClass: m4BlockerClass, + dependency: m4RuntimeDurableBlocked + ? "Cloud API /health/live must prove the postgres durable runtime adapter schema, 0001_cloud_core_skeleton migration ledger, and read-query readiness before live agent scheduling/evidence closure." + : m4SkillsInjectionBlocked + ? "Agent manager, skills service, and worker jobs must expose explicit skill commit and version fields before M4 live evidence can be trusted." + : "Cloud API /health/live must report DB ready=true, connected=true, endpointSource=secret-url-host, and liveDbEvidence=true before live agent scheduling/evidence closure.", evidence: [ `db.status=${cloudDb.status}`, `db.ready=${cloudDb.ready}`, `db.connected=${cloudDb.connected}`, `db.endpointSource=${cloudDb.endpointSource}`, - `db.liveDbEvidence=${cloudDb.liveDbEvidence}` + `db.liveDbEvidence=${cloudDb.liveDbEvidence}`, + `runtime.adapter=${runtimeDurability.adapter}`, + `runtime.blocker=${runtimeDurability.blocker}`, + `runtime.migration=${runtimeDurability.migration.requiredMigrationId}`, + `runtime.migration.ready=${runtimeDurability.migration.ready ?? "unknown"}`, + `runtime.queryResult=${runtimeDurability.queryResult}`, + `skills.missing=${skillsInjection.missing.join(",") || "none"}` ], - nextRequired: "Repair DB live readiness and rerun the M4 live preflight without scheduling a DEV agent task before preconditions pass.", - nonPromotionReason: "Frontend revision and read-only route reachability do not prove DB-backed agent runtime readiness." + nextRequired: m4RuntimeDurableBlocked + ? "Repair the runtime durable adapter query/migration readiness and rerun the M4 live preflight without scheduling a DEV agent task before preconditions pass." + : m4SkillsInjectionBlocked + ? "Inject skill commit/version into agent manager, skills service, and worker jobs, then rerun the M4 read-only/live preflight." + : "Repair DB live readiness and rerun the M4 live preflight without scheduling a DEV agent task before preconditions pass.", + nonPromotionReason: "Frontend revision and read-only route reachability do not prove DB-backed durable agent runtime readiness or skill version injection." }, { milestone: "M5", status: "blocked", currentLevel: "BLOCKED", - blockerClass: "composite-db-m3-m4-live", - dependency: "M5 needs DB live readiness, M3 trusted-loop DEV evidence, M4 live preflight/evidence closure, and current source/artifact coverage.", + blockerClass: m4RuntimeDurableBlocked + ? "composite-runtime-durable-m3-m4-live" + : m4SkillsInjectionBlocked + ? "composite-skills-injection-m3-m4-live" + : "composite-db-m3-m4-live", + dependency: "M5 needs DB live readiness, runtime durable adapter readiness, skills commit/version injection, M3 trusted-loop DEV evidence, M4 live preflight/evidence closure, and current source/artifact coverage.", evidence: [ "M5 dry-run is green", `db.ready=${cloudDb.ready}`, + `runtime.blocker=${runtimeDurability.blocker}`, + `skills.status=${skillsInjection.status}`, `m3.live=${m3Live}`, `m4.live=${m4Live}` ], - nextRequired: "After DB/M3/M4 blockers are cleared, run only the bounded DEV MVP live gate command with explicit DEV/non-PROD confirmations.", + nextRequired: "After DB/runtime durable adapter/skills/M3/M4 blockers are cleared, run only the bounded DEV MVP live gate command with explicit DEV/non-PROD confirmations.", nonPromotionReason: "No frontend, route-only, local, or dry-run evidence is allowed to stand in for bounded DEV-LIVE MVP e2e acceptance." } ]; @@ -1165,6 +1390,8 @@ function fallbackAction(scope) { if (scope.includes("edge") || scope.includes("ingress") || scope.includes("frp")) return "Repair frp/master-edge/D601 router path and rerun read-only DEV edge health."; if (scope.includes("cloud-api-db")) return "Configure DEV cloud-api DB env readiness and Secret URL host connectivity, then rerun health/preflight without exposing secrets."; if (scope === "db-live") return "Repair DEV cloud-api DB live readiness, then rerun the read-only health and M4 preflight reports without exposing secret values."; + if (scope === "runtime-durable-adapter") return "Repair DEV cloud-api postgres durable runtime adapter schema/migration/read-query readiness, then rerun /health/live and the M4 preflight without exposing secret values."; + if (scope === "skills-commit-version-injection" || scope === "agent-mgr-health") return "Inject explicit skills commit and version into agent manager, skills service, and worker jobs, then rerun the M4 read-only/live preflight."; if (scope === "m3-patch-panel-wiring") return "Load/apply DEV patch-panel wiring for res_boxsimu_1:DO1 -> res_boxsimu_2:DI1, then rerun the bounded DEV M3 smoke."; if (scope === "m3-box-simu-identity") return "Fix DEV box-simu instance identity so direct endpoints expose distinct res_boxsimu_1 and res_boxsimu_2 resources."; if (scope === "m3-gateway-simu-identity") return "Fix DEV gateway-simu instance identity so direct endpoints expose two distinct gateway sessions."; @@ -1181,6 +1408,8 @@ function evidenceRequiredFor(scope) { if (scope.includes("kubectl") || scope.includes("k3s")) return "Read-only kubectl/k3s report proving pods/services/configmaps are observable in hwlab-dev without reading Secrets."; if (scope.includes("cloud-api-db")) return "Cloud API health/live output showing DB env ready, endpointSource=secret-url-host, redacted secret references, and no secret material."; if (scope === "db-live") return "Cloud API /health/live output with ready=true, connected=true, endpointSource=secret-url-host, liveDbEvidence=true, and redacted secret references."; + if (scope === "runtime-durable-adapter") return "Cloud API /health/live output with runtime.adapter=postgres, durable=true, ready=true, liveRuntimeEvidence=true, migration 0001_cloud_core_skeleton ready, and read-query readiness passing."; + if (scope === "skills-commit-version-injection" || scope === "agent-mgr-health") return "Read-only hwlab-agent-mgr/hwlab-agent-skills health plus worker dry-run manifest showing skillCommitId/skillVersion and HWLAB_SKILL_COMMIT_ID/HWLAB_SKILL_VERSION injected."; if (scope === "m3-patch-panel-wiring") return "Read-only patch-panel /status and /wiring showing active res_boxsimu_1:DO1 -> res_boxsimu_2:DI1 before a bounded write/read smoke records operation, trace, audit, and evidence IDs."; if (scope === "m3-box-simu-identity") return "Read-only direct box-simu /health/live and /status output showing distinct res_boxsimu_1 and res_boxsimu_2 resources."; if (scope === "m3-gateway-simu-identity") return "Read-only direct gateway-simu /health/live and /status output showing two distinct gateway identities/sessions."; @@ -1299,7 +1528,7 @@ export async function buildReport() { green: dod.green, reason: dod.green ? "All #9 DEV DoD checks are green." - : `Frontend revision ${latestFrontendRevision} is usable in degraded/read-only mode and EDGE/ROUTE DEV-LIVE evidence exists on :16666/:16667, but M5 remains blocked by artifact source drift, API/runtime durability degradation, missing M3 trusted loop operation evidence, and blocked M4 agent-loop preflight.` + : `Frontend revision ${latestFrontendRevision} is usable in degraded/read-only mode and EDGE/ROUTE DEV-LIVE evidence exists on :16666/:16667, but M5 remains blocked by artifact source drift, runtime durable adapter or skills injection readiness, missing M3 trusted loop operation evidence, and blocked M4 agent-loop preflight.` }, latestFrontendDevFact: { ...frontendDevFact, diff --git a/scripts/src/dev-evidence-blocker-aggregator.test.mjs b/scripts/src/dev-evidence-blocker-aggregator.test.mjs new file mode 100644 index 00000000..4a682a09 --- /dev/null +++ b/scripts/src/dev-evidence-blocker-aggregator.test.mjs @@ -0,0 +1,34 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { buildReport } from "./dev-evidence-blocker-aggregator.mjs"; + +test("aggregator reports M4 current blocker as durable runtime or skills injection, not stale DB live", async () => { + const report = await buildReport(); + const m4 = report.milestoneBlockerClassification.find((item) => item.milestone === "M4"); + const m5 = report.milestoneBlockerClassification.find((item) => item.milestone === "M5"); + const dbLayer = report.currentDevLayering.dbLive; + const dbCheck = report.dod.checks.find((item) => item.id === "cloud-api-db-ready"); + + assert.ok(m4, "M4 blocker classification is present"); + assert.ok(m5, "M5 blocker classification is present"); + assert.equal(dbLayer.status, "pass"); + assert.equal(dbLayer.evidenceLevel, "DEV-LIVE"); + assert.equal(dbCheck.status, "pass"); + assert.equal(dbCheck.evidenceLevel, "DEV-LIVE"); + assert.equal( + report.blockers.some((blocker) => + ["cloud-api-db", "cloud-api-db-health-gate", "cloud-api-db-live", "db-live"].includes(blocker.scope) + ), + false, + "DB live blockers must be stale once current DB live evidence is pass" + ); + assert.notEqual(m4.blockerClass, "db-live-readiness"); + assert.ok( + ["runtime_durable_adapter_query_blocked", "skills-commit-version-injection"].includes(m4.blockerClass), + `unexpected M4 blocker class ${m4.blockerClass}` + ); + assert.match(m4.dependency, /durable runtime adapter|skill commit and version/u); + assert.match(m4.nonPromotionReason, /durable agent runtime readiness|skill version injection/u); + assert.match(m5.dependency, /runtime durable adapter readiness, skills commit\/version injection/u); +}); diff --git a/scripts/src/dev-m4-agent-loop-smoke-lib.mjs b/scripts/src/dev-m4-agent-loop-smoke-lib.mjs index ad9fedb9..d3afb828 100644 --- a/scripts/src/dev-m4-agent-loop-smoke-lib.mjs +++ b/scripts/src/dev-m4-agent-loop-smoke-lib.mjs @@ -9,6 +9,12 @@ import { AGENT_SKILLS_SERVICE_ID, AGENT_WORKER_SERVICE_ID } from "../../internal/agent/index.mjs"; +import { + RUNTIME_DURABLE_ADAPTER_MISSING, + RUNTIME_DURABILITY_REQUIRED_EVIDENCE, + RUNTIME_STORE_KIND_POSTGRES +} from "../../internal/db/runtime-store.mjs"; +import { CLOUD_CORE_MIGRATION_ID } from "../../internal/db/schema.mjs"; import { DEV_ENDPOINT, DEV_FRONTEND_ENDPOINT, ENVIRONMENT_DEV } from "../../internal/protocol/index.mjs"; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); @@ -20,6 +26,183 @@ export const d601K3sReadonlyCommand = export const workerServerDryRunCommand = "KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n hwlab-dev create -f - --dry-run=server -o json"; +export function isDbLiveReady(body) { + const db = body?.db; + return Boolean(db?.ready && db?.connected && db?.liveConnected !== false && db?.liveDbEvidence); +} + +export function isDbLiveBlocked(body) { + const db = body?.db; + if (!db || typeof db !== "object") { + return false; + } + if (isDbLiveReady(body)) { + return false; + } + return db.connected === false || + db.ready === false || + db.liveConnected === false || + db.liveDbEvidence === false || + ["blocked", "degraded", "failed"].includes(db.status); +} + +export function summarizeDbBlocker(body) { + const db = body?.db ?? {}; + const missing = Array.isArray(db.missingEnv) && db.missingEnv.length > 0 + ? ` missing ${db.missingEnv.join(", ")}` + : ""; + return `cloud-api /health/live reports DB ${db.status ?? "not ready"}; connected=${db.connected ?? "unknown"}; ready=${db.ready ?? "unknown"}; liveDbEvidence=${db.liveDbEvidence ?? "unknown"}.${missing}`; +} + +export function runtimeDurabilityFromHealth(body) { + const runtime = body?.runtime ?? {}; + const durability = body?.readiness?.durability ?? {}; + const migration = runtime.migration ?? {}; + const migrationGate = runtime.gates?.migration ?? durability.gates?.migration ?? {}; + const blocker = runtime.blocker ?? + durability.blocker ?? + (Array.isArray(body?.blockerCodes) + ? body.blockerCodes.find((code) => String(code).startsWith("runtime_durable_adapter_")) + : null) ?? + RUNTIME_DURABLE_ADAPTER_MISSING; + const durableRequested = Boolean( + runtime.durableRequested || + durability.durableRequested || + runtime.adapter === RUNTIME_STORE_KIND_POSTGRES || + durability.adapter === RUNTIME_STORE_KIND_POSTGRES + ); + const ready = Boolean( + runtime.durable === true && + runtime.ready === true && + runtime.liveRuntimeEvidence === true && + durability.ready !== false + ); + + return { + adapter: runtime.adapter ?? durability.adapter ?? "unknown", + durable: Boolean(runtime.durable), + durableRequested, + ready, + runtimeStatus: runtime.status ?? durability.runtimeStatus ?? "unknown", + blocker, + blockedLayer: runtime.durabilityContract?.blockedLayer ?? durability.blockedLayer ?? null, + requiredEvidence: runtime.durabilityContract?.requiredEvidence ?? + durability.requiredEvidence ?? + RUNTIME_DURABILITY_REQUIRED_EVIDENCE, + migration: { + checked: migration.checked ?? migrationGate.checked ?? false, + ready: migration.ready ?? migrationGate.ready ?? false, + missing: migration.missing ?? (migration.ready === false ? true : null), + requiredMigrationId: migration.requiredMigrationId ?? CLOUD_CORE_MIGRATION_ID, + appliedMigrationId: migration.appliedMigrationId ?? null + }, + queryAttempted: Boolean(runtime.connection?.queryAttempted ?? durability.queryAttempted), + queryResult: runtime.connection?.queryResult ?? durability.queryResult ?? "unknown" + }; +} + +export function isRuntimeDurableAdapterBlocked(body) { + const runtime = runtimeDurabilityFromHealth(body); + if (!runtime.durableRequested) { + return false; + } + return !runtime.ready || + ["blocked", "degraded", "failed"].includes(runtime.runtimeStatus) || + String(runtime.blocker).startsWith("runtime_durable_adapter_"); +} + +export function summarizeRuntimeDurableBlocker(body) { + const runtime = runtimeDurabilityFromHealth(body); + const migration = runtime.migration; + return `cloud-api durable runtime adapter is blocked: ${runtime.blocker}; adapter=${runtime.adapter}; durable=${runtime.durable}; ready=${runtime.ready}; migration=${migration.requiredMigrationId} checked=${migration.checked} ready=${migration.ready} missing=${migration.missing ?? "unknown"}; queryResult=${runtime.queryResult}.`; +} + +export function publicDbLiveSummary(body) { + const db = body?.db ?? null; + if (!db || typeof db !== "object") { + return null; + } + return { + status: db.status ?? null, + connected: db.connected ?? null, + liveConnected: db.liveConnected ?? null, + ready: db.ready ?? null, + configReady: db.configReady ?? null, + connectionChecked: db.connectionChecked ?? null, + connectionAttempted: db.connectionAttempted ?? null, + connectionResult: db.connectionResult ?? null, + endpointSource: db.endpointSource ?? db.connection?.endpointSource ?? null, + liveDbEvidence: db.liveDbEvidence ?? null, + evidence: db.evidence ?? null + }; +} + +export function publicRuntimeDurabilitySummary(body) { + if (!body?.runtime && !body?.readiness?.durability) { + return null; + } + const runtime = runtimeDurabilityFromHealth(body); + return { + adapter: runtime.adapter, + durable: runtime.durable, + durableRequested: runtime.durableRequested, + ready: runtime.ready, + status: runtime.runtimeStatus, + blocker: runtime.blocker, + blockedLayer: runtime.blockedLayer, + requiredEvidence: runtime.requiredEvidence, + liveRuntimeEvidence: Boolean(runtime.ready), + migration: runtime.migration, + queryAttempted: runtime.queryAttempted, + queryResult: runtime.queryResult + }; +} + +export function classifyCloudApiLiveReadiness(body) { + if (isDbLiveBlocked(body)) { + return { + blocked: true, + blockerType: "runtime_blocker", + blockerScope: "db-live", + sourceIssue: "pikasTech/HWLAB#49", + blockedClassification: "DB live", + blockerSummary: summarizeDbBlocker(body), + summary: "Blocked at DB live readiness before scheduling a DEV agent task.", + evidenceSuffix: "db-blocked", + dbLiveReady: false, + runtimeDurableReady: false + }; + } + + if (isRuntimeDurableAdapterBlocked(body)) { + const runtime = runtimeDurabilityFromHealth(body); + return { + blocked: true, + blockerType: "runtime_blocker", + blockerScope: "runtime-durable-adapter", + sourceIssue: "pikasTech/HWLAB#164", + blockedClassification: runtime.blocker, + blockerSummary: summarizeRuntimeDurableBlocker(body), + summary: "Blocked at runtime durable adapter readiness before scheduling a DEV agent task.", + evidenceSuffix: `runtime-durable-adapter-blocked:${runtime.blocker}`, + dbLiveReady: isDbLiveReady(body), + runtimeDurableReady: false + }; + } + + return { + blocked: false, + blockerType: null, + blockerScope: null, + blockedClassification: "none", + blockerSummary: "", + summary: "Live DEV preflight observed DB live and durable runtime adapter readiness.", + evidenceSuffix: null, + dbLiveReady: isDbLiveReady(body), + runtimeDurableReady: true + }; +} + export function requestJson(urlString, { method = "GET", headers = {}, body, timeoutMs = 5000 } = {}) { const url = new URL(urlString); const client = url.protocol === "https:" ? httpsRequest : httpRequest; @@ -348,12 +531,9 @@ export async function collectPublicEntrypoints() { serviceId: apiLive.body?.serviceId ?? null, environment: apiLive.body?.environment ?? null, status: apiLive.body?.status ?? null, - db: apiLive.body?.db ? { - status: apiLive.body.db.status ?? null, - connected: apiLive.body.db.connected ?? null, - ready: apiLive.body.db.ready ?? null, - evidence: apiLive.body.db.evidence ?? null - } : null, + db: publicDbLiveSummary(apiLive.body), + runtime: publicRuntimeDurabilitySummary(apiLive.body), + blockerCodes: Array.isArray(apiLive.body?.blockerCodes) ? apiLive.body.blockerCodes : [], error: apiLive.error ?? null } }, diff --git a/scripts/src/dev-m4-agent-loop-smoke-lib.test.mjs b/scripts/src/dev-m4-agent-loop-smoke-lib.test.mjs new file mode 100644 index 00000000..37980690 --- /dev/null +++ b/scripts/src/dev-m4-agent-loop-smoke-lib.test.mjs @@ -0,0 +1,157 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { + classifyCloudApiLiveReadiness, + publicDbLiveSummary, + publicRuntimeDurabilitySummary, + runtimeDurabilityFromHealth +} from "./dev-m4-agent-loop-smoke-lib.mjs"; + +test("M4 live readiness classifies durable adapter query blocker after DB live is ready", () => { + const payload = { + db: { + ready: true, + connected: true, + liveConnected: true, + liveDbEvidence: true, + status: "ok" + }, + runtime: { + adapter: "postgres", + durable: false, + durableRequested: true, + ready: false, + status: "blocked", + blocker: "runtime_durable_adapter_query_blocked", + reason: "durable runtime read query is blocked", + liveRuntimeEvidence: false, + connection: { + queryAttempted: true, + queryResult: "query_blocked" + }, + migration: { + checked: true, + ready: false, + missing: true, + requiredMigrationId: "0001_cloud_core_skeleton" + } + }, + readiness: { + durability: { + blockedLayer: "durability_query", + requiredEvidence: "runtime_adapter_schema_migration_read_query" + } + } + }; + + const classification = classifyCloudApiLiveReadiness(payload); + assert.equal(classification.blocked, true); + assert.equal(classification.blockerScope, "runtime-durable-adapter"); + assert.equal(classification.blockedClassification, "runtime_durable_adapter_query_blocked"); + assert.match(classification.blockerSummary, /0001_cloud_core_skeleton/u); + assert.match(classification.evidenceSuffix, /runtime_durable_adapter_query_blocked/u); + + const runtime = runtimeDurabilityFromHealth(payload); + assert.equal(runtime.blocker, "runtime_durable_adapter_query_blocked"); + assert.equal(runtime.migration.checked, true); + assert.equal(runtime.migration.ready, false); + assert.equal(runtime.queryResult, "query_blocked"); +}); + +test("M4 live readiness keeps DB live blocker when DB is not connected", () => { + const classification = classifyCloudApiLiveReadiness({ + db: { + ready: false, + connected: false, + liveDbEvidence: false, + status: "degraded" + }, + runtime: { + adapter: "postgres", + durableRequested: true, + blocker: "runtime_durable_adapter_query_blocked" + } + }); + + assert.equal(classification.blockerScope, "db-live"); + assert.equal(classification.blockedClassification, "DB live"); +}); + +test("public DB live summary preserves DB pass fields apart from durable adapter blocker", () => { + const summary = publicDbLiveSummary({ + db: { + status: "ok", + connected: true, + liveConnected: true, + ready: true, + configReady: true, + connectionChecked: true, + endpointSource: "secret-url-host", + liveDbEvidence: true, + evidence: "live_db_tcp_connection_ready" + }, + runtime: { + adapter: "postgres", + durable: false, + ready: false, + blocker: "runtime_durable_adapter_query_blocked" + } + }); + + assert.equal(summary.status, "ok"); + assert.equal(summary.connected, true); + assert.equal(summary.liveConnected, true); + assert.equal(summary.ready, true); + assert.equal(summary.configReady, true); + assert.equal(summary.connectionChecked, true); + assert.equal(summary.endpointSource, "secret-url-host"); + assert.equal(summary.liveDbEvidence, true); + assert.equal(summary.evidence, "live_db_tcp_connection_ready"); +}); + +test("public runtime durability summary preserves durable adapter fields separately from DB live", () => { + const summary = publicRuntimeDurabilitySummary({ + db: { + ready: true, + connected: true, + liveConnected: true, + liveDbEvidence: true + }, + runtime: { + adapter: "postgres", + durable: false, + durableRequested: true, + ready: false, + status: "blocked", + blocker: "runtime_durable_adapter_query_blocked", + liveRuntimeEvidence: false, + connection: { + queryAttempted: true, + queryResult: "query_blocked" + }, + migration: { + checked: false, + ready: false, + missing: true, + requiredMigrationId: "0001_cloud_core_skeleton" + }, + durabilityContract: { + blockedLayer: "durability_query", + requiredEvidence: "runtime_adapter_schema_migration_read_query" + } + } + }); + + assert.equal(summary.adapter, "postgres"); + assert.equal(summary.durableRequested, true); + assert.equal(summary.durable, false); + assert.equal(summary.ready, false); + assert.equal(summary.liveRuntimeEvidence, false); + assert.equal(summary.blocker, "runtime_durable_adapter_query_blocked"); + assert.equal(summary.blockedLayer, "durability_query"); + assert.equal(summary.requiredEvidence, "runtime_adapter_schema_migration_read_query"); + assert.equal(summary.queryAttempted, true); + assert.equal(summary.queryResult, "query_blocked"); + assert.equal(summary.migration.requiredMigrationId, "0001_cloud_core_skeleton"); +}); diff --git a/scripts/validate-dev-gate-report-guard.test.mjs b/scripts/validate-dev-gate-report-guard.test.mjs index b6334fb8..ec476e31 100644 --- a/scripts/validate-dev-gate-report-guard.test.mjs +++ b/scripts/validate-dev-gate-report-guard.test.mjs @@ -528,6 +528,8 @@ test("accepts the current blocked M5 aggregator non-promotion contract", async ( test("rejects aggregator DB acceptance promoted from frontend route evidence", async () => { const report = cloneBaseAggregatorReport(); + const m4Source = report.sourceReports.devM4Agent; + m4Source.path = "reports/dev-gate/dev-edge-health.json"; const check = report.dod.checks.find((item) => item.id === "cloud-api-db-ready"); Object.assign(check, { status: "pass", @@ -542,6 +544,24 @@ test("rejects aggregator DB acceptance promoted from frontend route evidence", a await assertRejected(report, /DB DEV-LIVE acceptance source reports/); }); +test("rejects aggregator stale DB runtime-env blocker when DB live is pass", async () => { + const report = cloneBaseAggregatorReport(); + report.blockers.push({ + id: "dev-gate-preflight:cloud-api-db-health-gate", + priority: "P1", + type: "runtime_blocker", + scope: "cloud-api-db-health-gate", + status: "open", + source: "reports/dev-gate/dev-preflight-report.json", + sourceIssue: "pikasTech/HWLAB#34", + summary: "cloud-api DB runtime env is not ready; missing HWLAB_CLOUD_DB_URL, HWLAB_CLOUD_DB_SSL_MODE.", + unblockOrder: 5, + unblocks: ["pikasTech/HWLAB#34", "pikasTech/HWLAB#33", "pikasTech/HWLAB#39"], + rationale: "Stale DB runtime-env blocker fixture." + }); + await assertRejected(report, /stale DB live\/runtime-env blocker/); +}); + test("rejects aggregator M3 acceptance promoted from source evidence", async () => { const report = cloneBaseAggregatorReport(); const sourceEvidence = report.evidence.find((item) => diff --git a/scripts/validate-dev-gate-report.mjs b/scripts/validate-dev-gate-report.mjs index 66f70fe4..08061ada 100644 --- a/scripts/validate-dev-gate-report.mjs +++ b/scripts/validate-dev-gate-report.mjs @@ -2468,9 +2468,14 @@ function observedLiveIdentifier(value) { function aggregatorLiveStateFromSources(sources) { const cloudApiDb = sources.devEdgeHealth.edgeHealth?.contracts?.deploy?.cloudApiDb ?? {}; - const dbLive = cloudApiDb.ready === true && + const edgeDbLive = cloudApiDb.ready === true && (cloudApiDb.connected === true || cloudApiDb.liveConnected === true) && cloudApiDb.liveDbEvidence === true; + const m4LiveDb = sources.devM4Agent.publicEntrypoints?.api?.live?.db ?? {}; + const m4DbLive = m4LiveDb.ready === true && + (m4LiveDb.connected === true || m4LiveDb.liveConnected === true) && + m4LiveDb.liveDbEvidence === true; + const dbLive = edgeDbLive || m4DbLive; const m3Operation = sources.devM3Hardware.liveOperation ?? {}; const m3Live = statusIsPass(m3Operation.status) && ["operationId", "traceId", "auditId", "evidenceId"].every((field) => @@ -2547,11 +2552,37 @@ function assertProtectedAggregatorDevLiveEvidence(item, label, liveState, assume ); } +function assertNoStaleDbRuntimeBlocker(report, label, liveState) { + if (!liveState.dbLive) { + return; + } + + const staleDbScopes = new Set([ + "cloud-api-db", + "cloud-api-db-health-gate", + "cloud-api-db-live", + "db-live" + ]); + const staleBlocker = report.blockers?.find((blocker) => staleDbScopes.has(blocker.scope)); + assert.equal( + staleBlocker, + undefined, + `${label}.blockers must not keep stale DB live/runtime-env blocker ${staleBlocker?.scope ?? ""} when DB live evidence is pass` + ); + + const dbCheck = findById(report.dod.checks, "cloud-api-db-ready"); + assertObject(dbCheck, `${label}.dod.checks.cloud-api-db-ready`); + assert.equal(dbCheck.status, "pass", `${label}.dod.checks.cloud-api-db-ready.status must be pass when DB live evidence is proven`); + assert.equal(dbCheck.evidenceLevel, "DEV-LIVE", `${label}.dod.checks.cloud-api-db-ready.evidenceLevel must be DEV-LIVE when DB live evidence is proven`); +} + async function assertAggregatorNonPromotionBoundaries(report, label) { const sources = await readAggregatorSourceReports(report, label); const liveState = aggregatorLiveStateFromSources(sources); const anyProtectedLiveBlocked = !liveState.dbLive || !liveState.m3Live || !liveState.m4Live || !liveState.m5Live; + assertNoStaleDbRuntimeBlocker(report, label, liveState); + assertObject(report.latestFrontendDevFact, `${label}.latestFrontendDevFact`); assert.equal( report.latestFrontendDevFact.promotesM3M4M5, diff --git a/web/hwlab-cloud-web/gate-summary.mjs b/web/hwlab-cloud-web/gate-summary.mjs index ad1bfff7..09ab513b 100644 --- a/web/hwlab-cloud-web/gate-summary.mjs +++ b/web/hwlab-cloud-web/gate-summary.mjs @@ -67,13 +67,14 @@ export const gateSummary = { }, "devPreconditions": { "status": "blocked", - "classification": "DB live; hardware loop", + "classification": "runtime durable adapter; skills injection; hardware loop", "requirements": [ "http://74.48.78.17:16666/ returns the DEV browser entry", "http://74.48.78.17:16667/health returns HWLAB DEV edge JSON", - "http://74.48.78.17:16667/health/live returns HWLAB cloud-api JSON with DB live readiness", + "http://74.48.78.17:16667/health/live returns HWLAB cloud-api JSON with DB live readiness and runtime durability separated", + "Runtime durable adapter proves postgres schema, migration 0001_cloud_core_skeleton, and read-query readiness before M4/M5 can promote", + "Agent manager, skills service, and worker jobs expose explicit skill commit/version before M4/M5 can promote", "M3 hardware loop proves box-simu-1 DO1 -> box-simu-2 DI1 via patch-panel", - "M4 agent runtime/evidence path is only attempted after DB live and M3 readiness are not blocked", "Keep the run DEV-only and avoid PROD, heavy e2e, secrets, restarts, or UniDesk substitution" ], "commands": [ @@ -85,20 +86,26 @@ export const gateSummary = { ], "evidence": [ "http://74.48.78.17:16666/ -> HTTP 200 text/html, title=HWLAB DEV MVP Gate", - "http://74.48.78.17:16667/health -> HTTP 200 serviceId=hwlab-edge-proxy status=ok commit=cb35ada6860653b27269b0a57991184118cbf4b1", - "http://74.48.78.17:16667/health/live -> HTTP 200 serviceId=hwlab-cloud-api status=degraded db.ready=false db.connected=false db.connectionChecked=false", - "dev-edge-health active report -> blocked by DB live readiness, while public 16667 TCP/HTTP and frp control/tunnel health are reachable", + "http://74.48.78.17:16667/health -> HTTP 200 serviceId=hwlab-edge-proxy status=ok commit=d881a50", + "http://74.48.78.17:16667/health/live -> HTTP 200 serviceId=hwlab-cloud-api status=degraded db.ready=true db.connected=true db.liveDbEvidence=true runtime.blocker=runtime_durable_adapter_query_blocked runtime.migration=0001_cloud_core_skeleton", + "dev-edge-health active report remains older DB-live evidence and is superseded for M4/M5 blocker naming by current d881a50 M4 preflight evidence", "dev-m3-hardware-loop active report -> blocked, classification=hardware loop, summary=patch-panel active DO1 -> DI1 connection missing", - "dev-m4-agent-loop active report -> blocked, classification=DB live, summary=cloud-api /health/live reports DB degraded; connected=false; ready=false" + "dev-m4-agent-loop active report -> blocked, classification=runtime_durable_adapter_query_blocked, summary=runtime durable adapter query/migration readiness is blocked; skills commit/version injection is incomplete" ], - "summary": "Public DEV route/frp is reachable on 16666/16667, but live MVP remains blocked by DB live readiness and the M3 hardware loop topology; no historical 6667 failure is counted as active evidence." + "summary": "Public DEV route/frp is reachable on 16666/16667, but live MVP remains blocked by runtime_durable_adapter_query_blocked, skills commit/version injection, and the M3 hardware loop topology; no DB-live-only blocker is counted as current M4/M5 evidence." }, "blockers": [ { "type": "runtime_blocker", - "scope": "db-live", + "scope": "runtime-durable-adapter", "status": "open", - "summary": "cloud-api /health/live reports DB degraded; db.ready=false, db.connected=false, and live DB evidence is not established." + "summary": "cloud-api durable runtime adapter is blocked: runtime_durable_adapter_query_blocked; migration 0001_cloud_core_skeleton is missing or not checked." + }, + { + "type": "agent_blocker", + "scope": "skills-commit-version-injection", + "status": "open", + "summary": "DEV skills injection is incomplete: hwlab-agent-skills exposes HWLAB_SKILLS_COMMIT_ID=d881a50 but no HWLAB_SKILLS_VERSION; worker template only exposes session mode unless commit/version are injected." }, { "type": "runtime_blocker", @@ -141,14 +148,14 @@ export const gateSummary = { "status": "blocked", "commandCount": 2, "evidenceCount": 2, - "summary": "Local M4 remains green, but active DEV-LIVE M4 is blocked at DB live readiness before agent runtime scheduling." + "summary": "Local M4 remains green, but active DEV-LIVE M4 is blocked at runtime durable adapter readiness and skills commit/version injection before agent runtime scheduling." }, { "id": "M5", "status": "blocked", "commandCount": 3, "evidenceCount": 4, - "summary": "The M5 orchestration is green in dry-run mode, but active DEV-LIVE acceptance is blocked by DB live and hardware loop evidence." + "summary": "The M5 orchestration is green in dry-run mode, but active DEV-LIVE acceptance is blocked by runtime durable adapter, skills injection, and hardware loop evidence." } ], "artifactCount": 13,