Merge pull request #272 from pikasTech/fix/workbench-user-facing-followup-117-120
fix: finish workbench first viewport cleanup
This commit is contained in:
@@ -71,12 +71,14 @@ const defaultHomepageForbiddenTerms = Object.freeze([
|
||||
"诊断",
|
||||
"验收",
|
||||
"M0-M5",
|
||||
"M3 基本",
|
||||
"BLOCKED",
|
||||
"SOURCE",
|
||||
"DRY-RUN",
|
||||
"DEV-LIVE",
|
||||
"db-live",
|
||||
"m3-hardware-loop-runtime",
|
||||
"trace/evidence",
|
||||
"执行轨迹",
|
||||
"runtime_durable_adapter_query_blocked",
|
||||
"OPENAI_API_KEY",
|
||||
@@ -108,7 +110,7 @@ const requiredWiringColumns = Object.freeze([
|
||||
"目标端口",
|
||||
"状态",
|
||||
"证据来源",
|
||||
"trace/evidence"
|
||||
"轨迹/证据"
|
||||
]);
|
||||
|
||||
const requiredTrustedRecordTerms = Object.freeze([
|
||||
@@ -351,7 +353,7 @@ function runStaticSmoke() {
|
||||
evidence: [...helpRouteAliases, "routeFromLocation final fallback is workspace"]
|
||||
});
|
||||
|
||||
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.", {
|
||||
addCheck(checks, blockers, "feedback-120-wiring-table", wiringTableContract(files.html), "Patch-panel wiring panel is a table with source, target, status, evidence source, and 轨迹/证据 columns.", {
|
||||
blocker: "contract_blocker",
|
||||
evidence: requiredWiringColumns
|
||||
});
|
||||
|
||||
@@ -871,7 +871,7 @@ function renderTaskList() {
|
||||
},
|
||||
{
|
||||
title: "2. 核对接线",
|
||||
detail: "打开右侧“接线”表,确认源设备、接线盘、目标设备和 trace/evidence 是否匹配。",
|
||||
detail: "打开右侧“接线”表,确认源设备、接线盘、目标设备和轨迹/证据是否匹配。",
|
||||
tone: "source"
|
||||
},
|
||||
{
|
||||
@@ -993,7 +993,7 @@ function renderHardwareStatus(summary) {
|
||||
value: statusLabel(patchPanel.state),
|
||||
detail: m3Live
|
||||
? `接线盘实况记录 ${liveM3Evidence.patchPanelLiveReport.reportId ?? liveM3Evidence.patchPanelLiveReport.patchPanelStatusId} 已绑定目标链路。`
|
||||
: `${patchPanel.activeConnectionCount} 条内置接线记录;工作台只展示目标链路,未升级为实况证据。`,
|
||||
: `${patchPanel.activeConnectionCount} 条内置接线记录;工作台只展示目标链路,未升级为可信闭环证据。`,
|
||||
sourceKind: m3Live ? "DEV-LIVE" : "SOURCE",
|
||||
tone: patchTone
|
||||
},
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
|
||||
- Agent 对话区显示用户工作台范围、建议起步方式和可查看内容,不展示 Gate、诊断、验收、BLOCKED 或 M0-M5 执行轨迹。
|
||||
- 工作清单区提供描述目标、核对接线、留存记录三类用户动作。
|
||||
- 底部输入栏用于 Code Agent 对话;右侧 M3 基本 IO 控制只调用 same-origin `/v1/m3/io`,由 `hwlab-cloud-api` 通过 gateway-simu、box-simu、`hwlab-patch-panel` 执行,不从浏览器直连 gateway/box-simu,也不调用泛化硬件写 RPC。
|
||||
- 底部输入栏用于 Code Agent 对话;右侧基础 IO 控制只调用 same-origin `/v1/m3/io`,由 `hwlab-cloud-api` 通过 gateway-simu、box-simu、`hwlab-patch-panel` 执行,不从浏览器直连 gateway/box-simu,也不调用泛化硬件写 RPC。
|
||||
|
||||
## 右侧硬件与状态面板
|
||||
|
||||
- BOX-SIMU / Gateway-SIMU / Patch Panel 卡片展示只读计数、source fallback 与硬件写入边界。
|
||||
- 接线:`hwlab-patch-panel` 面板使用表格展示源设备、源端口、接线盘、目标设备、目标端口、状态、证据来源和 trace/evidence。
|
||||
- 接线:`hwlab-patch-panel` 面板使用表格展示源设备、源端口、接线盘、目标设备、目标端口、状态、证据来源和轨迹/证据。
|
||||
- M3 虚拟硬件可信闭环的上位约束是:`res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1`。只有观察到这条完整链路并带有可信记录时,才可作为 M3 DEV-LIVE 依据。
|
||||
- Web 前端不会新增 `hardware.*` 写 RPC、patch-panel 写接口、audit 写接口或 evidence 写接口;M3 控制的 operation/audit/evidence 字段由 `/v1/m3/io` 响应返回,runtime durable 未 green 时必须显示 BLOCKED。
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
## 内部复核页
|
||||
|
||||
- Gate / 诊断 / 验收:从活动栏 `内部复核` 进入,作为验收复核二级页面保留。
|
||||
- 解阻路径:先完成 DB live readiness,让 same-origin `/health/live` 能证明 db.ready=true、db.connected=true;再通过 `hwlab-patch-panel` 建立 `res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1`,并附带可信 trace/evidence。
|
||||
- 解阻路径:先完成 DB live readiness,让 same-origin `/health/live` 能证明 db.ready=true、db.connected=true;再通过 `hwlab-patch-panel` 建立 `res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1`,并附带可信轨迹/证据。
|
||||
- 诊断探测检查 same-origin `/health/live`、same-origin `/v1`、`/v1/m3/io` 合同与只读 `/json-rpc` 方法。允许的 JSON-RPC 方法包括 `system.health`、`cloud.adapter.describe`、`audit.event.query`、`evidence.record.query`。
|
||||
|
||||
## 来源标签含义
|
||||
|
||||
@@ -255,10 +255,10 @@
|
||||
|
||||
<section class="side-panel active" id="panel-control" role="tabpanel" aria-labelledby="tab-control" data-side-panel="control">
|
||||
<div class="panel-title-row">
|
||||
<h2>M3 基本 IO 控制</h2>
|
||||
<h2>基础 IO 控制</h2>
|
||||
<span class="state-tag tone-blocked" id="m3-control-status">探测中</span>
|
||||
</div>
|
||||
<form class="m3-control-form" id="m3-control-form" aria-label="M3 DO1 写入">
|
||||
<form class="m3-control-form" id="m3-control-form" aria-label="DO1 写入">
|
||||
<div class="m3-control-grid">
|
||||
<label>
|
||||
<span>Gateway</span>
|
||||
@@ -310,7 +310,7 @@
|
||||
<th>目标端口</th>
|
||||
<th>状态</th>
|
||||
<th>证据来源</th>
|
||||
<th>trace/evidence</th>
|
||||
<th>轨迹/证据</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="wiring-body">
|
||||
|
||||
@@ -284,7 +284,7 @@ for (const helpTerm of [
|
||||
assert.match(helpMarkdown, new RegExp(escapeRegExp(helpTerm)), `missing help term: ${helpTerm}`);
|
||||
}
|
||||
const defaultFirstViewportText = visibleTextFromHtml(defaultFirstViewportHtml(html));
|
||||
for (const backendTerm of ["Gate", "诊断", "验收", "BLOCKED", "SOURCE", "DRY-RUN", "DEV-LIVE", "M0-M5", "执行轨迹", "OPENAI_API_KEY", "Secret", "secretRef", "hwlab-code-agent-provider", "m3-route-required", "waiting-for-dev-live"]) {
|
||||
for (const backendTerm of ["Gate", "诊断", "验收", "BLOCKED", "SOURCE", "DRY-RUN", "DEV-LIVE", "M0-M5", "M3 基本", "trace/evidence", "执行轨迹", "OPENAI_API_KEY", "Secret", "secretRef", "hwlab-code-agent-provider", "m3-route-required", "waiting-for-dev-live"]) {
|
||||
assert.equal(textHasForbiddenTerm(defaultFirstViewportText, backendTerm), false, `default workbench must not expose backend term ${backendTerm}`);
|
||||
}
|
||||
for (const fullNavLabel of ["工作台", "内部复核", "使用说明", "资源树"]) {
|
||||
@@ -294,7 +294,7 @@ for (const oneCharNavLabel of [">台<", ">证<", ">诊<", ">帮<"]) {
|
||||
assert.doesNotMatch(html, new RegExp(escapeRegExp(oneCharNavLabel)), `legacy one-character nav label must be removed: ${oneCharNavLabel}`);
|
||||
}
|
||||
assert.doesNotMatch(html, /data-side-tab-jump="records"/u, "trusted records must not remain a left-side standalone shortcut");
|
||||
for (const wiringHeader of ["源设备", "源端口", "接线盘", "目标设备", "目标端口", "状态", "证据来源", "trace/evidence"]) {
|
||||
for (const wiringHeader of ["源设备", "源端口", "接线盘", "目标设备", "目标端口", "状态", "证据来源", "轨迹/证据"]) {
|
||||
assert.match(html, new RegExp(`<th>${escapeRegExp(wiringHeader)}<\\/th>`), `missing wiring column ${wiringHeader}`);
|
||||
}
|
||||
assert.match(html, /id="unblock-list"/);
|
||||
|
||||
@@ -68,9 +68,9 @@ export function runM3ControlPanelGuard() {
|
||||
function assertControlPanelDom(html) {
|
||||
const controlPanel = elementSectionById(html, "panel-control");
|
||||
assert.match(controlPanel, /data-side-panel="control"/u, "right-side control tab panel must be present");
|
||||
assert.match(controlPanel, /<h2>M3 基本 IO 控制<\/h2>/u, "M3 control panel title must be user-visible");
|
||||
assert.match(controlPanel, /<h2>基础 IO 控制<\/h2>/u, "M3 control panel title must be user-visible without milestone shorthand");
|
||||
assert.match(controlPanel, /id="m3-control-status"[^>]*>探测中</u, "M3 control status starts as probing, not live");
|
||||
assert.match(controlPanel, /<form[^>]+id="m3-control-form"[^>]+aria-label="M3 DO1 写入"/u, "DO write form must be labeled");
|
||||
assert.match(controlPanel, /<form[^>]+id="m3-control-form"[^>]+aria-label="DO1 写入"/u, "DO write form must be labeled");
|
||||
assert.match(controlPanel, /id="m3-gateway-select"[\s\S]*value="gwsimu_1"[\s\S]*gateway-simu-1/u, "DO write uses source gateway");
|
||||
assert.match(controlPanel, /id="m3-box-select"[\s\S]*value="res_boxsimu_1"[\s\S]*box-simu-1 \/ res_boxsimu_1/u, "DO write uses source box resource");
|
||||
assert.match(controlPanel, /id="m3-port-select"[\s\S]*value="DO1"[\s\S]*DO1/u, "DO write exposes only DO1");
|
||||
@@ -85,7 +85,7 @@ function assertControlPanelDom(html) {
|
||||
for (const value of ["res_boxsimu_1", "DO1", "hwlab-patch-panel", "res_boxsimu_2", "DI1"]) {
|
||||
assert.match(wiringPanel, new RegExp(escapeRegExp(value), "u"), `wiring panel missing ${value}`);
|
||||
}
|
||||
for (const header of ["源设备", "源端口", "接线盘", "目标设备", "目标端口", "状态", "证据来源", "trace/evidence"]) {
|
||||
for (const header of ["源设备", "源端口", "接线盘", "目标设备", "目标端口", "状态", "证据来源", "轨迹/证据"]) {
|
||||
assert.match(wiringPanel, new RegExp(`<th>${escapeRegExp(header)}<\\/th>`, "u"), `wiring table missing ${header}`);
|
||||
}
|
||||
assert.match(wiringPanel, /待可信记录/u, "static wiring row must stay blocked");
|
||||
|
||||
Reference in New Issue
Block a user