fix: address Cloud Workbench feedback 117-120

This commit is contained in:
HWLAB Code Queue
2026-05-22 12:08:40 +00:00
parent 0614202f81
commit d44390ddbd
6 changed files with 421 additions and 203 deletions
+103 -7
View File
@@ -33,11 +33,44 @@ const chineseWorkbenchLabels = Object.freeze([
"HWLAB 云工作台",
"硬件资源",
"Agent 对话",
"执行轨迹",
"工作清单",
"可信记录",
"内部复核",
"使用说明"
]);
const defaultHomepageForbiddenTerms = Object.freeze([
"Gate",
"诊断",
"验收",
"M0-M5",
"BLOCKED",
"db-live",
"m3-hardware-loop-runtime",
"执行轨迹"
]);
const diagnosticsRequiredTerms = Object.freeze([
"Gate / 诊断 / 验收",
"M0-M5",
"BLOCKED",
"DB live readiness",
"patch-panel DO1 -> DI1"
]);
const incompletePrimaryNavLabels = Object.freeze(["台", "证", "诊", "帮"]);
const requiredWiringColumns = Object.freeze([
"源设备",
"源端口",
"接线盘",
"目标设备",
"目标端口",
"状态",
"证据来源",
"trace/evidence"
]);
const workbenchMarkers = Object.freeze([
"data-app-shell",
"workbench-shell",
@@ -46,7 +79,7 @@ const workbenchMarkers = Object.freeze([
"resource-tree",
"center-workspace",
"conversation-list",
"trace-list",
"task-list",
"right-sidebar",
"hardware-list",
"command-form"
@@ -129,6 +162,26 @@ function runStaticSmoke() {
evidence: chineseWorkbenchLabels
});
addCheck(checks, blockers, "feedback-117-default-no-backstage", defaultHomepageHidesBackstage(files), "Default workbench hides Gate, diagnostics, acceptance, BLOCKED, and M0-M5 backstage information.", {
blocker: "runtime_blocker",
evidence: defaultHomepageForbiddenTerms
});
addCheck(checks, blockers, "feedback-118-complete-nav", hasCompletePrimaryNavigation(files.html), "Primary navigation uses complete Chinese labels and removes the left-rail trusted-records shortcut.", {
blocker: "contract_blocker",
evidence: ["工作台", "内部复核", "使用说明", "资源树"]
});
addCheck(checks, blockers, "feedback-119-gate-retains-diagnostics", gateRetainsDiagnostics(files.html, files.app), "Internal Gate page retains BLOCKED/M0-M5 diagnostics and the DB live plus patch-panel DO1 -> DI1 unblock path.", {
blocker: "contract_blocker",
evidence: diagnosticsRequiredTerms
});
addCheck(checks, blockers, "feedback-120-wiring-table", wiringTableContract(files.html), "Patch-panel wiring panel is a table with source, target, status, evidence source, and trace/evidence columns.", {
blocker: "contract_blocker",
evidence: requiredWiringColumns
});
addCheck(checks, blockers, "outer-scroll-contract", hasScrollLockContract(files.styles), "Outer page scrolling is locked and internal workbench panes own scrolling.", {
blocker: "contract_blocker",
evidence: ["html/body overflow hidden", "workbench-shell 100vh/100dvh overflow hidden", ".view overflow auto"]
@@ -221,7 +274,15 @@ function baseReport({ mode, status, checks, blockers, evidenceLevel, devLive, he
return {
status,
task: "DC-DCSN-P0-2026-003",
refs: ["pikasTech/HWLAB#108", "pikasTech/HWLAB#99", "pikasTech/HWLAB#78"],
refs: [
"pikasTech/HWLAB#108",
"pikasTech/HWLAB#99",
"pikasTech/HWLAB#78",
"pikasTech/HWLAB#117",
"pikasTech/HWLAB#118",
"pikasTech/HWLAB#119",
"pikasTech/HWLAB#120"
],
mode,
url,
generatedAt: new Date().toISOString(),
@@ -299,14 +360,49 @@ function secondaryViewsAreNotDefault({ html, app }) {
const unhiddenSecondaryViews = [...html.matchAll(/<section\b[^>]*\bdata-view=["']([^"']+)["'][^>]*>/gu)]
.filter((match) => match[1] !== "workspace")
.filter((match) => !/\bhidden\b/u.test(match[0]));
const diagnosticsPanel = html.match(/<section\b[^>]*\bdata-side-panel=["']diagnostics["'][^>]*>/u)?.[0] ?? "";
return finalFallback === "workspace" && unhiddenSecondaryViews.length === 0 && /\bhidden\b/u.test(diagnosticsPanel);
return finalFallback === "workspace" && unhiddenSecondaryViews.length === 0;
}
function firstTagForDataView(html, view) {
return html.match(new RegExp(`<section\\b[^>]*\\bdata-view=["']${escapeRegExp(view)}["'][^>]*>`, "u"))?.[0] ?? null;
}
function sectionSource(source, viewId) {
const start = source.search(new RegExp(`<section\\b[^>]*\\bdata-view=["']${escapeRegExp(viewId)}["'][^>]*>`, "u"));
if (start === -1) return "";
const next = source.slice(start + 1).search(/<section\b[^>]*\bdata-view=["'][^"']+["'][^>]*>/u);
return next === -1 ? source.slice(start) : source.slice(start, start + 1 + next);
}
function beforeSection(source, viewId) {
const index = source.search(new RegExp(`<section\\b[^>]*\\bdata-view=["']${escapeRegExp(viewId)}["'][^>]*>`, "u"));
return index === -1 ? source : source.slice(0, index);
}
function defaultHomepageHidesBackstage({ html }) {
const workspaceHtml = sectionSource(html, "workspace");
const visibleShellHtml = `${beforeSection(html, "help")}\n${workspaceHtml}`;
return defaultHomepageForbiddenTerms.every((term) => !visibleShellHtml.includes(term));
}
function hasCompletePrimaryNavigation(html) {
const navLabelsPresent = ["工作台", "内部复核", "使用说明", "资源树"].every((label) => html.includes(`>${label}<`));
const legacyLabelsAbsent = incompletePrimaryNavLabels.every((label) => !html.includes(`>${label}<`));
const noLeftSideRecordsShortcut = !/data-side-tab-jump=["']records["']/u.test(html);
return navLabelsPresent && legacyLabelsAbsent && noLeftSideRecordsShortcut;
}
function gateRetainsDiagnostics(html, app) {
const gateHtml = sectionSource(html, "gate");
const gateSource = `${gateHtml}\n${app}`;
return diagnosticsRequiredTerms.every((term) => gateSource.includes(term));
}
function wiringTableContract(html) {
const wiringPanel = html.match(/<section\b[^>]*\bdata-side-panel=["']wiring["'][\s\S]*?<\/section>/u)?.[0] ?? "";
return requiredWiringColumns.every((column) => wiringPanel.includes(`<th>${column}</th>`));
}
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]);
@@ -323,7 +419,7 @@ function hasScrollLockContract(styles) {
/body\s*>\s*\[data-app-shell\]\s*\{[^\}]*min-height:\s*0;/su.test(styles) &&
/\.workbench-shell\s*\{[^\}]*height:\s*100(?:d)?vh;[^\}]*overflow:\s*hidden;/su.test(styles) &&
/\.view\s*\{[^\}]*min-height:\s*0;[^\}]*overflow:\s*auto;/su.test(styles) &&
/\.(?:resource-tree|compact-list|conversation-list|trace-list|hardware-list)[^\{]*\{[^\}]*min-height:\s*0;[^\}]*overflow:\s*auto;/su.test(styles)
/\.(?:resource-tree|compact-list|conversation-list|trace-list|task-list|hardware-list)[^\{]*\{[^\}]*min-height:\s*0;[^\}]*overflow:\s*auto;/su.test(styles)
);
}
@@ -499,7 +595,7 @@ async function inspectLiveDom(url) {
gateHidden: gate ? gate.hidden : null,
diagnosticsHidden: diagnostics ? diagnostics.hidden : null,
outerScrollLocked: document.documentElement.scrollHeight <= document.documentElement.clientHeight + 2,
labelsPresent: ["硬件资源", "Agent 对话", "执行轨迹", "可信记录", "使用说明"].every((label) => text.includes(label))
labelsPresent: ["硬件资源", "Agent 对话", "工作清单", "可信记录", "使用说明"].every((label) => text.includes(label))
};
});
const pass =