fix: harden M3 simulator identities and wiring

This commit is contained in:
Code Queue Review
2026-05-23 04:30:01 +00:00
parent 82b1f9491e
commit c095d4d943
14 changed files with 416 additions and 18 deletions
+13 -3
View File
@@ -8,12 +8,22 @@ import {
import { createHealth, createBoxState, selectInstanceValue } from "../../internal/sim/model.mjs";
import { createJsonServer, listen, parsePort, readJson } from "../../internal/sim/http.mjs";
const boxId = selectInstanceValue(process.env.HWLAB_BOX_ID, "boxsim_alpha");
const boxId = selectInstanceValue(process.env.HWLAB_BOX_ID, "boxsim_alpha", {
requireIndexForMultiple: true,
label: "HWLAB_BOX_ID"
});
const gatewaySessionId = selectInstanceValue(
process.env.HWLAB_GATEWAY_SESSION_ID,
"gws_mvp_simu"
"gws_mvp_simu",
{
requireIndexForMultiple: true,
label: "HWLAB_GATEWAY_SESSION_ID"
}
);
const resourceId = selectInstanceValue(process.env.HWLAB_BOX_RESOURCE_ID, "");
const resourceId = selectInstanceValue(process.env.HWLAB_BOX_RESOURCE_ID, "", {
requireIndexForMultiple: true,
label: "HWLAB_BOX_RESOURCE_ID"
});
const port = parsePort(process.env.PORT, 7201);
const state = createBoxState({ boxId, gatewaySessionId });
if (resourceId) {
+12 -6
View File
@@ -9,7 +9,10 @@ import {
import { fetchJson, createJsonServer, listen, parsePort, readJson } from "../../internal/sim/http.mjs";
import { createRpcErrorBody, ERROR_CODES } from "../../internal/protocol/index.mjs";
const gatewayId = selectInstanceValue(process.env.HWLAB_GATEWAY_ID, "gateway-a");
const gatewayId = selectInstanceValue(process.env.HWLAB_GATEWAY_ID, "gateway-a", {
requireIndexForMultiple: true,
label: "HWLAB_GATEWAY_ID"
});
const port = parsePort(process.env.PORT, 7101);
const endpoint = `http://127.0.0.1:${port}`;
const boxUrls = parseBoxUrls(process.env.HWLAB_BOX_URLS);
@@ -117,9 +120,9 @@ function parseList(value) {
}
function parseBoxes({ resources, ids, urls, instanceScoped = false }) {
const explicitResources = instanceScoped ? selectScopedList(resources) : parseList(resources);
const explicitIds = instanceScoped ? selectScopedList(ids) : parseList(ids);
const explicitUrls = instanceScoped ? selectScopedList(urls.join(",")) : urls;
const explicitResources = instanceScoped ? selectScopedList(resources, "HWLAB_BOX_RESOURCES") : parseList(resources);
const explicitIds = instanceScoped ? selectScopedList(ids, "HWLAB_BOX_IDS") : parseList(ids);
const explicitUrls = instanceScoped ? selectScopedList(urls.join(","), "HWLAB_BOX_URLS") : urls;
const count = Math.max(explicitResources.length, explicitIds.length, explicitUrls.length, 1);
const parsed = [];
@@ -140,12 +143,15 @@ function parseBoxes({ resources, ids, urls, instanceScoped = false }) {
return parsed;
}
function selectScopedList(value) {
function selectScopedList(value, label = "scoped list") {
const items = parseList(value);
if (items.length <= 1) {
return items;
}
const selected = selectInstanceValue(items.join(","), "");
const selected = selectInstanceValue(items.join(","), "", {
requireIndexForMultiple: true,
label
});
return selected ? [selected] : [];
}
+12 -2
View File
@@ -13,12 +13,13 @@ const topology = createM3Topology();
const endpointMap = parseEndpointMap(process.env.HWLAB_PATCH_PANEL_ENDPOINT_MAP) ?? {
res_boxsimu_2: "http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201"
};
const manifestWiringConfig = parseWiringConfig(process.env.HWLAB_PATCH_PANEL_WIRING_CONFIG);
const runtime = wiringConfigPath
? await createPatchPanelRuntimeFromFile({ path: wiringConfigPath })
: createPatchPanelRuntime({
wiringConfig: topology.wiringConfig,
wiringConfig: manifestWiringConfig ?? topology.wiringConfig,
endpointMap,
configSource: "internal:m3-topology"
configSource: manifestWiringConfig ? "env:HWLAB_PATCH_PANEL_WIRING_CONFIG" : "internal:m3-topology"
});
const routes = new Map();
@@ -51,3 +52,12 @@ function parseEndpointMap(value) {
return JSON.parse(source);
}
function parseWiringConfig(value) {
const source = String(value ?? "").trim();
if (!source) {
return null;
}
return JSON.parse(source);
}
+8
View File
@@ -301,9 +301,17 @@
"healthPath": "/health/live",
"profile": "dev",
"replicas": 1,
"m3Route": {
"fromResourceId": "res_boxsimu_1",
"fromPort": "DO1",
"patchPanelServiceId": "hwlab-patch-panel",
"toResourceId": "res_boxsimu_2",
"toPort": "DI1"
},
"env": {
"HWLAB_PATCH_PANEL_MODE": "dev-skeleton",
"HWLAB_PATCH_PANEL_ENDPOINT_MAP": "{\"res_boxsimu_2\":\"http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201\"}",
"HWLAB_PATCH_PANEL_WIRING_CONFIG": "{\"wiringConfigId\":\"wir_m3_do1_di1\",\"projectId\":\"prj_m3_hardware_loop\",\"gatewaySessionId\":\"gws_gwsimu_1\",\"name\":\"M3 simulated DO1 to DI1 patch wiring\",\"status\":\"active\",\"connections\":[{\"from\":{\"resourceId\":\"res_boxsimu_1\",\"port\":\"DO1\"},\"to\":{\"resourceId\":\"res_boxsimu_2\",\"port\":\"DI1\"},\"mode\":\"exclusive\"}],\"constraints\":{\"propagation\":\"patch-panel-only\",\"localLoopbackSubstituteAllowed\":false},\"createdAt\":\"2026-05-21T00:00:00.000Z\",\"updatedAt\":\"2026-05-21T00:00:00.000Z\"}",
"HWLAB_ENVIRONMENT": "dev"
}
},
+27
View File
@@ -353,6 +353,33 @@
"type": "string",
"enum": ["indexed-workloads"]
},
"m3Route": {
"type": "object",
"required": ["fromResourceId", "fromPort", "patchPanelServiceId", "toResourceId", "toPort"],
"properties": {
"fromResourceId": {
"type": "string",
"const": "res_boxsimu_1"
},
"fromPort": {
"type": "string",
"const": "DO1"
},
"patchPanelServiceId": {
"type": "string",
"const": "hwlab-patch-panel"
},
"toResourceId": {
"type": "string",
"const": "res_boxsimu_2"
},
"toPort": {
"type": "string",
"const": "DI1"
}
},
"additionalProperties": false
},
"env": {
"type": "object",
"additionalProperties": {
+4
View File
@@ -543,6 +543,10 @@
{
"name": "HWLAB_PATCH_PANEL_ENDPOINT_MAP",
"value": "{\"res_boxsimu_2\":\"http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201\"}"
},
{
"name": "HWLAB_PATCH_PANEL_WIRING_CONFIG",
"value": "{\"wiringConfigId\":\"wir_m3_do1_di1\",\"projectId\":\"prj_m3_hardware_loop\",\"gatewaySessionId\":\"gws_gwsimu_1\",\"name\":\"M3 simulated DO1 to DI1 patch wiring\",\"status\":\"active\",\"connections\":[{\"from\":{\"resourceId\":\"res_boxsimu_1\",\"port\":\"DO1\"},\"to\":{\"resourceId\":\"res_boxsimu_2\",\"port\":\"DI1\"},\"mode\":\"exclusive\"}],\"constraints\":{\"propagation\":\"patch-panel-only\",\"localLoopbackSubstituteAllowed\":false},\"createdAt\":\"2026-05-21T00:00:00.000Z\",\"updatedAt\":\"2026-05-21T00:00:00.000Z\"}"
}
],
"readinessProbe": {
+53
View File
@@ -1,6 +1,7 @@
import assert from "node:assert/strict";
import test from "node:test";
import { createM3Topology } from "./fixture.mjs";
import { createWiringConfig } from "./model.mjs";
import { createPatchPanelRuntime } from "./runtime.mjs";
@@ -110,3 +111,55 @@ test("runtime reports missing target endpoint as readable diagnostics", async ()
assert.equal(result.deliveryCount, 1);
assert.ok(result.diagnostics.some((diagnostic) => diagnostic.code === "target_endpoint_missing"));
});
test("runtime applies first-class DEV M3 wiring config for res_boxsimu_1 DO1 to res_boxsimu_2 DI1", async () => {
const topology = createM3Topology();
const writes = [];
const runtime = createPatchPanelRuntime({
wiringConfig: topology.wiringConfig,
endpointMap: {
res_boxsimu_2: "http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201"
},
configSource: "env:HWLAB_PATCH_PANEL_WIRING_CONFIG",
now: () => "2026-05-21T00:00:01.000Z",
requestJson: async (url, body) => {
writes.push({ url, body });
return {
status: 200,
json: {
accepted: true
}
};
}
});
assert.equal(runtime.status().state, "active");
assert.equal(runtime.status().metadata.configSource, "env:HWLAB_PATCH_PANEL_WIRING_CONFIG");
assert.deepEqual(runtime.status().activeConnections, [
{
fromResourceId: "res_boxsimu_1",
fromPort: "DO1",
toResourceId: "res_boxsimu_2",
toPort: "DI1"
}
]);
const result = await runtime.routeSignal({
fromResourceId: "res_boxsimu_1",
fromPort: "DO1",
value: true
});
assert.equal(result.accepted, true);
assert.equal(result.propagatedBy, "hwlab-patch-panel");
assert.equal(result.deliveryCount, 1);
assert.equal(writes.length, 1);
assert.equal(
writes[0].url,
"http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201/internal/ports/write"
);
assert.equal(writes[0].body.port, "DI1");
assert.equal(writes[0].body.sourceResourceId, "res_boxsimu_1");
assert.equal(writes[0].body.sourcePort, "DO1");
assert.equal(writes[0].body.propagatedBy, "hwlab-patch-panel");
});
+17 -3
View File
@@ -92,7 +92,16 @@ function isoNow() {
return new Date().toISOString();
}
export function selectInstanceValue(value, fallback, { index = process.env.HWLAB_INSTANCE_INDEX } = {}) {
export function selectInstanceValue(
value,
fallback,
{
index = process.env.HWLAB_INSTANCE_INDEX,
hostname = process.env.HOSTNAME,
requireIndexForMultiple = false,
label = "instance value"
} = {}
) {
const items = String(value ?? "")
.split(",")
.map((item) => item.trim())
@@ -110,12 +119,17 @@ export function selectInstanceValue(value, fallback, { index = process.env.HWLAB
return items[parsedIndex];
}
const hostname = process.env.HOSTNAME ?? "";
const ordinal = Number.parseInt(hostname.match(/-(\d+)$/)?.[1] ?? "", 10);
const ordinal = Number.parseInt(String(hostname ?? "").match(/-(\d+)$/)?.[1] ?? "", 10);
if (Number.isInteger(ordinal) && ordinal >= 0 && ordinal < items.length) {
return items[ordinal];
}
if (requireIndexForMultiple) {
throw new Error(
`${label} declares ${items.length} indexed values, but neither HWLAB_INSTANCE_INDEX nor a StatefulSet ordinal HOSTNAME selected one`
);
}
return items[0];
}
+53
View File
@@ -0,0 +1,53 @@
import assert from "node:assert/strict";
import test from "node:test";
import { selectInstanceValue } from "./model.mjs";
test("indexed simulator identity selection uses explicit HWLAB_INSTANCE_INDEX", () => {
assert.equal(
selectInstanceValue("boxsimu_1,boxsimu_2", "fallback", {
index: "1",
hostname: "deployment-shaped-pod",
requireIndexForMultiple: true,
label: "HWLAB_BOX_ID"
}),
"boxsimu_2"
);
});
test("indexed simulator identity selection uses StatefulSet hostname ordinal", () => {
assert.equal(
selectInstanceValue("res_boxsimu_1,res_boxsimu_2", "fallback", {
index: "",
hostname: "hwlab-box-simu-1",
requireIndexForMultiple: true,
label: "HWLAB_BOX_RESOURCE_ID"
}),
"res_boxsimu_2"
);
});
test("multi-value simulator identities fail closed without an instance index", () => {
assert.throws(
() =>
selectInstanceValue("gwsimu_1,gwsimu_2", "fallback", {
index: "",
hostname: "hwlab-gateway-simu-random-suffix",
requireIndexForMultiple: true,
label: "HWLAB_GATEWAY_ID"
}),
/HWLAB_GATEWAY_ID declares 2 indexed values/u
);
});
test("single simulator identity value still works without an index", () => {
assert.equal(
selectInstanceValue("boxsimu_1", "fallback", {
index: "",
hostname: "deployment-shaped-pod",
requireIndexForMultiple: true,
label: "HWLAB_BOX_ID"
}),
"boxsimu_1"
);
});
+1 -1
View File
@@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"validate": "node scripts/validate-contract.mjs && node scripts/deploy-contract-plan.mjs --check && node scripts/deploy-desired-state-plan.mjs --check && node scripts/dev-runtime-migration.mjs --check && node --test scripts/artifact-runtime-readiness-guard.test.mjs",
"check": "node --check internal/protocol/index.mjs && node --check internal/agent/index.mjs && node --check internal/agent/runtime.mjs && node --check internal/audit/index.mjs && node --check internal/db/runtime-store.mjs && node --check internal/db/runtime-store.test.mjs && node --check internal/mvp-gate/summary.mjs && node --check internal/cloud/db-contract.mjs && node --check internal/cloud/code-agent-contract.mjs && node --check internal/cloud/json-rpc.mjs && node --check internal/cloud/code-agent-chat.mjs && node --check internal/cloud/server.mjs && node --check internal/sim/model.mjs && node --check internal/sim/http.mjs && node --check internal/sim/l2-runtime.mjs && node --check internal/patchpanel/model.mjs && node --check internal/patchpanel/runtime.mjs && node --check cmd/hwlab-cloud-api/main.mjs && node --check cmd/hwlab-agent-mgr/main.mjs && node --check cmd/hwlab-agent-worker/main.mjs && node --check cmd/hwlab-box-simu/main.mjs && node --check cmd/hwlab-gateway-simu/main.mjs && node --check scripts/cloud-api-runtime-smoke.mjs && node --check scripts/code-agent-chat-smoke.mjs && node --check scripts/dev-edge-health-smoke.mjs && node --check scripts/src/dev-edge-health-smoke-lib.mjs && node --check scripts/validate-contract.mjs && node --check scripts/deploy-desired-state-plan.mjs && node --check scripts/src/deploy-desired-state-plan.mjs && node --check scripts/artifact-runtime-readiness-guard.mjs && node --check scripts/src/artifact-runtime-readiness-guard.mjs && node --check scripts/artifact-runtime-readiness-guard.test.mjs && node --check scripts/report-lifecycle.mjs && node --check scripts/report-lifecycle.test.mjs && node --check scripts/validate-dev-gate-report.mjs && node --check scripts/dev-m3-hardware-loop-smoke.test.mjs && node --check scripts/dev-cloud-workbench-smoke.test.mjs && node --check scripts/validate-dev-m3-cardinality.mjs && node --check scripts/validate-artifact-catalog.mjs && node --check scripts/refresh-artifact-catalog.mjs && node --check scripts/dev-artifact-publish.mjs && node --check scripts/src/dev-artifact-services.mjs && node --check scripts/src/registry-capabilities.mjs && node --check scripts/preflight-dev-base-image.mjs && node --check scripts/src/dev-base-image-preflight.mjs && node --check scripts/dev-evidence-blocker-aggregator.mjs && node --check scripts/src/dev-evidence-blocker-aggregator.mjs && node --check scripts/d601-k3s-readonly-observability.mjs && node --check scripts/src/d601-k3s-readonly-observability.mjs && node --check scripts/dev-runtime-migration.mjs && node --check scripts/src/dev-runtime-migration.mjs && node --check scripts/src/dev-runtime-migration.test.mjs && node --check scripts/l2-runtime-contract-smoke.mjs && node --check scripts/patch-panel-runtime-smoke.mjs && node --check scripts/export-web-gate-summary.mjs && node --check scripts/l6-cli-web-smoke.mjs && node --check tools/hwlab-cli/bin/hwlab-cli.mjs && node --check tools/hwlab-cli/lib/cli.mjs && node --check web/hwlab-cloud-web/app.mjs && node --check web/hwlab-cloud-web/gate-summary.mjs && node --check web/hwlab-cloud-web/scripts/check.mjs && node --check web/hwlab-cloud-web/scripts/build.mjs && node --check web/hwlab-cloud-web/scripts/dist-contract.mjs && node scripts/validate-contract.mjs && node scripts/validate-dev-gate-report.mjs && node scripts/report-lifecycle.test.mjs && node scripts/validate-dev-m3-cardinality.mjs && node scripts/validate-artifact-catalog.mjs && node scripts/deploy-desired-state-plan.mjs --check && node scripts/dev-runtime-migration.mjs --check && node scripts/dev-evidence-blocker-aggregator.mjs --check && node scripts/l2-runtime-contract-smoke.mjs && node scripts/l6-cli-web-smoke.mjs && node web/hwlab-cloud-web/scripts/check.mjs && node scripts/code-agent-chat-smoke.mjs && node --test scripts/artifact-runtime-readiness-guard.test.mjs scripts/dev-m3-hardware-loop-smoke.test.mjs scripts/dev-cloud-workbench-smoke.test.mjs scripts/deploy-desired-state-plan.test.mjs scripts/src/dev-deploy-apply.test.mjs scripts/src/dev-runtime-migration.test.mjs internal/agent/index.test.mjs internal/audit/index.test.mjs internal/db/schema.test.mjs internal/db/runtime-store.test.mjs internal/cloud/json-rpc.test.mjs internal/cloud/server.test.mjs internal/patchpanel/model.test.mjs internal/patchpanel/runtime.test.mjs && node scripts/cloud-api-runtime-smoke.mjs && sh -n scripts/bootstrap-skills.sh scripts/worker-entrypoint.sh",
"check": "node --check internal/protocol/index.mjs && node --check internal/agent/index.mjs && node --check internal/agent/runtime.mjs && node --check internal/audit/index.mjs && node --check internal/db/runtime-store.mjs && node --check internal/db/runtime-store.test.mjs && node --check internal/mvp-gate/summary.mjs && node --check internal/cloud/db-contract.mjs && node --check internal/cloud/code-agent-contract.mjs && node --check internal/cloud/json-rpc.mjs && node --check internal/cloud/code-agent-chat.mjs && node --check internal/cloud/server.mjs && node --check internal/sim/model.mjs && node --check internal/sim/model.test.mjs && node --check internal/sim/http.mjs && node --check internal/sim/l2-runtime.mjs && node --check internal/patchpanel/model.mjs && node --check internal/patchpanel/runtime.mjs && node --check cmd/hwlab-cloud-api/main.mjs && node --check cmd/hwlab-agent-mgr/main.mjs && node --check cmd/hwlab-agent-worker/main.mjs && node --check cmd/hwlab-box-simu/main.mjs && node --check cmd/hwlab-gateway-simu/main.mjs && node --check scripts/cloud-api-runtime-smoke.mjs && node --check scripts/code-agent-chat-smoke.mjs && node --check scripts/dev-edge-health-smoke.mjs && node --check scripts/src/dev-edge-health-smoke-lib.mjs && node --check scripts/validate-contract.mjs && node --check scripts/deploy-desired-state-plan.mjs && node --check scripts/src/deploy-desired-state-plan.mjs && node --check scripts/artifact-runtime-readiness-guard.mjs && node --check scripts/src/artifact-runtime-readiness-guard.mjs && node --check scripts/artifact-runtime-readiness-guard.test.mjs && node --check scripts/report-lifecycle.mjs && node --check scripts/report-lifecycle.test.mjs && node --check scripts/validate-dev-gate-report.mjs && node --check scripts/dev-m3-hardware-loop-smoke.test.mjs && node --check scripts/dev-cloud-workbench-smoke.test.mjs && node --check scripts/validate-dev-m3-cardinality.mjs && node --check scripts/validate-artifact-catalog.mjs && node --check scripts/refresh-artifact-catalog.mjs && node --check scripts/dev-artifact-publish.mjs && node --check scripts/src/dev-artifact-services.mjs && node --check scripts/src/registry-capabilities.mjs && node --check scripts/preflight-dev-base-image.mjs && node --check scripts/src/dev-base-image-preflight.mjs && node --check scripts/dev-evidence-blocker-aggregator.mjs && node --check scripts/src/dev-evidence-blocker-aggregator.mjs && node --check scripts/d601-k3s-readonly-observability.mjs && node --check scripts/src/d601-k3s-readonly-observability.mjs && node --check scripts/dev-runtime-migration.mjs && node --check scripts/src/dev-runtime-migration.mjs && node --check scripts/src/dev-runtime-migration.test.mjs && node --check scripts/l2-runtime-contract-smoke.mjs && node --check scripts/patch-panel-runtime-smoke.mjs && node --check scripts/export-web-gate-summary.mjs && node --check scripts/l6-cli-web-smoke.mjs && node --check tools/hwlab-cli/bin/hwlab-cli.mjs && node --check tools/hwlab-cli/lib/cli.mjs && node --check web/hwlab-cloud-web/app.mjs && node --check web/hwlab-cloud-web/gate-summary.mjs && node --check web/hwlab-cloud-web/scripts/check.mjs && node --check web/hwlab-cloud-web/scripts/build.mjs && node --check web/hwlab-cloud-web/scripts/dist-contract.mjs && node scripts/validate-contract.mjs && node scripts/validate-dev-gate-report.mjs && node scripts/report-lifecycle.test.mjs && node scripts/validate-dev-m3-cardinality.mjs && node scripts/validate-artifact-catalog.mjs && node scripts/deploy-desired-state-plan.mjs --check && node scripts/dev-runtime-migration.mjs --check && node scripts/dev-evidence-blocker-aggregator.mjs --check && node scripts/l2-runtime-contract-smoke.mjs && node scripts/l6-cli-web-smoke.mjs && node web/hwlab-cloud-web/scripts/check.mjs && node scripts/code-agent-chat-smoke.mjs && node --test scripts/artifact-runtime-readiness-guard.test.mjs scripts/dev-m3-hardware-loop-smoke.test.mjs scripts/dev-cloud-workbench-smoke.test.mjs scripts/deploy-desired-state-plan.test.mjs scripts/src/dev-deploy-apply.test.mjs scripts/src/dev-runtime-migration.test.mjs internal/agent/index.test.mjs internal/audit/index.test.mjs internal/db/schema.test.mjs internal/db/runtime-store.test.mjs internal/cloud/json-rpc.test.mjs internal/cloud/server.test.mjs internal/sim/model.test.mjs internal/patchpanel/model.test.mjs internal/patchpanel/runtime.test.mjs && node scripts/cloud-api-runtime-smoke.mjs && sh -n scripts/bootstrap-skills.sh scripts/worker-entrypoint.sh",
"dev-base-image:preflight": "node scripts/preflight-dev-base-image.mjs",
"cloud-api:smoke": "node scripts/cloud-api-runtime-smoke.mjs",
"m1:smoke": "node scripts/m1-contract-smoke.mjs",
+24 -2
View File
@@ -419,6 +419,21 @@ async function collectReadOnlySupplementalEvidence() {
const patchPanelEndpointMap = patchPanel?.env.HWLAB_PATCH_PANEL_ENDPOINT_MAP
? JSON.parse(patchPanel.env.HWLAB_PATCH_PANEL_ENDPOINT_MAP)
: {};
const patchPanelWiringConfig = patchPanel?.env.HWLAB_PATCH_PANEL_WIRING_CONFIG
? JSON.parse(patchPanel.env.HWLAB_PATCH_PANEL_WIRING_CONFIG)
: null;
const patchPanelHasManifestRoute =
patchPanelWiringConfig?.status === "active" &&
patchPanelWiringConfig?.constraints?.propagation === "patch-panel-only" &&
patchPanelWiringConfig?.constraints?.localLoopbackSubstituteAllowed === false &&
(patchPanelWiringConfig?.connections ?? []).some(
(connection) =>
connection.from?.resourceId === requiredM3Connection.fromResourceId &&
connection.from?.port === requiredM3Connection.fromPort &&
connection.to?.resourceId === requiredM3Connection.toResourceId &&
connection.to?.port === requiredM3Connection.toPort &&
connection.mode === "exclusive"
);
const boxReadyForM3 =
box?.kind === "StatefulSet" &&
box?.replicas === 2 &&
@@ -436,7 +451,13 @@ async function collectReadOnlySupplementalEvidence() {
]);
const patchPanelReadyForM3 =
patchPanel?.replicas === 1 &&
patchPanelEndpointMap.res_boxsimu_2 === "http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201";
patchPanel?.m3Route?.fromResourceId === requiredM3Connection.fromResourceId &&
patchPanel?.m3Route?.fromPort === requiredM3Connection.fromPort &&
patchPanel?.m3Route?.patchPanelServiceId === "hwlab-patch-panel" &&
patchPanel?.m3Route?.toResourceId === requiredM3Connection.toResourceId &&
patchPanel?.m3Route?.toPort === requiredM3Connection.toPort &&
patchPanelEndpointMap.res_boxsimu_2 === "http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201" &&
patchPanelHasManifestRoute;
const manifestReadyForM3 = boxReadyForM3 && gatewayReadyForM3 && patchPanelReadyForM3 && hasInstanceServices;
return [
@@ -466,7 +487,8 @@ async function collectReadOnlySupplementalEvidence() {
},
"hwlab-patch-panel": {
replicas: patchPanel?.replicas ?? null,
env: patchPanel?.env ?? null
env: patchPanel?.env ?? null,
firstClassWiringConfig: patchPanelWiringConfig
},
instanceServices: [...serviceMap.keys()].filter((name) =>
name.startsWith("hwlab-box-simu-") || name.startsWith("hwlab-gateway-simu-")
+74
View File
@@ -144,9 +144,11 @@ function validateSource(ctx, manifest) {
const cloudApi = servicesById.get("hwlab-cloud-api");
const tunnelClient = servicesById.get("hwlab-tunnel-client");
const cli = servicesById.get("hwlab-cli");
const patchPanel = servicesById.get("hwlab-patch-panel");
expectEqual(ctx, cloudApi?.env?.HWLAB_PUBLIC_ENDPOINT, endpoints.api?.url, "$.services.hwlab-cloud-api.env.HWLAB_PUBLIC_ENDPOINT", "cloud API public endpoint env");
validateCloudApiDbSource(ctx, cloudApi?.env ?? {});
validateCloudApiCodeAgentSource(ctx, cloudApi?.env ?? {});
validateM3PatchPanelSource(ctx, patchPanel);
expectEqual(ctx, cli?.env?.HWLAB_CLI_ENDPOINT, endpoints.api?.url, "$.services.hwlab-cli.env.HWLAB_CLI_ENDPOINT", "CLI endpoint env");
expectEqual(ctx, tunnelClient?.env?.HWLAB_FRP_PUBLIC_PORT, String(expectedPorts.api), "$.services.hwlab-tunnel-client.env.HWLAB_FRP_PUBLIC_PORT", "tunnel API public port env");
expectEqual(ctx, tunnelClient?.env?.HWLAB_FRP_WEB_PUBLIC_PORT, String(expectedPorts.frontend), "$.services.hwlab-tunnel-client.env.HWLAB_FRP_WEB_PUBLIC_PORT", "tunnel frontend public port env");
@@ -189,6 +191,78 @@ function validateSource(ctx, manifest) {
return { manifest, endpoints, servicesById, proxies, serviceMappings, healthPath: manifest.health?.path };
}
function requiredM3Route() {
return {
fromResourceId: "res_boxsimu_1",
fromPort: "DO1",
patchPanelServiceId: "hwlab-patch-panel",
toResourceId: "res_boxsimu_2",
toPort: "DI1"
};
}
function parseJsonEnv(ctx, value, jsonPath, label) {
try {
return JSON.parse(String(value ?? ""));
} catch (error) {
addDiagnostic(ctx, "invalid_json", jsonPath, `${label} must be JSON`, {
actual: error instanceof Error ? error.message : String(error)
});
return null;
}
}
function validateM3PatchPanelSource(ctx, patchPanel) {
const route = requiredM3Route();
expectEqual(ctx, patchPanel?.m3Route?.fromResourceId, route.fromResourceId, "$.services.hwlab-patch-panel.m3Route.fromResourceId", "patch panel first-class M3 source resource");
expectEqual(ctx, patchPanel?.m3Route?.fromPort, route.fromPort, "$.services.hwlab-patch-panel.m3Route.fromPort", "patch panel first-class M3 source port");
expectEqual(ctx, patchPanel?.m3Route?.patchPanelServiceId, route.patchPanelServiceId, "$.services.hwlab-patch-panel.m3Route.patchPanelServiceId", "patch panel first-class M3 route owner");
expectEqual(ctx, patchPanel?.m3Route?.toResourceId, route.toResourceId, "$.services.hwlab-patch-panel.m3Route.toResourceId", "patch panel first-class M3 target resource");
expectEqual(ctx, patchPanel?.m3Route?.toPort, route.toPort, "$.services.hwlab-patch-panel.m3Route.toPort", "patch panel first-class M3 target port");
const endpointMap = parseJsonEnv(
ctx,
patchPanel?.env?.HWLAB_PATCH_PANEL_ENDPOINT_MAP,
"$.services.hwlab-patch-panel.env.HWLAB_PATCH_PANEL_ENDPOINT_MAP",
"patch panel endpoint map"
);
expectEqual(
ctx,
endpointMap?.res_boxsimu_2,
"http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201",
"$.services.hwlab-patch-panel.env.HWLAB_PATCH_PANEL_ENDPOINT_MAP.res_boxsimu_2",
"patch panel M3 target endpoint"
);
const wiring = parseJsonEnv(
ctx,
patchPanel?.env?.HWLAB_PATCH_PANEL_WIRING_CONFIG,
"$.services.hwlab-patch-panel.env.HWLAB_PATCH_PANEL_WIRING_CONFIG",
"patch panel wiring config"
);
const connection = wiring?.connections?.[0];
expectEqual(ctx, wiring?.status, "active", "$.services.hwlab-patch-panel.env.HWLAB_PATCH_PANEL_WIRING_CONFIG.status", "patch panel M3 wiring status");
expectEqual(
ctx,
wiring?.constraints?.propagation,
"patch-panel-only",
"$.services.hwlab-patch-panel.env.HWLAB_PATCH_PANEL_WIRING_CONFIG.constraints.propagation",
"patch panel M3 wiring propagation"
);
expectEqual(
ctx,
wiring?.constraints?.localLoopbackSubstituteAllowed,
false,
"$.services.hwlab-patch-panel.env.HWLAB_PATCH_PANEL_WIRING_CONFIG.constraints.localLoopbackSubstituteAllowed",
"patch panel M3 wiring forbids loopback substitute"
);
expectEqual(ctx, connection?.from?.resourceId, route.fromResourceId, "$.services.hwlab-patch-panel.env.HWLAB_PATCH_PANEL_WIRING_CONFIG.connections[0].from.resourceId", "patch panel M3 source resource");
expectEqual(ctx, connection?.from?.port, route.fromPort, "$.services.hwlab-patch-panel.env.HWLAB_PATCH_PANEL_WIRING_CONFIG.connections[0].from.port", "patch panel M3 source port");
expectEqual(ctx, connection?.to?.resourceId, route.toResourceId, "$.services.hwlab-patch-panel.env.HWLAB_PATCH_PANEL_WIRING_CONFIG.connections[0].to.resourceId", "patch panel M3 target resource");
expectEqual(ctx, connection?.to?.port, route.toPort, "$.services.hwlab-patch-panel.env.HWLAB_PATCH_PANEL_WIRING_CONFIG.connections[0].to.port", "patch panel M3 target port");
expectEqual(ctx, connection?.mode, "exclusive", "$.services.hwlab-patch-panel.env.HWLAB_PATCH_PANEL_WIRING_CONFIG.connections[0].mode", "patch panel M3 route mode");
}
function validateCloudApiDbSource(ctx, env) {
const alias = DEV_DB_ENV_CONTRACT.dns;
expectEqual(ctx, env.HWLAB_CLOUD_DB_URL, "secretRef:hwlab-cloud-api-dev-db/database-url", "$.services.hwlab-cloud-api.env.HWLAB_CLOUD_DB_URL", "cloud API DB URL Secret reference");
+46
View File
@@ -155,7 +155,9 @@ assert.equal(deployServicesById.has("hwlab-agent-worker"), true);
assert.equal(deployServicesById.has("hwlab-agent-skills"), true);
const cloudApi = deployServicesById.get("hwlab-cloud-api");
const patchPanel = deployServicesById.get("hwlab-patch-panel");
const cloudApiWorkloadEnv = getWorkloadEnv(k8sWorkloads, "hwlab-cloud-api");
const patchPanelWorkloadEnv = getWorkloadEnv(k8sWorkloads, "hwlab-patch-panel");
assert.equal(deployManifest.health.path, "/health/live", "deploy health source path");
assert.equal(deployManifest.publicEndpoints.frontend.url, "http://74.48.78.17:16666", "deploy frontend endpoint");
assert.equal(deployManifest.publicEndpoints.api.url, "http://74.48.78.17:16667", "deploy api endpoint");
@@ -208,6 +210,7 @@ assert.notEqual(
);
assert.equal(cloudApi.env.OPENAI_API_KEY, codeAgentSecretRefPlaceholder(), "cloud-api Code Agent OpenAI key must be a Secret reference placeholder");
assertCodeAgentProviderWorkloadContract(cloudApiWorkloadEnv);
assertM3PatchPanelDeployContract(patchPanel, patchPanelWorkloadEnv);
assertDbForbiddenInvalidHostContract();
assertValidationDoesNotExposeSecretValues(cloudApi.env, cloudApiWorkloadEnv);
@@ -262,6 +265,49 @@ function assertCodeAgentProviderWorkloadContract(env) {
assert.equal(apiKey?.key, secretRef.secretKey, "cloud-api workload Code Agent OpenAI Secret key");
}
function assertM3PatchPanelDeployContract(manifestService, workloadEnv) {
const route = {
fromResourceId: "res_boxsimu_1",
fromPort: "DO1",
patchPanelServiceId: "hwlab-patch-panel",
toResourceId: "res_boxsimu_2",
toPort: "DI1"
};
assert.deepEqual(manifestService.m3Route, route, "patch-panel deploy manifest first-class M3 route");
const manifestEndpointMap = JSON.parse(manifestService.env.HWLAB_PATCH_PANEL_ENDPOINT_MAP);
const workloadEndpointMap = JSON.parse(workloadEnv.HWLAB_PATCH_PANEL_ENDPOINT_MAP?.value ?? "{}");
assert.equal(
manifestEndpointMap.res_boxsimu_2,
"http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201",
"patch-panel deploy manifest M3 endpoint"
);
assert.deepEqual(workloadEndpointMap, manifestEndpointMap, "patch-panel workload M3 endpoint map");
const manifestWiring = JSON.parse(manifestService.env.HWLAB_PATCH_PANEL_WIRING_CONFIG);
const workloadWiring = JSON.parse(workloadEnv.HWLAB_PATCH_PANEL_WIRING_CONFIG?.value ?? "{}");
assert.deepEqual(workloadWiring, manifestWiring, "patch-panel workload first-class M3 wiring config");
assert.equal(manifestWiring.status, "active", "patch-panel M3 wiring status");
assert.equal(manifestWiring.constraints.propagation, "patch-panel-only", "patch-panel M3 wiring propagation");
assert.equal(
manifestWiring.constraints.localLoopbackSubstituteAllowed,
false,
"patch-panel M3 wiring loopback substitute"
);
assert.deepEqual(manifestWiring.connections, [
{
from: {
resourceId: route.fromResourceId,
port: route.fromPort
},
to: {
resourceId: route.toResourceId,
port: route.toPort
},
mode: "exclusive"
}
], "patch-panel M3 wiring route");
}
function assertDbForbiddenInvalidHostContract() {
const invalidHost = parseDbUrlContract("postgres://user:password@hwlab-dev-db.invalid:5432/hwlab");
assert.equal(invalidHost.ok, false, "DEV DB contract must reject .invalid runtime hosts");
+72 -1
View File
@@ -9,6 +9,32 @@ const namespace = "hwlab-dev";
const m3EndpointMap = Object.freeze({
res_boxsimu_2: "http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201"
});
const m3WiringConfig = Object.freeze({
wiringConfigId: "wir_m3_do1_di1",
projectId: "prj_m3_hardware_loop",
gatewaySessionId: "gws_gwsimu_1",
name: "M3 simulated DO1 to DI1 patch wiring",
status: "active",
connections: [
{
from: {
resourceId: "res_boxsimu_1",
port: "DO1"
},
to: {
resourceId: "res_boxsimu_2",
port: "DI1"
},
mode: "exclusive"
}
],
constraints: {
propagation: "patch-panel-only",
localLoopbackSubstituteAllowed: false
},
createdAt: "2026-05-21T00:00:00.000Z",
updatedAt: "2026-05-21T00:00:00.000Z"
});
const expected = Object.freeze({
"hwlab-box-simu": {
kind: "StatefulSet",
@@ -39,8 +65,16 @@ const expected = Object.freeze({
"hwlab-patch-panel": {
kind: "Deployment",
replicas: 1,
m3Route: {
fromResourceId: "res_boxsimu_1",
fromPort: "DO1",
patchPanelServiceId: "hwlab-patch-panel",
toResourceId: "res_boxsimu_2",
toPort: "DI1"
},
env: {
HWLAB_PATCH_PANEL_ENDPOINT_MAP: m3EndpointMap
HWLAB_PATCH_PANEL_ENDPOINT_MAP: m3EndpointMap,
HWLAB_PATCH_PANEL_WIRING_CONFIG: m3WiringConfig
}
}
});
@@ -197,6 +231,9 @@ function assertDeployManifestCardinality(deploy) {
assertSameMembers(service.instanceNames ?? [], requirement.instanceNames, `${serviceId} deploy instanceNames`);
assert.equal(service.identityStrategy, "indexed-workloads", `${serviceId} deploy identityStrategy`);
}
if (requirement.m3Route) {
assert.deepEqual(service.m3Route, requirement.m3Route, `${serviceId} deploy first-class M3 route`);
}
for (const [name, values] of Object.entries(requirement.env)) {
if (Array.isArray(values)) {
@@ -245,6 +282,25 @@ function assertM3FixtureWiring(topology) {
], "M3 fixture wiring config");
}
function assertM3RouteConfig(value, label) {
assert.deepEqual(value, m3WiringConfig, label);
assert.deepEqual(value.connections, [
{
from: {
resourceId: "res_boxsimu_1",
port: "DO1"
},
to: {
resourceId: "res_boxsimu_2",
port: "DI1"
},
mode: "exclusive"
}
], `${label} required route`);
assert.equal(value.constraints.propagation, "patch-panel-only", `${label} propagation`);
assert.equal(value.constraints.localLoopbackSubstituteAllowed, false, `${label} loopback substitute`);
}
const [deploy, workloads, services, topology] = await Promise.all([
readJson("deploy/deploy.json"),
readJson("deploy/k8s/base/workloads.yaml"),
@@ -252,10 +308,25 @@ const [deploy, workloads, services, topology] = await Promise.all([
readJson("fixtures/mvp/m3-hardware-loop/topology.json")
]);
const patchPanelWorkloadEnv = envMap(
workloadByName(workloads)
.get("hwlab-patch-panel")
?.spec?.template?.spec?.containers?.find((item) => item.name === "hwlab-patch-panel")
?.env
);
assertDeployManifestCardinality(deploy);
assertDeployManifestInstanceMappings(deploy);
assertDevK8sCardinality(workloads);
assertDevK8sInstanceServices(services);
assertM3FixtureWiring(topology);
assertM3RouteConfig(
parseEnvValue(patchPanelWorkloadEnv.HWLAB_PATCH_PANEL_WIRING_CONFIG),
"DEV workload first-class M3 patch-panel wiring config"
);
assertM3RouteConfig(
parseEnvValue(deploy.services.find((service) => service.serviceId === "hwlab-patch-panel")?.env?.HWLAB_PATCH_PANEL_WIRING_CONFIG),
"deploy manifest first-class M3 patch-panel wiring config"
);
console.log("validated DEV M3 manifest cardinality: 2 distinct box-simu, 2 distinct gateway-simu, 1 patch-panel, M3 DO1->DI1 wiring");