test: accept sanitized blocked code agent evidence
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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`);
|
||||
|
||||
Reference in New Issue
Block a user