feat: add RPT-004 MVP E2E harness

This commit is contained in:
Code Queue Review
2026-05-23 14:07:44 +00:00
parent 8a04c27d31
commit 88e37edd19
5 changed files with 1562 additions and 3 deletions
+4 -2
View File
File diff suppressed because one or more lines are too long
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env node
import {
formatRpt004Failure,
runRpt004Cli
} from "./src/rpt004-mvp-e2e-harness.mjs";
try {
process.exitCode = await runRpt004Cli(process.argv.slice(2));
} catch (error) {
process.stdout.write(`${JSON.stringify(formatRpt004Failure(error), null, 2)}\n`);
process.exitCode = 1;
}
File diff suppressed because it is too large Load Diff
+214
View File
@@ -0,0 +1,214 @@
import assert from "node:assert/strict";
import test from "node:test";
import {
classifyCodeAgentSkillPath,
classifyM3DurableEvidence,
classifyRpt004Conclusion,
parseArgs,
validateLiveBoundary
} from "./rpt004-mvp-e2e-harness.mjs";
test("RPT-004 CLI accepts the required live URL/API URL shape", () => {
const args = parseArgs([
"--live",
"--url",
"http://74.48.78.17:16666/",
"--api-url",
"http://74.48.78.17:16667/"
]);
assert.equal(args.live, true);
assert.equal(args.url, "http://74.48.78.17:16666/");
assert.equal(args.apiUrl, "http://74.48.78.17:16667/");
assert.equal(args.writeReport, true);
assert.doesNotThrow(() => validateLiveBoundary(args));
});
test("RPT-004 live boundary rejects deprecated or swapped active DEV ports", () => {
assert.throws(
() => validateLiveBoundary({ url: "http://74.48.78.17:6666/", apiUrl: "http://74.48.78.17:16667/" }),
/16666/u
);
assert.throws(
() => validateLiveBoundary({ url: "http://74.48.78.17:16667/", apiUrl: "http://74.48.78.17:16666/" }),
/16666/u
);
});
test("RPT-004 classifies deployed revision drift as NOT_CURRENT, never MVP PASS", () => {
const conclusion = classifyRpt004Conclusion([
check("expected-artifact-current", "pass"),
check("live-revision-current", "not_current"),
check("live-health-ready", "pass"),
check("16666-default-chinese-workbench", "pass")
]);
assert.equal(conclusion.status, "not_current");
assert.equal(conclusion.classification, "NOT_CURRENT");
assert.equal(conclusion.mvpPass, false);
});
test("RPT-004 classifies degraded live health as BLOCKED, never MVP PASS", () => {
const conclusion = classifyRpt004Conclusion([
check("expected-artifact-current", "pass"),
check("live-revision-current", "pass"),
check("live-health-ready", "blocked"),
check("16666-default-chinese-workbench", "pass"),
check("m3-v1-io-trusted-loop", "not_run")
]);
assert.equal(conclusion.status, "blocked");
assert.equal(conclusion.classification, "BLOCKED");
assert.equal(conclusion.mvpPass, false);
});
test("RPT-004 reports MVP_PASS only when every required live dimension passes", () => {
const conclusion = classifyRpt004Conclusion([
check("expected-artifact-current", "pass"),
check("live-revision-current", "pass"),
check("live-health-ready", "pass"),
check("16666-default-chinese-workbench", "pass"),
check("layout-smoke-hit-target", "pass"),
check("code-agent-skill-path", "pass"),
check("m3-v1-io-trusted-loop", "pass"),
check("operation-audit-evidence-durable-green", "pass")
]);
assert.equal(conclusion.status, "pass");
assert.equal(conclusion.classification, "MVP_PASS");
assert.equal(conclusion.mvpPass, true);
});
test("Code Agent OpenAI fallback cannot satisfy skill path/toolCalls readiness", () => {
const result = classifyCodeAgentSkillPath({
status: "completed",
provider: "openai-responses",
model: "gpt-5.5",
backend: "hwlab-cloud-api/openai-responses",
implementationType: "openai-responses-fallback",
runner: {
kind: "openai-responses-fallback"
},
reply: {
content: "text answer"
},
toolCalls: [],
skills: {
status: "not_requested",
items: []
}
}, { httpStatus: 200, httpOk: true });
assert.equal(result.status, "blocked");
assert.match(result.summary, /openai-fallback/u);
});
test("Code Agent Codex stdio skill discovery passes with completed toolCalls and repo skill", () => {
const result = classifyCodeAgentSkillPath({
status: "completed",
conversationId: "cnv_test",
sessionId: "ses_test",
messageId: "msg_test",
traceId: "trc_test",
provider: "codex-stdio",
model: "gpt-5.5",
backend: "hwlab-cloud-api/codex-mcp-stdio-runner",
workspace: "/workspace/hwlab",
sandbox: "workspace-write",
capabilityLevel: "long-lived-codex-stdio-session",
sessionMode: "codex-mcp-stdio-long-lived",
implementationType: "repo-owned-codex-mcp-stdio-session",
session: {
sessionId: "ses_test",
status: "idle",
idleTimeoutMs: 1800000,
lastTraceId: "trc_test",
longLivedSession: true,
codexStdio: true
},
sessionReuse: {
reused: false,
turn: 1
},
runner: {
kind: "codex-mcp-stdio-runner",
workspace: "/workspace/hwlab",
sandbox: "workspace-write",
sessionMode: "codex-mcp-stdio-long-lived",
implementationType: "repo-owned-codex-mcp-stdio-session",
codexStdio: true,
writeCapable: true,
durableSession: true
},
runnerTrace: {
runnerKind: "codex-mcp-stdio-runner"
},
longLivedSessionGate: {
status: "pass",
pass: true
},
toolCalls: [{
name: "skills.discover",
status: "completed",
type: "file-read"
}],
skills: {
status: "ready",
items: [{
name: "hwlab-agent-runtime",
source: "/workspace/hwlab/skills/hwlab-agent-runtime/SKILL.md"
}],
count: 1
}
}, { httpStatus: 200, httpOk: true });
assert.equal(result.status, "pass");
});
test("M3 durable evidence requires operation, audit, evidence, DEV-LIVE green, and persisted writes", () => {
const green = classifyM3DurableEvidence(greenOperations());
assert.equal(green.status, "pass");
const blockedOps = greenOperations();
blockedOps[2].evidenceState.writeStatus = "written_non_durable";
const blocked = classifyM3DurableEvidence(blockedOps);
assert.equal(blocked.status, "blocked");
assert.equal(blocked.observations.sequence[2].persisted, false);
});
function check(id, status) {
return {
id,
status,
summary: `${id} ${status}`
};
}
function greenOperations() {
return [
operation("write-do1-true", "do.write", true),
operation("read-di1-true", "di.read", true),
operation("write-do1-false", "do.write", false),
operation("read-di1-false", "di.read", false)
];
}
function operation(id, action, value) {
return {
id,
action,
status: "succeeded",
operationId: `op_${id}`,
traceId: `trc_${id}`,
auditId: `aud_${id}`,
evidenceId: `evd_${id}`,
resultValue: value,
evidenceState: {
status: "green",
sourceKind: "DEV-LIVE",
durable: true,
writeStatus: "persisted"
}
};
}
+137 -1
View File
@@ -32,7 +32,8 @@ const issueFamily = Object.freeze({
DEV_EVIDENCE_BLOCKER_AGGREGATOR: "pikasTech/HWLAB#41",
DEV_M5_GATE_AGGREGATOR_V2: "pikasTech/HWLAB#58",
DEV_CLOUD_WORKBENCH_LIVE: "pikasTech/HWLAB#7",
DEV_CLOUD_WORKBENCH_LAYOUT: "pikasTech/HWLAB#273"
DEV_CLOUD_WORKBENCH_LAYOUT: "pikasTech/HWLAB#273",
RPT004_MVP_E2E: "pikasTech/HWLAB#316"
});
const allowedIssues = new Set([
contractIssue,
@@ -526,6 +527,10 @@ async function validateReport(relativePath) {
await validateAggregatorV2Report(relativePath, report);
return;
}
if (report.reportKind === "rpt-004-mvp-e2e-harness" || report.taskId === "rpt004-mvp-e2e-harness") {
await validateRpt004MvpE2eReport(report, label);
return;
}
if (report.issue === requiredDevM3Issue || report.taskId === "dev-m3-hardware-loop") {
await validateDevM3Report(report, label);
@@ -772,6 +777,137 @@ async function validateReport(relativePath) {
}
}
async function validateRpt004MvpE2eReport(report, label) {
for (const field of [
"$schema",
"$id",
"reportVersion",
"reportKind",
"issue",
"taskId",
"acceptanceLevel",
"devOnly",
"prodDisabled",
"mode",
"status",
"conclusion",
"sourceContract",
"validationCommands",
"devPreconditions",
"target",
"expected",
"dimensions",
"checks",
"blockers",
"safety"
]) {
assert.ok(Object.hasOwn(report, field), `${label} missing ${field}`);
}
assert.equal(report.reportVersion, "v1", `${label}.reportVersion`);
assert.equal(report.reportKind, "rpt-004-mvp-e2e-harness", `${label}.reportKind`);
assert.equal(report.issue, issueFamily.RPT004_MVP_E2E, `${label}.issue`);
assert.equal(report.taskId, "rpt004-mvp-e2e-harness", `${label}.taskId`);
assert.equal(report.acceptanceLevel, "rpt004_mvp_e2e", `${label}.acceptanceLevel`);
assert.equal(report.devOnly, true, `${label}.devOnly`);
assert.equal(report.prodDisabled, true, `${label}.prodDisabled`);
assert.ok(["source-check", "dev-live"].includes(report.mode), `${label}.mode`);
assert.ok(["pass", "blocked", "not_current"].includes(report.status), `${label}.status`);
assertObject(report.conclusion, `${label}.conclusion`);
assert.ok(
["CHECK_ONLY", "BLOCKED", "NOT_CURRENT", "MVP_PASS"].includes(report.conclusion.classification),
`${label}.conclusion.classification`
);
assert.equal(typeof report.conclusion.mvpPass, "boolean", `${label}.conclusion.mvpPass`);
if (report.status !== "pass" || report.conclusion.classification !== "MVP_PASS") {
assert.equal(report.conclusion.mvpPass, false, `${label}.conclusion.mvpPass must stay false outside MVP_PASS`);
}
if (report.conclusion.classification === "NOT_CURRENT") {
assert.equal(report.status, "not_current", `${label}.status for NOT_CURRENT`);
}
assertCommandList(report.validationCommands, `${label}.validationCommands`, { minLength: 5 });
for (const requiredCommand of [
"node --check scripts/rpt004-mvp-e2e-harness.mjs",
"node --check scripts/src/rpt004-mvp-e2e-harness.mjs",
"node --test scripts/src/rpt004-mvp-e2e-harness.test.mjs",
"node scripts/rpt004-mvp-e2e-harness.mjs --check --no-write",
"node scripts/rpt004-mvp-e2e-harness.mjs --live --url http://74.48.78.17:16666/ --api-url http://74.48.78.17:16667/"
]) {
assert.ok(
report.validationCommands.includes(requiredCommand),
`${label}.validationCommands missing ${requiredCommand}`
);
}
assertObject(report.target, `${label}.target`);
assert.equal(report.target.environment, "dev", `${label}.target.environment`);
assert.equal(report.target.browserUrl, "http://74.48.78.17:16666/", `${label}.target.browserUrl`);
assert.equal(report.target.apiUrl, "http://74.48.78.17:16667/", `${label}.target.apiUrl`);
assert.equal(report.target.m3IoUrl, "http://74.48.78.17:16667/v1/m3/io", `${label}.target.m3IoUrl`);
assertObject(report.expected, `${label}.expected`);
assert.match(report.expected.commitId, /^[a-f0-9]{7,40}$/u, `${label}.expected.commitId`);
assert.equal(report.expected.serviceCount, report.expected.requiredServiceCount, `${label}.expected.serviceCount`);
const requiredDimensions = [
"artifact",
"revision",
"health",
"workbench",
"layout",
"m3TrustedLoop",
"durableEvidence",
"codeAgentSkillPath"
];
assertObject(report.dimensions, `${label}.dimensions`);
for (const id of requiredDimensions) {
assert.ok(Object.hasOwn(report.dimensions, id), `${label}.dimensions missing ${id}`);
}
assertArray(report.checks, `${label}.checks`);
const minimumCheckCount = report.mode === "dev-live" ? 4 : 2;
assert.ok(
report.checks.length >= minimumCheckCount,
`${label}.checks must include at least ${minimumCheckCount} entries`
);
const checkIds = new Set(report.checks.map((check) => check.id));
assert.ok(checkIds.has("expected-artifact-current"), `${label}.checks missing expected-artifact-current`);
if (report.mode === "dev-live") {
for (const id of [
"live-revision-current",
"live-health-ready",
"16666-default-chinese-workbench",
"layout-smoke-hit-target",
"code-agent-skill-path",
"m3-v1-io-trusted-loop",
"operation-audit-evidence-durable-green"
]) {
assert.ok(checkIds.has(id) || report.status !== "pass", `${label}.checks missing ${id}`);
}
}
for (const [index, check] of report.checks.entries()) {
assertObject(check, `${label}.checks[${index}]`);
assertString(check.id, `${label}.checks[${index}].id`);
assert.ok(["pass", "blocked", "not_current", "not_run"].includes(check.status), `${label}.checks[${index}].status`);
assertString(check.summary, `${label}.checks[${index}].summary`);
}
assertBlockers(report.blockers, `${label}.blockers`);
assertObject(report.safety, `${label}.safety`);
assert.equal(report.safety.devOnly, true, `${label}.safety.devOnly`);
assert.equal(report.safety.prodTouched, false, `${label}.safety.prodTouched`);
assert.equal(report.safety.deployApplyAttempted, false, `${label}.safety.deployApplyAttempted`);
assert.equal(report.safety.rolloutAttempted, false, `${label}.safety.rolloutAttempted`);
assert.equal(report.safety.secretValuesPrinted, false, `${label}.safety.secretValuesPrinted`);
assert.equal(report.safety.openAiFallbackCountsAsPass, false, `${label}.safety.openAiFallbackCountsAsPass`);
if (report.artifacts?.artifactReport) {
assert.match(report.artifacts.artifactReport, /not applicable/u, `${label}.artifacts.artifactReport`);
}
}
async function assertDocumentSet(documentsValue, label, requiredDocs) {
assertStringArray(documentsValue, label, { minLength: requiredDocs.length });
const documents = new Set(documentsValue);