docs: refresh DEV gate evidence layering
This commit is contained in:
@@ -38,16 +38,29 @@ promoted to live DEV evidence:
|
||||
| `SOURCE` | Repository contracts, schemas, docs, manifests, or examples. |
|
||||
| `LOCAL` | Localhost or in-process smoke results only. |
|
||||
| `DRY-RUN` | Fixture-backed or no-mutation dry-run readiness evidence. |
|
||||
| `DEV-LIVE` | Read-only evidence observed from the real DEV route. |
|
||||
| `DEV-LIVE` | Read-only evidence observed from the real DEV route; this may prove entrypoint reachability without proving downstream DB, M3, M4, or M5 acceptance. |
|
||||
| `BLOCKED` | Missing issue reads, missing DEV evidence, missing artifacts, or other open blockers. |
|
||||
|
||||
## Current DEV Layering
|
||||
|
||||
The v2 report exposes `currentDevLayering` so the public route can be green
|
||||
without promoting the full gate:
|
||||
|
||||
| Layer | Meaning |
|
||||
| --- | --- |
|
||||
| `EDGE/ROUTE live` | `16666` browser and `16667` health route evidence from read-only probes. |
|
||||
| `DB live/degraded` | Cloud API DB readiness from redacted health evidence; route success does not imply DB live. |
|
||||
| `M3 hardware trusted loop` | Real DEV `res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1` plus operation, trace, audit, and evidence identifiers. |
|
||||
| `M4 agent loop` | Agent loop live preflight and scheduling/evidence closure, gated by DB and runtime readiness. |
|
||||
| `artifact/desired-state source` | Artifact source coverage for the current target ref plus desired-state dry-run/apply status. |
|
||||
|
||||
## Current Contract Map
|
||||
|
||||
| Milestone | Current visible level | Contract entry |
|
||||
| --- | --- | --- |
|
||||
| M0 | `SOURCE` | `docs/m0-contract-audit.md`, protocol schemas, evidence chain examples, M0 validator. |
|
||||
| M1 | `LOCAL` | `docs/m1-local-smoke.md`, `fixtures/mvp/runtime.json`, `scripts/m1-contract-smoke.mjs`. |
|
||||
| M2 | `DRY-RUN` | DEV deploy smoke fixture, artifact catalog, runtime boundary, and M2 dry-run validator. |
|
||||
| M2 | `DEV-LIVE` for route only | DEV deploy smoke fixture plus active read-only `16666`/`16667` endpoint smoke. |
|
||||
| M3 | `LOCAL` | Hardware trusted loop topology fixture and local patch-panel smoke. |
|
||||
| M4 | `LOCAL` | Agent automation loop fixture, local runtime skeleton, skills commit, evidence, and cleanup smoke. |
|
||||
| M5 | `DRY-RUN` | MVP e2e dry-run plan and orchestration validator. |
|
||||
@@ -60,14 +73,17 @@ The v2 report reads committed report fixtures only:
|
||||
- `reports/dev-gate/dev-deploy-report.json`
|
||||
- `reports/dev-gate/dev-artifacts.json`
|
||||
- `reports/dev-gate/dev-edge-health.json`
|
||||
- `reports/dev-gate/dev-m2-deploy-smoke-active.json`
|
||||
- `reports/dev-gate/dev-m3-hardware-loop.json`
|
||||
- `reports/dev-gate/dev-m4-agent-loop.json`
|
||||
- `reports/dev-gate/dev-mvp-gate-report.json`
|
||||
- `reports/d601-k3s-readonly-observability.json`
|
||||
|
||||
The report is blocked until real DEV-LIVE evidence is attached for the DEV
|
||||
route and downstream HWLAB runtime services. Current source, local, and dry-run
|
||||
contracts are useful readiness signals, but they are not live DEV proof.
|
||||
The report remains blocked until real DEV-LIVE evidence is attached for the
|
||||
required downstream HWLAB runtime services. Public route evidence on
|
||||
`16666/16667` is necessary but not sufficient: DB live readiness, M3 trusted
|
||||
loop evidence, M4 agent-loop evidence, and artifact/desired-state source
|
||||
coverage stay separate.
|
||||
|
||||
The legacy base-image blocker is not current when the committed reports show an
|
||||
approved Node 20 builder base. Artifact/current remains stricter: a target ref
|
||||
|
||||
@@ -29,6 +29,11 @@ Each committed DEV gate report must carry `reportLifecycle`:
|
||||
`res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1` in real DEV
|
||||
plus operation, audit, and evidence identifiers. SOURCE, LOCAL, DRY-RUN,
|
||||
fixture, and historical report evidence are support/diagnostics only.
|
||||
- M5 DEV-LIVE cannot be inferred from public entrypoint reachability. Reports
|
||||
must keep EDGE/ROUTE live, DB live/degraded, M3 hardware trusted loop,
|
||||
M4 agent loop, and artifact/desired-state source status as separate layers.
|
||||
If the only read-only evidence is `16666/16667` reachability, M5 must remain
|
||||
blocked/non-green.
|
||||
|
||||
## Files
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"pikasTech/HWLAB#39",
|
||||
"pikasTech/HWLAB#42"
|
||||
],
|
||||
"generatedAt": "2026-05-21T17:34:15.046Z",
|
||||
"generatedAt": "2026-05-22T07:21:35.915Z",
|
||||
"mode": "read-only",
|
||||
"devOnly": true,
|
||||
"prodDisabled": true,
|
||||
@@ -48,18 +48,28 @@
|
||||
"node --check scripts/src/d601-k3s-readonly-observability.mjs",
|
||||
"node scripts/d601-k3s-readonly-observability.mjs"
|
||||
],
|
||||
"conclusion": "blocked",
|
||||
"conclusion": "ready",
|
||||
"environment": {
|
||||
"binaries": {
|
||||
"kubectl": {
|
||||
"name": "kubectl",
|
||||
"available": false,
|
||||
"path": null
|
||||
"available": true,
|
||||
"path": "/usr/local/bin/kubectl",
|
||||
"version": {
|
||||
"ok": true,
|
||||
"command": "kubectl version --client=true",
|
||||
"output": "Client Version: v1.36.1 Kustomize Version: v5.8.1"
|
||||
}
|
||||
},
|
||||
"k3s": {
|
||||
"name": "k3s",
|
||||
"available": false,
|
||||
"path": null
|
||||
"available": true,
|
||||
"path": "/usr/local/bin/k3s",
|
||||
"version": {
|
||||
"ok": true,
|
||||
"command": "k3s --version",
|
||||
"output": "k3s version v1.34.1+k3s1 (24fc436e) go version go1.24.6"
|
||||
}
|
||||
},
|
||||
"k3sctl": {
|
||||
"name": "k3sctl",
|
||||
@@ -73,11 +83,19 @@
|
||||
"version": {
|
||||
"ok": true,
|
||||
"command": "ssh -V",
|
||||
"output": "OpenSSH_10.0p2 Debian-7+deb13u2, OpenSSL 3.5.5 27 Jan 2026"
|
||||
"output": "OpenSSH_10.0p2 Debian-7+deb13u4, OpenSSL 3.5.6 7 Apr 2026"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kubeconfig": [
|
||||
{
|
||||
"source": "KUBECONFIG",
|
||||
"path": "/var/lib/unidesk/code-queue/kubeconfig",
|
||||
"exists": true,
|
||||
"readable": true,
|
||||
"type": "file",
|
||||
"sizeBytes": 410
|
||||
},
|
||||
{
|
||||
"source": "default",
|
||||
"path": "~/.kube/config",
|
||||
@@ -147,7 +165,7 @@
|
||||
"exists": true,
|
||||
"readable": true,
|
||||
"type": "file",
|
||||
"sizeBytes": 4208
|
||||
"sizeBytes": 5186
|
||||
}
|
||||
],
|
||||
"configured": false,
|
||||
@@ -156,36 +174,459 @@
|
||||
},
|
||||
"cluster": {
|
||||
"namespace": "hwlab-dev",
|
||||
"attemptedExecutors": [],
|
||||
"readable": false,
|
||||
"executors": []
|
||||
"attemptedExecutors": [
|
||||
"kubectl",
|
||||
"k3s-kubectl"
|
||||
],
|
||||
"readable": true,
|
||||
"executors": [
|
||||
{
|
||||
"executor": "kubectl",
|
||||
"ready": true,
|
||||
"probes": {
|
||||
"clientVersion": {
|
||||
"ok": true,
|
||||
"command": "kubectl version --client=true",
|
||||
"exitCode": 0,
|
||||
"stdout": "Client Version: v1.36.1\nKustomize Version: v5.8.1"
|
||||
},
|
||||
"currentContext": {
|
||||
"ok": true,
|
||||
"command": "kubectl config current-context",
|
||||
"exitCode": 0,
|
||||
"stdout": "in-cluster"
|
||||
},
|
||||
"namespace": {
|
||||
"ok": true,
|
||||
"command": "kubectl get namespace hwlab-dev -o json",
|
||||
"exitCode": 0
|
||||
},
|
||||
"auth": {
|
||||
"pods": {
|
||||
"allowed": true,
|
||||
"ok": true,
|
||||
"command": "kubectl auth can-i get pods -n hwlab-dev",
|
||||
"exitCode": 0,
|
||||
"stdout": "yes"
|
||||
},
|
||||
"services": {
|
||||
"allowed": true,
|
||||
"ok": true,
|
||||
"command": "kubectl auth can-i get services -n hwlab-dev",
|
||||
"exitCode": 0,
|
||||
"stdout": "yes"
|
||||
},
|
||||
"configmaps": {
|
||||
"allowed": true,
|
||||
"ok": true,
|
||||
"command": "kubectl auth can-i get configmaps -n hwlab-dev",
|
||||
"exitCode": 0,
|
||||
"stdout": "yes"
|
||||
}
|
||||
},
|
||||
"pods": {
|
||||
"ok": true,
|
||||
"command": "kubectl -n hwlab-dev get pods -o json",
|
||||
"exitCode": 0
|
||||
},
|
||||
"services": {
|
||||
"ok": true,
|
||||
"command": "kubectl -n hwlab-dev get services -o json",
|
||||
"exitCode": 0
|
||||
},
|
||||
"configMaps": {
|
||||
"ok": true,
|
||||
"command": "kubectl -n hwlab-dev get configmaps -o json",
|
||||
"exitCode": 0
|
||||
}
|
||||
},
|
||||
"summaries": {
|
||||
"pods": {
|
||||
"count": 13,
|
||||
"items": [
|
||||
{
|
||||
"name": "hwlab-agent-mgr-75544c7747-5dzt9",
|
||||
"phase": "Running",
|
||||
"readyContainers": 1,
|
||||
"totalContainers": 1,
|
||||
"restartCount": 0,
|
||||
"nodeName": "d601"
|
||||
},
|
||||
{
|
||||
"name": "hwlab-agent-skills-849d9976f4-2nsmw",
|
||||
"phase": "Running",
|
||||
"readyContainers": 1,
|
||||
"totalContainers": 1,
|
||||
"restartCount": 0,
|
||||
"nodeName": "d601"
|
||||
},
|
||||
{
|
||||
"name": "hwlab-box-simu-55688cbcc7-pftxc",
|
||||
"phase": "Running",
|
||||
"readyContainers": 1,
|
||||
"totalContainers": 1,
|
||||
"restartCount": 0,
|
||||
"nodeName": "d601"
|
||||
},
|
||||
{
|
||||
"name": "hwlab-box-simu-55688cbcc7-sq5fx",
|
||||
"phase": "Running",
|
||||
"readyContainers": 1,
|
||||
"totalContainers": 1,
|
||||
"restartCount": 0,
|
||||
"nodeName": "d601"
|
||||
},
|
||||
{
|
||||
"name": "hwlab-cloud-api-f48dcdfb-9n6zh",
|
||||
"phase": "Running",
|
||||
"readyContainers": 1,
|
||||
"totalContainers": 1,
|
||||
"restartCount": 0,
|
||||
"nodeName": "d601"
|
||||
},
|
||||
{
|
||||
"name": "hwlab-cloud-web-cd9655b98-gxtmc",
|
||||
"phase": "Running",
|
||||
"readyContainers": 1,
|
||||
"totalContainers": 1,
|
||||
"restartCount": 0,
|
||||
"nodeName": "d601"
|
||||
},
|
||||
{
|
||||
"name": "hwlab-edge-proxy-567f978cdd-8fwc8",
|
||||
"phase": "Running",
|
||||
"readyContainers": 1,
|
||||
"totalContainers": 1,
|
||||
"restartCount": 0,
|
||||
"nodeName": "d601"
|
||||
},
|
||||
{
|
||||
"name": "hwlab-frpc-59f57d479c-wm5l2",
|
||||
"phase": "Running",
|
||||
"readyContainers": 1,
|
||||
"totalContainers": 1,
|
||||
"restartCount": 0,
|
||||
"nodeName": "d601"
|
||||
},
|
||||
{
|
||||
"name": "hwlab-gateway-simu-699fd486b-j9pk7",
|
||||
"phase": "Running",
|
||||
"readyContainers": 1,
|
||||
"totalContainers": 1,
|
||||
"restartCount": 0,
|
||||
"nodeName": "d601"
|
||||
},
|
||||
{
|
||||
"name": "hwlab-gateway-simu-699fd486b-phcgp",
|
||||
"phase": "Running",
|
||||
"readyContainers": 1,
|
||||
"totalContainers": 1,
|
||||
"restartCount": 0,
|
||||
"nodeName": "d601"
|
||||
},
|
||||
{
|
||||
"name": "hwlab-patch-panel-757f9f44d5-7np8r",
|
||||
"phase": "Running",
|
||||
"readyContainers": 1,
|
||||
"totalContainers": 1,
|
||||
"restartCount": 0,
|
||||
"nodeName": "d601"
|
||||
},
|
||||
{
|
||||
"name": "hwlab-router-5d9cbdf89b-p7r94",
|
||||
"phase": "Running",
|
||||
"readyContainers": 1,
|
||||
"totalContainers": 1,
|
||||
"restartCount": 0,
|
||||
"nodeName": "d601"
|
||||
},
|
||||
{
|
||||
"name": "hwlab-tunnel-client-65c7858c8-wjqsh",
|
||||
"phase": "Running",
|
||||
"readyContainers": 1,
|
||||
"totalContainers": 1,
|
||||
"restartCount": 0,
|
||||
"nodeName": "d601"
|
||||
}
|
||||
]
|
||||
},
|
||||
"services": {
|
||||
"count": 12,
|
||||
"items": [
|
||||
{
|
||||
"name": "hwlab-agent-mgr",
|
||||
"type": "ClusterIP",
|
||||
"clusterIP": "10.43.175.43",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"port": 7410,
|
||||
"targetPort": "http",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hwlab-agent-skills",
|
||||
"type": "ClusterIP",
|
||||
"clusterIP": "10.43.218.243",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"port": 7430,
|
||||
"targetPort": "http",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hwlab-box-simu",
|
||||
"type": "ClusterIP",
|
||||
"clusterIP": "10.43.192.161",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"port": 7201,
|
||||
"targetPort": "http",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hwlab-cli",
|
||||
"type": "ClusterIP",
|
||||
"clusterIP": "10.43.94.68",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"port": 7501,
|
||||
"targetPort": "http",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hwlab-cloud-api",
|
||||
"type": "ClusterIP",
|
||||
"clusterIP": "10.43.88.144",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"port": 6667,
|
||||
"targetPort": "http",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hwlab-cloud-web",
|
||||
"type": "ClusterIP",
|
||||
"clusterIP": "10.43.112.75",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"port": 8080,
|
||||
"targetPort": "http",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hwlab-edge-proxy",
|
||||
"type": "ClusterIP",
|
||||
"clusterIP": "10.43.104.107",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"port": 6667,
|
||||
"targetPort": "http",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hwlab-gateway",
|
||||
"type": "ClusterIP",
|
||||
"clusterIP": "10.43.70.176",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"port": 7001,
|
||||
"targetPort": "http",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hwlab-gateway-simu",
|
||||
"type": "ClusterIP",
|
||||
"clusterIP": "10.43.70.14",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"port": 7101,
|
||||
"targetPort": "http",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hwlab-patch-panel",
|
||||
"type": "ClusterIP",
|
||||
"clusterIP": "10.43.7.35",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"port": 7301,
|
||||
"targetPort": "http",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hwlab-router",
|
||||
"type": "ClusterIP",
|
||||
"clusterIP": "10.43.6.240",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"port": 7401,
|
||||
"targetPort": "http",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hwlab-tunnel-client",
|
||||
"type": "ClusterIP",
|
||||
"clusterIP": "10.43.129.225",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"port": 7402,
|
||||
"targetPort": "http",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"configMaps": {
|
||||
"count": 3,
|
||||
"items": [
|
||||
{
|
||||
"name": "hwlab-dev-health-contract",
|
||||
"keys": [
|
||||
"agent",
|
||||
"cloud-api",
|
||||
"cloud-api-db",
|
||||
"cloud-web",
|
||||
"edge-proxy",
|
||||
"endpoint",
|
||||
"patch-panel",
|
||||
"router",
|
||||
"runtime-substitute-policy",
|
||||
"sim",
|
||||
"tunnel-client"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hwlab-frpc-config",
|
||||
"keys": [
|
||||
"frpc.toml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "kube-root-ca.crt",
|
||||
"keys": [
|
||||
"ca.crt"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"executor": "k3s-kubectl",
|
||||
"ready": false,
|
||||
"probes": {
|
||||
"clientVersion": {
|
||||
"ok": false,
|
||||
"command": "k3s kubectl version --client=true",
|
||||
"exitCode": 1,
|
||||
"stderr": "error: unknown command \"kubectl\" for \"kubectl\"",
|
||||
"error": "Command failed: k3s kubectl version --client=true error: unknown command \"kubectl\" for \"kubectl\""
|
||||
},
|
||||
"currentContext": {
|
||||
"ok": false,
|
||||
"command": "k3s kubectl config current-context",
|
||||
"exitCode": 1,
|
||||
"stderr": "error: unknown command \"kubectl\" for \"kubectl\"",
|
||||
"error": "Command failed: k3s kubectl config current-context error: unknown command \"kubectl\" for \"kubectl\""
|
||||
},
|
||||
"namespace": {
|
||||
"ok": false,
|
||||
"command": "k3s kubectl get namespace hwlab-dev -o json",
|
||||
"exitCode": 1,
|
||||
"stderr": "error: unknown command \"kubectl\" for \"kubectl\"",
|
||||
"error": "Command failed: k3s kubectl get namespace hwlab-dev -o json error: unknown command \"kubectl\" for \"kubectl\""
|
||||
},
|
||||
"auth": {
|
||||
"pods": {
|
||||
"allowed": false,
|
||||
"ok": false,
|
||||
"command": "k3s kubectl auth can-i get pods -n hwlab-dev",
|
||||
"exitCode": 1,
|
||||
"stderr": "error: unknown command \"kubectl\" for \"kubectl\"",
|
||||
"error": "Command failed: k3s kubectl auth can-i get pods -n hwlab-dev error: unknown command \"kubectl\" for \"kubectl\""
|
||||
},
|
||||
"services": {
|
||||
"allowed": false,
|
||||
"ok": false,
|
||||
"command": "k3s kubectl auth can-i get services -n hwlab-dev",
|
||||
"exitCode": 1,
|
||||
"stderr": "error: unknown command \"kubectl\" for \"kubectl\"",
|
||||
"error": "Command failed: k3s kubectl auth can-i get services -n hwlab-dev error: unknown command \"kubectl\" for \"kubectl\""
|
||||
},
|
||||
"configmaps": {
|
||||
"allowed": false,
|
||||
"ok": false,
|
||||
"command": "k3s kubectl auth can-i get configmaps -n hwlab-dev",
|
||||
"exitCode": 1,
|
||||
"stderr": "error: unknown command \"kubectl\" for \"kubectl\"",
|
||||
"error": "Command failed: k3s kubectl auth can-i get configmaps -n hwlab-dev error: unknown command \"kubectl\" for \"kubectl\""
|
||||
}
|
||||
},
|
||||
"pods": {
|
||||
"ok": false,
|
||||
"command": "k3s kubectl -n hwlab-dev get pods -o json",
|
||||
"exitCode": 1,
|
||||
"stderr": "error: unknown command \"kubectl\" for \"kubectl\"",
|
||||
"error": "Command failed: k3s kubectl -n hwlab-dev get pods -o json error: unknown command \"kubectl\" for \"kubectl\""
|
||||
},
|
||||
"services": {
|
||||
"ok": false,
|
||||
"command": "k3s kubectl -n hwlab-dev get services -o json",
|
||||
"exitCode": 1,
|
||||
"stderr": "error: unknown command \"kubectl\" for \"kubectl\"",
|
||||
"error": "Command failed: k3s kubectl -n hwlab-dev get services -o json error: unknown command \"kubectl\" for \"kubectl\""
|
||||
},
|
||||
"configMaps": {
|
||||
"ok": false,
|
||||
"command": "k3s kubectl -n hwlab-dev get configmaps -o json",
|
||||
"exitCode": 1,
|
||||
"stderr": "error: unknown command \"kubectl\" for \"kubectl\"",
|
||||
"error": "Command failed: k3s kubectl -n hwlab-dev get configmaps -o json error: unknown command \"kubectl\" for \"kubectl\""
|
||||
}
|
||||
},
|
||||
"summaries": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"blockers": [
|
||||
{
|
||||
"status": "open",
|
||||
"type": "environment_blocker",
|
||||
"scope": "d601-k3s-client-binary",
|
||||
"summary": "Neither kubectl nor k3s is installed in this runner PATH.",
|
||||
"nextTask": "Install kubectl in the D601 Code Queue runner image, or mount an approved k3s kubectl client path for read-only hwlab-dev observation."
|
||||
},
|
||||
{
|
||||
"status": "open",
|
||||
"type": "environment_blocker",
|
||||
"scope": "d601-kubeconfig-path",
|
||||
"summary": "No readable KUBECONFIG/default k3s kubeconfig path was found by metadata checks.",
|
||||
"nextTask": "Mount a read-only kubeconfig for hwlab-dev, or document the approved k3s local kubeconfig path without exposing token material."
|
||||
},
|
||||
{
|
||||
"status": "open",
|
||||
"type": "environment_blocker",
|
||||
"scope": "d601-maintenance-ssh-bridge",
|
||||
"summary": "No SSH maintenance bridge was detected from ssh binary plus known environment variables or ~/.ssh/config metadata.",
|
||||
"nextTask": "Provide a documented read-only maintenance bridge variable or config path if kubectl cannot be mounted directly in the runner."
|
||||
}
|
||||
],
|
||||
"blockers": [],
|
||||
"runnerImageFollowUp": {
|
||||
"needed": true,
|
||||
"suggestedUniDeskIssue": "Code Queue runner image should include kubectl and a documented read-only hwlab-dev kubeconfig mount/permission path; keep Secret resources and token material inaccessible to reports."
|
||||
"needed": false,
|
||||
"suggestedUniDeskIssue": null
|
||||
},
|
||||
"notes": "This report uses command availability checks, file metadata checks, kubectl/k3s kubectl read-only get probes, and auth can-i checks only. It does not print kubeconfig contents, token material, Secret resources, ConfigMap values, SSH config contents, or private keys."
|
||||
}
|
||||
|
||||
@@ -4,12 +4,20 @@
|
||||
"reportVersion": "v1",
|
||||
"issue": "pikasTech/HWLAB#23",
|
||||
"taskId": "m2-dev-deploy-smoke",
|
||||
"commitId": "54f927b02bc1",
|
||||
"commitId": "8e89409dda5d",
|
||||
"acceptanceLevel": "dev_m2_deploy_smoke",
|
||||
"devOnly": true,
|
||||
"prodDisabled": true,
|
||||
"reportLifecycle": {
|
||||
"version": "v1",
|
||||
"state": "active",
|
||||
"activeEndpoint": "http://74.48.78.17:16667",
|
||||
"activeBrowserEndpoint": "http://74.48.78.17:16666",
|
||||
"deprecatedEndpoint": null,
|
||||
"summary": "Active M2 read-only public endpoint smoke; route reachability is not M5 DEV-LIVE acceptance."
|
||||
},
|
||||
"status": "pass",
|
||||
"generatedAt": "2026-05-22T04:53:00.375Z",
|
||||
"generatedAt": "2026-05-22T07:18:45.192Z",
|
||||
"endpoint": "http://74.48.78.17:16667",
|
||||
"frontendEndpoint": "http://74.48.78.17:16666",
|
||||
"sourceContract": {
|
||||
@@ -76,7 +84,7 @@
|
||||
"runtimeSmoke": {
|
||||
"mode": "live-read-only",
|
||||
"status": "pass",
|
||||
"generatedAt": "2026-05-22T04:53:00.375Z",
|
||||
"generatedAt": "2026-05-22T07:18:45.192Z",
|
||||
"apiEndpoint": "http://74.48.78.17:16667",
|
||||
"frontendEndpoint": "http://74.48.78.17:16666",
|
||||
"safety": {
|
||||
@@ -95,12 +103,12 @@
|
||||
"ok": true,
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"durationMs": 939,
|
||||
"durationMs": 3916,
|
||||
"contentType": "application/json; charset=utf-8",
|
||||
"headers": {
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"content-length": "661",
|
||||
"date": "Fri, 22 May 2026 04:53:00 GMT"
|
||||
"date": "Fri, 22 May 2026 07:18:43 GMT"
|
||||
},
|
||||
"json": {
|
||||
"serviceId": "hwlab-edge-proxy",
|
||||
@@ -122,7 +130,7 @@
|
||||
"digest": "unknown"
|
||||
},
|
||||
"endpoint": "http://74.48.78.17:16667",
|
||||
"observedAt": "2026-05-22T04:53:00.143Z",
|
||||
"observedAt": "2026-05-22T07:18:43.527Z",
|
||||
"details": {
|
||||
"upstream": "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667",
|
||||
"mode": "dev-edge-proxy"
|
||||
@@ -137,12 +145,12 @@
|
||||
"ok": true,
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"durationMs": 936,
|
||||
"durationMs": 3510,
|
||||
"contentType": "application/json; charset=utf-8",
|
||||
"headers": {
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"cache-control": "no-store",
|
||||
"date": "Fri, 22 May 2026 04:53:00 GMT"
|
||||
"date": "Fri, 22 May 2026 07:18:44 GMT"
|
||||
},
|
||||
"json": {
|
||||
"serviceId": "hwlab-cloud-api",
|
||||
@@ -164,7 +172,7 @@
|
||||
"digest": "unknown"
|
||||
},
|
||||
"endpoint": "http://74.48.78.17:16667",
|
||||
"observedAt": "2026-05-22T04:53:00.145Z",
|
||||
"observedAt": "2026-05-22T07:18:44.622Z",
|
||||
"db": {
|
||||
"contractVersion": "v1",
|
||||
"environment": "dev",
|
||||
@@ -225,7 +233,7 @@
|
||||
}
|
||||
},
|
||||
"title": null,
|
||||
"bodyPreview": "{\"serviceId\":\"hwlab-cloud-api\",\"environment\":\"dev\",\"status\":\"degraded\",\"service\":{\"id\":\"hwlab-cloud-api\",\"role\":\"cloud-api\",\"healthPath\":\"/health\",\"livePath\":\"/health/live\"},\"commit\":{\"id\":\"cb35ada\",\"source\":\"runtime-env\"},\"image\":{\"reference\":\"127.0.0.1:5000/hwlab/hwlab-cloud-api:cb35ada\",\"tag\":\"cb35ada\",\"digest\":\"unknown\"},\"endpoint\":\"http://74.48.78.17:16667\",\"observedAt\":\"2026-05-22T04:53:00.145Z\",\"db\":{\"contractVersion\":\"v1\",\"environment\":\"dev\",\"connected\":false,\"connectionChecked\":false,\"configReady\":"
|
||||
"bodyPreview": "{\"serviceId\":\"hwlab-cloud-api\",\"environment\":\"dev\",\"status\":\"degraded\",\"service\":{\"id\":\"hwlab-cloud-api\",\"role\":\"cloud-api\",\"healthPath\":\"/health\",\"livePath\":\"/health/live\"},\"commit\":{\"id\":\"cb35ada\",\"source\":\"runtime-env\"},\"image\":{\"reference\":\"127.0.0.1:5000/hwlab/hwlab-cloud-api:cb35ada\",\"tag\":\"cb35ada\",\"digest\":\"unknown\"},\"endpoint\":\"http://74.48.78.17:16667\",\"observedAt\":\"2026-05-22T07:18:44.622Z\",\"db\":{\"contractVersion\":\"v1\",\"environment\":\"dev\",\"connected\":false,\"connectionChecked\":false,\"configReady\":"
|
||||
},
|
||||
{
|
||||
"id": "frontend-root",
|
||||
@@ -233,12 +241,12 @@
|
||||
"ok": true,
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"durationMs": 923,
|
||||
"durationMs": 1027,
|
||||
"contentType": "text/html; charset=utf-8",
|
||||
"headers": {
|
||||
"content-type": "text/html; charset=utf-8",
|
||||
"content-length": "4230",
|
||||
"date": "Fri, 22 May 2026 04:53:00 GMT"
|
||||
"date": "Fri, 22 May 2026 07:18:42 GMT"
|
||||
},
|
||||
"json": null,
|
||||
"title": "HWLAB DEV MVP Gate",
|
||||
@@ -252,13 +260,5 @@
|
||||
"summary": "Legacy public :6667 observations are retained only as historical blockers and are not accepted as active green evidence."
|
||||
}
|
||||
},
|
||||
"notes": "M2 active report uses read-only GET probes only; PROD, service restarts, deployments, and script-driven apply flows were not touched.",
|
||||
"reportLifecycle": {
|
||||
"version": "v1",
|
||||
"state": "active",
|
||||
"activeEndpoint": "http://74.48.78.17:16667",
|
||||
"activeBrowserEndpoint": "http://74.48.78.17:16666",
|
||||
"deprecatedEndpoint": null,
|
||||
"summary": "Current M2 DEV deploy smoke report; legacy public endpoints are deprecated and not active green evidence."
|
||||
}
|
||||
"notes": "M2 active report uses read-only GET probes only; PROD, service restarts, deployments, and script-driven apply flows were not touched."
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -24,13 +24,21 @@
|
||||
],
|
||||
"target": {
|
||||
"ref": "origin/main",
|
||||
"commitId": "82ad96c5c67c3906732a057d52395ec72851e4a9",
|
||||
"shortCommitId": "82ad96c"
|
||||
"commitId": "8e89409dda5d0b028de7f7b7b0e006a60bdd6d44",
|
||||
"shortCommitId": "8e89409"
|
||||
},
|
||||
"generatedAt": "2026-05-22T05:35:39.805Z",
|
||||
"generatedAt": "2026-05-22T07:12:46.951Z",
|
||||
"mode": "read-only",
|
||||
"devOnly": true,
|
||||
"prodDisabled": true,
|
||||
"reportLifecycle": {
|
||||
"version": "v1",
|
||||
"state": "active",
|
||||
"activeEndpoint": "http://74.48.78.17:16667",
|
||||
"activeBrowserEndpoint": "http://74.48.78.17:16666",
|
||||
"deprecatedEndpoint": null,
|
||||
"summary": "Current read-only DEV preflight report; source, local, and dry-run checks are not DEV-LIVE acceptance."
|
||||
},
|
||||
"forbiddenActions": [
|
||||
"prod-deploy",
|
||||
"secret-material-read",
|
||||
@@ -52,8 +60,8 @@
|
||||
"artifactIdentity": {
|
||||
"source": {
|
||||
"ref": "origin/main",
|
||||
"commitId": "82ad96c5c67c3906732a057d52395ec72851e4a9",
|
||||
"shortCommitId": "82ad96c"
|
||||
"commitId": "8e89409dda5d0b028de7f7b7b0e006a60bdd6d44",
|
||||
"shortCommitId": "8e89409"
|
||||
},
|
||||
"artifactSource": {
|
||||
"source": "artifact-publish-report",
|
||||
@@ -67,17 +75,21 @@
|
||||
"sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33",
|
||||
"sourceShortCommitId": "7e29522",
|
||||
"targetRef": "origin/main",
|
||||
"targetCommitId": "82ad96c5c67c3906732a057d52395ec72851e4a9",
|
||||
"targetShortCommitId": "82ad96c",
|
||||
"targetCommitId": "8e89409dda5d0b028de7f7b7b0e006a60bdd6d44",
|
||||
"targetShortCommitId": "8e89409",
|
||||
"mode": "artifact-input-drift",
|
||||
"covered": false,
|
||||
"artifactBuildInputChanged": true,
|
||||
"changedPathCount": 22,
|
||||
"changedPathCount": 48,
|
||||
"changedArtifactInputPaths": [
|
||||
"internal/dev-report-lifecycle.mjs",
|
||||
"internal/mvp-gate/summary.mjs",
|
||||
"internal/protocol/index.mjs",
|
||||
"package.json",
|
||||
"scripts/dev-artifact-publish.mjs",
|
||||
"web/hwlab-cloud-web/gate-summary.mjs"
|
||||
],
|
||||
"changedNonArtifactPathCount": 20,
|
||||
"changedNonArtifactPathCount": 42,
|
||||
"reason": "target changes since the artifact source touch artifact build inputs"
|
||||
},
|
||||
"deployManifest": {
|
||||
@@ -274,7 +286,7 @@
|
||||
"registryCapabilities": {
|
||||
"version": "v1",
|
||||
"registryPrefix": "127.0.0.1:5000/hwlab",
|
||||
"generatedAt": "2026-05-22T05:35:38.863Z",
|
||||
"generatedAt": "2026-05-22T07:12:45.056Z",
|
||||
"classification": "degraded",
|
||||
"dimensions": {
|
||||
"processHttpAccess": {
|
||||
@@ -322,7 +334,7 @@
|
||||
"names": "unidesk-artifact-registry",
|
||||
"ports": "127.0.0.1:5000->5000/tcp",
|
||||
"state": "running",
|
||||
"status": "Up 28 hours"
|
||||
"status": "Up 30 hours"
|
||||
}
|
||||
],
|
||||
"pushAttempted": false,
|
||||
@@ -461,13 +473,13 @@
|
||||
"id": "target-commit-pinning",
|
||||
"category": "contract",
|
||||
"status": "blocked",
|
||||
"summary": "Source origin/main 82ad96c is not covered by artifact source 7e29522: target changes since the artifact source touch artifact build inputs.",
|
||||
"summary": "Source origin/main 8e89409 is not covered by artifact source 7e29522: target changes since the artifact source touch artifact build inputs.",
|
||||
"evidence": [
|
||||
{
|
||||
"source": {
|
||||
"ref": "origin/main",
|
||||
"commitId": "82ad96c5c67c3906732a057d52395ec72851e4a9",
|
||||
"shortCommitId": "82ad96c"
|
||||
"commitId": "8e89409dda5d0b028de7f7b7b0e006a60bdd6d44",
|
||||
"shortCommitId": "8e89409"
|
||||
},
|
||||
"artifactSource": {
|
||||
"source": "artifact-publish-report",
|
||||
@@ -481,17 +493,21 @@
|
||||
"sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33",
|
||||
"sourceShortCommitId": "7e29522",
|
||||
"targetRef": "origin/main",
|
||||
"targetCommitId": "82ad96c5c67c3906732a057d52395ec72851e4a9",
|
||||
"targetShortCommitId": "82ad96c",
|
||||
"targetCommitId": "8e89409dda5d0b028de7f7b7b0e006a60bdd6d44",
|
||||
"targetShortCommitId": "8e89409",
|
||||
"mode": "artifact-input-drift",
|
||||
"covered": false,
|
||||
"artifactBuildInputChanged": true,
|
||||
"changedPathCount": 22,
|
||||
"changedPathCount": 48,
|
||||
"changedArtifactInputPaths": [
|
||||
"internal/dev-report-lifecycle.mjs",
|
||||
"internal/mvp-gate/summary.mjs",
|
||||
"internal/protocol/index.mjs",
|
||||
"package.json",
|
||||
"scripts/dev-artifact-publish.mjs",
|
||||
"web/hwlab-cloud-web/gate-summary.mjs"
|
||||
],
|
||||
"changedNonArtifactPathCount": 20,
|
||||
"changedNonArtifactPathCount": 42,
|
||||
"reason": "target changes since the artifact source touch artifact build inputs"
|
||||
},
|
||||
"deployManifest": {
|
||||
@@ -778,7 +794,7 @@
|
||||
"names": "unidesk-artifact-registry",
|
||||
"ports": "127.0.0.1:5000->5000/tcp",
|
||||
"state": "running",
|
||||
"status": "Up 28 hours"
|
||||
"status": "Up 30 hours"
|
||||
}
|
||||
],
|
||||
"pushAttempted": false,
|
||||
@@ -916,7 +932,7 @@
|
||||
{
|
||||
"version": "v1",
|
||||
"registryPrefix": "127.0.0.1:5000/hwlab",
|
||||
"generatedAt": "2026-05-22T05:35:38.863Z",
|
||||
"generatedAt": "2026-05-22T07:12:45.056Z",
|
||||
"classification": "degraded",
|
||||
"dimensions": {
|
||||
"processHttpAccess": {
|
||||
@@ -964,7 +980,7 @@
|
||||
"names": "unidesk-artifact-registry",
|
||||
"ports": "127.0.0.1:5000->5000/tcp",
|
||||
"state": "running",
|
||||
"status": "Up 28 hours"
|
||||
"status": "Up 30 hours"
|
||||
}
|
||||
],
|
||||
"pushAttempted": false,
|
||||
@@ -1406,7 +1422,7 @@
|
||||
"method": "GET",
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"body": "{\"serviceId\":\"hwlab-cloud-api\",\"environment\":\"dev\",\"status\":\"degraded\",\"service\":{\"id\":\"hwlab-cloud-api\",\"role\":\"cloud-api\",\"healthPath\":\"/health\",\"livePath\":\"/health/live\"},\"commit\":{\"id\":\"cb35ada\",\"source\":\"runtime-env\"},\"image\":{\"reference\":\"127.0.0.1:5000/hwlab/hwlab-cloud-api:cb35ada\",\"tag\":\"cb35ada\",\"digest\":\"unknown\"},\"endpoint\":\"http://74.48.78.17:16667\",\"observedAt\":\"2026-05-22T05:35:39.607Z\",\"db\":{\"contractVersion\":\"v1\",\"environment\":\"dev\",\"connected\":false,\"connectionChecked\":false,\"c"
|
||||
"body": "{\"serviceId\":\"hwlab-cloud-api\",\"environment\":\"dev\",\"status\":\"degraded\",\"service\":{\"id\":\"hwlab-cloud-api\",\"role\":\"cloud-api\",\"healthPath\":\"/health\",\"livePath\":\"/health/live\"},\"commit\":{\"id\":\"cb35ada\",\"source\":\"runtime-env\"},\"image\":{\"reference\":\"127.0.0.1:5000/hwlab/hwlab-cloud-api:cb35ada\",\"tag\":\"cb35ada\",\"digest\":\"unknown\"},\"endpoint\":\"http://74.48.78.17:16667\",\"observedAt\":\"2026-05-22T07:12:46.583Z\",\"db\":{\"contractVersion\":\"v1\",\"environment\":\"dev\",\"connected\":false,\"connectionChecked\":false,\"c"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1528,7 +1544,7 @@
|
||||
"status": "open",
|
||||
"type": "contract_blocker",
|
||||
"scope": "artifact-source-commit",
|
||||
"summary": "source commit origin/main 82ad96c is not covered by artifact source 7e29522; target changes since the artifact source touch artifact build inputs.",
|
||||
"summary": "source commit origin/main 8e89409 is not covered by artifact source 7e29522; target changes since the artifact source touch artifact build inputs.",
|
||||
"nextTask": "Refresh without fake digests using `node scripts/refresh-artifact-catalog.mjs --target-ref origin/main --blocked`, or after a successful publish run `node scripts/refresh-artifact-catalog.mjs --target-ref origin/main --publish-report reports/dev-gate/dev-artifacts.json`."
|
||||
},
|
||||
{
|
||||
@@ -1542,7 +1558,7 @@
|
||||
"status": "open",
|
||||
"type": "runtime_blocker",
|
||||
"scope": "dev-artifact-publish",
|
||||
"summary": "reports/dev-gate/dev-artifacts.json does not prove all required HWLAB service artifacts for origin/main 82ad96c; current status is published with 13/13 required services published and source states resolved: 13 source-present, 0 intentionally-disabled.",
|
||||
"summary": "reports/dev-gate/dev-artifacts.json does not prove all required HWLAB service artifacts for origin/main 8e89409; current status is published with 13/13 required services published and source states resolved: 13 source-present, 0 intentionally-disabled.",
|
||||
"nextTask": "Complete DEV artifact publishing for every enabled required HWLAB service at the artifact source commit, or prove the target commit has no artifact build input changes; keep disabled services marked not_published with reasons."
|
||||
},
|
||||
{
|
||||
@@ -1553,13 +1569,5 @@
|
||||
"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."
|
||||
}
|
||||
],
|
||||
"notes": "No PROD action, secret read, UniDesk runtime substitution, heavy e2e, browser e2e, runtime restart, or force push was performed.",
|
||||
"reportLifecycle": {
|
||||
"version": "v1",
|
||||
"state": "active",
|
||||
"activeEndpoint": "http://74.48.78.17:16667",
|
||||
"activeBrowserEndpoint": "http://74.48.78.17:16666",
|
||||
"deprecatedEndpoint": null,
|
||||
"summary": "Current read-only DEV preflight report; source, local, and dry-run checks are not DEV-LIVE acceptance."
|
||||
}
|
||||
"notes": "No PROD action, secret read, UniDesk runtime substitution, heavy e2e, browser e2e, runtime restart, or force push was performed."
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import path from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { activeReportLifecycle } from "../internal/dev-report-lifecycle.mjs";
|
||||
import {
|
||||
DEV_ENDPOINT,
|
||||
DEV_FRONTEND_ENDPOINT,
|
||||
@@ -299,6 +300,9 @@ function buildActiveReport({ fixture, probes, generatedAt, commitId }) {
|
||||
acceptanceLevel: "dev_m2_deploy_smoke",
|
||||
devOnly: true,
|
||||
prodDisabled: true,
|
||||
reportLifecycle: activeReportLifecycle(
|
||||
"Active M2 read-only public endpoint smoke; route reachability is not M5 DEV-LIVE acceptance."
|
||||
),
|
||||
status,
|
||||
generatedAt,
|
||||
endpoint: DEV_ENDPOINT,
|
||||
|
||||
@@ -113,15 +113,20 @@ async function run(command, args = [], options = {}) {
|
||||
timeout: options.timeoutMs ?? 5000,
|
||||
maxBuffer: 1024 * 1024
|
||||
});
|
||||
return {
|
||||
const probe = {
|
||||
ok: true,
|
||||
command: commandText(command, args),
|
||||
exitCode: 0,
|
||||
stdout: redacted(result.stdout.trim()),
|
||||
stderr: redacted(result.stderr.trim())
|
||||
};
|
||||
Object.defineProperty(probe, "rawStdout", {
|
||||
value: result.stdout.trim(),
|
||||
enumerable: false
|
||||
});
|
||||
return probe;
|
||||
} catch (error) {
|
||||
return {
|
||||
const probe = {
|
||||
ok: false,
|
||||
command: commandText(command, args),
|
||||
exitCode: typeof error.code === "number" ? error.code : 1,
|
||||
@@ -129,6 +134,11 @@ async function run(command, args = [], options = {}) {
|
||||
stderr: redacted(String(error.stderr ?? "").trim()),
|
||||
error: oneLine(error.message)
|
||||
};
|
||||
Object.defineProperty(probe, "rawStdout", {
|
||||
value: String(error.stdout ?? "").trim(),
|
||||
enumerable: false
|
||||
});
|
||||
return probe;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,9 +249,10 @@ async function collectSshBridge(binaries) {
|
||||
}
|
||||
|
||||
function parseJsonProbe(probe) {
|
||||
if (!probe.ok || !probe.stdout) return null;
|
||||
const raw = probe.rawStdout ?? probe.stdout;
|
||||
if (!probe.ok || !raw) return null;
|
||||
try {
|
||||
return JSON.parse(probe.stdout);
|
||||
return JSON.parse(raw);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
@@ -407,7 +418,7 @@ function buildBlockers({ binaries, kubeconfig, sshBridge, cluster }) {
|
||||
nextTask: "Mount a read-only kubeconfig for hwlab-dev, or document the approved k3s local kubeconfig path without exposing token material."
|
||||
});
|
||||
}
|
||||
if (!sshBridge.configured) {
|
||||
if (!sshBridge.configured && !cluster.readable) {
|
||||
addBlocker(blockers, {
|
||||
type: "environment_blocker",
|
||||
scope: "d601-maintenance-ssh-bridge",
|
||||
|
||||
@@ -5,7 +5,7 @@ import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { activeReportLifecycle, reportIsHistorical, reportLifecycleState } from "../../internal/dev-report-lifecycle.mjs";
|
||||
import { DEV_ENDPOINT, ENVIRONMENT_DEV, SERVICE_IDS } from "../../internal/protocol/index.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)), "../..");
|
||||
|
||||
@@ -42,6 +42,7 @@ const reportPaths = Object.freeze({
|
||||
devDeploy: "reports/dev-gate/dev-deploy-report.json",
|
||||
devArtifacts: "reports/dev-gate/dev-artifacts.json",
|
||||
devEdgeHealth: "reports/dev-gate/dev-edge-health.json",
|
||||
devM2Smoke: "reports/dev-gate/dev-m2-deploy-smoke-active.json",
|
||||
devM3Hardware: "reports/dev-gate/dev-m3-hardware-loop.json",
|
||||
devM4Agent: "reports/dev-gate/dev-m4-agent-loop.json",
|
||||
devM5Gate: "reports/dev-gate/dev-mvp-gate-report.json",
|
||||
@@ -189,6 +190,19 @@ function hasBlocker(blockers, predicate) {
|
||||
return blockers.some(predicate);
|
||||
}
|
||||
|
||||
function isRouteTransportBlocker(blocker) {
|
||||
const scope = blocker.scope ?? "";
|
||||
return scope === "m2-public-endpoints" ||
|
||||
scope === "dev-edge" ||
|
||||
scope === "dev-edge-health" ||
|
||||
scope === "hwlab-router" ||
|
||||
scope === "hwlab-tunnel-client" ||
|
||||
scope === "hwlab-edge-proxy" ||
|
||||
scope.includes("edge-frp") ||
|
||||
scope.includes("ingress") ||
|
||||
scope.includes("frp");
|
||||
}
|
||||
|
||||
function applyPriority(blocker) {
|
||||
if (blocker.scope === "base-image") {
|
||||
return {
|
||||
@@ -234,6 +248,16 @@ function applyPriority(blocker) {
|
||||
};
|
||||
}
|
||||
|
||||
if (blocker.scope === "hwlab-dev-readonly-rbac") {
|
||||
return {
|
||||
...blocker,
|
||||
priority: "P1",
|
||||
unblockOrder: 4,
|
||||
unblocks: [issue(34), issue(33), issue(36), issue(38), issue(39)],
|
||||
rationale: "D601 has a client path, but read-only hwlab-dev pods/services/configmaps must be observable before live acceptance evidence can be trusted."
|
||||
};
|
||||
}
|
||||
|
||||
if (blocker.scope.includes("cloud-api-db")) {
|
||||
return {
|
||||
...blocker,
|
||||
@@ -244,6 +268,16 @@ function applyPriority(blocker) {
|
||||
};
|
||||
}
|
||||
|
||||
if (blocker.scope === "db-live") {
|
||||
return {
|
||||
...blocker,
|
||||
priority: "P1",
|
||||
unblockOrder: 5,
|
||||
unblocks: [issue(37), issue(39)],
|
||||
rationale: "M4 and M5 cannot claim live agent or MVP evidence until cloud-api /health/live proves DB readiness with redacted live evidence."
|
||||
};
|
||||
}
|
||||
|
||||
if (isEdgeOrFrpBlocker(blocker)) {
|
||||
return {
|
||||
...blocker,
|
||||
@@ -254,6 +288,16 @@ function applyPriority(blocker) {
|
||||
};
|
||||
}
|
||||
|
||||
if (blocker.scope === "m3-hardware-loop-runtime") {
|
||||
return {
|
||||
...blocker,
|
||||
priority: "P0",
|
||||
unblockOrder: 6,
|
||||
unblocks: [issue(38), issue(39), issue(64)],
|
||||
rationale: "M3 remains blocked until the real DEV hardware trusted loop proves DO1 -> patch-panel -> DI1 with operation, trace, audit, and evidence identifiers."
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...blocker,
|
||||
priority: blocker.priority ?? "P3",
|
||||
@@ -264,11 +308,7 @@ function applyPriority(blocker) {
|
||||
}
|
||||
|
||||
function isEdgeOrFrpBlocker(blocker) {
|
||||
return blocker.scope.includes("edge") ||
|
||||
blocker.scope.includes("ingress") ||
|
||||
blocker.scope.includes("frp") ||
|
||||
blocker.summary.includes(":16667") ||
|
||||
blocker.summary.includes("16667");
|
||||
return isRouteTransportBlocker(blocker);
|
||||
}
|
||||
|
||||
function sourceEntry(entry) {
|
||||
@@ -309,9 +349,11 @@ function collectM2Evidence(reports) {
|
||||
const deploy = reports.devDeploy;
|
||||
const artifacts = reports.devArtifacts;
|
||||
const edge = reports.devEdgeHealth;
|
||||
const m2Smoke = reports.devM2Smoke;
|
||||
const d601 = reports.d601Observability;
|
||||
const artifactIdentity = preflight.artifactIdentity;
|
||||
const edgeLive = hasCurrentLiveEdgeEvidence(edge);
|
||||
const m2EndpointLive = hasCurrentM2EndpointEvidence(m2Smoke);
|
||||
|
||||
return [
|
||||
reportEvidence({
|
||||
@@ -352,6 +394,18 @@ function collectM2Evidence(reports) {
|
||||
commands: artifacts.validationCommands,
|
||||
summary: "Artifact publish preflight exists but is blocked before any real image publish."
|
||||
}),
|
||||
reportEvidence({
|
||||
milestone: "M2",
|
||||
level: m2EndpointLive ? "DEV-LIVE" : "BLOCKED",
|
||||
report: m2Smoke,
|
||||
status: m2Smoke.runtimeSmoke?.status ?? primaryStatus(m2Smoke),
|
||||
category: "public-entrypoints-read-only",
|
||||
evidence: publicEndpointEvidence(m2Smoke),
|
||||
commands: m2Smoke.devPreconditions?.commands ?? [],
|
||||
summary: m2EndpointLive
|
||||
? "Read-only probes prove the frozen public DEV entrypoints on :16666/:16667 are reachable; this is EDGE/ROUTE evidence only."
|
||||
: "The active M2 public endpoint smoke does not prove the frozen :16666/:16667 entrypoints."
|
||||
}),
|
||||
reportEvidence({
|
||||
milestone: "M2",
|
||||
level: edgeLive ? "DEV-LIVE" : "SOURCE",
|
||||
@@ -388,6 +442,7 @@ function collectM2Evidence(reports) {
|
||||
function collectM3Evidence(reports) {
|
||||
const m3 = reports.devM3Hardware;
|
||||
const m5Milestone = reports.devM5Gate.milestones?.find((item) => item.id === "M3");
|
||||
const liveSummary = m3TrustedLoopSummary(m3);
|
||||
return [
|
||||
reportEvidence({
|
||||
milestone: "M3",
|
||||
@@ -422,11 +477,20 @@ function collectM3Evidence(reports) {
|
||||
`evidenceId=${m3.liveOperation?.evidenceId ?? "not_observed"}`
|
||||
],
|
||||
commands: ["node scripts/dev-m3-hardware-loop-smoke.mjs --live --confirm-dev --confirmed-non-production"],
|
||||
summary: m3.liveOperation?.summary ?? "No live M3 hardware operation was observed."
|
||||
summary: liveSummary
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
function m3TrustedLoopSummary(m3Report) {
|
||||
const operationSummary = m3Report.liveOperation?.summary ?? "No live M3 hardware operation was observed.";
|
||||
const blockerSummary = m3Report.blockers?.find((blocker) => blocker.scope === "m3-hardware-loop-runtime")?.summary;
|
||||
if (statusIsPass(m3Report.liveOperation?.status) || !blockerSummary) {
|
||||
return operationSummary;
|
||||
}
|
||||
return `${operationSummary} Blocker: ${blockerSummary}.`;
|
||||
}
|
||||
|
||||
function collectM4Evidence(reports) {
|
||||
const m4 = reports.devM4Agent;
|
||||
return [
|
||||
@@ -537,6 +601,25 @@ function hasCurrentLiveEdgeEvidence(edgeReport) {
|
||||
publicHttp.some((probe) => probe.url === `${DEV_ENDPOINT}/health/live` && probe.ok === true && probe.status === 200);
|
||||
}
|
||||
|
||||
function hasCurrentM2EndpointEvidence(m2Report) {
|
||||
const probes = m2Report?.runtimeSmoke?.probes ?? [];
|
||||
return m2Report?.endpoint === DEV_ENDPOINT &&
|
||||
m2Report?.frontendEndpoint === DEV_FRONTEND_ENDPOINT &&
|
||||
m2Report?.runtimeSmoke?.mode === "live-read-only" &&
|
||||
m2Report?.runtimeSmoke?.status === "pass" &&
|
||||
probes.some((probe) => probe.url === `${DEV_ENDPOINT}/health` && probe.ok === true && probe.status === 200) &&
|
||||
probes.some((probe) => probe.url === `${DEV_ENDPOINT}/health/live` && probe.ok === true && probe.status === 200) &&
|
||||
probes.some((probe) => probe.url === `${DEV_FRONTEND_ENDPOINT}/` && probe.ok === true && probe.status === 200);
|
||||
}
|
||||
|
||||
function publicEndpointEvidence(m2Report) {
|
||||
return (m2Report?.runtimeSmoke?.probes ?? []).map((probe) => {
|
||||
const identity = probe.json?.serviceId ?? probe.title ?? "unknown";
|
||||
const status = probe.json?.status ?? (probe.ok ? "ok" : "failed");
|
||||
return `${probe.url} -> HTTP ${probe.status ?? "none"} identity=${identity} status=${status}`;
|
||||
});
|
||||
}
|
||||
|
||||
function isStaleLegacyIngressBlocker(blocker, sourceReport, reports) {
|
||||
const text = `${blocker.scope ?? ""} ${blocker.summary ?? ""}`;
|
||||
const staleLegacyPort = text.includes(deprecatedLegacyPublicEndpoint);
|
||||
@@ -571,6 +654,7 @@ function deriveMilestones(evidence, blockers) {
|
||||
const liveItems = items.filter((item) => item.level === "DEV-LIVE");
|
||||
const highestVisibleLevel = levels.findLast((level) => items.some((item) => item.level === level && level !== "BLOCKED" && statusHasEvidence(item.status))) ?? "BLOCKED";
|
||||
const hasPassingLive = liveItems.some((item) => statusIsPass(item.status));
|
||||
const hasBlockedEvidence = items.some((item) => item.level === "BLOCKED" && ["blocked", "failed", "degraded"].includes(item.status));
|
||||
const milestoneBlockers = blockers.filter((blocker) => {
|
||||
if (milestone === "M2") return blocker.unblocks.includes(issue(33)) || blocker.unblocks.includes(issue(35)) || blocker.unblocks.includes(issue(36));
|
||||
if (milestone === "M3") return blocker.unblocks.includes(issue(38));
|
||||
@@ -578,7 +662,7 @@ function deriveMilestones(evidence, blockers) {
|
||||
if (milestone === "M5") return blocker.unblocks.includes(issue(39));
|
||||
return false;
|
||||
});
|
||||
const status = milestoneBlockers.length > 0 || (liveItems.length > 0 && !hasPassingLive)
|
||||
const status = milestoneBlockers.length > 0 || hasBlockedEvidence || (liveItems.length > 0 && !hasPassingLive)
|
||||
? "blocked"
|
||||
: worstStatus(items.map((item) => item.status));
|
||||
|
||||
@@ -642,7 +726,16 @@ function buildDoD(reports, milestones, blockers) {
|
||||
const preflight = reports.devPreflight;
|
||||
const artifactIdentity = preflight.artifactIdentity;
|
||||
const d601 = reports.d601Observability;
|
||||
const d601HasClient = d601.environment?.binaries?.kubectl?.available === true ||
|
||||
d601.environment?.binaries?.k3s?.available === true;
|
||||
const edgeLive = hasCurrentLiveEdgeEvidence(reports.devEdgeHealth);
|
||||
const artifactCurrent = artifactIdentity.publishVerified === true &&
|
||||
artifactIdentity.targetCoverage?.covered !== false &&
|
||||
artifactIdentity.artifactCatalog?.matchesTarget !== false;
|
||||
const cloudApiDb = cloudApiDbStatus(reports);
|
||||
const dbReady = cloudApiDb.ready === true && cloudApiDb.connected === true && cloudApiDb.liveDbEvidence === true;
|
||||
const m3Live = statusIsPass(reports.devM3Hardware.liveOperation?.status);
|
||||
const m4Live = statusIsPass(reports.devM4Agent.livePreflight?.status);
|
||||
|
||||
return {
|
||||
status: blockers.length === 0 && milestones.every((item) => item.status === "pass") ? "green" : "blocked",
|
||||
@@ -662,9 +755,9 @@ function buildDoD(reports, milestones, blockers) {
|
||||
},
|
||||
{
|
||||
id: "artifact-publish-digests",
|
||||
status: artifactIdentity.publishVerified ? "pass" : "blocked",
|
||||
evidenceLevel: artifactIdentity.publishVerified ? "DEV-LIVE" : "BLOCKED",
|
||||
summary: `artifactState=${artifactIdentity.artifactCatalog.artifactState}, ciPublished=${artifactIdentity.artifactCatalog.ciPublished}, registryVerified=${artifactIdentity.artifactCatalog.registryVerified}, sha256=${artifactIdentity.artifactCatalog.digestCounts.sha256}, not_published=${artifactIdentity.artifactCatalog.digestCounts.notPublished}`
|
||||
status: artifactCurrent ? "pass" : "blocked",
|
||||
evidenceLevel: artifactCurrent ? "DEV-LIVE" : "BLOCKED",
|
||||
summary: `artifactState=${artifactIdentity.artifactCatalog.artifactState}, ciPublished=${artifactIdentity.artifactCatalog.ciPublished}, registryVerified=${artifactIdentity.artifactCatalog.registryVerified}, sha256=${artifactIdentity.artifactCatalog.digestCounts.sha256}, not_published=${artifactIdentity.artifactCatalog.digestCounts.notPublished}, targetCovered=${artifactIdentity.targetCoverage?.covered ?? "unknown"}`
|
||||
},
|
||||
{
|
||||
id: "d601-k3s-observability",
|
||||
@@ -672,21 +765,39 @@ function buildDoD(reports, milestones, blockers) {
|
||||
evidenceLevel: d601.cluster?.readable ? "DEV-LIVE" : "BLOCKED",
|
||||
summary: d601.cluster?.readable
|
||||
? "D601 hwlab-dev cluster was readable."
|
||||
: "D601 runner lacks kubectl/k3s/kubeconfig observability for hwlab-dev."
|
||||
: d601HasClient
|
||||
? "D601 kubectl/k3s clients are present, but read-only hwlab-dev pods/services/configmaps probes are still blocked."
|
||||
: "D601 runner lacks kubectl/k3s/kubeconfig observability for hwlab-dev."
|
||||
},
|
||||
{
|
||||
id: "dev-edge-frp-16667",
|
||||
status: edgeLive && !hasBlocker(blockers, isEdgeOrFrpBlocker) ? "pass" : "blocked",
|
||||
evidenceLevel: edgeLive && !hasBlocker(blockers, isEdgeOrFrpBlocker) ? "DEV-LIVE" : "BLOCKED",
|
||||
status: edgeLive && !hasBlocker(blockers, isRouteTransportBlocker) ? "pass" : "blocked",
|
||||
evidenceLevel: edgeLive && !hasBlocker(blockers, isRouteTransportBlocker) ? "DEV-LIVE" : "BLOCKED",
|
||||
summary: edgeLive
|
||||
? "Committed edge report proves read-only public HTTP on :16667 /health and /health/live; old preflight network probe blockers are stale."
|
||||
? "Committed edge report proves read-only public HTTP on :16667 /health and /health/live; this is route evidence, not DB/M3/M4/M5 acceptance."
|
||||
: "No committed report proves live HTTP 200/JSON on http://74.48.78.17:16667."
|
||||
},
|
||||
{
|
||||
id: "cloud-api-db-ready",
|
||||
status: hasBlocker(blockers, (blocker) => blocker.scope.includes("cloud-api-db")) ? "blocked" : "pass",
|
||||
evidenceLevel: "BLOCKED",
|
||||
summary: "Manifest-level DB env exists, but live DB health readiness is still blocked/missing."
|
||||
status: dbReady ? "pass" : "blocked",
|
||||
evidenceLevel: dbReady ? "DEV-LIVE" : "BLOCKED",
|
||||
summary: `cloud-api DB status=${cloudApiDb.status}; ready=${cloudApiDb.ready}; connected=${cloudApiDb.connected}; liveDbEvidence=${cloudApiDb.liveDbEvidence}.`
|
||||
},
|
||||
{
|
||||
id: "m3-hardware-trusted-loop",
|
||||
status: m3Live ? "pass" : "blocked",
|
||||
evidenceLevel: m3Live ? "DEV-LIVE" : "BLOCKED",
|
||||
summary: m3Live
|
||||
? "M3 hardware trusted loop has operation, trace, audit, and evidence identifiers."
|
||||
: "M3 trusted loop is blocked until res_boxsimu_1:DO1 -> patch-panel -> res_boxsimu_2:DI1 is proven with operation/trace/audit/evidence."
|
||||
},
|
||||
{
|
||||
id: "m4-agent-loop-live",
|
||||
status: m4Live ? "pass" : "blocked",
|
||||
evidenceLevel: m4Live ? "DEV-LIVE" : "BLOCKED",
|
||||
summary: m4Live
|
||||
? "M4 agent loop live preflight passed."
|
||||
: "M4 agent loop live path is blocked before accepted agent scheduling/evidence closure."
|
||||
},
|
||||
{
|
||||
id: "m5-mvp-dev-live",
|
||||
@@ -698,6 +809,99 @@ function buildDoD(reports, milestones, blockers) {
|
||||
};
|
||||
}
|
||||
|
||||
function cloudApiDbStatus(reports) {
|
||||
const db = reports.devEdgeHealth.edgeHealth?.contracts?.deploy?.cloudApiDb ?? {};
|
||||
return {
|
||||
status: db.status ?? "unknown",
|
||||
ready: db.ready === true,
|
||||
connected: db.connected === true || db.liveConnected === true,
|
||||
configReady: db.configReady === true,
|
||||
connectionChecked: db.connectionChecked === true,
|
||||
liveDbEvidence: db.liveDbEvidence === true
|
||||
};
|
||||
}
|
||||
|
||||
function buildCurrentDevLayering(reports, blockers) {
|
||||
const m2EndpointLive = hasCurrentM2EndpointEvidence(reports.devM2Smoke);
|
||||
const edgeLive = hasCurrentLiveEdgeEvidence(reports.devEdgeHealth);
|
||||
const cloudDb = cloudApiDbStatus(reports);
|
||||
const dbReady = cloudDb.ready && cloudDb.connected && cloudDb.liveDbEvidence;
|
||||
const m3Live = statusIsPass(reports.devM3Hardware.liveOperation?.status);
|
||||
const m4Live = statusIsPass(reports.devM4Agent.livePreflight?.status);
|
||||
const artifactIdentity = reports.devPreflight.artifactIdentity;
|
||||
const artifactCurrent = artifactIdentity.publishVerified === true &&
|
||||
artifactIdentity.targetCoverage?.covered !== false &&
|
||||
artifactIdentity.artifactCatalog?.matchesTarget !== false;
|
||||
const desired = reports.devDeploy.devDeployApply ?? {};
|
||||
const artifactSourceStates = artifactSourceStateCounts(reports.devArtifacts);
|
||||
|
||||
return {
|
||||
edgeRoute: {
|
||||
label: "EDGE/ROUTE live",
|
||||
status: m2EndpointLive || edgeLive ? "pass" : "blocked",
|
||||
evidenceLevel: m2EndpointLive || edgeLive ? "DEV-LIVE" : "BLOCKED",
|
||||
summary: m2EndpointLive
|
||||
? "16666 browser, 16667 /health, and 16667 /health/live returned accepted HWLAB DEV responses in the active M2 read-only smoke."
|
||||
: "No active read-only public endpoint report proves both 16666 and 16667.",
|
||||
evidence: publicEndpointEvidence(reports.devM2Smoke),
|
||||
nextRequired: "Keep this separated from DB readiness, M3/M4 loop evidence, and M5 acceptance."
|
||||
},
|
||||
dbLive: {
|
||||
label: "DB live/degraded",
|
||||
status: dbReady ? "pass" : "blocked",
|
||||
evidenceLevel: dbReady ? "DEV-LIVE" : "BLOCKED",
|
||||
summary: `cloud-api DB status=${cloudDb.status}; configReady=${cloudDb.configReady}; ready=${cloudDb.ready}; connected=${cloudDb.connected}; liveDbEvidence=${cloudDb.liveDbEvidence}.`,
|
||||
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."
|
||||
},
|
||||
m3HardwareTrustedLoop: {
|
||||
label: "M3 hardware trusted loop",
|
||||
status: m3Live ? "pass" : "blocked",
|
||||
evidenceLevel: m3Live ? "DEV-LIVE" : "BLOCKED",
|
||||
summary: m3TrustedLoopSummary(reports.devM3Hardware),
|
||||
evidence: [
|
||||
`operationId=${reports.devM3Hardware.liveOperation?.operationId ?? "not_observed"}`,
|
||||
`traceId=${reports.devM3Hardware.liveOperation?.traceId ?? "not_observed"}`,
|
||||
`auditId=${reports.devM3Hardware.liveOperation?.auditId ?? "not_observed"}`,
|
||||
`evidenceId=${reports.devM3Hardware.liveOperation?.evidenceId ?? "not_observed"}`
|
||||
],
|
||||
nextRequired: "Only a real DEV res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1 observation with operation/trace/audit/evidence can clear M3."
|
||||
},
|
||||
m4AgentLoop: {
|
||||
label: "M4 agent loop",
|
||||
status: m4Live ? "pass" : "blocked",
|
||||
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."
|
||||
},
|
||||
artifactDesiredStateSource: {
|
||||
label: "artifact/desired-state source",
|
||||
status: artifactCurrent && desired.conclusion?.status === "ready" ? "pass" : "blocked",
|
||||
evidenceLevel: artifactCurrent ? "SOURCE" : "BLOCKED",
|
||||
summary: `artifact targetCovered=${artifactIdentity.targetCoverage?.covered ?? "unknown"}; artifactSource=${short(artifactIdentity.artifactSource?.commitId)}; target=${short(artifactIdentity.source?.commitId)}; desiredApplyMode=${desired.mode ?? "unknown"}; mutationAttempted=${desired.mutationAttempted === true}.`,
|
||||
evidence: [
|
||||
`artifactState=${artifactIdentity.artifactCatalog?.artifactState ?? "unknown"}`,
|
||||
`sourceStates=${artifactSourceStates.sourcePresent} source-present, ${artifactSourceStates.intentionallyDisabled} intentionally-disabled`,
|
||||
`desiredState=${desired.conclusion?.status ?? "not_reported"} dry-run-only`
|
||||
],
|
||||
nextRequired: "Refresh artifact/source coverage for current origin/main and keep desired-state apply separate from read-only route proof."
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function artifactSourceStateCounts(artifactReport) {
|
||||
const counts = { sourcePresent: 0, intentionallyDisabled: 0 };
|
||||
for (const service of artifactReport.artifactPublish?.services ?? []) {
|
||||
if (service.sourceState === "source-present") {
|
||||
counts.sourcePresent += 1;
|
||||
} else if (service.sourceState === "intentionally-disabled") {
|
||||
counts.intentionallyDisabled += 1;
|
||||
}
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
function buildNextSteps(blockers) {
|
||||
const byOrder = new Map();
|
||||
for (const blocker of blockers) {
|
||||
@@ -737,6 +941,8 @@ function fallbackAction(scope) {
|
||||
if (scope.includes("artifact")) return "Publish DEV artifacts for every frozen HWLAB service and record immutable registry digests.";
|
||||
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 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 === "m3-hardware-loop-runtime") return "Prove the real DEV M3 trusted loop res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1 with operation, trace, audit, and evidence identifiers.";
|
||||
if (scope.includes("kubectl") || scope.includes("k3s")) return "Provide read-only kubectl/kubeconfig observability for hwlab-dev.";
|
||||
return "Resolve the blocker and attach source/local/dry-run/DEV-live evidence at the correct level.";
|
||||
}
|
||||
@@ -747,6 +953,8 @@ function evidenceRequiredFor(scope) {
|
||||
if (scope.includes("artifact") || scope === "ghcr") return "Artifact publish report with ciPublished=true, registryVerified=true, and sha256 digest for each frozen service ID.";
|
||||
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 and redacted secret references, without secret material.";
|
||||
if (scope === "db-live") return "Cloud API /health/live output with ready=true, connected=true, liveDbEvidence=true, and redacted secret references.";
|
||||
if (scope === "m3-hardware-loop-runtime") return "Operation, trace, audit, and evidence IDs from the real DEV DO1 -> patch-panel -> DI1 trusted loop.";
|
||||
if (scope.includes("edge") || scope.includes("ingress") || scope.includes("frp")) return "Read-only DEV route observation for :16667/frp/edge/router with HWLAB service identity and artifact identity.";
|
||||
return "A committed report with the exact evidence level and command used.";
|
||||
}
|
||||
@@ -787,6 +995,7 @@ export async function buildReport() {
|
||||
const blockers = collectBlockers(reports);
|
||||
const milestones = deriveMilestones(evidence, blockers);
|
||||
const dod = buildDoD(reports, milestones, blockers);
|
||||
const currentDevLayering = buildCurrentDevLayering(reports, blockers);
|
||||
const nextSteps = buildNextSteps(blockers);
|
||||
const report = {
|
||||
"$schema": "https://hwlab.pikastech.local/schemas/dev-m5-gate-aggregator-v2.schema.json",
|
||||
@@ -794,7 +1003,7 @@ export async function buildReport() {
|
||||
reportVersion: "v2",
|
||||
reportKind: "dev-m5-gate-aggregator",
|
||||
issue: issue(58),
|
||||
supports: [issue(7), issue(9), issue(31), issue(33), issue(34), issue(35), issue(36), issue(37), issue(38), issue(39), issue(46)],
|
||||
supports: [issue(7), issue(9), issue(23), issue(26), issue(31), issue(33), issue(34), issue(35), issue(36), issue(37), issue(38), issue(39), issue(46), issue(64)],
|
||||
generatedAt: new Date().toISOString(),
|
||||
generatedFromCommit: gitCommit(),
|
||||
environment: ENVIRONMENT_DEV,
|
||||
@@ -832,9 +1041,10 @@ export async function buildReport() {
|
||||
green: dod.green,
|
||||
reason: dod.green
|
||||
? "All #9 DEV DoD checks are green."
|
||||
: "SOURCE, LOCAL, DRY-RUN, and edge DEV-LIVE evidence exists, but acceptance is still blocked by artifact, observability, DB, and loop evidence gaps."
|
||||
: "EDGE/ROUTE DEV-LIVE evidence exists on :16666/:16667, but M5 remains blocked by artifact source drift, DB live degradation, missing M3 trusted loop operation evidence, and blocked M4 agent-loop preflight."
|
||||
},
|
||||
dod,
|
||||
currentDevLayering,
|
||||
milestones,
|
||||
evidence,
|
||||
levels: groupByLevel(evidence, blockers),
|
||||
@@ -862,6 +1072,9 @@ export function formatCheckSummary(report) {
|
||||
}
|
||||
|
||||
export function renderMarkdown(report) {
|
||||
const layeringLines = Object.entries(report.currentDevLayering)
|
||||
.map(([, layer]) => `| ${layer.label} | ${layer.status} | ${layer.evidenceLevel} | ${layer.summary} | ${layer.nextRequired} |`)
|
||||
.join("\n");
|
||||
const milestoneLines = report.milestones
|
||||
.map((milestone) => `| ${milestone.id} | ${milestone.status} | ${milestone.highestVisibleLevel} | ${milestone.liveEvidence} | ${milestone.summary} |`)
|
||||
.join("\n");
|
||||
@@ -885,6 +1098,12 @@ Scope: DEV only, report-only
|
||||
|
||||
${report.overall.reason}
|
||||
|
||||
## Current DEV Layering
|
||||
|
||||
| Layer | Status | Evidence level | Current conclusion | Required next proof |
|
||||
| --- | --- | --- | --- | --- |
|
||||
${layeringLines}
|
||||
|
||||
## Milestones
|
||||
|
||||
| Milestone | Status | Highest visible level | Live evidence | Summary |
|
||||
|
||||
Reference in New Issue
Block a user