Merge pull request #363 from pikasTech/feat/d601-m3-code-agent-io
feat: expose M3 Code Agent route evidence
This commit is contained in:
@@ -486,10 +486,12 @@ export function classifySkillCliM3Route(payload, { httpStatus = null, httpOk = f
|
||||
) ?? null;
|
||||
const providerTrace = payload?.providerTrace && typeof payload.providerTrace === "object" ? payload.providerTrace : null;
|
||||
const route = skillTool?.route ?? providerTrace?.route ?? payload?.skills?.items?.find((item) => item?.route)?.route ?? null;
|
||||
const method = skillTool?.method ?? providerTrace?.method ?? null;
|
||||
const operationId = skillTool?.operationId ?? providerTrace?.operationId ?? null;
|
||||
const traceId = skillTool?.traceId ?? providerTrace?.traceId ?? payload?.traceId ?? null;
|
||||
const auditId = skillTool?.audit?.auditId ?? null;
|
||||
const evidenceId = skillTool?.evidence?.evidenceId ?? null;
|
||||
const auditId = skillTool?.auditId ?? skillTool?.audit?.auditId ?? providerTrace?.auditId ?? null;
|
||||
const evidenceId = skillTool?.evidenceId ?? skillTool?.evidence?.evidenceId ?? providerTrace?.evidenceId ?? null;
|
||||
const readback = skillTool?.readback ?? providerTrace?.readback ?? skillTool?.result?.targetReadback ?? null;
|
||||
const blockers = normalizeM3RouteBlockers(payload, skillTool);
|
||||
const safety = {
|
||||
directGatewayCalls: Boolean(skillTool?.directGatewayCalls ?? payload?.runnerTrace?.directGatewayCalls ?? false),
|
||||
@@ -510,6 +512,7 @@ export function classifySkillCliM3Route(payload, { httpStatus = null, httpOk = f
|
||||
skillTool &&
|
||||
["completed", "blocked"].includes(skillTool.status) &&
|
||||
routeEvidenceReady &&
|
||||
method === "POST" &&
|
||||
safety.directGatewayCalls === false &&
|
||||
safety.directBoxCalls === false &&
|
||||
safety.directPatchPanelCalls === false &&
|
||||
@@ -523,6 +526,7 @@ export function classifySkillCliM3Route(payload, { httpStatus = null, httpOk = f
|
||||
if (payload?.provider !== "hwlab-skill-cli") missing.push("provider=hwlab-skill-cli");
|
||||
if (!skillTool) missing.push(`${HWLAB_M3_IO_SKILL_NAME} toolCall`);
|
||||
if (!routeEvidenceReady) missing.push(`route=${HWLAB_M3_IO_API_ROUTE}`);
|
||||
if (method !== "POST") missing.push("method=POST");
|
||||
if (safety.directGatewayCalls || safety.directBoxCalls || safety.directPatchPanelCalls || safety.directTargetMentioned) {
|
||||
missing.push("no direct gateway/box/patch-panel target");
|
||||
}
|
||||
@@ -544,6 +548,7 @@ export function classifySkillCliM3Route(payload, { httpStatus = null, httpOk = f
|
||||
backend: payload?.backend ?? null,
|
||||
route,
|
||||
expectedRoute: HWLAB_M3_IO_API_ROUTE,
|
||||
method,
|
||||
toolCallStatus: skillTool?.status ?? null,
|
||||
accepted,
|
||||
controlReady,
|
||||
@@ -552,6 +557,7 @@ export function classifySkillCliM3Route(payload, { httpStatus = null, httpOk = f
|
||||
traceId,
|
||||
auditId,
|
||||
evidenceId,
|
||||
readback,
|
||||
idsPresent,
|
||||
structuredBlocker,
|
||||
blockers,
|
||||
@@ -1277,6 +1283,22 @@ function buildCommanderSummary(report) {
|
||||
auditId: route.auditId ?? null,
|
||||
evidenceId: route.evidenceId ?? null
|
||||
};
|
||||
const routeEvidenceContract = {
|
||||
route: route.route ?? null,
|
||||
expectedRoute: route.expectedRoute ?? HWLAB_M3_IO_API_ROUTE,
|
||||
method: route.method ?? null,
|
||||
operationId: ids.operationId,
|
||||
traceId: ids.traceId,
|
||||
auditId: ids.auditId,
|
||||
evidenceId: ids.evidenceId,
|
||||
accepted: route.accepted ?? null,
|
||||
status: route.toolCallStatus ?? null,
|
||||
controlReady: route.controlReady ?? null,
|
||||
readback: route.readback ?? null,
|
||||
visible: route.route === HWLAB_M3_IO_API_ROUTE &&
|
||||
route.method === "POST" &&
|
||||
Boolean(ids.operationId && ids.traceId && ids.auditId && ids.evidenceId)
|
||||
};
|
||||
return {
|
||||
reportClass: "live-mutating",
|
||||
allowedLiveMutationUsed: report.liveMutationUsed === true,
|
||||
@@ -1307,10 +1329,15 @@ function buildCommanderSummary(report) {
|
||||
status: report.dimensions.skillCliM3Route?.status ?? "not_run",
|
||||
route: route.route ?? null,
|
||||
expectedRoute: route.expectedRoute ?? HWLAB_M3_IO_API_ROUTE,
|
||||
method: route.method ?? null,
|
||||
structuredBlocker: route.structuredBlocker === true,
|
||||
idsPresent: route.idsPresent === true,
|
||||
accepted: route.accepted ?? null,
|
||||
controlReady: route.controlReady ?? null,
|
||||
readback: route.readback ?? null,
|
||||
safety: route.safety ?? null
|
||||
},
|
||||
exactRouteEvidenceContract: routeEvidenceContract,
|
||||
evidenceIds: Object.fromEntries(Object.entries(ids).filter(([, value]) => Boolean(value))),
|
||||
blockers: report.blockers.map((blocker) => ({
|
||||
scope: blocker.scope,
|
||||
@@ -1383,6 +1410,10 @@ export function renderMarkdown(report) {
|
||||
`- liveMutationUsed: ${report.liveMutationUsed === true}`,
|
||||
`- allowM3Write: ${report.runPolicy?.allowM3Write === true}`,
|
||||
`- skillCliRoute: ${report.commanderSummary?.m3RouteResult?.route ?? "not_run"}`,
|
||||
`- skillCliMethod: ${report.commanderSummary?.m3RouteResult?.method ?? "not_run"}`,
|
||||
`- operationId: ${report.commanderSummary?.evidenceIds?.operationId ?? "none"}`,
|
||||
`- traceId: ${report.commanderSummary?.evidenceIds?.traceId ?? "none"}`,
|
||||
`- auditId: ${report.commanderSummary?.evidenceIds?.auditId ?? "none"}`,
|
||||
`- evidenceId: ${report.commanderSummary?.evidenceIds?.evidenceId ?? "none"}`,
|
||||
"",
|
||||
"| Dimension | Status | Summary |",
|
||||
|
||||
@@ -291,7 +291,11 @@ test("Skill CLI M3 route passes with operation/audit/evidence ids and no direct
|
||||
|
||||
assert.equal(result.status, "pass");
|
||||
assert.equal(result.observations.route, "/v1/m3/io");
|
||||
assert.equal(result.observations.method, "POST");
|
||||
assert.equal(result.observations.idsPresent, true);
|
||||
assert.equal(result.observations.accepted, true);
|
||||
assert.equal(result.observations.controlReady, true);
|
||||
assert.equal(result.observations.readback.value, true);
|
||||
assert.equal(result.observations.safety.directGatewayCalls, false);
|
||||
assert.equal(result.observations.safety.fallbackUsed, false);
|
||||
});
|
||||
@@ -327,6 +331,14 @@ test("Skill CLI M3 route blocks missing route and direct gateway evidence", () =
|
||||
assert.equal(result.observations.safety.directGatewayCalls, true);
|
||||
});
|
||||
|
||||
test("Skill CLI M3 route requires explicit POST method evidence", () => {
|
||||
const result = classifySkillCliM3Route(skillCliPayload({ method: null }), { httpStatus: 200, httpOk: true });
|
||||
|
||||
assert.equal(result.status, "blocked");
|
||||
assert.match(result.summary, /method=POST/u);
|
||||
assert.ok(result.observations.missing.includes("method=POST"));
|
||||
});
|
||||
|
||||
test("RPT-004 live report defaults to read-only M3 route checks and records no live mutation", async () => {
|
||||
const report = await buildRpt004Report({
|
||||
...parseArgs([
|
||||
@@ -387,6 +399,20 @@ test("RPT-004 live report defaults to read-only M3 route checks and records no l
|
||||
assert.equal(report.commanderSummary.sessionResult.status, "pass");
|
||||
assert.equal(report.commanderSummary.skillCliResult.status, "pass");
|
||||
assert.equal(report.commanderSummary.m3RouteResult.route, "/v1/m3/io");
|
||||
assert.equal(report.commanderSummary.m3RouteResult.method, "POST");
|
||||
assert.equal(report.commanderSummary.m3RouteResult.accepted, true);
|
||||
assert.equal(report.commanderSummary.m3RouteResult.controlReady, true);
|
||||
assert.equal(report.commanderSummary.m3RouteResult.readback.value, true);
|
||||
assert.equal(report.commanderSummary.exactRouteEvidenceContract.visible, true);
|
||||
assert.equal(report.commanderSummary.exactRouteEvidenceContract.route, "/v1/m3/io");
|
||||
assert.equal(report.commanderSummary.exactRouteEvidenceContract.method, "POST");
|
||||
assert.equal(report.commanderSummary.exactRouteEvidenceContract.operationId, "op_rpt004_skill_m3");
|
||||
assert.equal(report.commanderSummary.exactRouteEvidenceContract.traceId, "trc_rpt004_skill");
|
||||
assert.equal(report.commanderSummary.exactRouteEvidenceContract.auditId, "aud_rpt004_skill_m3");
|
||||
assert.equal(report.commanderSummary.exactRouteEvidenceContract.evidenceId, "evd_rpt004_skill_m3");
|
||||
assert.equal(report.commanderSummary.exactRouteEvidenceContract.accepted, true);
|
||||
assert.equal(report.commanderSummary.exactRouteEvidenceContract.status, "completed");
|
||||
assert.equal(report.commanderSummary.exactRouteEvidenceContract.readback.value, true);
|
||||
assert.equal(report.commanderSummary.evidenceIds.evidenceId, "evd_rpt004_skill_m3");
|
||||
assert.equal(report.dimensions.skillCliM3Route.status, "pass");
|
||||
assert.equal(report.dimensions.m3TrustedLoop.observations.skippedWriteLoop, true);
|
||||
@@ -597,6 +623,7 @@ function openAiFallbackPayload({ reply = "text fallback" } = {}) {
|
||||
|
||||
function skillCliPayload({
|
||||
route = "/v1/m3/io",
|
||||
method = "POST",
|
||||
accepted = true,
|
||||
status = "succeeded",
|
||||
operationId = "op_rpt004_skill_m3",
|
||||
@@ -632,6 +659,7 @@ function skillCliPayload({
|
||||
runnerTrace: {
|
||||
traceId: "trc_rpt004_skill",
|
||||
runnerKind: "hwlab-m3-io-skill-cli",
|
||||
method,
|
||||
route,
|
||||
fallbackUsed: false,
|
||||
directGatewayCalls,
|
||||
@@ -641,22 +669,34 @@ function skillCliPayload({
|
||||
providerTrace: {
|
||||
runnerKind: "hwlab-m3-io-skill-cli",
|
||||
route,
|
||||
method,
|
||||
traceId: "trc_rpt004_skill",
|
||||
operationId,
|
||||
auditId,
|
||||
evidenceId,
|
||||
accepted,
|
||||
controlReady: accepted,
|
||||
fallbackUsed: false
|
||||
fallbackUsed: false,
|
||||
readback: {
|
||||
status: "succeeded",
|
||||
value: true,
|
||||
resourceId: "res_boxsimu_2",
|
||||
port: "DI1"
|
||||
}
|
||||
},
|
||||
toolCalls: [{
|
||||
name: "hwlab-agent-runtime.m3-io",
|
||||
status: accepted ? "completed" : "blocked",
|
||||
type: "skill-cli",
|
||||
route,
|
||||
method,
|
||||
accepted,
|
||||
controlReady: accepted,
|
||||
capabilityLevel: accepted ? "hwlab-api-control-ready" : "hwlab-api-control-blocked",
|
||||
operationId,
|
||||
traceId: "trc_rpt004_skill",
|
||||
auditId,
|
||||
evidenceId,
|
||||
audit: {
|
||||
auditId,
|
||||
status
|
||||
@@ -672,6 +712,12 @@ function skillCliPayload({
|
||||
status: "degraded",
|
||||
blocker: blocker?.code ?? "runtime_durable_not_green"
|
||||
},
|
||||
readback: {
|
||||
status: "succeeded",
|
||||
value: true,
|
||||
resourceId: "res_boxsimu_2",
|
||||
port: "DI1"
|
||||
},
|
||||
blocker,
|
||||
blockers: blocker ? [blocker] : [{
|
||||
code: "runtime_durable_not_green",
|
||||
|
||||
Reference in New Issue
Block a user