新增项目发布 readiness summary view model

This commit is contained in:
2026-06-16 10:43:18 +08:00
parent 7f937bd56c
commit 1d383b475c
9 changed files with 177 additions and 7 deletions

View File

@@ -9,7 +9,7 @@ turn-by-turn continuation notes and next-batch execution records.
## Current Status ## Current Status
- Active continuation file: `text14.txt` - Active continuation file: `text14.txt`
- Latest completed batch: project release gate result matrix SDK API - Latest completed batch: project release readiness summary view-model API
- Latest relevant commit: use `git log -1 --oneline` after each committed batch - Latest relevant commit: use `git log -1 --oneline` after each committed batch
- Working tree at tracker creation: clean - Working tree at tracker creation: clean
- Required policy: LinuxCNC remains the only CNC semantic source - Required policy: LinuxCNC remains the only CNC semantic source
@@ -43,7 +43,7 @@ The project is complete for the current scope when all of the following are true
| Native nc_files baseline | Stable | Last recorded Layer 1: `total 107`, `pass 101`, `expected_fail 6`, `unexpected_fail 0`. | | Native nc_files baseline | Stable | Last recorded Layer 1: `total 107`, `pass 101`, `expected_fail 6`, `unexpected_fail 0`. |
| Native sim configs baseline | Stable | Last recorded Layer 2: `total 159`, `pass 151`, `expected_fail 8`, `unexpected_fail 0`. | | Native sim configs baseline | Stable | Last recorded Layer 2: `total 159`, `pass 151`, `expected_fail 8`, `unexpected_fail 0`. |
| OPFS/session persistence | Stable for current scope | Browser smoke passes, SDK re-exports OPFS/session helpers including `readMachineSessionReadiness()` and `createMachineSessionPersistenceSummary()`, the UI exposes `getMachineSessionPersistenceSummary()`, and `docs/opfs-session-persistence.md` defines the project-level release gate. | | OPFS/session persistence | Stable for current scope | Browser smoke passes, SDK re-exports OPFS/session helpers including `readMachineSessionReadiness()` and `createMachineSessionPersistenceSummary()`, the UI exposes `getMachineSessionPersistenceSummary()`, and `docs/opfs-session-persistence.md` defines the project-level release gate. |
| SDK/API surface | Stable for current scope | SDK re-exports core, OPFS/session readiness/load helpers, sim-config staging, project release gate manifest/result matrix, project release readiness report/artifact validation plus URL loading workflow, INI panel entry/launcher/control-page/shell/workflow overview helpers, the session readiness workflow report, and the shell API surface inventory with a Node surface smoke. | | SDK/API surface | Stable for current scope | SDK re-exports core, OPFS/session readiness/load helpers, sim-config staging, project release gate manifest/result matrix/readiness summary view-model, project release readiness report/artifact validation plus URL loading workflow, INI panel entry/launcher/control-page/shell/workflow overview helpers, the session readiness workflow report, and the shell API surface inventory with a Node surface smoke. |
| Browser/UI workflow | Stable for current scope | Workflow overview embedding mount DOM closure is complete, and INI panel session workflows plus read-only control/shell handoff expose, render, fetch, validate, and report OPFS/session readiness, OPFS/session persistence summary, release readiness artifact validation/rendering, and API surface inventory before loading or reading WASM session state. | | Browser/UI workflow | Stable for current scope | Workflow overview embedding mount DOM closure is complete, and INI panel session workflows plus read-only control/shell handoff expose, render, fetch, validate, and report OPFS/session readiness, OPFS/session persistence summary, release readiness artifact validation/rendering, and API surface inventory before loading or reading WASM session state. |
| Workflow overview embedding | Stable for current scope | Report/display/render, DOM contract/readiness/renderer, mount result/display/render, mount DOM contract/readiness/renderer/wrapper exist. | | Workflow overview embedding | Stable for current scope | Report/display/render, DOM contract/readiness/renderer, mount result/display/render, mount DOM contract/readiness/renderer/wrapper exist. |
| Host/runtime boundary proof | Stable blocked state | Host smoke passes; runtime families remain blocked and `docs/host-runtime-boundary-handoff.md` records host readiness, promotion blockers, dispatch, and evidence gates. | | Host/runtime boundary proof | Stable blocked state | Host smoke passes; runtime families remain blocked and `docs/host-runtime-boundary-handoff.md` records host readiness, promotion blockers, dispatch, and evidence gates. |

View File

@@ -3429,3 +3429,60 @@ project_release_readiness_artifact_node_smoke=ok
继续只做实质能力推进。优先选择可执行 gate、SDK/API surface、或真实 browser workflow 继续只做实质能力推进。优先选择可执行 gate、SDK/API surface、或真实 browser workflow
不要做只整理断言或局部常量化的批次。 不要做只整理断言或局部常量化的批次。
四十四、2026-06-16 继续执行记录project release readiness summary view-model API
本轮继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力”推进,
把 project release readiness report 进一步封装成 dashboard/UI 可直接消费的 summary view-model。外部
CI 或页面现在可以直接读取稳定 statusLine 和 rows而不必自己解释 gate matrix、sim-config inventory
和 blocked runtime family 字段。
完成内容:
- `runtime/sdk/src/project-release-readiness.js` 新增:
- `createProjectReleaseReadinessSummaryViewModel()`
- 新 summary view-model 返回:
- `apiName: project-release-readiness-summary-view-model`
- `viewModelVersion: 1`
- `phase/ready`
- `title/statusText/detailText/statusLine`
- rows`gate-results`、`unknown-gates`、`sim-config-inventory`、`blocked-runtime-families`、`missing`
- 支持 ready、waiting、promoted blocked family 三类显示状态;
- `runtime/sdk/src/index.js` re-export 新 summary API
- `verify_project_release_gate_manifest.mjs` 覆盖 ready summary、default waiting summary、blocked promotion row
- `verify_sdk_surface.mjs` 覆盖 SDK export 和 ready statusLine
- `runtime/sdk/README.md`、`docs/project-release-handoff.md`、`verify_project_release_handoff_docs.mjs`
记录 summary API
- `PROJECT_COMPLETION_TRACKER.md` 同步 latest completed batch 和 SDK/API surface 状态;
- 未新增控制按钮;
- 未执行 release gate/action-plan
- 未解析 G-code
- 未解释 canonical events
- 未新增 JS CNC 语义;
- 未运行 opt-in runtime probes
- 未 promotion blocked runtime family
- 未改变 LinuxCNC interpreter、tool、parameter、planner、kinematics、remap 或 LinuxCNC-owned
runtime semantics。
验证已通过(先行 targeted
```bash
wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh
wasm-port/tests/sdk/node/verify_sdk_surface.sh
wasm-port/tests/docs/node/verify_project_release_handoff_docs.sh
wasm-port/tests/host/verify_project_release_readiness_artifact.sh wasm-port/tests/fixtures/project-release-readiness-ready.json
```
关键输出:
```text
project_release_gate_manifest_node_smoke=ok
sdk_surface_node_smoke=ok
project_release_handoff_docs_node_smoke=ok
project_release_readiness_artifact_node_smoke=ok
```
下一步工作计划:
继续只做实质能力推进。优先选择可执行 gate、SDK/API surface、或真实 browser workflow
不要做只整理断言或局部常量化的批次。

View File

@@ -40,11 +40,13 @@ continuation records are in `../text14.txt`.
observed outputs onto passed/unknown gate rows, and observed outputs onto passed/unknown gate rows, and
`createProjectReleaseReadinessReport()` for a machine-readable release `createProjectReleaseReadinessReport()` for a machine-readable release
readiness report covering the same manifest, result matrix, sim-config readiness report covering the same manifest, result matrix, sim-config
inventory baseline, and blocked runtime families. The report helper is inventory baseline, and blocked runtime families.
evidence driven and only reports `ready: true` when the caller supplies `createProjectReleaseReadinessSummaryViewModel()` turns that report into
release gate evidence such as `project_release_gate=ok`. The project release stable status text and rows for dashboards. The report helper is evidence
gate writes the same report to `build/project-release-readiness.json` and driven and only reports `ready: true` when the caller supplies release gate
validates it with `verify_project_release_readiness_artifact.sh`. evidence such as `project_release_gate=ok`. The project release gate writes
the same report to `build/project-release-readiness.json` and validates it
with `verify_project_release_readiness_artifact.sh`.
External tools can load that artifact with External tools can load that artifact with
`parseProjectReleaseReadinessArtifactJson()` and validate it with `parseProjectReleaseReadinessArtifactJson()` and validate it with
`createProjectReleaseReadinessArtifactValidation()`. `createProjectReleaseReadinessArtifactValidation()`.

View File

@@ -55,6 +55,7 @@ import {
createProjectReleaseGateResultMatrix, createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
createProjectReleaseReadinessSummaryViewModel,
createSessionSnapshot, createSessionSnapshot,
defaultMachinePaths, defaultMachinePaths,
gcodeFilenameFromProgramPath, gcodeFilenameFromProgramPath,
@@ -200,6 +201,9 @@ readiness report that embeds the same manifest and result matrix beside the
sim-config inventory baseline and blocked runtime families. It is evidence sim-config inventory baseline and blocked runtime families. It is evidence
driven: the default report is `ready: false` until the caller supplies observed driven: the default report is `ready: false` until the caller supplies observed
gate output such as `project_release_gate=ok` or explicit gate results. gate output such as `project_release_gate=ok` or explicit gate results.
`createProjectReleaseReadinessSummaryViewModel()` turns the report into stable
status text, a status line, and display rows for dashboards without executing
any gate.
This helper does not execute shell commands and does not inspect or implement This helper does not execute shell commands and does not inspect or implement
CNC behavior. It only packages project-level release evidence and blocked CNC behavior. It only packages project-level release evidence and blocked

View File

@@ -5,6 +5,7 @@ export {
createProjectReleaseGateResultMatrix, createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
createProjectReleaseReadinessSummaryViewModel,
parseProjectReleaseReadinessArtifactJson, parseProjectReleaseReadinessArtifactJson,
} from "./project-release-readiness.js"; } from "./project-release-readiness.js";
export { export {

View File

@@ -214,6 +214,58 @@ export function createProjectReleaseReadinessReport({
}; };
} }
export function createProjectReleaseReadinessSummaryViewModel(
report = createProjectReleaseReadinessReport(),
) {
const missing = Array.isArray(report?.missing) ? report.missing : [];
const gateResultMatrix = objectOrEmpty(report?.gateResultMatrix);
const simConfigInventory = objectOrEmpty(report?.simConfigInventory);
const blockedRuntimeFamilies = arrayOrEmpty(report?.blockedRuntimeFamilies);
const promotedBlockedFamilies = arrayOrEmpty(report?.promotedBlockedFamilies);
const ready = report?.ready === true;
const statusText = ready ? "Ready" : "Waiting";
const detailText = ready ? "No blocking reasons" : (missing.length > 0 ? missing.join(", ") : "release evidence missing");
return {
apiName: "project-release-readiness-summary-view-model",
viewModelVersion: 1,
phase: ready ? "ready" : "waiting",
ready,
title: "Project release readiness",
statusText,
detailText,
statusLine: `${statusText}: ${detailText}`,
rows: [
{
id: "gate-results",
label: "Release gates",
value: `${gateResultMatrix.passedCount ?? 0}/${gateResultMatrix.gateCount ?? 0} passed`,
},
{
id: "unknown-gates",
label: "Unknown gates",
value: `${gateResultMatrix.unknownCount ?? 0}`,
},
{
id: "sim-config-inventory",
label: "Sim config inventory",
value: `unexpected_fail=${simConfigInventory.unexpectedFail ?? "unknown"}`,
},
{
id: "blocked-runtime-families",
label: "Blocked runtime families",
value: promotedBlockedFamilies.length > 0
? `promoted: ${promotedBlockedFamilies.join(", ")}`
: (blockedRuntimeFamilies.join(", ") || "none"),
},
{
id: "missing",
label: "Missing readiness",
value: missing.length > 0 ? missing.join(", ") : "none",
},
],
};
}
export function parseProjectReleaseReadinessArtifactJson(text) { export function parseProjectReleaseReadinessArtifactJson(text) {
return JSON.parse(String(text)); return JSON.parse(String(text));
} }

View File

@@ -34,6 +34,7 @@ for (const phrase of [
"createProjectReleaseGateManifest()", "createProjectReleaseGateManifest()",
"createProjectReleaseGateResultMatrix()", "createProjectReleaseGateResultMatrix()",
"createProjectReleaseReadinessReport()", "createProjectReleaseReadinessReport()",
"createProjectReleaseReadinessSummaryViewModel()",
"parseProjectReleaseReadinessArtifactJson()", "parseProjectReleaseReadinessArtifactJson()",
"createProjectReleaseReadinessArtifactValidation()", "createProjectReleaseReadinessArtifactValidation()",
"validateWorkflowOverviewReleaseReadinessArtifactJson()", "validateWorkflowOverviewReleaseReadinessArtifactJson()",
@@ -101,6 +102,7 @@ for (const phrase of [
"createProjectReleaseGateManifest()", "createProjectReleaseGateManifest()",
"createProjectReleaseGateResultMatrix()", "createProjectReleaseGateResultMatrix()",
"createProjectReleaseReadinessReport()", "createProjectReleaseReadinessReport()",
"createProjectReleaseReadinessSummaryViewModel()",
"parseProjectReleaseReadinessArtifactJson()", "parseProjectReleaseReadinessArtifactJson()",
"createProjectReleaseReadinessArtifactValidation()", "createProjectReleaseReadinessArtifactValidation()",
"loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl()", "loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl()",

View File

@@ -4,6 +4,7 @@ import {
createProjectReleaseGateManifest, createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix, createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
createProjectReleaseReadinessSummaryViewModel,
} from "../../../runtime/sdk/src/index.js"; } from "../../../runtime/sdk/src/index.js";
const manifest = createProjectReleaseGateManifest(); const manifest = createProjectReleaseGateManifest();
@@ -85,5 +86,53 @@ assert.equal(report.gateResultMatrix.ready, true);
assert.equal(report.gateResultMatrix.passedCount, manifest.gateCount); assert.equal(report.gateResultMatrix.passedCount, manifest.gateCount);
assert.deepEqual(report.gates.map(({ id }) => id), manifest.gateIds); assert.deepEqual(report.gates.map(({ id }) => id), manifest.gateIds);
assert.equal(report.gates.length, manifest.gateCount); assert.equal(report.gates.length, manifest.gateCount);
assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
apiName: "project-release-readiness-summary-view-model",
viewModelVersion: 1,
phase: "ready",
ready: true,
title: "Project release readiness",
statusText: "Ready",
detailText: "No blocking reasons",
statusLine: "Ready: No blocking reasons",
rows: [
{
id: "gate-results",
label: "Release gates",
value: "10/10 passed",
},
{
id: "unknown-gates",
label: "Unknown gates",
value: "0",
},
{
id: "sim-config-inventory",
label: "Sim config inventory",
value: "unexpected_fail=0",
},
{
id: "blocked-runtime-families",
label: "Blocked runtime families",
value: "L4-USER-M-PROCESS, L4-TOOL-DB, L4-PYTHON-REMAP",
},
{
id: "missing",
label: "Missing readiness",
value: "none",
},
],
});
assert.equal(
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport()).statusLine,
"Waiting: project-release-gate",
);
assert.equal(
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
promotedRuntimeFamilies: ["L4-TOOL-DB"],
})).rows.find(({ id }) => id === "blocked-runtime-families")?.value,
"promoted: L4-TOOL-DB",
);
console.log("project_release_gate_manifest_node_smoke=ok"); console.log("project_release_gate_manifest_node_smoke=ok");

View File

@@ -36,6 +36,7 @@ import {
createProjectReleaseGateResultMatrix, createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
createProjectReleaseReadinessSummaryViewModel,
parseProjectReleaseReadinessArtifactJson, parseProjectReleaseReadinessArtifactJson,
createSessionSnapshot, createSessionSnapshot,
defaultMachinePaths, defaultMachinePaths,
@@ -89,6 +90,7 @@ const requiredExports = [
["createProjectReleaseGateResultMatrix", createProjectReleaseGateResultMatrix], ["createProjectReleaseGateResultMatrix", createProjectReleaseGateResultMatrix],
["createProjectReleaseReadinessArtifactValidation", createProjectReleaseReadinessArtifactValidation], ["createProjectReleaseReadinessArtifactValidation", createProjectReleaseReadinessArtifactValidation],
["createProjectReleaseReadinessReport", createProjectReleaseReadinessReport], ["createProjectReleaseReadinessReport", createProjectReleaseReadinessReport],
["createProjectReleaseReadinessSummaryViewModel", createProjectReleaseReadinessSummaryViewModel],
["parseProjectReleaseReadinessArtifactJson", parseProjectReleaseReadinessArtifactJson], ["parseProjectReleaseReadinessArtifactJson", parseProjectReleaseReadinessArtifactJson],
[ [
"validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson", "validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson",
@@ -220,6 +222,7 @@ assert.equal(releaseReadinessReady.ready, true);
assert.equal(releaseReadinessReady.releaseGate.passed, true); assert.equal(releaseReadinessReady.releaseGate.passed, true);
assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest()); assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest());
assert.equal(releaseReadinessReady.gateResultMatrix.rows.length, 10); assert.equal(releaseReadinessReady.gateResultMatrix.rows.length, 10);
assert.equal(createProjectReleaseReadinessSummaryViewModel(releaseReadinessReady).statusLine, "Ready: No blocking reasons");
assert.equal( assert.equal(
createProjectReleaseGateResultMatrix({ createProjectReleaseGateResultMatrix({
observedOutputs: ["project_release_gate=ok"], observedOutputs: ["project_release_gate=ok"],