test: tighten M3 preflight blocker classifications

This commit is contained in:
Code Queue Review
2026-05-22 22:48:38 +00:00
parent 0ff408696a
commit 012a89bfe1
3 changed files with 128 additions and 13 deletions
+21 -8
View File
@@ -7,7 +7,7 @@ import { access, mkdir, readFile, writeFile } from "node:fs/promises";
import { request as httpRequest } from "node:http";
import { request as httpsRequest } from "node:https";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { fileURLToPath, pathToFileURL } from "node:url";
import { activeReportLifecycle } from "../internal/dev-report-lifecycle.mjs";
import { DEV_ENDPOINT, DEV_FRONTEND_ENDPOINT, ENVIRONMENT_DEV } from "../internal/protocol/index.mjs";
@@ -143,7 +143,7 @@ function requireSafetyGates({ flags, dryRun, live }) {
const confirmedLive =
live &&
flags.has("--confirm-dev") &&
flags.has("--confirmed-non-production") &&
(flags.has("--expect-non-prod") || flags.has("--confirmed-non-production")) &&
!dryRun &&
!flags.has("--allow-live");
@@ -671,7 +671,8 @@ function createLiveOperationPlan() {
failureClassifications: Object.values(m3FailureClassifications),
refusalPolicy: [
"no arguments defaults to refusal",
"commands missing any of --live --confirm-dev --confirmed-non-production stay source/read-only",
"commands missing --live, --confirm-dev, or non-production confirmation stay source/read-only",
"non-production confirmation is --expect-non-prod; legacy --confirmed-non-production remains accepted",
"a dry-run, source, local, fixture, or read-only blocker report leaves liveOperation.status as not_run or blocked, never pass"
]
};
@@ -1715,9 +1716,21 @@ async function main() {
}
}
try {
await main();
} catch (error) {
console.error(`[dev-m3-smoke] ${error instanceof Error ? error.message : String(error)}`);
process.exitCode = 1;
export {
classifyDirectTargetBlockerScope,
classifyLiveOperationError,
createLiveOperationPlan,
hasRequiredM3Connection,
m3FailureClassifications,
parseArgs,
requireSafetyGates
};
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
try {
await main();
} catch (error) {
console.error(`[dev-m3-smoke] ${error instanceof Error ? error.message : String(error)}`);
process.exitCode = 1;
}
}
@@ -5,6 +5,14 @@ import path from "node:path";
import test from "node:test";
import { fileURLToPath } from "node:url";
import {
classifyDirectTargetBlockerScope,
createLiveOperationPlan,
hasRequiredM3Connection,
parseArgs,
requireSafetyGates
} from "./dev-m3-hardware-loop-smoke.mjs";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const testOutputDir = "tmp/dev-m3-hardware-loop-smoke-test";
@@ -57,3 +65,91 @@ test("--dry-run aliases the same source/read-only no-write path", async () => {
await rm(path.join(repoRoot, output), { force: true });
}
});
test("M3 safety gates keep incomplete live commands source/read-only", () => {
assert.deepEqual(requireSafetyGates(parseArgs([])), {
mode: "dry-run",
liveWriteAllowed: false
});
assert.deepEqual(requireSafetyGates(parseArgs(["--live", "--confirm-dev"])), {
mode: "dry-run",
liveWriteAllowed: false
});
assert.deepEqual(requireSafetyGates(parseArgs(["--dry-run", "--live", "--confirm-dev", "--expect-non-prod"])), {
mode: "dry-run",
liveWriteAllowed: false
});
assert.deepEqual(requireSafetyGates(parseArgs(["--live", "--confirm-dev", "--expect-non-prod"])), {
mode: "live",
liveWriteAllowed: true
});
assert.deepEqual(requireSafetyGates(parseArgs(["--live", "--confirm-dev", "--confirmed-non-production"])), {
mode: "live",
liveWriteAllowed: true
});
});
test("M3 blocker scopes classify missing identities and patch-panel route gaps", () => {
assert.equal(classifyDirectTargetBlockerScope("m3-service-discovery"), "target_missing");
assert.equal(classifyDirectTargetBlockerScope("m3-direct-target-missing"), "target_missing");
assert.equal(classifyDirectTargetBlockerScope("m3-box-simu-identity"), "identity_not_distinct");
assert.equal(classifyDirectTargetBlockerScope("m3-gateway-simu-identity"), "identity_not_distinct");
assert.equal(classifyDirectTargetBlockerScope("m3-patch-panel-wiring"), "patch_panel_wiring_missing");
const plan = createLiveOperationPlan();
assert.equal(plan.evidenceLevel, "DRY-RUN");
assert.equal(plan.liveWriteWillRun, false);
assert.ok(plan.failureClassifications.includes("identity_not_distinct"));
assert.ok(plan.failureClassifications.includes("patch_panel_wiring_missing"));
assert.match(plan.refusalPolicy.join("\n"), /source\/read-only/u);
});
test("patch-panel route preflight rejects missing res_boxsimu_1 DO1 to res_boxsimu_2 DI1 wiring", () => {
const wrongRoute = hasRequiredM3Connection({
status: {
activeConnections: [
{
fromResourceId: "res_boxsimu_1",
fromPort: "DO1",
toResourceId: "res_boxsimu_1",
toPort: "DI1"
}
]
},
wiring: {
connections: [
{
from: { resourceId: "res_boxsimu_1", port: "DO1" },
to: { resourceId: "res_boxsimu_1", port: "DI1" },
mode: "exclusive"
}
]
}
});
assert.equal(Boolean(wrongRoute.active), false);
assert.equal(Boolean(wrongRoute.configured), false);
const requiredRoute = hasRequiredM3Connection({
status: {
activeConnections: [
{
fromResourceId: "res_boxsimu_1",
fromPort: "DO1",
toResourceId: "res_boxsimu_2",
toPort: "DI1"
}
]
},
wiring: {
connections: [
{
from: { resourceId: "res_boxsimu_1", port: "DO1" },
to: { resourceId: "res_boxsimu_2", port: "DI1" },
mode: "exclusive"
}
]
}
});
assert.equal(Boolean(requiredRoute.active), true);
assert.equal(Boolean(requiredRoute.configured), true);
});