feat: group Cloud Workbench trusted records

Merge PR #146 after Code Queue rebase to main a444387 and validation. This improves the deployed workbench UI surface for trusted records with explicit DEV-LIVE/SOURCE/DRY-RUN/BLOCKED labels. It does not claim M3 DEV-LIVE and does not change PROD or secrets.
This commit is contained in:
Lyon
2026-05-23 00:24:27 +08:00
committed by GitHub
parent a44438779f
commit 88e647e380
6 changed files with 499 additions and 35 deletions
@@ -73,6 +73,21 @@ const requiredWiringColumns = Object.freeze([
"trace/evidence"
]);
const requiredTrustedRecordTerms = Object.freeze([
"Code Agent 对话记录",
"接线/operation",
"audit 记录",
"evidence 证据",
"trace/messageId",
"audit.event.query + SOURCE 回退",
"evidence.record.query + SOURCE 回退",
"conversationId、traceId 和 messageId",
"失败原因=",
"只读 audit 缺少 M3 patch-panel live report / operation / trace / evidence 绑定",
"只读 evidence 缺少 M3 patch-panel live report / operation / trace / audit 绑定",
"当前显示 SOURCE 回退,不能冒充 DEV-LIVE"
]);
const requiredHardwareStatusTerms = Object.freeze([
"hardware-source-status",
"hardwareGroup",
@@ -208,6 +223,11 @@ function runStaticSmoke() {
evidence: requiredWiringColumns
});
addCheck(checks, blockers, "trusted-record-groups", trustedRecordGroups(files), "Trusted Records panel groups Code Agent, wiring/operation, audit, and evidence summaries with source labels and live-failure fallback.", {
blocker: "observability_blocker",
evidence: requiredTrustedRecordTerms
});
addCheck(checks, blockers, "hardware-status-rich-structure", hardwareStatusStructureContract(files), "Right hardware status renders Gateway/Box, DI/DO/AI/AO/FREQ, patch-panel, and DO1 -> DI1 source-labeled link state.", {
blocker: "contract_blocker",
evidence: requiredHardwareStatusTerms
@@ -512,6 +532,38 @@ function defaultTopologyIsM3Only() {
);
}
function trustedRecordGroups({ html, app, styles }) {
const source = `${html}\n${app}\n${styles}`;
return (
/id=["']records-list["']/u.test(html) &&
/function\s+renderRecords\s*\(/u.test(app) &&
/function\s+codeAgentRecordCards\s*\(/u.test(app) &&
/function\s+operationRecordCards\s*\(/u.test(app) &&
/function\s+auditRecordCards\s*\(/u.test(app) &&
/function\s+evidenceRecordCards\s*\(/u.test(app) &&
/function\s+auditCard\s*\(/u.test(app) &&
/function\s+evidenceCard\s*\(/u.test(app) &&
/function\s+recordGroup\s*\(/u.test(app) &&
/function\s+recordField\s*\(/u.test(app) &&
/function\s+sourceKindLabel\s*\(/u.test(app) &&
/function\s+liveFailureCard\s*\(/u.test(app) &&
/state\.liveSurface/u.test(app) &&
/renderRecords\(state\.liveSurface\)/u.test(app) &&
/traceId: error\.traceId \|\| traceId/u.test(app) &&
/error\.traceId = traceId/u.test(app) &&
requiredTrustedRecordTerms.every((term) => source.includes(term)) &&
/\.record-group\s*\{/u.test(styles) &&
/\.record-group-title\s*\{/u.test(styles) &&
/\.record-group-list\s*\{/u.test(styles) &&
/replaceChildren\(el\.recordsList, \.\.\.groups\)/u.test(app) &&
/gateSummary\.operations\.map/u.test(app) &&
/gateSummary\.auditEvents\.slice\(0,\s*4\)\.map/u.test(app) &&
/gateSummary\.evidenceRecords\.map/u.test(app) &&
/message\.status === "failed" \? "blocked" : "source"/u.test(app) &&
!/message\.status === "completed"\s*\?\s*"dev-live"/u.test(app)
);
}
function finalRouteFallback(app) {
const body = app.match(/function\s+routeFromLocation\s*\(\)\s*\{([\s\S]*?)\n\}/u)?.[1] ?? "";
const returns = [...body.matchAll(/return\s+["']([^"']+)["']\s*;/gu)].map((match) => match[1]);
@@ -591,6 +643,8 @@ function sourceEvidenceNotDevLive(source) {
const m3 = gateSummary.milestones.find((item) => item.id === "M3");
return (
m3?.status === "blocked" &&
gateSummary.operations.every((operation) => operation.dryRun === true) &&
gateSummary.auditEvents.every((event) => event.dryRun === true) &&
gateSummary.evidenceRecords.every((record) => record.dryRun === true) &&
gateSummary.safety.allowNetwork === false &&
["来源 SOURCE", "演练 DRY-RUN", "开发实况 DEV-LIVE", "阻塞 BLOCKED"].every((label) => source.includes(label))