fix: clean workbench first viewport labels

This commit is contained in:
Code Queue Review
2026-05-23 06:23:40 +00:00
parent 29686823c5
commit 61ee5779a4
5 changed files with 217 additions and 106 deletions
+82 -16
View File
@@ -72,6 +72,9 @@ const defaultHomepageForbiddenTerms = Object.freeze([
"验收",
"M0-M5",
"BLOCKED",
"SOURCE",
"DRY-RUN",
"DEV-LIVE",
"db-live",
"m3-hardware-loop-runtime",
"执行轨迹",
@@ -133,7 +136,7 @@ const requiredHardwareStatusTerms = Object.freeze([
"Patch Panel 连线",
"DO1 -> patch-panel -> DI1",
"AI/AO/FREQ",
"future capability",
"后续能力",
"SOURCE",
"DEV-LIVE",
"BLOCKED"
@@ -1109,14 +1112,12 @@ function beforeSection(source, viewId) {
}
function defaultHomepageHidesBackstage({ html }) {
const workspaceHtml = sectionSource(html, "workspace");
const visibleShellHtml = `${beforeSection(html, "help")}\n${workspaceHtml}`;
return defaultHomepageForbiddenTerms.every((term) => !visibleShellHtml.includes(term));
const firstViewportText = visibleTextFromHtml(defaultFirstViewportHtml(html));
return defaultHomepageForbiddenTerms.every((term) => !textHasForbiddenTerm(firstViewportText, term));
}
function defaultWorkspaceIsSanitized({ html, app }) {
const workspaceHtml = sectionSource(html, "workspace");
const defaultShell = `${beforeSection(html, "help")}\n${workspaceHtml}`;
const defaultShellText = visibleTextFromHtml(defaultFirstViewportHtml(html));
const userFacingBodies = [
functionBody(app, "codeAgentStatusMessage"),
functionBody(app, "codeAgentPromptText"),
@@ -1127,14 +1128,47 @@ function defaultWorkspaceIsSanitized({ html, app }) {
functionBody(app, "renderWiringList")
].join("\n");
return (
!/OPENAI_API_KEY|hwlab-code-agent-provider|Secret|secretRef|m3-route-required|waiting-for-dev-live/u.test(defaultShell) &&
!/OPENAI_API_KEY|hwlab-code-agent-provider|Secret|secretRef|m3-route-required|waiting-for-dev-live|BLOCKED 凭证缺口/u.test(userFacingBodies) &&
defaultHomepageForbiddenTerms.every((term) => !textHasForbiddenTerm(defaultShellText, term)) &&
!/OPENAI_API_KEY|hwlab-code-agent-provider|Secret|secretRef|m3-route-required|waiting-for-dev-live|BLOCKED 凭证缺口|SOURCE fixture|DEV-LIVE/u.test(userFacingBodies) &&
/服务受阻/u.test(functionBody(app, "renderAgentChatStatus")) &&
/Code Agent 服务暂不可用/u.test(functionBody(app, "codeAgentBlockedSummary")) &&
/等待可信记录/u.test(functionBody(app, "renderWiringList"))
);
}
function defaultFirstViewportHtml(html) {
return `${beforeSection(html, "help")}\n${rightSidebarDefaultHtml(html)}`;
}
function rightSidebarDefaultHtml(html) {
const start = html.search(/<aside\b[^>]*\bclass=["'][^"']*\bright-sidebar\b[^"']*["'][^>]*>/u);
if (start === -1) return "";
const wiringStart = html.slice(start).search(/<section\b[^>]*\bid=["']panel-wiring["'][^>]*>/u);
if (wiringStart !== -1) return html.slice(start, start + wiringStart);
const end = html.slice(start).search(/<\/aside>/u);
return end === -1 ? html.slice(start) : html.slice(start, start + end);
}
function visibleTextFromHtml(fragment) {
return fragment
.replace(/<script\b[\s\S]*?<\/script>/giu, " ")
.replace(/<style\b[\s\S]*?<\/style>/giu, " ")
.replace(/<[^>]+>/gu, " ")
.replace(/&nbsp;/gu, " ")
.replace(/&gt;/gu, ">")
.replace(/&lt;/gu, "<")
.replace(/&amp;/gu, "&")
.replace(/\s+/gu, " ")
.trim();
}
function textHasForbiddenTerm(text, term) {
if (/^[A-Za-z][A-Za-z0-9-]*$/u.test(term)) {
return new RegExp(`\\b${escapeRegExp(term)}\\b`, "u").test(text);
}
return text.includes(term);
}
function hasCompletePrimaryNavigation(html) {
const navLabelsPresent = ["工作台", "内部复核", "使用说明", "资源树"].every((label) => html.includes(`>${label}<`));
const legacyLabelsAbsent = incompletePrimaryNavLabels.every((label) => !html.includes(`>${label}<`));
@@ -1190,7 +1224,7 @@ function hardwareStatusStructureContract({ html, app, styles }) {
/function\s+hasTrustedM3LiveEvidence\s*\(/u.test(app) &&
/function\s+linkStatusLabel\s*\(/u.test(app) &&
/function\s+normalizePort\s*\(/u.test(app) &&
/Web 不提供硬件写 RPC;不会新增伪硬件写操作。/u.test(app) &&
/页面不直连模拟器,也不伪造硬件状态。/u.test(app) &&
/\.hardware-section\s*\{/u.test(styles) &&
/\.hardware-row\s*\{/u.test(styles)
);
@@ -1370,8 +1404,8 @@ function hasCodeAgentReadinessVisibility({ html, app }) {
blockedCodeAgentUiLabels.some((label) => app.includes(label)) &&
/provider_unavailable/u.test(app) &&
/OPENAI_API_KEY/u.test(app) &&
/只有真实 completed 回复才标 DEV-LIVE/u.test(app) &&
/不能因为只有 conversationId 标为开发实况/u.test(app) &&
/只有真实完成回复才显示为完成/u.test(app) &&
/不能因为只有会话编号就当成实况完成/u.test(app) &&
/isTrustedCodeAgentProvider/u.test(app) &&
/Boolean\(value\?\.model\)/u.test(app) &&
/Boolean\(value\?\.backend\)/u.test(app) &&
@@ -1492,8 +1526,8 @@ function m3FixtureCountsNotDevLive(app) {
/hasTrustedPatchPanelLiveReport/u.test(liveEvidenceBody) &&
/serviceId\s*!==\s*M3_TRUSTED_ROUTE\.patchPanelServiceId/u.test(patchPanelReportBody) &&
/trustedM3Link\(\[link\]\)/u.test(patchPanelReportBody) &&
/counts 不是 patch-panel live report/u.test(linkDetailBody) &&
/SOURCE fixture 包含目标接线/u.test(linkDetailBody)
/只读运行计数已返回,但还不能证明接线盘闭环/u.test(linkDetailBody) &&
/来源拓扑包含目标接线/u.test(linkDetailBody)
);
}
@@ -1869,7 +1903,7 @@ async function inspectLiveDom(url) {
browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1366, height: 768 } });
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 8000 });
const dom = await page.evaluate(async () => {
const dom = await page.evaluate(async (forbiddenTerms) => {
const workspace = document.querySelector('[data-view="workspace"]');
const gate = document.querySelector('[data-view="gate"]');
const help = document.querySelector('[data-view="help"]');
@@ -1884,10 +1918,34 @@ async function inspectLiveDom(url) {
const box = element.getBoundingClientRect();
return style.visibility !== "hidden" && style.display !== "none" && box.width > 0 && box.height > 0;
};
const visibleTextInFirstViewport = () => {
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
const chunks = [];
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
const textContent = node.textContent?.replace(/\s+/gu, " ").trim();
if (!textContent) continue;
const parent = node.parentElement;
if (!parent) continue;
const style = getComputedStyle(parent);
if (style.visibility === "hidden" || style.display === "none") continue;
const box = parent.getBoundingClientRect();
const inViewport = box.width > 0 && box.height > 0 && box.bottom > 0 && box.right > 0 && box.top < window.innerHeight && box.left < window.innerWidth;
if (inViewport) chunks.push(textContent);
}
return chunks.join(" ").replace(/\s+/gu, " ").trim();
};
const textHasForbiddenTerm = (candidateText, term) => {
if (/^[A-Za-z][A-Za-z0-9-]*$/u.test(term)) {
const escaped = term.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
return new RegExp(`\\b${escaped}\\b`, "u").test(candidateText);
}
return candidateText.includes(term);
};
window.scrollTo(0, 240);
document.documentElement.scrollTop = 240;
document.body.scrollTop = 240;
await new Promise((resolve) => requestAnimationFrame(resolve));
const firstViewportText = visibleTextInFirstViewport();
const rootAfterScrollAttempt = {
windowScrollY: window.scrollY,
htmlScrollTop: document.documentElement.scrollTop,
@@ -1903,6 +1961,8 @@ async function inspectLiveDom(url) {
diagnosticsHidden: diagnostics ? diagnostics.hidden : null,
outerScrollLocked: document.documentElement.scrollHeight <= document.documentElement.clientHeight + 2,
rootAfterScrollAttempt,
firstViewportForbiddenPresent: forbiddenTerms.filter((term) => textHasForbiddenTerm(firstViewportText, term)),
firstViewportTextSample: firstViewportText.slice(0, 400),
labelsPresent: ["硬件资源", "Agent 对话", "工作清单", "可信记录", "使用说明"].every((label) => text.includes(label)),
coreControlsVisible: {
commandInput: visible("#command-input"),
@@ -1914,7 +1974,7 @@ async function inspectLiveDom(url) {
recordsTab: visible("#tab-records")
}
};
});
}, [...defaultHomepageForbiddenTerms]);
const pass =
dom.title === "HWLAB 云工作台" &&
dom.bodyOverflow === "hidden" &&
@@ -1927,12 +1987,18 @@ async function inspectLiveDom(url) {
dom.rootAfterScrollAttempt.windowScrollY === 0 &&
dom.rootAfterScrollAttempt.htmlScrollTop === 0 &&
dom.rootAfterScrollAttempt.bodyScrollTop === 0 &&
dom.firstViewportForbiddenPresent.length === 0 &&
dom.labelsPresent &&
Object.values(dom.coreControlsVisible).every(Boolean);
return {
status: pass ? "pass" : "blocked",
summary: pass ? "Browser DOM confirms the default workbench, core controls, and outer scroll lock." : "Browser DOM did not satisfy the workbench contract.",
evidence: [`title=${dom.title}`, `bodyOverflow=${dom.bodyOverflow}`, `htmlOverflow=${dom.htmlOverflow}`],
evidence: [
`title=${dom.title}`,
`bodyOverflow=${dom.bodyOverflow}`,
`htmlOverflow=${dom.htmlOverflow}`,
`firstViewportForbidden=${dom.firstViewportForbiddenPresent.join(",") || "none"}`
],
observations: dom
};
} catch (error) {