test: accept sanitized blocked code agent evidence

This commit is contained in:
Code Queue Review
2026-05-23 03:57:09 +00:00
parent 74177c8cc4
commit 0b26610e66
5 changed files with 111 additions and 15 deletions
@@ -143,6 +143,36 @@ test("Code Agent browser classifier blocks provider 502 response shapes", () =>
assert.equal(classification.blocker, "provider-upstream");
});
test("Code Agent browser classifier treats sanitized 服务受阻 UI as blocked provider evidence", () => {
const summary = sanitizeAgentChatBody({
status: "failed",
provider: "openai-responses",
model: "gpt-5.5",
backend: "hwlab-cloud-api/openai-responses",
traceId: "trc_provider_502",
error: {
code: "provider_unavailable",
message: "OpenAI Responses returned HTTP 502: request rejected",
providerStatus: 502,
missingEnv: []
}
}, { httpStatus: 200 });
const classification = classifyCodeAgentBrowserJourney({
responseSummary: summary,
httpOk: true,
httpStatus: 200,
ui: {
agentChatStatus: "服务受阻",
completedMessageVisible: false,
failedMessageVisible: true
}
});
assert.equal(classification.status, "blocked");
assert.equal(classification.blocker, "provider-upstream");
});
test("Code Agent readiness classifier blocks completed payloads over any non-2xx HTTP status", () => {
const completedPayload = {
conversationId: "cnv_completed_non_2xx",
+2 -1
View File
@@ -1,5 +1,6 @@
const trustedLiveProviders = new Set(["openai-responses", "codex-cli"]);
const untrustedProviderPattern = /\b(?:echo|mock|fixture|stub|sample)\b/iu;
const blockedCodeAgentUiLabels = new Set(["发送失败", "服务受阻", "BLOCKED 凭证缺口"]);
export function classifyCodeAgentChatReadiness(payload, { realDevLive = false, httpStatus = null } = {}) {
const providerBlock = classifyCodeAgentProviderBlock(payload, { httpStatus });
@@ -193,7 +194,7 @@ export function summarizeCodeAgentPayload(payload, { httpStatus = null, traceId
export function classifyCodeAgentBrowserJourney({ responseSummary, httpOk = false, httpStatus = null, ui = null } = {}) {
const providerBlock = classifyCodeAgentProviderBlock(responseSummary, { httpStatus });
const uiCompleted = ui?.agentChatStatus === "DEV-LIVE 回复" || ui?.agentChatStatus === "已回复" || ui?.completedMessageVisible === true;
const uiFailed = ui?.failedMessageVisible === true || ui?.agentChatStatus === "发送失败" || ui?.agentChatStatus === "BLOCKED 凭证缺口";
const uiFailed = ui?.failedMessageVisible === true || blockedCodeAgentUiLabels.has(ui?.agentChatStatus);
const evidence = inspectSanitizedCompletionEvidence(responseSummary);
const backendCompleted =
httpOk &&
@@ -148,6 +148,8 @@ const requiredCodeAgentEvidenceTerms = Object.freeze([
"message-evidence-chip"
]);
const blockedCodeAgentUiLabels = Object.freeze(["服务受阻", "BLOCKED 凭证缺口"]);
const forbiddenWritePatterns = Object.freeze([
/callRpc\(\s*["']hardware\./u,
/hardware\.operation\.request/u,
@@ -317,7 +319,7 @@ function runStaticSmoke() {
addCheck(checks, blockers, "code-agent-provider-readiness-visibility", hasCodeAgentReadinessVisibility(files), "Workbench shows provider credential blockers and only real completed replies can become DEV-LIVE.", {
blocker: "observability_blocker",
evidence: ["BLOCKED 凭证缺口", "provider_unavailable", "OPENAI_API_KEY", "hwlab-code-agent-provider/openai-api-key", "completed -> dev-live guard"]
evidence: ["服务受阻 or legacy BLOCKED 凭证缺口", "provider_unavailable classifier", "completed -> dev-live guard"]
});
addCheck(checks, blockers, "code-agent-completed-evidence-visible", hasCodeAgentCompletedEvidenceVisibility(files), "Completed Code Agent replies expose provider/model/trace/conversation evidence and reject echo/mock/stub completions.", {
@@ -331,7 +333,7 @@ function runStaticSmoke() {
"DEV-LIVE 回复",
"SOURCE 回复",
"发送失败",
"BLOCKED 凭证缺口",
"服务受阻 or legacy BLOCKED 凭证缺口",
"message-evidence/details/chips"
]
});
@@ -1207,10 +1209,9 @@ function hasCodeAgentReadinessVisibility({ html, app }) {
/codeAgentAvailability/u.test(app) &&
/codeAgentAvailabilityFrom/u.test(app) &&
/latestCompletedAgentMessage/u.test(app) &&
/BLOCKED 凭证缺口/u.test(app) &&
blockedCodeAgentUiLabels.some((label) => app.includes(label)) &&
/provider_unavailable/u.test(app) &&
/OPENAI_API_KEY/u.test(app) &&
/hwlab-code-agent-provider\/openai-api-key/u.test(app) &&
/只有真实 completed 回复才标 DEV-LIVE/u.test(app) &&
/不能因为只有 conversationId 标为开发实况/u.test(app) &&
/isTrustedCodeAgentProvider/u.test(app) &&
@@ -357,8 +357,7 @@ test("rejects accepted workbench evidence that mixes provider_unavailable blocke
await assertRejected(report, /provider_unavailable, OPENAI_API_KEY, or providerStatus blockers/);
});
test("accepts blocked workbench evidence for provider HTTP 502 without promoting completion", async () => {
const report = cloneBaseReport();
function applyProviderBlockedJourney(report, { uiLabel = "发送失败" } = {}) {
const journey = report.checks.find((check) => check.id === "live-code-agent-browser-journey");
report.status = "blocked";
report.evidenceLevel = "BLOCKED";
@@ -392,7 +391,7 @@ test("accepts blocked workbench evidence for provider HTTP 502 without promoting
"provider=openai-responses",
"model=gpt-5.5",
"traceId=trc_provider_502",
"uiStatus=发送失败",
`uiStatus=${uiLabel}`,
"blocker=provider-upstream"
];
journey.observations.request.status = 200;
@@ -415,7 +414,75 @@ test("accepts blocked workbench evidence for provider HTTP 502 without promoting
providerStatus: 502,
reason: "Code Agent HTTP non-2xx status 502 means the upstream response is blocked, not completed"
};
journey.observations.ui.agentChatStatus = "发送失败";
journey.observations.ui.agentChatStatus = uiLabel;
journey.observations.ui.inputCleared = false;
journey.observations.ui.completedMessageVisible = false;
journey.observations.ui.failedMessageVisible = true;
journey.observations.networkEvents = [
{
method: "POST",
status: 200,
urlPath: "/v1/agent/chat",
body: journey.observations.response
}
];
return report;
}
test("accepts blocked workbench evidence for provider HTTP 502 without promoting completion", async () => {
const report = applyProviderBlockedJourney(cloneBaseReport());
await withReport(report, async (reportPath) => {
const result = runValidator(reportPath);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /validated 1 dev-gate report JSON file/);
});
});
test("accepts sanitized 服务受阻 provider blocker wording only as blocked evidence", async () => {
const report = applyProviderBlockedJourney(cloneBaseReport(), { uiLabel: "服务受阻" });
await withReport(report, async (reportPath) => {
const result = runValidator(reportPath);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /validated 1 dev-gate report JSON file/);
});
});
test("rejects sanitized 服务受阻 provider blocker evidence promoted to full workbench acceptance", async () => {
const report = fullAcceptanceReport();
const journey = report.checks.find((check) => check.id === "live-code-agent-browser-journey");
journey.status = "pass";
journey.summary = "Incorrectly promoted provider blocker evidence.";
journey.evidence = [
"POST /v1/agent/chat HTTP 200",
"status=failed",
"provider=openai-responses",
"model=gpt-5.5",
"traceId=trc_provider_502",
"uiStatus=服务受阻",
"blocker=provider-upstream"
];
journey.observations.response = {
status: "failed",
provider: "openai-responses",
model: "gpt-5.5",
backend: "hwlab-cloud-api/openai-responses",
traceId: "trc_provider_502",
hasReply: false,
error: {
code: "provider_unavailable",
missingEnv: [],
providerStatus: 502
}
};
journey.observations.classification = {
status: "blocked",
blocker: "provider-upstream",
providerStatus: 502,
reason: "Code Agent HTTP non-2xx status 502 means the upstream response is blocked, not completed"
};
journey.observations.ui.agentChatStatus = "服务受阻";
journey.observations.ui.inputCleared = false;
journey.observations.ui.completedMessageVisible = false;
journey.observations.ui.failedMessageVisible = true;
@@ -428,11 +495,7 @@ test("accepts blocked workbench evidence for provider HTTP 502 without promoting
}
];
await withReport(report, async (reportPath) => {
const result = runValidator(reportPath);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /validated 1 dev-gate report JSON file/);
});
await assertRejected(report, /response\.status|provider_unavailable, OPENAI_API_KEY, or providerStatus blockers/);
});
test("rejects accepted workbench evidence that hides providerStatus 502 in network events", async () => {
+2 -1
View File
@@ -42,6 +42,7 @@ const evidenceLevels = new Set(["SOURCE", "LOCAL", "DRY-RUN", "DEV-LIVE", "BLOCK
const devCloudWorkbenchRuntimeIdentityMaxSkewMs = 15 * 60 * 1000;
const syntheticAcceptedCodeAgentModels = new Set(["gpt-5"]);
const deploymentPreflightCodeAgentErrorCode = "deployment_identity_preflight";
const blockedCodeAgentUiLabels = new Set(["发送失败", "服务受阻", "BLOCKED 凭证缺口"]);
const requiredValidationCommands = [
"node --check scripts/validate-dev-gate-report.mjs",
"node scripts/validate-dev-gate-report.mjs"
@@ -1548,7 +1549,7 @@ function assertDevCloudWorkbenchCodeAgentBlocked(report, label, journey) {
assertObject(journey.observations.ui, `${label}.live-code-agent-browser-journey.ui`);
assert.ok(
["发送失败", "BLOCKED 凭证缺口"].includes(journey.observations.ui.agentChatStatus),
blockedCodeAgentUiLabels.has(journey.observations.ui.agentChatStatus),
`${label}.live-code-agent-browser-journey.ui.agentChatStatus`
);
assert.equal(journey.observations.ui.completedMessageVisible, false, `${label}.live-code-agent-browser-journey.ui.completedMessageVisible`);