新增 release artifact JSON workflow API

This commit is contained in:
2026-06-16 13:34:48 +08:00
parent 640f1e4438
commit d789920d7f
9 changed files with 212 additions and 2 deletions

View File

@@ -9,7 +9,7 @@ turn-by-turn continuation notes and next-batch execution records.
## Current Status
- Active continuation file: `text14.txt`
- Latest completed batch: project release readiness artifact validation action-plan API
- Latest completed batch: project release readiness artifact JSON workflow API
- Latest relevant commit: use `git log -1 --oneline` after each committed batch
- Working tree at tracker creation: clean
- 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 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. |
| 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. |
| 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 JSON workflow/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. |
| 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. |

View File

@@ -3308,6 +3308,68 @@ ui_node_smokes=ok
继续只做实质能力推进。优先选择可执行 gate、SDK/API surface、或真实 browser workflow
不要做只整理断言或局部常量化的批次。
五十二、2026-06-16 继续执行记录project release readiness artifact JSON workflow API
本轮继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力”推进,
把 release readiness artifact JSON 文本处理提升为 non-throwing workflow API。外部 CI/dashboard 现在
可以一次调用完成 parse、artifact validation、summary view-model 和 action-plan不需要自行串联 SDK
helper 或捕获 JSON parse error。
完成内容:
- `runtime/sdk/src/project-release-readiness.js` 新增:
- `createProjectReleaseReadinessArtifactJsonWorkflow()`
- 新 workflow 返回:
- `apiName: project-release-readiness-artifact-json-workflow`
- `workflowVersion: 1`
- `phase/ready`
- `parsed/parseError`
- `artifactApiName`
- `validation`
- `summaryViewModel`
- `actionPlan`
- `missing`
- display `rows[]`
- ready artifact JSON 返回 parsed/ready workflow 和空 action-plan commands
- stale artifact JSON 返回 parsed/blocked workflow 和 artifact validation action-plan
- invalid JSON 返回 parsed=false 的 blocked workflow不向外抛异常并给出相同 next-command action-plan
- `runtime/sdk/src/index.js` re-export 新 workflow API
- `verify_project_release_gate_manifest.mjs` 覆盖 ready/stale/invalid JSON workflow
- `verify_sdk_surface.mjs` 覆盖 SDK export、README 可发现性、ready summary 和 invalid parse
- `runtime/sdk/README.md`、`docs/project-release-handoff.md`、`verify_project_release_handoff_docs.mjs`
记录 JSON workflow 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 继续执行记录project release readiness artifact validation action-plan API
本轮继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力”推进,

View File

@@ -59,6 +59,9 @@ continuation records are in `../text14.txt`.
`createProjectReleaseReadinessArtifactValidationActionPlan()` turns a blocked
artifact validation into the project release gate and artifact validation
commands to run next, plus the missing evidence rows.
`createProjectReleaseReadinessArtifactJsonWorkflow()` is the one-call
non-throwing JSON text workflow for dashboards that need parse, validation,
summary, and action-plan output from the readiness artifact.
Browser shells can also pass the JSON to
`linuxCncIniPanelWorkflowOverviewApi.validateWorkflowOverviewReleaseReadinessArtifactJson()`
through the workflow overview iframe and receive stable validation rows. They

View File

@@ -56,6 +56,7 @@ import {
createProjectReleaseGateActionPlan,
createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactJsonWorkflow,
createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessArtifactValidationActionPlan,
createProjectReleaseReadinessArtifactValidationSummaryViewModel,
@@ -237,6 +238,11 @@ browser shells without reinterpreting the artifact fields.
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.
`createProjectReleaseReadinessArtifactJsonWorkflow()` is the non-throwing
one-call wrapper for dashboards that receive artifact JSON text. It parses,
validates, builds the validation summary, and builds the action plan. Invalid
JSON returns a blocked workflow with `parsed: false` and the same next-command
action plan instead of throwing.
## OPFS/session persistence exports

View File

@@ -4,6 +4,7 @@ export {
createProjectReleaseGateActionPlan,
createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactJsonWorkflow,
createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessArtifactValidationActionPlan,
createProjectReleaseReadinessArtifactValidationSummaryViewModel,

View File

@@ -444,6 +444,80 @@ export function parseProjectReleaseReadinessArtifactJson(text) {
return JSON.parse(String(text));
}
export function createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson = "" } = {}) {
try {
const artifact = parseProjectReleaseReadinessArtifactJson(artifactJson);
const validation = createProjectReleaseReadinessArtifactValidation(artifact);
const summaryViewModel = createProjectReleaseReadinessArtifactValidationSummaryViewModel(validation);
const actionPlan = createProjectReleaseReadinessArtifactValidationActionPlan(validation);
return {
apiName: "project-release-readiness-artifact-json-workflow",
workflowVersion: 1,
phase: validation.ready === true ? "ready" : "blocked",
ready: validation.ready === true,
parsed: true,
parseError: null,
artifactApiName: validation.artifactApiName,
validation,
summaryViewModel,
actionPlan,
missing: validation.missing,
rows: [
{
id: "parse",
label: "Artifact JSON parse",
value: "ready",
},
{
id: "validation",
label: "Artifact validation",
value: validation.ready === true ? "ready" : "blocked",
},
{
id: "next-command",
label: "Next command",
value: actionPlan.nextCommand ?? "none",
},
],
};
} catch (error) {
const validation = createProjectReleaseReadinessArtifactValidation({});
const summaryViewModel = createProjectReleaseReadinessArtifactValidationSummaryViewModel(validation);
const actionPlan = createProjectReleaseReadinessArtifactValidationActionPlan(validation);
const parseError = error?.message ?? "artifact JSON parse error";
return {
apiName: "project-release-readiness-artifact-json-workflow",
workflowVersion: 1,
phase: "blocked",
ready: false,
parsed: false,
parseError,
artifactApiName: null,
validation,
summaryViewModel,
actionPlan,
missing: ["artifact-json", ...validation.missing],
rows: [
{
id: "parse",
label: "Artifact JSON parse",
value: "blocked",
},
{
id: "validation",
label: "Artifact validation",
value: "blocked",
},
{
id: "next-command",
label: "Next command",
value: actionPlan.nextCommand ?? "none",
},
],
};
}
}
export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
const artifactObject = objectOrEmpty(artifact);
const releaseGate = objectOrEmpty(artifactObject.releaseGate);

View File

@@ -37,6 +37,7 @@ for (const phrase of [
"createProjectReleaseReadinessReport()",
"createProjectReleaseReadinessSummaryViewModel()",
"parseProjectReleaseReadinessArtifactJson()",
"createProjectReleaseReadinessArtifactJsonWorkflow()",
"createProjectReleaseReadinessArtifactValidation()",
"createProjectReleaseReadinessArtifactValidationActionPlan()",
"createProjectReleaseReadinessArtifactValidationSummaryViewModel()",
@@ -110,6 +111,7 @@ for (const phrase of [
"createProjectReleaseReadinessReport()",
"createProjectReleaseReadinessSummaryViewModel()",
"parseProjectReleaseReadinessArtifactJson()",
"createProjectReleaseReadinessArtifactJsonWorkflow()",
"createProjectReleaseReadinessArtifactValidation()",
"createProjectReleaseReadinessArtifactValidationActionPlan()",
"createProjectReleaseReadinessArtifactValidationSummaryViewModel()",

View File

@@ -4,6 +4,7 @@ import {
createProjectReleaseGateActionPlan,
createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactJsonWorkflow,
createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessArtifactValidationActionPlan,
createProjectReleaseReadinessArtifactValidationSummaryViewModel,
@@ -180,6 +181,9 @@ const artifactValidationSummary = createProjectReleaseReadinessArtifactValidatio
const artifactValidationActionPlan = createProjectReleaseReadinessArtifactValidationActionPlan(
createProjectReleaseReadinessArtifactValidation(report),
);
const artifactJsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({
artifactJson: JSON.stringify(report),
});
assert.equal(
artifactValidationSummary.apiName,
"project-release-readiness-artifact-validation-summary-view-model",
@@ -227,11 +231,35 @@ assert.deepEqual(artifactValidationActionPlan, {
},
],
});
assert.equal(artifactJsonWorkflow.apiName, "project-release-readiness-artifact-json-workflow");
assert.equal(artifactJsonWorkflow.workflowVersion, 1);
assert.equal(artifactJsonWorkflow.phase, "ready");
assert.equal(artifactJsonWorkflow.ready, true);
assert.equal(artifactJsonWorkflow.parsed, true);
assert.equal(artifactJsonWorkflow.parseError, null);
assert.equal(artifactJsonWorkflow.artifactApiName, "project-release-readiness-report");
assert.equal(artifactJsonWorkflow.validation.ready, true);
assert.equal(artifactJsonWorkflow.summaryViewModel.statusLine, "Ready: Artifact evidence complete");
assert.equal(artifactJsonWorkflow.actionPlan.commandCount, 0);
assert.deepEqual(
artifactJsonWorkflow.rows.map(({ id, value }) => [id, value]),
[
["parse", "ready"],
["validation", "ready"],
["next-command", "none"],
],
);
const staleArtifactValidationSummary = createProjectReleaseReadinessArtifactValidationSummaryViewModel(
createProjectReleaseReadinessArtifactValidation({}),
);
const staleArtifactValidation = createProjectReleaseReadinessArtifactValidation({});
const staleArtifactValidationActionPlan = createProjectReleaseReadinessArtifactValidationActionPlan(staleArtifactValidation);
const staleArtifactJsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({
artifactJson: "{}",
});
const invalidArtifactJsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({
artifactJson: "{not-json",
});
assert.equal(staleArtifactValidationSummary.phase, "blocked");
assert.match(staleArtifactValidationSummary.statusLine, /^Blocked: apiName, reportVersion/);
assert.equal(staleArtifactValidationSummary.rows.find(({ id }) => id === "gate-action-plan")?.value, "missing");
@@ -254,5 +282,27 @@ assert.equal(
staleArtifactValidationActionPlan.rows.find(({ id }) => id === "missing")?.value,
staleArtifactValidation.missing.join(", "),
);
assert.equal(staleArtifactJsonWorkflow.parsed, true);
assert.equal(staleArtifactJsonWorkflow.ready, false);
assert.equal(staleArtifactJsonWorkflow.phase, "blocked");
assert.equal(staleArtifactJsonWorkflow.actionPlan.commandCount, 2);
assert.deepEqual(staleArtifactJsonWorkflow.missing, staleArtifactValidation.missing);
assert.equal(invalidArtifactJsonWorkflow.parsed, false);
assert.equal(invalidArtifactJsonWorkflow.ready, false);
assert.equal(invalidArtifactJsonWorkflow.phase, "blocked");
assert.match(invalidArtifactJsonWorkflow.parseError, /JSON/);
assert.equal(invalidArtifactJsonWorkflow.missing[0], "artifact-json");
assert.equal(
invalidArtifactJsonWorkflow.actionPlan.nextCommand,
"wasm-port/tests/host/verify_project_release_gate.sh",
);
assert.deepEqual(
invalidArtifactJsonWorkflow.rows.map(({ id, value }) => [id, value]),
[
["parse", "blocked"],
["validation", "blocked"],
["next-command", "wasm-port/tests/host/verify_project_release_gate.sh"],
],
);
console.log("project_release_gate_manifest_node_smoke=ok");

View File

@@ -37,6 +37,7 @@ import {
createProjectReleaseGateActionPlan,
createProjectReleaseGateManifest,
createProjectReleaseGateResultMatrix,
createProjectReleaseReadinessArtifactJsonWorkflow,
createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessArtifactValidationActionPlan,
createProjectReleaseReadinessArtifactValidationSummaryViewModel,
@@ -94,6 +95,7 @@ const requiredExports = [
["createProjectReleaseGateActionPlan", createProjectReleaseGateActionPlan],
["createProjectReleaseGateManifest", createProjectReleaseGateManifest],
["createProjectReleaseGateResultMatrix", createProjectReleaseGateResultMatrix],
["createProjectReleaseReadinessArtifactJsonWorkflow", createProjectReleaseReadinessArtifactJsonWorkflow],
["createProjectReleaseReadinessArtifactValidation", createProjectReleaseReadinessArtifactValidation],
[
"createProjectReleaseReadinessArtifactValidationActionPlan",
@@ -343,6 +345,16 @@ assert.equal(
.nextCommand,
"wasm-port/tests/host/verify_project_release_gate.sh",
);
assert.equal(
createProjectReleaseReadinessArtifactJsonWorkflow({
artifactJson: JSON.stringify(releaseReadinessArtifact),
}).summaryViewModel.statusLine,
"Ready: Artifact evidence complete",
);
assert.equal(
createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson: "not-json" }).parsed,
false,
);
assert.deepEqual(
createProjectReleaseReadinessArtifactValidation({}).missing,
[