Merge pull request #232 from pikasTech/fix/m4-m5-current-blocker-evidence

fix: report current M4/M5 runtime blockers
This commit is contained in:
Lyon
2026-05-23 14:20:02 +08:00
committed by GitHub
16 changed files with 1261 additions and 475 deletions
+2 -2
View File
@@ -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,
+1 -1
View File
File diff suppressed because one or more lines are too long
+206 -36
View File
@@ -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."
}
+166 -250
View File
@@ -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"
],
+16 -18
View File
@@ -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.
+39 -24
View File
@@ -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": {
+10 -8
View File
@@ -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.
+128 -103
View File
@@ -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();
+3 -1
View File
@@ -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);
+242 -13
View File
@@ -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,
@@ -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);
});
+186 -6
View File
@@ -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
}
},
@@ -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");
});
@@ -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) =>
+32 -1
View File
@@ -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,
+19 -12
View File
@@ -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,