{ "$schema": "https://hwlab.pikastech.local/docs/dev-acceptance-checklist.schema.json", "$id": "https://hwlab.pikastech.local/docs/dev-acceptance-checklist.json", "matrixVersion": "v1", "issue": "pikasTech/HWLAB#22", "environment": "dev", "endpoint": "http://74.48.78.17:16667", "publicEndpoints": { "frontend": "http://74.48.78.17:16666", "api": "http://74.48.78.17:16667", "edge": "http://74.48.78.17:16667" }, "internalServicePorts": [ { "serviceId": "hwlab-cloud-api", "scope": "k3s-service", "port": 6667, "note": "Internal k3s service port only; not a public DEV endpoint." }, { "serviceId": "hwlab-edge-proxy", "scope": "k3s-service", "port": 6667, "note": "Internal k3s service/listen port only; public API/edge ingress is 16667." } ], "runtimeSubstitutePolicy": { "allowedExternalRoles": [ "scheduling", "ci", "cd" ], "forbiddenRuntimeSubstitutes": [ "unidesk-backend", "unidesk-provider-gateway", "unidesk-microservice-proxy" ] }, "manualAcceptance": { "referenceDoc": "docs/reference/m3-loop-rollout-runbook.md", "m3MvpPassCondition": "res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1 with two distinct box simulators, two distinct gateway simulators, one patch-panel participant, and linked operation, trace, audit, and evidence IDs.", "mustHaveEvidence": [ "two distinct box-simu identities", "two distinct gateway-simu identities", "patch-panel ownership of the route", "operationId", "traceId", "auditId", "evidenceId" ], "supportOnlySignals": [ "SOURCE", "LOCAL", "DRY-RUN", "fixture-only", "edge-only", "Cloud Web polish", "generic console work", "artifact report", "desired-state plan" ], "forbiddenM3PassSubstitutes": [ "box loopback", "front-end direct state edit", "UniDesk runtime substitution", "UniDesk proxy substitution" ], "failureClasses": [ "contract_blocker", "environment_blocker", "network_blocker", "runtime_blocker", "agent_blocker", "observability_blocker", "safety_blocker" ] }, "m3EvidenceClassification": { "p0Scope": "M3 virtual hardware trusted loop", "requiredCardinality": { "hwlab-box-simu": 2, "hwlab-gateway-simu": 2, "hwlab-patch-panel": 1 }, "m3Live": { "requiredLink": "DO1 -> hwlab-patch-panel -> DI1", "requiredIdentifiers": [ "operationId", "traceId", "auditId", "evidenceId" ], "classification": "DEV-LIVE" }, "m3Support": [ "endpoint-freeze", "read-only-edge-curl", "source-contract", "static-manifest-cardinality", "local-smoke", "dry-run-fixture" ], "nonP0": [ "SOURCE", "LOCAL", "DRY-RUN", "fixture-only", "edge-only-diagnostic" ], "promotionRule": "Do not classify M3 support or non-P0 evidence as DEV-LIVE unless the traced DO1 -> hwlab-patch-panel -> DI1 path is observed." }, "artifactObservabilityFields": [ "serviceId", "commitId", "image", "tag", "digest", "buildSource", "deployEnv", "healthTimestamp" ], "artifactDigestPolicy": { "requiredFor": [ "container-image", "digestable-build-output" ], "notApplicableValue": "not_applicable", "reasonRequiredWhenNotApplicable": true }, "route": [ { "id": "dev-api-edge-endpoint", "label": "DEV API/edge endpoint", "serviceId": "hwlab-edge-proxy", "endpoint": "http://74.48.78.17:16667", "role": "public-api-edge-ingress" }, { "id": "dev-frontend-endpoint", "label": "DEV frontend endpoint", "serviceId": "hwlab-cloud-web", "endpoint": "http://74.48.78.17:16666", "role": "public-frontend" }, { "id": "master-edge-proxy", "label": "master edge proxy", "serviceId": "hwlab-edge-proxy", "role": "edge-route" }, { "id": "frp", "label": "frp", "serviceId": "hwlab-tunnel-client", "role": "tunnel" }, { "id": "d601-router", "label": "D601 hwlab-dev/hwlab-router", "serviceId": "hwlab-router", "namespace": "hwlab-dev", "role": "dev-router" }, { "id": "cloud-api-web", "label": "cloud-api/web", "serviceIds": [ "hwlab-cloud-api", "hwlab-cloud-web" ], "role": "cloud-surface" } ], "healthContracts": [ { "component": "DEV ingress", "serviceId": "hwlab-edge-proxy", "probe": { "method": "GET", "url": "http://74.48.78.17:16667/health" }, "requiredFields": [ "serviceId", "deployEnv", "healthTimestamp" ], "success": "HTTP 2xx/3xx reaches the HWLAB DEV route and reports edge or downstream HWLAB identity.", "failure": "Timeout, non-HWLAB response, PROD route, or missing artifact fields." }, { "component": "master edge proxy", "serviceId": "hwlab-edge-proxy", "probe": { "type": "route-observation", "target": "http://74.48.78.17:16667" }, "requiredFields": [ "serviceId", "commitId", "image", "tag", "digest", "buildSource", "deployEnv", "healthTimestamp" ], "success": "DEV route forwards to frp and identifies its artifact.", "failure": "No DEV route, wrong port, or missing route observability." }, { "component": "frp", "serviceId": "hwlab-tunnel-client", "probe": { "type": "tunnel-status", "target": "D601 hwlab-dev/hwlab-router" }, "requiredFields": [ "serviceId", "commitId", "image", "tag", "digest", "buildSource", "deployEnv", "healthTimestamp" ], "success": "Tunnel is established from master edge to D601 router for DEV.", "failure": "Tunnel down, wrong target, or ambiguous service identity." }, { "component": "D601 router", "serviceId": "hwlab-router", "namespace": "hwlab-dev", "probe": { "type": "router-health", "target": "hwlab-dev/hwlab-router" }, "requiredFields": [ "serviceId", "commitId", "image", "tag", "digest", "buildSource", "deployEnv", "healthTimestamp" ], "success": "Router is live in dev and forwards only to HWLAB DEV services.", "failure": "Namespace mismatch, missing route, or stale health timestamp." }, { "component": "Cloud API", "serviceId": "hwlab-cloud-api", "probe": { "methods": [ "GET /health", "GET /live", "POST /rpc" ] }, "requiredFields": [ "serviceId", "deployEnv", "healthTimestamp" ], "success": "Health is valid JSON with HWLAB cloud API identity and dev environment.", "failure": "HTTP error, wrong service ID, wrong environment, or invalid JSON-RPC envelope." }, { "component": "Cloud Web", "serviceId": "hwlab-cloud-web", "probe": { "commands": [ "npm run web:m3-readonly", "npm run web:check", "npm run web:build" ] }, "requiredFields": [ "serviceId", "commitId", "buildSource", "deployEnv", "healthTimestamp" ], "success": "Static assets exist, build succeeds, runtime uses public frontend :16666 and API/edge :16667, and Cloud Web exposes only read-only M3 diagnostics for topology, route, health, blockers, and evidence summary.", "failure": "Endpoint drift, direct hardware write/control path, edge-only diagnostic promoted to M3 DEV-LIVE, or missing asset." }, { "component": "Agent manager", "serviceId": "hwlab-agent-mgr", "probe": { "type": "agent-scheduler-health" }, "requiredFields": [ "serviceId", "commitId", "image", "tag", "digest", "buildSource", "deployEnv", "healthTimestamp" ], "success": "Scheduler is live for dev and does not leave worker sessions open after smoke.", "failure": "Scheduler unavailable, wrong environment, or unbounded worker session." }, { "component": "Agent worker", "serviceId": "hwlab-agent-worker", "probe": { "type": "scoped-worker-health" }, "requiredFields": [ "serviceId", "commitId", "image", "tag", "digest", "buildSource", "deployEnv", "healthTimestamp" ], "success": "Worker session is scoped to the DEV project and emits trace/audit identifiers.", "failure": "Missing session identity, unsafe mutation, or cleanup failure." }, { "component": "Agent skills", "serviceId": "hwlab-agent-skills", "probe": { "type": "skill-bundle-version" }, "requiredFields": [ "serviceId", "commitId", "buildSource", "deployEnv", "healthTimestamp" ], "success": "Skill artifact is traceable to commit/build source and compatible with worker.", "failure": "Unversioned skill bundle or missing build source." }, { "component": "Gateway", "serviceId": "hwlab-gateway", "probe": { "methods": [ "GET /health/live", "GET /status" ] }, "requiredFields": [ "serviceId", "deployEnv", "healthTimestamp" ], "success": "Gateway boundary is live for dev and does not bypass patch-panel constraints.", "failure": "Hardware boundary unavailable, non-dev gateway, or direct box mutation." }, { "component": "Gateway simulator", "serviceId": "hwlab-gateway-simu", "probe": { "methods": [ "GET /health/live", "GET /status", "GET /boxes" ] }, "requiredFields": [ "serviceId", "deployEnv", "healthTimestamp" ], "success": "Simulator is live, lists expected box resources, and identifies the DEV project.", "failure": "Simulator down, box list missing, or stale timestamp." }, { "component": "Box simulator", "serviceId": "hwlab-box-simu", "probe": { "methods": [ "GET /health/live", "GET /status" ] }, "requiredFields": [ "serviceId", "deployEnv", "healthTimestamp" ], "success": "Simulator is live and reports resources, ports, and patch-panel-only propagation.", "failure": "Cross-device propagation outside patch panel or missing resource state." }, { "component": "Patch panel", "serviceId": "hwlab-patch-panel", "probe": { "methods": [ "GET /health/live", "GET /status", "GET /wiring" ] }, "requiredFields": [ "serviceId", "deployEnv", "healthTimestamp" ], "success": "Wiring config is parseable and patch-panel state owns routing decisions.", "failure": "Invalid topology, direct bypass path, or stale patch state." }, { "component": "CLI", "serviceId": "hwlab-cli", "probe": { "commands": [ "npm run cli:health", "npm run cli:dry-run" ] }, "requiredFields": [ "serviceId", "commitId", "buildSource", "deployEnv", "healthTimestamp" ], "success": "CLI uses the fixed DEV endpoint and dry-run states no DEV/PROD changes were made.", "failure": "Wrong endpoint, missing dry-run guard, or real mutation attempted." } ], "smokeSteps": [ { "id": "static-contract-parse", "order": 1, "probe": "Parse docs/dev-acceptance-checklist.json and confirm docs/dev-acceptance-matrix.md exists.", "successCriteria": "Checklist JSON parses and markdown file is present.", "failureCriteria": "Missing file or invalid JSON.", "blockerClass": "contract_blocker" }, { "id": "endpoint-freeze", "order": 2, "probe": "Compare documented and fixture DEV endpoints.", "successCriteria": "Public frontend is exactly http://74.48.78.17:16666; public API/edge is exactly http://74.48.78.17:16667; internal 6667 appears only as a k3s service/listen port.", "failureCriteria": "Any alternate public DEV endpoint, old public :6667, or PROD target.", "blockerClass": "environment_blocker" }, { "id": "dev-ingress-health", "order": 3, "probe": "Observe GET http://74.48.78.17:16667/health or recorded equivalent.", "successCriteria": "Request reaches HWLAB DEV route and returns HWLAB identity or accepted downstream health.", "failureCriteria": "Timeout, non-HWLAB target, or wrong port.", "blockerClass": "network_blocker" }, { "id": "edge-route", "order": 4, "probe": "Observe master edge proxy route table for DEV.", "successCriteria": "Edge route maps DEV endpoint to frp and records artifact identity.", "failureCriteria": "Missing route, stale route, or missing artifact identity.", "blockerClass": "network_blocker" }, { "id": "frp-tunnel", "order": 5, "probe": "Observe frp tunnel status.", "successCriteria": "Tunnel links master edge to D601 router for DEV.", "failureCriteria": "Tunnel down or target mismatch.", "blockerClass": "network_blocker" }, { "id": "d601-router", "order": 6, "probe": "Observe hwlab-dev/hwlab-router health/status.", "successCriteria": "Router is live and forwards only to HWLAB DEV services.", "failureCriteria": "Namespace mismatch or route bypass.", "blockerClass": "runtime_blocker" }, { "id": "cloud-surface", "order": 7, "probe": "Check cloud API health and cloud web endpoint configuration.", "successCriteria": "Cloud API health is valid on public :16667; web assets are served on public :16666 and point API traffic at :16667 or the internal k3s API service.", "failureCriteria": "Bad health, endpoint drift, or direct hardware control from web.", "blockerClass": "runtime_blocker" }, { "id": "gateway-sim-patch-panel", "order": 8, "probe": "Check gateway, gateway simulator, box simulator, and patch-panel health/status.", "successCriteria": "Health/status JSON is parseable and topology keeps routing under patch-panel ownership; for M3 live, a traced DO1 -> patch-panel -> DI1 operation exists.", "failureCriteria": "Missing health, invalid topology, bypass path, or any attempt to promote support/fixture evidence to M3 live.", "blockerClass": "runtime_blocker" }, { "id": "agent-runtime", "order": 9, "probe": "Check agent manager, worker, and skills contracts.", "successCriteria": "Agent artifacts are traceable; dry-run/scoped smoke emits session, trace, audit, and cleanup evidence.", "failureCriteria": "Missing traceability, unsafe mutation, or cleanup leak.", "blockerClass": "agent_blocker" }, { "id": "artifact-observability", "order": 10, "probe": "Verify required artifact observability fields for each accepted artifact.", "successCriteria": "Every accepted artifact has service ID, commit, image/tag/digest or reason, build source, env, and health timestamp.", "failureCriteria": "Missing required observability field.", "blockerClass": "observability_blocker" } ], "blockerClasses": [ { "id": "contract_blocker", "description": "Static contract, schema, checklist, or documentation cannot be parsed or contradicts frozen names." }, { "id": "environment_blocker", "description": "DEV endpoint, environment, namespace, or PROD boundary is wrong." }, { "id": "network_blocker", "description": "Master edge, frp, D601 route, or public DEV ingress cannot be observed or routes incorrectly." }, { "id": "runtime_blocker", "description": "HWLAB cloud, router, gateway, simulator, box simulator, or patch-panel runtime contract fails." }, { "id": "agent_blocker", "description": "Agent manager, worker, skills, trace, audit, or cleanup contract fails." }, { "id": "observability_blocker", "description": "Artifact identity, commit, image/tag/digest, build source, deploy env, or health timestamp is missing." }, { "id": "safety_blocker", "description": "A prohibited action was attempted: real deployment, PROD target, heavyweight e2e, secret read, force push, or UniDesk runtime substitution." } ], "prohibitedActions": [ "real-dev-deploy", "prod-deploy", "prod-smoke", "heavyweight-e2e", "secret-or-token-read", "force-push", "unidesk-runtime-substitution" ], "passRule": "All smoke steps must be successful or explicitly not applicable with a non-production reason, and every accepted artifact must satisfy the observability contract.", "failRule": "Fail on any blocker class, wrong endpoint, wrong environment, missing HWLAB service identity, missing artifact observability, invalid JSON, or prohibited action." }