新增 release artifact validation action plan API

This commit is contained in:
2026-06-16 11:50:38 +08:00
parent e00c7b038b
commit 640f1e4438
9 changed files with 206 additions and 2 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: workflow overview release artifact URL workflow summary view-model API - Latest completed batch: project release readiness artifact validation action-plan 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/action-plan/readiness summary view-model, project release readiness report/artifact validation summary 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/action-plan/readiness summary view-model, project release readiness report/artifact validation summary/action-plan 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, summarize, and report OPFS/session readiness, OPFS/session persistence summary, release readiness artifact validation/rendering/URL workflow summaries, 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, summarize, and report OPFS/session readiness, OPFS/session persistence summary, release readiness artifact validation/rendering/URL workflow summaries, 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

@@ -3308,6 +3308,67 @@ ui_node_smokes=ok
继续只做实质能力推进。优先选择可执行 gate、SDK/API surface、或真实 browser workflow 继续只做实质能力推进。优先选择可执行 gate、SDK/API surface、或真实 browser workflow
不要做只整理断言或局部常量化的批次。 不要做只整理断言或局部常量化的批次。
五十一、2026-06-16 继续执行记录project release readiness artifact validation action-plan API
本轮继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力”推进,
把 release readiness artifact validation 的 blocked 状态提升为外部可调用 action-plan API。外部
CI/dashboard 现在可以在 artifact validation 不通过时直接拿到下一步命令、缺失 evidence 列表和可执行
shell script而不需要复制 host gate 或 artifact validator 的顺序。
完成内容:
- `runtime/sdk/src/project-release-readiness.js` 新增:
- `createProjectReleaseReadinessArtifactValidationActionPlan()`
- 新 action-plan 返回:
- `apiName: project-release-readiness-artifact-validation-action-plan`
- `planVersion: 1`
- `phase/ready`
- `missing/missingCount`
- `commandCount/nextActionId/nextCommand`
- `commands[]`
- `shellScript`
- display `rows[]`
- ready validation 返回空 commands 和 ready shellScript
- blocked validation 返回:
- `wasm-port/tests/host/verify_project_release_gate.sh`
- `wasm-port/tests/host/verify_project_release_readiness_artifact.sh build/project-release-readiness.json`
- `runtime/sdk/src/index.js` re-export 新 action-plan API
- `verify_project_release_gate_manifest.mjs` 覆盖 ready 和 stale/blocked artifact validation action-plan
- `verify_sdk_surface.mjs` 覆盖 SDK export、README 可发现性、ready shellScript 和 blocked nextCommand
- `runtime/sdk/README.md`、`docs/project-release-handoff.md`、`verify_project_release_handoff_docs.mjs`
记录 action-plan 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
```
关键输出:
```text
project_release_gate_manifest_node_smoke=ok
sdk_surface_node_smoke=ok
project_release_handoff_docs_node_smoke=ok
```
下一步工作计划:
继续只做实质能力推进。优先选择可执行 gate、SDK/API surface、或真实 browser workflow
不要做只整理断言或局部常量化的批次。
五十、2026-06-16 继续执行记录workflow overview release artifact URL workflow summary view-model API 五十、2026-06-16 继续执行记录workflow overview release artifact URL workflow summary view-model API
本轮继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力”推进, 本轮继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力”推进,

View File

@@ -56,6 +56,9 @@ continuation records are in `../text14.txt`.
ready, so stale release JSON cannot pass the executable artifact gate. ready, so stale release JSON cannot pass the executable artifact gate.
`createProjectReleaseReadinessArtifactValidationSummaryViewModel()` turns the `createProjectReleaseReadinessArtifactValidationSummaryViewModel()` turns the
validation into stable dashboard rows without reinterpreting artifact fields. validation into stable dashboard rows without reinterpreting artifact fields.
`createProjectReleaseReadinessArtifactValidationActionPlan()` turns a blocked
artifact validation into the project release gate and artifact validation
commands to run next, plus the missing evidence rows.
Browser shells can also pass the JSON to Browser shells can also pass the JSON to
`linuxCncIniPanelWorkflowOverviewApi.validateWorkflowOverviewReleaseReadinessArtifactJson()` `linuxCncIniPanelWorkflowOverviewApi.validateWorkflowOverviewReleaseReadinessArtifactJson()`
through the workflow overview iframe and receive stable validation rows. They through the workflow overview iframe and receive stable validation rows. They

View File

@@ -57,6 +57,7 @@ import {
createProjectReleaseGateManifest, createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix, createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessArtifactValidationActionPlan,
createProjectReleaseReadinessArtifactValidationSummaryViewModel, createProjectReleaseReadinessArtifactValidationSummaryViewModel,
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
createProjectReleaseReadinessSummaryViewModel, createProjectReleaseReadinessSummaryViewModel,
@@ -232,6 +233,10 @@ chain fail the same executable gate.
`createProjectReleaseReadinessArtifactValidationSummaryViewModel()` turns that `createProjectReleaseReadinessArtifactValidationSummaryViewModel()` turns that
validation result into stable status text and display rows for dashboards or validation result into stable status text and display rows for dashboards or
browser shells without reinterpreting the artifact fields. browser shells without reinterpreting the artifact fields.
`createProjectReleaseReadinessArtifactValidationActionPlan()` turns a blocked
artifact validation result into the exact project release gate and artifact
validation commands to run next, plus the missing evidence list and shell
script. Ready validations return no commands.
## OPFS/session persistence exports ## OPFS/session persistence exports

View File

@@ -5,6 +5,7 @@ export {
createProjectReleaseGateManifest, createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix, createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessArtifactValidationActionPlan,
createProjectReleaseReadinessArtifactValidationSummaryViewModel, createProjectReleaseReadinessArtifactValidationSummaryViewModel,
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
createProjectReleaseReadinessSummaryViewModel, createProjectReleaseReadinessSummaryViewModel,

View File

@@ -377,6 +377,69 @@ export function createProjectReleaseReadinessArtifactValidationSummaryViewModel(
}; };
} }
export function createProjectReleaseReadinessArtifactValidationActionPlan(
validation = createProjectReleaseReadinessArtifactValidation(),
) {
const missing = arrayOrEmpty(validation?.missing);
const ready = validation?.ready === true;
const commands = ready ? [] : [
{
step: 1,
id: "project-release-gate",
label: "Run project release gate",
command: "wasm-port/tests/host/verify_project_release_gate.sh",
expectedOutput: "project_release_gate=ok",
},
{
step: 2,
id: "artifact-validation",
label: "Validate project release readiness artifact",
command: "wasm-port/tests/host/verify_project_release_readiness_artifact.sh build/project-release-readiness.json",
expectedOutput: "project_release_readiness_artifact_node_smoke=ok",
},
];
const shellScript = commands.length > 0
? [
"#!/usr/bin/env bash",
"set -euo pipefail",
"",
...commands.map(({ command }) => command),
"",
].join("\n")
: "# Project release readiness artifact validation is already ready.\n";
return {
apiName: "project-release-readiness-artifact-validation-action-plan",
planVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
missing,
missingCount: missing.length,
commandCount: commands.length,
nextActionId: commands[0]?.id ?? null,
nextCommand: commands[0]?.command ?? null,
commands,
shellScript,
rows: [
{
id: "artifact-validation",
label: "Artifact validation",
value: ready ? "ready" : "blocked",
},
{
id: "missing",
label: "Missing artifact evidence",
value: missing.length > 0 ? missing.join(", ") : "none",
},
{
id: "next-command",
label: "Next command",
value: commands[0]?.command ?? "none",
},
],
};
}
export function parseProjectReleaseReadinessArtifactJson(text) { export function parseProjectReleaseReadinessArtifactJson(text) {
return JSON.parse(String(text)); return JSON.parse(String(text));
} }

View File

@@ -38,6 +38,7 @@ for (const phrase of [
"createProjectReleaseReadinessSummaryViewModel()", "createProjectReleaseReadinessSummaryViewModel()",
"parseProjectReleaseReadinessArtifactJson()", "parseProjectReleaseReadinessArtifactJson()",
"createProjectReleaseReadinessArtifactValidation()", "createProjectReleaseReadinessArtifactValidation()",
"createProjectReleaseReadinessArtifactValidationActionPlan()",
"createProjectReleaseReadinessArtifactValidationSummaryViewModel()", "createProjectReleaseReadinessArtifactValidationSummaryViewModel()",
"embedded gate manifest, result matrix, and action plan", "embedded gate manifest, result matrix, and action plan",
"validateWorkflowOverviewReleaseReadinessArtifactJson()", "validateWorkflowOverviewReleaseReadinessArtifactJson()",
@@ -110,6 +111,7 @@ for (const phrase of [
"createProjectReleaseReadinessSummaryViewModel()", "createProjectReleaseReadinessSummaryViewModel()",
"parseProjectReleaseReadinessArtifactJson()", "parseProjectReleaseReadinessArtifactJson()",
"createProjectReleaseReadinessArtifactValidation()", "createProjectReleaseReadinessArtifactValidation()",
"createProjectReleaseReadinessArtifactValidationActionPlan()",
"createProjectReleaseReadinessArtifactValidationSummaryViewModel()", "createProjectReleaseReadinessArtifactValidationSummaryViewModel()",
"gateManifestReady", "gateManifestReady",
"gateResultMatrixReady", "gateResultMatrixReady",

View File

@@ -5,6 +5,7 @@ import {
createProjectReleaseGateManifest, createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix, createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessArtifactValidationActionPlan,
createProjectReleaseReadinessArtifactValidationSummaryViewModel, createProjectReleaseReadinessArtifactValidationSummaryViewModel,
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
createProjectReleaseReadinessSummaryViewModel, createProjectReleaseReadinessSummaryViewModel,
@@ -176,6 +177,9 @@ assert.equal(
const artifactValidationSummary = createProjectReleaseReadinessArtifactValidationSummaryViewModel( const artifactValidationSummary = createProjectReleaseReadinessArtifactValidationSummaryViewModel(
createProjectReleaseReadinessArtifactValidation(report), createProjectReleaseReadinessArtifactValidation(report),
); );
const artifactValidationActionPlan = createProjectReleaseReadinessArtifactValidationActionPlan(
createProjectReleaseReadinessArtifactValidation(report),
);
assert.equal( assert.equal(
artifactValidationSummary.apiName, artifactValidationSummary.apiName,
"project-release-readiness-artifact-validation-summary-view-model", "project-release-readiness-artifact-validation-summary-view-model",
@@ -193,11 +197,62 @@ assert.deepEqual(
["missing", "none"], ["missing", "none"],
], ],
); );
assert.deepEqual(artifactValidationActionPlan, {
apiName: "project-release-readiness-artifact-validation-action-plan",
planVersion: 1,
phase: "ready",
ready: true,
missing: [],
missingCount: 0,
commandCount: 0,
nextActionId: null,
nextCommand: null,
commands: [],
shellScript: "# Project release readiness artifact validation is already ready.\n",
rows: [
{
id: "artifact-validation",
label: "Artifact validation",
value: "ready",
},
{
id: "missing",
label: "Missing artifact evidence",
value: "none",
},
{
id: "next-command",
label: "Next command",
value: "none",
},
],
});
const staleArtifactValidationSummary = createProjectReleaseReadinessArtifactValidationSummaryViewModel( const staleArtifactValidationSummary = createProjectReleaseReadinessArtifactValidationSummaryViewModel(
createProjectReleaseReadinessArtifactValidation({}), createProjectReleaseReadinessArtifactValidation({}),
); );
const staleArtifactValidation = createProjectReleaseReadinessArtifactValidation({});
const staleArtifactValidationActionPlan = createProjectReleaseReadinessArtifactValidationActionPlan(staleArtifactValidation);
assert.equal(staleArtifactValidationSummary.phase, "blocked"); assert.equal(staleArtifactValidationSummary.phase, "blocked");
assert.match(staleArtifactValidationSummary.statusLine, /^Blocked: apiName, reportVersion/); assert.match(staleArtifactValidationSummary.statusLine, /^Blocked: apiName, reportVersion/);
assert.equal(staleArtifactValidationSummary.rows.find(({ id }) => id === "gate-action-plan")?.value, "missing"); assert.equal(staleArtifactValidationSummary.rows.find(({ id }) => id === "gate-action-plan")?.value, "missing");
assert.equal(staleArtifactValidationActionPlan.phase, "blocked");
assert.equal(staleArtifactValidationActionPlan.ready, false);
assert.equal(staleArtifactValidationActionPlan.missingCount, staleArtifactValidation.missing.length);
assert.equal(staleArtifactValidationActionPlan.commandCount, 2);
assert.equal(staleArtifactValidationActionPlan.nextActionId, "project-release-gate");
assert.equal(
staleArtifactValidationActionPlan.nextCommand,
"wasm-port/tests/host/verify_project_release_gate.sh",
);
assert.equal(
staleArtifactValidationActionPlan.commands[1].command,
"wasm-port/tests/host/verify_project_release_readiness_artifact.sh build/project-release-readiness.json",
);
assert.match(staleArtifactValidationActionPlan.shellScript, /^#!\/usr\/bin\/env bash\nset -euo pipefail\n/);
assert.match(staleArtifactValidationActionPlan.shellScript, /verify_project_release_readiness_artifact\.sh/);
assert.equal(
staleArtifactValidationActionPlan.rows.find(({ id }) => id === "missing")?.value,
staleArtifactValidation.missing.join(", "),
);
console.log("project_release_gate_manifest_node_smoke=ok"); console.log("project_release_gate_manifest_node_smoke=ok");

View File

@@ -38,6 +38,7 @@ import {
createProjectReleaseGateManifest, createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix, createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessArtifactValidationActionPlan,
createProjectReleaseReadinessArtifactValidationSummaryViewModel, createProjectReleaseReadinessArtifactValidationSummaryViewModel,
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
createProjectReleaseReadinessSummaryViewModel, createProjectReleaseReadinessSummaryViewModel,
@@ -94,6 +95,10 @@ const requiredExports = [
["createProjectReleaseGateManifest", createProjectReleaseGateManifest], ["createProjectReleaseGateManifest", createProjectReleaseGateManifest],
["createProjectReleaseGateResultMatrix", createProjectReleaseGateResultMatrix], ["createProjectReleaseGateResultMatrix", createProjectReleaseGateResultMatrix],
["createProjectReleaseReadinessArtifactValidation", createProjectReleaseReadinessArtifactValidation], ["createProjectReleaseReadinessArtifactValidation", createProjectReleaseReadinessArtifactValidation],
[
"createProjectReleaseReadinessArtifactValidationActionPlan",
createProjectReleaseReadinessArtifactValidationActionPlan,
],
[ [
"createProjectReleaseReadinessArtifactValidationSummaryViewModel", "createProjectReleaseReadinessArtifactValidationSummaryViewModel",
createProjectReleaseReadinessArtifactValidationSummaryViewModel, createProjectReleaseReadinessArtifactValidationSummaryViewModel,
@@ -329,6 +334,15 @@ assert.equal(
).statusLine, ).statusLine,
"Ready: Artifact evidence complete", "Ready: Artifact evidence complete",
); );
assert.equal(
createProjectReleaseReadinessArtifactValidationActionPlan(releaseReadinessArtifactValidation).shellScript,
"# Project release readiness artifact validation is already ready.\n",
);
assert.equal(
createProjectReleaseReadinessArtifactValidationActionPlan(createProjectReleaseReadinessArtifactValidation({}))
.nextCommand,
"wasm-port/tests/host/verify_project_release_gate.sh",
);
assert.deepEqual( assert.deepEqual(
createProjectReleaseReadinessArtifactValidation({}).missing, createProjectReleaseReadinessArtifactValidation({}).missing,
[ [