From 343ad0acd6b2c6631eb685b56fda3c9ccba1e334 Mon Sep 17 00:00:00 2001 From: wangdequan Date: Tue, 16 Jun 2026 10:31:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9A=B4=E9=9C=B2=E9=A1=B9=E7=9B=AE=E5=8F=91?= =?UTF-8?q?=E5=B8=83=20gate=20manifest=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PROJECT_COMPLETION_TRACKER.md | 4 +- text14.txt | 63 +++++++++++++++++++ wasm-port/docs/project-release-handoff.md | 20 +++--- wasm-port/runtime/sdk/README.md | 19 +++--- wasm-port/runtime/sdk/src/index.js | 1 + .../sdk/src/project-release-readiness.js | 21 ++++++- .../verify_project_release_handoff_docs.mjs | 2 + ...ify_project_release_readiness_artifact.mjs | 13 ++++ .../verify_project_release_gate_manifest.mjs | 52 +++++++++++++++ .../verify_project_release_gate_manifest.sh | 6 ++ .../tests/sdk/node/verify_sdk_surface.mjs | 5 ++ .../tests/sdk/node/verify_sdk_surface.sh | 1 + 12 files changed, 188 insertions(+), 19 deletions(-) create mode 100644 wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs create mode 100755 wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh diff --git a/PROJECT_COMPLETION_TRACKER.md b/PROJECT_COMPLETION_TRACKER.md index 93a84db..9a350a1 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: release artifact URL browser workflow gate +- Latest completed batch: project release gate manifest SDK API/gate - 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 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, 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. | | 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. | diff --git a/text14.txt b/text14.txt index dc698a3..cf640eb 100644 --- a/text14.txt +++ b/text14.txt @@ -3307,3 +3307,66 @@ ui_node_smokes=ok 继续只做实质能力推进。优先选择可执行 gate、SDK/API surface、或真实 browser workflow; 不要做只整理断言或局部常量化的批次。 + +四十二、2026-06-16 继续执行记录:project release gate manifest SDK API/gate + +本轮继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力”推进, +把 project release required gates 从 SDK 内部常量提升为外部可调用 manifest API,并新增独立 Node gate +验证 manifest 与 readiness report/artifact validation 共用同一套 gate 定义。 + +完成内容: + +- `runtime/sdk/src/project-release-readiness.js` 新增: + - `createProjectReleaseGateManifest()`; +- 新 manifest 返回: + - `apiName: project-release-gate-manifest`; + - `manifestVersion: 1`; + - `gateCount: 10`; + - `gateIds`; + - `gates[]` 的 `id/label/command/expectedOutput`; +- `createProjectReleaseReadinessReport()` 现在内嵌同一 `gateManifest`; +- `createProjectReleaseReadinessArtifactValidation()` 现在返回: + - `expectedGateCount`; + - `expectedGateIds`; +- `runtime/sdk/src/index.js` re-export 新 manifest API; +- 新增可执行 SDK gate: + - `tests/sdk/node/verify_project_release_gate_manifest.mjs`; + - `tests/sdk/node/verify_project_release_gate_manifest.sh`; +- `verify_sdk_surface.sh` 聚合运行新 gate; +- `verify_sdk_surface.mjs` 覆盖 SDK export、report.gateManifest 和 validation expected gate fields; +- `verify_project_release_readiness_artifact.mjs` 覆盖 expected gate fields; +- `runtime/sdk/README.md`、`docs/project-release-handoff.md`、`verify_project_release_handoff_docs.mjs` + 记录 manifest 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/host/verify_project_release_readiness_artifact.sh wasm-port/tests/fixtures/project-release-readiness-ready.json +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_readiness_artifact_node_smoke=ok +project_release_handoff_docs_node_smoke=ok +``` + +下一步工作计划: + +继续只做实质能力推进。优先选择可执行 gate、SDK/API surface、或真实 browser workflow; +不要做只整理断言或局部常量化的批次。 diff --git a/wasm-port/docs/project-release-handoff.md b/wasm-port/docs/project-release-handoff.md index 8ef9dfe..f52fa6c 100644 --- a/wasm-port/docs/project-release-handoff.md +++ b/wasm-port/docs/project-release-handoff.md @@ -35,15 +35,17 @@ continuation records are in `../text14.txt`. machine-readable session readiness phase and missing reasons. The workflow overview exposes the API surface inventory as a read-only summary row. - CI dashboards and external SDK callers can use - `createProjectReleaseReadinessReport()` for a machine-readable release - 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`. 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 `parseProjectReleaseReadinessArtifactJson()` - and validate it with `createProjectReleaseReadinessArtifactValidation()`. + `createProjectReleaseGateManifest()` to discover required gate commands and + expected smoke outputs, and `createProjectReleaseReadinessReport()` for a + machine-readable release readiness report covering the same manifest, the + sim-config inventory baseline, and blocked runtime families. The report + helper is evidence driven and only reports `ready: true` when the caller + supplies 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`. + External tools can load that artifact with + `parseProjectReleaseReadinessArtifactJson()` and validate it with + `createProjectReleaseReadinessArtifactValidation()`. Browser shells can also pass the JSON to `linuxCncIniPanelWorkflowOverviewApi.validateWorkflowOverviewReleaseReadinessArtifactJson()` through the workflow overview iframe and receive stable validation rows. They diff --git a/wasm-port/runtime/sdk/README.md b/wasm-port/runtime/sdk/README.md index 63fd94d..ba675b1 100644 --- a/wasm-port/runtime/sdk/README.md +++ b/wasm-port/runtime/sdk/README.md @@ -51,6 +51,7 @@ import { createMachineSessionPersistenceRenderState, createMachineSessionPersistenceSummary, createMachineSessionSnapshotPayload, + createProjectReleaseGateManifest, createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessReport, createSessionSnapshot, @@ -189,12 +190,14 @@ It does not implement or reinterpret LinuxCNC error semantics. ## Project release readiness export -`createProjectReleaseReadinessReport()` returns a machine-readable release -readiness report for CI dashboards and external SDK callers. The report lists -the required project release gate command, expected smoke outputs, the current -sim-config inventory baseline, and blocked runtime families. It is evidence -driven: the default report is `ready: false` until the caller supplies observed -gate output such as `project_release_gate=ok` or explicit gate results. +`createProjectReleaseGateManifest()` returns the required release gate IDs, +commands, expected smoke outputs, and gate count for CI dashboards and external +SDK callers. `createProjectReleaseReadinessReport()` returns a +machine-readable release readiness report that embeds the same manifest beside +the current gate results, sim-config inventory baseline, and blocked runtime +families. It is evidence driven: the default report is `ready: false` until the +caller supplies observed gate output such as `project_release_gate=ok` or +explicit gate results. This helper does not execute shell commands and does not inspect or implement CNC behavior. It only packages project-level release evidence and blocked @@ -207,7 +210,9 @@ validates that artifact with `tests/host/verify_project_release_readiness_artifact.sh`. External tools can load that JSON with `parseProjectReleaseReadinessArtifactJson()` and validate it with `createProjectReleaseReadinessArtifactValidation()` without copying the -host test assertions. +host test assertions. The validation result includes `expectedGateCount` and +`expectedGateIds` so external callers can compare an artifact against the +current manifest. ## OPFS/session persistence exports diff --git a/wasm-port/runtime/sdk/src/index.js b/wasm-port/runtime/sdk/src/index.js index 5202abb..7af4530 100644 --- a/wasm-port/runtime/sdk/src/index.js +++ b/wasm-port/runtime/sdk/src/index.js @@ -1,6 +1,7 @@ export { createLinuxCncIniSdk } from "./linuxcnc-ini.js"; export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js"; export { + createProjectReleaseGateManifest, createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessReport, parseProjectReleaseReadinessArtifactJson, diff --git a/wasm-port/runtime/sdk/src/project-release-readiness.js b/wasm-port/runtime/sdk/src/project-release-readiness.js index 87bf8f1..3000772 100644 --- a/wasm-port/runtime/sdk/src/project-release-readiness.js +++ b/wasm-port/runtime/sdk/src/project-release-readiness.js @@ -98,6 +98,21 @@ function arrayOrEmpty(value) { return Array.isArray(value) ? value : []; } +export function createProjectReleaseGateManifest() { + return { + apiName: "project-release-gate-manifest", + manifestVersion: 1, + gateCount: REQUIRED_RELEASE_GATES.length, + gateIds: REQUIRED_RELEASE_GATES.map(({ id }) => id), + gates: REQUIRED_RELEASE_GATES.map((gate) => ({ + id: gate.id, + label: gate.label, + command: gate.command, + expectedOutput: gate.expectedOutput ?? null, + })), + }; +} + export function createProjectReleaseReadinessReport({ gateResults = {}, observedOutputs = [], @@ -106,7 +121,8 @@ export function createProjectReleaseReadinessReport({ promotedRuntimeFamilies = [], } = {}) { const outputs = Array.isArray(observedOutputs) ? observedOutputs : []; - const gateRows = REQUIRED_RELEASE_GATES.map((gate) => { + const gateManifest = createProjectReleaseGateManifest(); + const gateRows = gateManifest.gates.map((gate) => { const passed = readGatePassed({ gate, gateResults, observedOutputs: outputs }); return { id: gate.id, @@ -149,6 +165,7 @@ export function createProjectReleaseReadinessReport({ blockedRuntimeFamilies: blockedFamilies, promotedRuntimeFamilies: promotedFamilies, promotedBlockedFamilies, + gateManifest, gates: gateRows, rows: [ { @@ -215,6 +232,8 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) { artifactApiName: artifactObject.apiName ?? null, artifactVersion: artifactObject.reportVersion ?? null, gateCount: gates.length, + expectedGateCount: REQUIRED_RELEASE_GATES.length, + expectedGateIds: createProjectReleaseGateManifest().gateIds, blockedRuntimeFamilies: arrayOrEmpty(artifactObject.blockedRuntimeFamilies), rows: [ { 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 a47ee85..0dbee32 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 @@ -31,6 +31,7 @@ for (const phrase of [ "docs/drift-report.md", "PROJECT_COMPLETION_TRACKER.md", "runtime/sdk/src/index.js", + "createProjectReleaseGateManifest()", "createProjectReleaseReadinessReport()", "parseProjectReleaseReadinessArtifactJson()", "createProjectReleaseReadinessArtifactValidation()", @@ -96,6 +97,7 @@ for (const phrase of [ for (const phrase of [ "OPFS/session persistence exports", "Project release readiness export", + "createProjectReleaseGateManifest()", "createProjectReleaseReadinessReport()", "parseProjectReleaseReadinessArtifactJson()", "createProjectReleaseReadinessArtifactValidation()", diff --git a/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs b/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs index 7a7cf13..d44900e 100644 --- a/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs +++ b/wasm-port/tests/host/verify_project_release_readiness_artifact.mjs @@ -21,5 +21,18 @@ assert.deepEqual(validation.missing, []); assert.equal(validation.artifactApiName, "project-release-readiness-report"); assert.equal(validation.artifactVersion, 1); assert.equal(validation.gateCount, 10); +assert.equal(validation.expectedGateCount, 10); +assert.deepEqual(validation.expectedGateIds, [ + "diff-check", + "vendor-sync", + "standalone-cnc-semantics", + "interp-wasm", + "sim-config-inventory", + "ini-panel-browser", + "release-artifact-url-browser", + "ui-node-smokes", + "host-smokes", + "project-release-gate", +]); console.log("project_release_readiness_artifact_node_smoke=ok"); diff --git a/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs b/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs new file mode 100644 index 0000000..4fd4c13 --- /dev/null +++ b/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs @@ -0,0 +1,52 @@ +import assert from "node:assert/strict"; + +import { + createProjectReleaseGateManifest, + createProjectReleaseReadinessReport, +} from "../../../runtime/sdk/src/index.js"; + +const manifest = createProjectReleaseGateManifest(); + +assert.equal(manifest.apiName, "project-release-gate-manifest"); +assert.equal(manifest.manifestVersion, 1); +assert.equal(manifest.gateCount, 10); +assert.deepEqual(manifest.gateIds, [ + "diff-check", + "vendor-sync", + "standalone-cnc-semantics", + "interp-wasm", + "sim-config-inventory", + "ini-panel-browser", + "release-artifact-url-browser", + "ui-node-smokes", + "host-smokes", + "project-release-gate", +]); +assert.deepEqual( + manifest.gates.map(({ expectedOutput }) => expectedOutput), + [ + null, + "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", + "browser_release_artifact_url_workflow_smoke=ok", + "ui_node_smokes=ok", + "host_wasm_opfs_browser_smokes=ok", + "project_release_gate=ok", + ], +); +assert.equal( + manifest.gates.find(({ id }) => id === "release-artifact-url-browser")?.command, + "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh", +); + +const report = createProjectReleaseReadinessReport({ + gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])), +}); +assert.deepEqual(report.gateManifest, manifest); +assert.deepEqual(report.gates.map(({ id }) => id), manifest.gateIds); +assert.equal(report.gates.length, manifest.gateCount); + +console.log("project_release_gate_manifest_node_smoke=ok"); diff --git a/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh b/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh new file mode 100755 index 0000000..817e529 --- /dev/null +++ b/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)" + +node "$ROOT_DIR/tests/sdk/node/verify_project_release_gate_manifest.mjs" diff --git a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs index 9b50c24..7ef9ba7 100644 --- a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs +++ b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs @@ -32,6 +32,7 @@ import { createMachineSessionPersistenceDisplayViewModel, createMachineSessionPersistenceRenderState, createMachineSessionPersistenceSummary, + createProjectReleaseGateManifest, createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessReport, parseProjectReleaseReadinessArtifactJson, @@ -83,6 +84,7 @@ const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md const requiredExports = [ ["createLinuxCncIniSdk", createLinuxCncIniSdk], ["createLinuxCncInterpSdk", createLinuxCncInterpSdk], + ["createProjectReleaseGateManifest", createProjectReleaseGateManifest], ["createProjectReleaseReadinessArtifactValidation", createProjectReleaseReadinessArtifactValidation], ["createProjectReleaseReadinessReport", createProjectReleaseReadinessReport], ["parseProjectReleaseReadinessArtifactJson", parseProjectReleaseReadinessArtifactJson], @@ -214,6 +216,7 @@ const releaseReadinessReady = createProjectReleaseReadinessReport({ }); assert.equal(releaseReadinessReady.ready, true); assert.equal(releaseReadinessReady.releaseGate.passed, true); +assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest()); const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.stringify(createProjectReleaseReadinessReport({ gateResults: { "diff-check": true, @@ -238,6 +241,8 @@ assert.deepEqual(createProjectReleaseReadinessArtifactValidation(releaseReadines artifactApiName: "project-release-readiness-report", artifactVersion: 1, gateCount: 10, + expectedGateCount: 10, + expectedGateIds: createProjectReleaseGateManifest().gateIds, blockedRuntimeFamilies: [ "L4-USER-M-PROCESS", "L4-TOOL-DB", diff --git a/wasm-port/tests/sdk/node/verify_sdk_surface.sh b/wasm-port/tests/sdk/node/verify_sdk_surface.sh index 8205b15..38dd35d 100755 --- a/wasm-port/tests/sdk/node/verify_sdk_surface.sh +++ b/wasm-port/tests/sdk/node/verify_sdk_surface.sh @@ -4,3 +4,4 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)" node "$ROOT_DIR/tests/sdk/node/verify_sdk_surface.mjs" +"$ROOT_DIR/tests/sdk/node/verify_project_release_gate_manifest.sh"