fix: wire distinct M3 simulator identities

Merge PR #159 after Code Queue rebase and validation. Runner verified M3 cardinality, hardware-loop smoke, patch-panel runtime smoke, m3 readonly contract, and git diff check. This is source/config blocker removal only; it does not claim DEV-LIVE M3 pass.
This commit is contained in:
Lyon
2026-05-23 00:09:29 +08:00
committed by GitHub
parent 792262c5d1
commit 7a0648088c
20 changed files with 791 additions and 53 deletions
+59 -12
View File
@@ -17,6 +17,7 @@ const defaultReportPath = "reports/dev-gate/dev-m3-hardware-loop.json";
const namespace = "hwlab-dev";
const issue = "pikasTech/HWLAB#38";
const deployWorkloadsPath = "deploy/k8s/base/workloads.yaml";
const deployServicesPath = "deploy/k8s/base/services.yaml";
const requiredManifestCardinalityCommand = "node scripts/validate-dev-m3-cardinality.mjs";
const requiredM3BoxIds = Object.freeze(["boxsimu_1", "boxsimu_2"]);
const requiredM3BoxResources = Object.freeze(["res_boxsimu_1", "res_boxsimu_2"]);
@@ -26,6 +27,7 @@ const requiredM3Connection = Object.freeze({
toResourceId: "res_boxsimu_2",
toPort: "DI1"
});
const requiredM3GatewayIds = Object.freeze(["gwsimu_1", "gwsimu_2"]);
const runnerK3sKubeconfigPath = "/etc/rancher/k3s/k3s.yaml";
const serviceTargets = Object.freeze([
@@ -285,11 +287,25 @@ function hasSameMembers(actual, expected) {
return actual.length === expected.length && expected.every((item) => actual.includes(item));
}
function listItems(document) {
return document?.kind === "List" ? document.items ?? [] : [document].filter(Boolean);
}
function serviceByName(services) {
return new Map(
listItems(services)
.filter((item) => item?.kind === "Service")
.map((item) => [item.metadata?.name, item])
);
}
async function collectReadOnlySupplementalEvidence() {
const raw = await readFile(path.join(repoRoot, deployWorkloadsPath), "utf8");
const workloads = JSON.parse(raw);
const [workloads, services] = await Promise.all([
readFile(path.join(repoRoot, deployWorkloadsPath), "utf8").then(JSON.parse),
readFile(path.join(repoRoot, deployServicesPath), "utf8").then(JSON.parse)
]);
const targets = ["hwlab-box-simu", "hwlab-gateway-simu", "hwlab-patch-panel"];
const observed = workloads.items
const observed = listItems(workloads)
.filter((item) => targets.includes(item.metadata?.name))
.map((item) => {
const container = item.spec?.template?.spec?.containers?.[0] ?? {};
@@ -308,28 +324,56 @@ async function collectReadOnlySupplementalEvidence() {
const gateway = observed.find((item) => item.name === "hwlab-gateway-simu");
const patchPanel = observed.find((item) => item.name === "hwlab-patch-panel");
const boxIds = splitList(box?.env.HWLAB_BOX_ID);
const boxResourceIds = splitList(box?.env.HWLAB_BOX_RESOURCE_ID);
const boxGatewaySessionIds = splitList(box?.env.HWLAB_GATEWAY_SESSION_ID);
const gatewayIds = splitList(gateway?.env.HWLAB_GATEWAY_ID);
const gatewayBoxResources = splitList(gateway?.env.HWLAB_BOX_RESOURCES);
const boxReadyForM3 = box?.replicas === 2 && hasSameMembers(boxIds, requiredM3BoxIds);
const gatewayBoxUrls = splitList(gateway?.env.HWLAB_BOX_URLS);
const serviceMap = serviceByName(services);
const hasInstanceServices = [
"hwlab-box-simu-1",
"hwlab-box-simu-2",
"hwlab-gateway-simu-1",
"hwlab-gateway-simu-2"
].every((name) => serviceMap.has(name));
const patchPanelEndpointMap = patchPanel?.env.HWLAB_PATCH_PANEL_ENDPOINT_MAP
? JSON.parse(patchPanel.env.HWLAB_PATCH_PANEL_ENDPOINT_MAP)
: {};
const boxReadyForM3 =
box?.kind === "StatefulSet" &&
box?.replicas === 2 &&
hasSameMembers(boxIds, requiredM3BoxIds) &&
hasSameMembers(boxResourceIds, requiredM3BoxResources) &&
hasSameMembers(boxGatewaySessionIds, ["gws_gwsimu_1", "gws_gwsimu_2"]);
const gatewayReadyForM3 =
gateway?.replicas === 2 && hasSameMembers(gatewayBoxResources, requiredM3BoxResources);
const patchPanelReadyForM3 = patchPanel?.replicas === 1;
const manifestReadyForM3 = boxReadyForM3 && gatewayReadyForM3 && patchPanelReadyForM3;
gateway?.kind === "StatefulSet" &&
gateway?.replicas === 2 &&
hasSameMembers(gatewayIds, requiredM3GatewayIds) &&
hasSameMembers(gatewayBoxResources, requiredM3BoxResources) &&
hasSameMembers(gatewayBoxUrls, [
"http://hwlab-box-simu-1.hwlab-dev.svc.cluster.local:7201",
"http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201"
]);
const patchPanelReadyForM3 =
patchPanel?.replicas === 1 &&
patchPanelEndpointMap.res_boxsimu_2 === "http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201";
const manifestReadyForM3 = boxReadyForM3 && gatewayReadyForM3 && patchPanelReadyForM3 && hasInstanceServices;
return [
{
id: "deploy-skeleton-m3-cardinality",
status: manifestReadyForM3 ? "manifest-ready" : "gap",
blockerClass: manifestReadyForM3 ? null : "runtime_blocker",
source: deployWorkloadsPath,
source: [deployWorkloadsPath, deployServicesPath],
summary: manifestReadyForM3
? "Checked-in DEV deploy skeleton declares M3 cardinality: two box simulators, two gateway simulators, and one patch panel."
: "Read-only static comparison found the checked-in DEV deploy skeleton does not declare the required M3 simulator and patch-panel cardinality; proving two of each simulator in live DEV requires a separate deploy/runtime task or authorized runtime observation.",
? "Checked-in DEV deploy skeleton declares distinct indexed M3 identities for two box simulators, two gateway simulators, one patch panel, and the M3 DO1 -> DI1 endpoint map."
: "Read-only static comparison found the checked-in DEV deploy skeleton does not declare distinct indexed M3 simulator identities and patch-panel M3 wiring; proving live DEV still requires a separate deploy/runtime task or authorized runtime observation.",
evidence: {
required: {
"hwlab-box-simu": 2,
"hwlab-gateway-simu": 2,
"hwlab-patch-panel": 1,
wiring: "box-simu-1 DO1 -> box-simu-2 DI1"
wiring: "res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1"
},
observed: {
"hwlab-box-simu": {
@@ -343,7 +387,10 @@ async function collectReadOnlySupplementalEvidence() {
"hwlab-patch-panel": {
replicas: patchPanel?.replicas ?? null,
env: patchPanel?.env ?? null
}
},
instanceServices: [...serviceMap.keys()].filter((name) =>
name.startsWith("hwlab-box-simu-") || name.startsWith("hwlab-gateway-simu-")
)
}
},
validationCommand: requiredManifestCardinalityCommand,