326 lines
10 KiB
JavaScript
326 lines
10 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
CODE_AGENT_M3_IO_ROUTE,
|
|
codeAgentM3EvidenceContractSummary,
|
|
extractCodeAgentM3Evidence,
|
|
isCodeAgentM3SkillCompletion,
|
|
m3EvidenceRows
|
|
} from "./code-agent-m3-evidence.mjs";
|
|
|
|
test("renders accepted M3 Skill CLI operation/audit/evidence metadata", () => {
|
|
const evidence = extractCodeAgentM3Evidence(m3Message({
|
|
status: "succeeded",
|
|
accepted: true,
|
|
operationId: "op_m3_cli_accepted",
|
|
traceId: "trc_m3_cli_accepted",
|
|
auditId: "aud_m3_cli_accepted_succeeded",
|
|
evidenceId: "evd_m3_cli_accepted_succeeded",
|
|
value: true,
|
|
readbackValue: true
|
|
}));
|
|
|
|
assert.equal(evidence.verdict.key, "accepted");
|
|
assert.equal(evidence.responseType, "m3_io_result");
|
|
assert.equal(evidence.route, CODE_AGENT_M3_IO_ROUTE);
|
|
assert.equal(evidence.target.resourceId, "res_boxsimu_1");
|
|
assert.equal(evidence.target.port, "DO1");
|
|
assert.equal(evidence.target.value, true);
|
|
assert.equal(evidence.readback.resourceId, "res_boxsimu_2");
|
|
assert.equal(evidence.readback.port, "DI1");
|
|
assert.equal(evidence.readback.value, true);
|
|
assert.equal(evidence.ids.operationId, "op_m3_cli_accepted");
|
|
assert.equal(evidence.ids.traceId, "trc_m3_cli_accepted");
|
|
assert.equal(evidence.ids.auditId, "aud_m3_cli_accepted_succeeded");
|
|
assert.equal(evidence.ids.evidenceId, "evd_m3_cli_accepted_succeeded");
|
|
assert.equal(isCodeAgentM3SkillCompletion(m3Message()), true);
|
|
assert.equal(rowValue(evidence, "路径"), "Code Agent -> Skill CLI -> HWLAB API /v1/m3/io");
|
|
assert.equal(rowValue(evidence, "接线"), "res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1");
|
|
assert.match(rowValue(evidence, "DO1"), /res_boxsimu_1:DO1 value=true/u);
|
|
assert.match(rowValue(evidence, "DI1"), /res_boxsimu_2:DI1 value=true/u);
|
|
assert.equal(rowValue(evidence, "evidenceId"), "evd_m3_cli_accepted_succeeded");
|
|
});
|
|
|
|
test("renders blocker state from Skill CLI without trusted pass", () => {
|
|
const evidence = extractCodeAgentM3Evidence(m3Message({
|
|
status: "blocked",
|
|
accepted: false,
|
|
operationId: "op_m3_blocked",
|
|
blocker: {
|
|
code: "m3_wiring_missing",
|
|
zh: "hwlab-patch-panel 未确认 active DO1 -> DI1 接线"
|
|
}
|
|
}));
|
|
|
|
assert.equal(evidence.verdict.key, "blocked");
|
|
assert.equal(evidence.responseType, "m3_io_blocker");
|
|
assert.equal(evidence.verdict.tone, "blocked");
|
|
assert.match(rowValue(evidence, "blocker"), /m3_wiring_missing/u);
|
|
assert.equal(isCodeAgentM3SkillCompletion({ ...m3Message(), status: "failed" }), false);
|
|
});
|
|
|
|
test("renders accepted but non-durable M3 IO result as degraded, not DEV-LIVE trusted", () => {
|
|
const evidence = extractCodeAgentM3Evidence(m3Message({
|
|
status: "succeeded",
|
|
accepted: true,
|
|
value: false,
|
|
readbackValue: false,
|
|
trust: {
|
|
trusted: false,
|
|
durable: false,
|
|
durableStatus: "degraded",
|
|
durableBlocker: "runtime_durable_not_green"
|
|
},
|
|
trustBlocker: {
|
|
code: "runtime_durable_not_green",
|
|
layer: "runtime-durable",
|
|
zh: "runtime durable 未 green"
|
|
}
|
|
}));
|
|
|
|
assert.equal(evidence.verdict.key, "accepted-untrusted");
|
|
assert.equal(evidence.verdict.tone, "degraded");
|
|
assert.equal(rowValue(evidence, "trusted"), "false");
|
|
assert.equal(rowValue(evidence, "durable"), "false");
|
|
assert.match(rowValue(evidence, "DI1"), /value=false/u);
|
|
});
|
|
|
|
test("shows missing evidenceId as unavailable proof instead of fabricating one", () => {
|
|
const evidence = extractCodeAgentM3Evidence(m3Message({
|
|
status: "succeeded",
|
|
accepted: true,
|
|
evidenceId: null
|
|
}));
|
|
|
|
assert.equal(evidence.verdict.key, "accepted-missing-proof");
|
|
assert.equal(rowValue(evidence, "evidenceId"), "未产生/不可证明");
|
|
assert.equal(row(evidence, "evidenceId").copyable, false);
|
|
});
|
|
|
|
test("classifies DO1 target and DI1 readback mismatch as blocked proof", () => {
|
|
const evidence = extractCodeAgentM3Evidence(m3Message({
|
|
status: "blocked",
|
|
accepted: false,
|
|
operationId: "op_m3_readback_mismatch",
|
|
value: true,
|
|
readbackValue: false,
|
|
blocker: {
|
|
code: "m3_readback_mismatch",
|
|
zh: "DI1 回读值 false 与 DO1 写入值 true 不一致"
|
|
}
|
|
}));
|
|
|
|
assert.equal(evidence.readbackMismatch, true);
|
|
assert.equal(evidence.verdict.key, "readback-mismatch");
|
|
assert.match(rowValue(evidence, "DI1"), /res_boxsimu_2:DI1 value=false/u);
|
|
});
|
|
|
|
test("classifies direct gateway/box/patch-panel path as invalid and never trusted", () => {
|
|
const evidence = extractCodeAgentM3Evidence(m3Message({
|
|
status: "succeeded",
|
|
accepted: true,
|
|
operationId: "op_m3_direct_path",
|
|
controlPath: {
|
|
cloudApi: false,
|
|
gatewaySimu: true,
|
|
boxSimu: true,
|
|
patchPanel: true,
|
|
frontendBypass: true
|
|
},
|
|
blocker: {
|
|
code: "direct_hardware_target_blocked",
|
|
zh: "Skill CLI target must be HWLAB cloud-api, not gateway/box/patch-panel."
|
|
}
|
|
}));
|
|
|
|
assert.equal(evidence.directPathInvalid, true);
|
|
assert.equal(evidence.verdict.key, "direct-path-invalid");
|
|
assert.equal(evidence.verdict.tone, "blocked");
|
|
});
|
|
|
|
test("contract summary documents operation/audit/evidence visibility", () => {
|
|
assert.match(codeAgentM3EvidenceContractSummary(), /operationId/u);
|
|
assert.match(codeAgentM3EvidenceContractSummary(), /auditId/u);
|
|
assert.match(codeAgentM3EvidenceContractSummary(), /evidenceId/u);
|
|
assert.match(codeAgentM3EvidenceContractSummary(), /direct gateway\/box\/patch-panel/u);
|
|
});
|
|
|
|
function row(evidence, label) {
|
|
const found = m3EvidenceRows(evidence).find((item) => item.label === label);
|
|
assert.ok(found, `missing row ${label}`);
|
|
return found;
|
|
}
|
|
|
|
function rowValue(evidence, label) {
|
|
return row(evidence, label).value;
|
|
}
|
|
|
|
function m3Message(overrides = {}) {
|
|
const operationId = overrides.operationId ?? "op_m3_cli_accepted";
|
|
const traceId = overrides.traceId ?? "trc_m3_cli_accepted";
|
|
const auditId = Object.hasOwn(overrides, "auditId") ? overrides.auditId : "aud_m3_cli_accepted_succeeded";
|
|
const evidenceId = Object.hasOwn(overrides, "evidenceId") ? overrides.evidenceId : "evd_m3_cli_accepted_succeeded";
|
|
const value = Object.hasOwn(overrides, "value") ? overrides.value : true;
|
|
const readbackValue = Object.hasOwn(overrides, "readbackValue") ? overrides.readbackValue : value;
|
|
const status = overrides.status ?? "succeeded";
|
|
const accepted = Object.hasOwn(overrides, "accepted") ? overrides.accepted : true;
|
|
const responseType = overrides.responseType ?? (accepted && status !== "blocked" ? "m3_io_result" : "m3_io_blocker");
|
|
const trust = overrides.trust ?? {
|
|
trusted: responseType === "m3_io_result",
|
|
durable: responseType === "m3_io_result",
|
|
durableStatus: responseType === "m3_io_result" ? "green" : "blocked",
|
|
durableBlocker: responseType === "m3_io_result" ? null : "m3_io_blocked"
|
|
};
|
|
const toolCall = {
|
|
id: "tool_m3_fixture",
|
|
type: "skill-cli",
|
|
name: "hwlab-agent-runtime.m3-io",
|
|
responseType,
|
|
status: accepted ? "completed" : "blocked",
|
|
route: CODE_AGENT_M3_IO_ROUTE,
|
|
accepted,
|
|
operationId,
|
|
traceId,
|
|
audit: {
|
|
auditId
|
|
},
|
|
evidence: {
|
|
evidenceId,
|
|
status: evidenceId ? "green" : "blocked",
|
|
sourceKind: evidenceId ? "DEV-LIVE" : "BLOCKED"
|
|
},
|
|
command: {
|
|
action: "do.write",
|
|
resourceId: "res_boxsimu_1",
|
|
port: "DO1",
|
|
value
|
|
},
|
|
result: {
|
|
value,
|
|
targetReadback: {
|
|
resourceId: "res_boxsimu_2",
|
|
port: "DI1",
|
|
value: readbackValue
|
|
}
|
|
},
|
|
blocker: overrides.blocker ?? null,
|
|
trustBlocker: overrides.trustBlocker ?? null,
|
|
blockers: [overrides.blocker, overrides.trustBlocker].filter(Boolean),
|
|
controlPath: overrides.controlPath ?? {
|
|
cloudApi: true,
|
|
gatewaySimu: true,
|
|
boxSimu: true,
|
|
patchPanel: true,
|
|
frontendBypass: false
|
|
},
|
|
safety: {
|
|
cloudApiRouteOnly: true,
|
|
allowedRoute: CODE_AGENT_M3_IO_ROUTE,
|
|
directGatewayCalls: false,
|
|
directBoxCalls: false,
|
|
directPatchPanelCalls: false,
|
|
fallbackUsed: false
|
|
},
|
|
stdout: JSON.stringify({
|
|
route: CODE_AGENT_M3_IO_ROUTE,
|
|
status,
|
|
accepted,
|
|
traceId,
|
|
operationId,
|
|
audit: { auditId },
|
|
evidence: { evidenceId },
|
|
blocker: overrides.blocker ?? null,
|
|
result: {
|
|
value,
|
|
targetReadback: {
|
|
resourceId: "res_boxsimu_2",
|
|
port: "DI1",
|
|
value: readbackValue
|
|
}
|
|
}
|
|
})
|
|
};
|
|
|
|
return {
|
|
status: "completed",
|
|
responseType,
|
|
m3Io: {
|
|
type: responseType,
|
|
status,
|
|
action: "do.write",
|
|
accepted,
|
|
do1: {
|
|
resourceId: "res_boxsimu_1",
|
|
port: "DO1",
|
|
targetValue: value
|
|
},
|
|
di1: {
|
|
resourceId: "res_boxsimu_2",
|
|
port: "DI1",
|
|
observedValue: readbackValue
|
|
},
|
|
wiring: {
|
|
from: "res_boxsimu_1:DO1",
|
|
via: "hwlab-patch-panel",
|
|
to: "res_boxsimu_2:DI1",
|
|
label: "res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1"
|
|
},
|
|
path: {
|
|
summary: "Code Agent -> Skill CLI -> HWLAB API /v1/m3/io",
|
|
segments: ["Code Agent", "Skill CLI", "HWLAB API"],
|
|
hwlabApi: {
|
|
route: CODE_AGENT_M3_IO_ROUTE,
|
|
method: "POST"
|
|
}
|
|
},
|
|
operation: {
|
|
operationId,
|
|
auditId,
|
|
evidenceId
|
|
},
|
|
trace: {
|
|
traceId,
|
|
route: CODE_AGENT_M3_IO_ROUTE,
|
|
method: "POST"
|
|
},
|
|
trust,
|
|
blocker: overrides.blocker ?? null,
|
|
blockers: [overrides.blocker, overrides.trustBlocker].filter(Boolean)
|
|
},
|
|
provider: "hwlab-skill-cli",
|
|
model: "controlled-m3-io",
|
|
backend: "hwlab-cloud-api/hwlab-agent-runtime-skill-cli",
|
|
traceId,
|
|
conversationId: "conv_m3_fixture",
|
|
sessionId: "sess_m3_fixture",
|
|
messageId: "msg_m3_fixture",
|
|
runner: {
|
|
kind: "hwlab-m3-io-skill-cli"
|
|
},
|
|
runnerTrace: {
|
|
runnerKind: "hwlab-m3-io-skill-cli",
|
|
route: CODE_AGENT_M3_IO_ROUTE,
|
|
status,
|
|
accepted,
|
|
operationId,
|
|
traceId
|
|
},
|
|
providerTrace: {
|
|
runnerKind: "hwlab-m3-io-skill-cli",
|
|
skill: "hwlab-agent-runtime.m3-io",
|
|
responseType,
|
|
route: CODE_AGENT_M3_IO_ROUTE,
|
|
status,
|
|
accepted,
|
|
operationId,
|
|
traceId,
|
|
fallbackUsed: false
|
|
},
|
|
toolCalls: [toolCall],
|
|
reply: {
|
|
content: "M3 IO Skill CLI result fixture."
|
|
}
|
|
};
|
|
}
|