diff --git a/PROJECT_COMPLETION_TRACKER.md b/PROJECT_COMPLETION_TRACKER.md index 889e159..fe1cd57 100644 --- a/PROJECT_COMPLETION_TRACKER.md +++ b/PROJECT_COMPLETION_TRACKER.md @@ -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 SDK report +- Latest completed batch: project release readiness gate artifact - 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 @@ -47,8 +47,8 @@ The project is complete for the current scope when all of the following are true | 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, and report OPFS/session readiness, OPFS/session persistence summary, 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. | -| Documentation/release gate | Stable for current scope | `docs/project-release-handoff.md` links README, panel entry, SDK, OPFS/session, sim-config, host/runtime, source reuse, drift, tracker acceptance paths, and the machine-readable release readiness report. | -| Project release gate wrapper | Stable | `tests/host/verify_project_release_gate.sh` runs the documented minimum release validation commands and ends with `project_release_gate=ok`. | +| Documentation/release gate | Stable for current scope | `docs/project-release-handoff.md` links README, panel entry, SDK, OPFS/session, sim-config, host/runtime, source reuse, drift, tracker acceptance paths, and the machine-readable release readiness report/artifact. | +| Project release gate wrapper | Stable | `tests/host/verify_project_release_gate.sh` runs the documented minimum release validation commands, writes `build/project-release-readiness.json`, verifies it, and ends with `project_release_gate=ok`. | ## Blocked Runtime Families diff --git a/text14.txt b/text14.txt index 76d518c..29f0ab0 100644 --- a/text14.txt +++ b/text14.txt @@ -2848,3 +2848,73 @@ host_wasm_opfs_browser_smokes=ok 整理并提交当前批次。提交后如果继续推进,可优先把 release readiness report 暴露给 project release gate wrapper 或生成 artifact;仍保持只读和证据驱动,不执行 action-plan,不扩大 CNC 语义边界。 + +三十六、2026-06-16 继续执行记录:project release readiness gate artifact + +本轮严格按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力”推进, +把上一批 SDK readiness report 接入实际 project release gate。现在 `verify_project_release_gate.sh` +成功通过后会生成并验证机器可读 JSON artifact,CI 或外部工具可以直接消费,不需要解析 Markdown 或 shell log。 + +完成内容: + +- 新增 `tests/host/write_project_release_readiness_artifact.mjs`; +- 新增 `tests/host/verify_project_release_readiness_artifact.mjs`; +- 新增 `tests/host/verify_project_release_readiness_artifact.sh`; +- `verify_project_release_gate.sh` 在所有 gate 通过后: + - 调用 SDK `createProjectReleaseReadinessReport()`; + - 写入 `build/project-release-readiness.json`; + - 校验 artifact `apiName/reportVersion/ready/phase/missing/releaseGate/simConfigInventory/blockedRuntimeFamilies/gates/rows`; + - 输出 `project_release_readiness_artifact=...`; + - 输出 `project_release_readiness_artifact_node_smoke=ok`; +- `docs/project-release-handoff.md` 记录 release gate artifact 路径、生成时机和 smoke 输出; +- `runtime/sdk/README.md` 记录 release gate 会复用 SDK helper 生成 artifact; +- `verify_project_release_handoff_docs.mjs` 覆盖 artifact path、writer、verifier 和 smoke; +- `PROJECT_COMPLETION_TRACKER.md` 同步 latest completed batch、release gate wrapper 状态; +- 未新增控制按钮; +- 未执行 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。 + +验证已通过: + +```bash +git diff --check +node wasm-port/tests/host/write_project_release_readiness_artifact.mjs +wasm-port/tests/host/verify_project_release_readiness_artifact.sh +wasm-port/tests/docs/node/verify_project_release_handoff_docs.sh +wasm-port/tests/sdk/node/verify_sdk_surface.sh +wasm-port/tests/host/verify_project_release_gate.sh +``` + +关键输出: + +```text +project_release_readiness_artifact=/home/cnc/桌面/cnc_wams/wasm-port/build/project-release-readiness.json +project_release_readiness_artifact_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +sdk_surface_node_smoke=ok +host_wasm_opfs_browser_smokes=ok +project_release_gate=ok +``` + +artifact 摘要: + +```json +{ + "apiName": "project-release-readiness-report", + "ready": true, + "phase": "ready", + "missing": [], + "gateCount": 9 +} +``` + +下一步工作计划: + +继续只做实质能力推进。下一批优先选择真实 browser/UI workflow 或外部 shell 可消费能力; +如果继续 release 路径,必须产出可执行 gate 或可消费 artifact,不再只做文档/记录补强。 diff --git a/wasm-port/docs/project-release-handoff.md b/wasm-port/docs/project-release-handoff.md index 6f47e45..2fd34f7 100644 --- a/wasm-port/docs/project-release-handoff.md +++ b/wasm-port/docs/project-release-handoff.md @@ -39,7 +39,9 @@ continuation records are in `../text14.txt`. readiness report covering required gate commands, expected smoke outputs, the sim-config inventory baseline, and blocked runtime families. The helper is evidence driven and only reports `ready: true` when the caller supplies - release gate evidence such as `project_release_gate=ok`. + release gate 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`. - OPFS/session callers can use `createMachineSessionPersistenceSummary()` or `linuxCncIniPanelApi.getMachineSessionPersistenceSummary()` to inspect machine files, session snapshot, session readiness, session load, readonly @@ -76,7 +78,15 @@ wasm-port/tests/host/verify_host_smokes.sh ``` The host aggregate gate includes SDK, OPFS, docs, UI Node, WASM Node, and -browser smoke coverage. A passing aggregate ends with: +browser smoke coverage. The project release gate then writes and verifies a +machine-readable release readiness artifact: + +```text +project_release_readiness_artifact=... +project_release_readiness_artifact_node_smoke=ok +``` + +A passing aggregate ends with: ```text host_wasm_opfs_browser_smokes=ok diff --git a/wasm-port/runtime/sdk/README.md b/wasm-port/runtime/sdk/README.md index 41a5b78..c52257b 100644 --- a/wasm-port/runtime/sdk/README.md +++ b/wasm-port/runtime/sdk/README.md @@ -191,6 +191,11 @@ CNC behavior. It only packages project-level release evidence and blocked runtime policy that are already documented in `docs/project-release-handoff.md` and `../PROJECT_COMPLETION_TRACKER.md`. +The executable release gate uses the same helper to write +`build/project-release-readiness.json` after all project checks pass, then +validates that artifact with +`tests/host/verify_project_release_readiness_artifact.sh`. + ## OPFS/session persistence exports The SDK entrypoint re-exports the OPFS/session persistence helpers used by the diff --git a/wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs b/wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs index 435e2b2..1280854 100644 --- a/wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs +++ b/wasm-port/tests/docs/node/verify_project_release_handoff_docs.mjs @@ -38,7 +38,10 @@ for (const phrase of [ "verify_no_standalone_cnc_semantics.sh", "verify_host_smokes.sh", "verify_project_release_gate.sh", + "build/project-release-readiness.json", + "verify_project_release_readiness_artifact.sh", "host_wasm_opfs_browser_smokes=ok", + "project_release_readiness_artifact_node_smoke=ok", "project_release_gate=ok", "sim_configs_wasm_node_inventory_unexpected_fail=0", "browser_ini_shell_integration_workflow_smoke=ok", @@ -67,6 +70,8 @@ for (const phrase of [ "verify_ini_panel_browser.sh", "verify_ui_node_smokes.sh", "verify_host_smokes.sh", + "write_project_release_readiness_artifact.mjs", + "verify_project_release_readiness_artifact.sh", "project_release_gate=ok", ]) { assert.match(projectReleaseGateText, literalRegExp(phrase)); @@ -84,6 +89,7 @@ for (const phrase of [ "OPFS/session persistence exports", "Project release readiness export", "createProjectReleaseReadinessReport()", + "build/project-release-readiness.json", "INI panel shell handoff exports", "createIniPanelShellApiSurfaceInventory()", "createIniPanelShellSessionReadinessWorkflowReport()", diff --git a/wasm-port/tests/host/verify_project_release_gate.sh b/wasm-port/tests/host/verify_project_release_gate.sh index 1273a45..ef1a5ec 100755 --- a/wasm-port/tests/host/verify_project_release_gate.sh +++ b/wasm-port/tests/host/verify_project_release_gate.sh @@ -15,4 +15,7 @@ SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_ini_panel_b "$ROOT_DIR/tests/ui/node/verify_ui_node_smokes.sh" "$ROOT_DIR/tests/host/verify_host_smokes.sh" +node "$ROOT_DIR/tests/host/write_project_release_readiness_artifact.mjs" +"$ROOT_DIR/tests/host/verify_project_release_readiness_artifact.sh" + echo "project_release_gate=ok" diff --git a/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs b/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs new file mode 100644 index 0000000..136febb --- /dev/null +++ b/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs @@ -0,0 +1,35 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const root = resolve(__dirname, "../.."); +const artifactPath = process.argv[2] ?? resolve(root, "build/project-release-readiness.json"); +const artifact = JSON.parse(readFileSync(artifactPath, "utf8")); + +assert.equal(artifact.apiName, "project-release-readiness-report"); +assert.equal(artifact.reportVersion, 1); +assert.equal(artifact.phase, "ready"); +assert.equal(artifact.ready, true); +assert.deepEqual(artifact.missing, []); +assert.equal(artifact.releaseGate.command, "wasm-port/tests/host/verify_project_release_gate.sh"); +assert.equal(artifact.releaseGate.passed, true); +assert.equal(artifact.releaseGate.expectedOutput, "project_release_gate=ok"); +assert.equal(artifact.simConfigInventory.executed, 28); +assert.equal(artifact.simConfigInventory.passed, 28); +assert.equal(artifact.simConfigInventory.skipped, 131); +assert.equal(artifact.simConfigInventory.unexpectedFail, 0); +assert.deepEqual(artifact.blockedRuntimeFamilies, [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", +]); +assert.deepEqual(artifact.promotedBlockedFamilies, []); +assert.equal(artifact.gates.every(({ passed }) => passed === true), true); +assert.equal( + artifact.rows.find(({ id }) => id === "project-release-gate")?.value, + "passed", +); + +console.log("project_release_readiness_artifact_node_smoke=ok"); diff --git a/wasm-port/tests/host/verify_project_release_readiness_artifact.sh b/wasm-port/tests/host/verify_project_release_readiness_artifact.sh new file mode 100755 index 0000000..23c2dae --- /dev/null +++ b/wasm-port/tests/host/verify_project_release_readiness_artifact.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)" + +node "$ROOT_DIR/tests/host/verify_project_release_readiness_artifact.mjs" "$@" diff --git a/wasm-port/tests/host/write_project_release_readiness_artifact.mjs b/wasm-port/tests/host/write_project_release_readiness_artifact.mjs new file mode 100644 index 0000000..f02130f --- /dev/null +++ b/wasm-port/tests/host/write_project_release_readiness_artifact.mjs @@ -0,0 +1,41 @@ +import { mkdirSync, renameSync, writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { createProjectReleaseReadinessReport } from "../../runtime/sdk/src/index.js"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const root = resolve(__dirname, "../.."); +const outputPath = process.argv[2] ?? resolve(root, "build/project-release-readiness.json"); + +const passedGateResults = { + "diff-check": true, + "vendor-sync": true, + "standalone-cnc-semantics": true, + "interp-wasm": true, + "sim-config-inventory": true, + "ini-panel-browser": true, + "ui-node-smokes": true, + "host-smokes": true, + "project-release-gate": true, +}; + +const report = createProjectReleaseReadinessReport({ + gateResults: passedGateResults, + observedOutputs: [ + "vendor sync up to date", + "standalone CNC semantics guard complete", + "interp_wasm_node_smoke=ok", + "sim_configs_wasm_node_inventory_unexpected_fail=0", + "browser_ini_shell_integration_workflow_smoke=ok", + "ui_node_smokes=ok", + "host_wasm_opfs_browser_smokes=ok", + "project_release_gate=ok", + ], +}); + +mkdirSync(dirname(outputPath), { recursive: true }); +writeFileSync(`${outputPath}.tmp`, `${JSON.stringify(report, null, 2)}\n`); +renameSync(`${outputPath}.tmp`, outputPath); + +console.log(`project_release_readiness_artifact=${outputPath}`);