diff --git a/PROJECT_COMPLETION_TRACKER.md b/PROJECT_COMPLETION_TRACKER.md index 9a350a1..36ff204 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 gate manifest SDK API/gate +- Latest completed batch: project release gate result matrix SDK 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, 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, 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 cf640eb..932a911 100644 --- a/text14.txt +++ b/text14.txt @@ -3370,3 +3370,62 @@ project_release_handoff_docs_node_smoke=ok 继续只做实质能力推进。优先选择可执行 gate、SDK/API surface、或真实 browser workflow; 不要做只整理断言或局部常量化的批次。 + +四十三、2026-06-16 继续执行记录:project release gate result matrix SDK API + +本轮继续按“每批必须产出可调用 API、可验证 workflow、可执行 gate、或真实 UI/browser 能力”推进, +把 project release gate evidence matching 从 report 内部实现提升为外部可调用 result matrix API。外部 +CI 现在可以直接把 observed outputs 和 explicit gate results 归一化为每个 required gate 的 passed/unknown +行,不需要复制 SDK 内部匹配逻辑。 + +完成内容: + +- `runtime/sdk/src/project-release-readiness.js` 新增: + - `createProjectReleaseGateResultMatrix()`; +- 新 matrix 返回: + - `apiName: project-release-gate-result-matrix`; + - `matrixVersion: 1`; + - `phase/ready`; + - `gateCount/passedCount/unknownCount`; + - `missingGateIds`; + - `manifest`; + - `rows[]` 的 `id/label/command/expectedOutput/status/passed`; +- `createProjectReleaseReadinessReport()` 现在复用该 matrix,并返回 `gateResultMatrix`; +- `runtime/sdk/src/index.js` re-export 新 matrix API; +- `verify_project_release_gate_manifest.mjs` 覆盖 partial observed outputs、explicit gate result 和 unknown rows; +- `verify_sdk_surface.mjs` 覆盖 SDK export、report.gateResultMatrix 和单 gate observed output matching; +- `runtime/sdk/README.md`、`docs/project-release-handoff.md`、`verify_project_release_handoff_docs.mjs` + 记录 result matrix 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; +不要做只整理断言或局部常量化的批次。 diff --git a/wasm-port/docs/project-release-handoff.md b/wasm-port/docs/project-release-handoff.md index f52fa6c..523d904 100644 --- a/wasm-port/docs/project-release-handoff.md +++ b/wasm-port/docs/project-release-handoff.md @@ -36,13 +36,15 @@ continuation records are in `../text14.txt`. overview exposes the API surface inventory as a read-only summary row. - CI dashboards and external SDK callers can use `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`. + expected smoke outputs, `createProjectReleaseGateResultMatrix()` to map + observed outputs onto passed/unknown gate rows, and + `createProjectReleaseReadinessReport()` for a machine-readable release + readiness report covering the same manifest, result matrix, 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()`. diff --git a/wasm-port/runtime/sdk/README.md b/wasm-port/runtime/sdk/README.md index ba675b1..6c4475f 100644 --- a/wasm-port/runtime/sdk/README.md +++ b/wasm-port/runtime/sdk/README.md @@ -52,6 +52,7 @@ import { createMachineSessionPersistenceSummary, createMachineSessionSnapshotPayload, createProjectReleaseGateManifest, + createProjectReleaseGateResultMatrix, createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessReport, createSessionSnapshot, @@ -192,12 +193,13 @@ It does not implement or reinterpret LinuxCNC error semantics. `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. +SDK callers. `createProjectReleaseGateResultMatrix()` maps observed gate output +and explicit gate results onto that manifest as stable `passed`/`unknown` rows. +`createProjectReleaseReadinessReport()` returns a machine-readable release +readiness report that embeds the same manifest and result matrix beside the +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 diff --git a/wasm-port/runtime/sdk/src/index.js b/wasm-port/runtime/sdk/src/index.js index 7af4530..9a2495d 100644 --- a/wasm-port/runtime/sdk/src/index.js +++ b/wasm-port/runtime/sdk/src/index.js @@ -2,6 +2,7 @@ export { createLinuxCncIniSdk } from "./linuxcnc-ini.js"; export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js"; export { createProjectReleaseGateManifest, + createProjectReleaseGateResultMatrix, 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 3000772..240c651 100644 --- a/wasm-port/runtime/sdk/src/project-release-readiness.js +++ b/wasm-port/runtime/sdk/src/project-release-readiness.js @@ -113,16 +113,14 @@ export function createProjectReleaseGateManifest() { }; } -export function createProjectReleaseReadinessReport({ +export function createProjectReleaseGateResultMatrix({ gateResults = {}, observedOutputs = [], - simConfigInventory = DEFAULT_SIM_CONFIG_INVENTORY_BASELINE, - blockedRuntimeFamilies = BLOCKED_RUNTIME_FAMILIES, - promotedRuntimeFamilies = [], + manifest = createProjectReleaseGateManifest(), } = {}) { const outputs = Array.isArray(observedOutputs) ? observedOutputs : []; - const gateManifest = createProjectReleaseGateManifest(); - const gateRows = gateManifest.gates.map((gate) => { + const gates = Array.isArray(manifest?.gates) ? manifest.gates : []; + const rows = gates.map((gate) => { const passed = readGatePassed({ gate, gateResults, observedOutputs: outputs }); return { id: gate.id, @@ -133,6 +131,34 @@ export function createProjectReleaseReadinessReport({ passed, }; }); + return { + apiName: "project-release-gate-result-matrix", + matrixVersion: 1, + phase: rows.every(({ passed }) => passed) ? "ready" : "waiting", + ready: rows.every(({ passed }) => passed), + gateCount: rows.length, + passedCount: rows.filter(({ passed }) => passed).length, + unknownCount: rows.filter(({ passed }) => !passed).length, + missingGateIds: rows.filter(({ passed }) => !passed).map(({ id }) => id), + manifest, + rows, + }; +} + +export function createProjectReleaseReadinessReport({ + gateResults = {}, + observedOutputs = [], + simConfigInventory = DEFAULT_SIM_CONFIG_INVENTORY_BASELINE, + blockedRuntimeFamilies = BLOCKED_RUNTIME_FAMILIES, + promotedRuntimeFamilies = [], +} = {}) { + const gateManifest = createProjectReleaseGateManifest(); + const gateResultMatrix = createProjectReleaseGateResultMatrix({ + gateResults, + observedOutputs, + manifest: gateManifest, + }); + const gateRows = gateResultMatrix.rows; const releaseGatePassed = gateRows.find(({ id }) => id === "project-release-gate")?.passed === true; const inventory = { ...DEFAULT_SIM_CONFIG_INVENTORY_BASELINE, @@ -166,6 +192,7 @@ export function createProjectReleaseReadinessReport({ promotedRuntimeFamilies: promotedFamilies, promotedBlockedFamilies, gateManifest, + gateResultMatrix, gates: gateRows, 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 0dbee32..7b90ece 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 @@ -32,6 +32,7 @@ for (const phrase of [ "PROJECT_COMPLETION_TRACKER.md", "runtime/sdk/src/index.js", "createProjectReleaseGateManifest()", + "createProjectReleaseGateResultMatrix()", "createProjectReleaseReadinessReport()", "parseProjectReleaseReadinessArtifactJson()", "createProjectReleaseReadinessArtifactValidation()", @@ -98,6 +99,7 @@ for (const phrase of [ "OPFS/session persistence exports", "Project release readiness export", "createProjectReleaseGateManifest()", + "createProjectReleaseGateResultMatrix()", "createProjectReleaseReadinessReport()", "parseProjectReleaseReadinessArtifactJson()", "createProjectReleaseReadinessArtifactValidation()", 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 index 4fd4c13..2d9d173 100644 --- a/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs +++ b/wasm-port/tests/sdk/node/verify_project_release_gate_manifest.mjs @@ -2,6 +2,7 @@ import assert from "node:assert/strict"; import { createProjectReleaseGateManifest, + createProjectReleaseGateResultMatrix, createProjectReleaseReadinessReport, } from "../../../runtime/sdk/src/index.js"; @@ -42,10 +43,46 @@ assert.equal( "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh", ); +const partialMatrix = createProjectReleaseGateResultMatrix({ + gateResults: { + "diff-check": true, + }, + observedOutputs: [ + "vendor sync up to date", + "browser_release_artifact_url_workflow_smoke=ok", + ], +}); +assert.equal(partialMatrix.apiName, "project-release-gate-result-matrix"); +assert.equal(partialMatrix.matrixVersion, 1); +assert.equal(partialMatrix.phase, "waiting"); +assert.equal(partialMatrix.ready, false); +assert.equal(partialMatrix.gateCount, 10); +assert.equal(partialMatrix.passedCount, 3); +assert.equal(partialMatrix.unknownCount, 7); +assert.deepEqual(partialMatrix.missingGateIds, [ + "standalone-cnc-semantics", + "interp-wasm", + "sim-config-inventory", + "ini-panel-browser", + "ui-node-smokes", + "host-smokes", + "project-release-gate", +]); +assert.deepEqual( + partialMatrix.rows.filter(({ passed }) => passed).map(({ id }) => id), + [ + "diff-check", + "vendor-sync", + "release-artifact-url-browser", + ], +); + const report = createProjectReleaseReadinessReport({ gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])), }); assert.deepEqual(report.gateManifest, manifest); +assert.equal(report.gateResultMatrix.ready, true); +assert.equal(report.gateResultMatrix.passedCount, manifest.gateCount); assert.deepEqual(report.gates.map(({ id }) => id), manifest.gateIds); assert.equal(report.gates.length, manifest.gateCount); diff --git a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs index 7ef9ba7..20fb0c0 100644 --- a/wasm-port/tests/sdk/node/verify_sdk_surface.mjs +++ b/wasm-port/tests/sdk/node/verify_sdk_surface.mjs @@ -33,6 +33,7 @@ import { createMachineSessionPersistenceRenderState, createMachineSessionPersistenceSummary, createProjectReleaseGateManifest, + createProjectReleaseGateResultMatrix, createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessReport, parseProjectReleaseReadinessArtifactJson, @@ -85,6 +86,7 @@ const requiredExports = [ ["createLinuxCncIniSdk", createLinuxCncIniSdk], ["createLinuxCncInterpSdk", createLinuxCncInterpSdk], ["createProjectReleaseGateManifest", createProjectReleaseGateManifest], + ["createProjectReleaseGateResultMatrix", createProjectReleaseGateResultMatrix], ["createProjectReleaseReadinessArtifactValidation", createProjectReleaseReadinessArtifactValidation], ["createProjectReleaseReadinessReport", createProjectReleaseReadinessReport], ["parseProjectReleaseReadinessArtifactJson", parseProjectReleaseReadinessArtifactJson], @@ -217,6 +219,13 @@ const releaseReadinessReady = createProjectReleaseReadinessReport({ assert.equal(releaseReadinessReady.ready, true); assert.equal(releaseReadinessReady.releaseGate.passed, true); assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest()); +assert.equal(releaseReadinessReady.gateResultMatrix.rows.length, 10); +assert.equal( + createProjectReleaseGateResultMatrix({ + observedOutputs: ["project_release_gate=ok"], + }).rows.find(({ id }) => id === "project-release-gate")?.passed, + true, +); const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.stringify(createProjectReleaseReadinessReport({ gateResults: { "diff-check": true,