暴露项目发布 gate manifest API

This commit is contained in:
2026-06-16 10:31:50 +08:00
parent 6c087499fd
commit 343ad0acd6
12 changed files with 188 additions and 19 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: 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 - 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 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. | | 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. | | 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

@@ -3307,3 +3307,66 @@ ui_node_smokes=ok
继续只做实质能力推进。优先选择可执行 gate、SDK/API surface、或真实 browser workflow 继续只做实质能力推进。优先选择可执行 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
不要做只整理断言或局部常量化的批次。

View File

@@ -35,15 +35,17 @@ continuation records are in `../text14.txt`.
machine-readable session readiness phase and missing reasons. The workflow machine-readable session readiness phase and missing reasons. The workflow
overview exposes the API surface inventory as a read-only summary row. overview exposes the API surface inventory as a read-only summary row.
- CI dashboards and external SDK callers can use - CI dashboards and external SDK callers can use
`createProjectReleaseReadinessReport()` for a machine-readable release `createProjectReleaseGateManifest()` to discover required gate commands and
readiness report covering required gate commands, expected smoke outputs, the expected smoke outputs, and `createProjectReleaseReadinessReport()` for a
sim-config inventory baseline, and blocked runtime families. The helper is machine-readable release readiness report covering the same manifest, the
evidence driven and only reports `ready: true` when the caller supplies sim-config inventory baseline, and blocked runtime families. The report
release gate evidence such as `project_release_gate=ok`. The project release helper is evidence driven and only reports `ready: true` when the caller
gate writes the same report to `build/project-release-readiness.json` and supplies release gate evidence such as `project_release_gate=ok`. The project
validates it with `verify_project_release_readiness_artifact.sh`. External release gate writes the same report to `build/project-release-readiness.json`
tools can load that artifact with `parseProjectReleaseReadinessArtifactJson()` and validates it with `verify_project_release_readiness_artifact.sh`.
and validate it with `createProjectReleaseReadinessArtifactValidation()`. External tools can load that artifact with
`parseProjectReleaseReadinessArtifactJson()` and validate it with
`createProjectReleaseReadinessArtifactValidation()`.
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

@@ -51,6 +51,7 @@ import {
createMachineSessionPersistenceRenderState, createMachineSessionPersistenceRenderState,
createMachineSessionPersistenceSummary, createMachineSessionPersistenceSummary,
createMachineSessionSnapshotPayload, createMachineSessionSnapshotPayload,
createProjectReleaseGateManifest,
createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
createSessionSnapshot, createSessionSnapshot,
@@ -189,12 +190,14 @@ It does not implement or reinterpret LinuxCNC error semantics.
## Project release readiness export ## Project release readiness export
`createProjectReleaseReadinessReport()` returns a machine-readable release `createProjectReleaseGateManifest()` returns the required release gate IDs,
readiness report for CI dashboards and external SDK callers. The report lists commands, expected smoke outputs, and gate count for CI dashboards and external
the required project release gate command, expected smoke outputs, the current SDK callers. `createProjectReleaseReadinessReport()` returns a
sim-config inventory baseline, and blocked runtime families. It is evidence machine-readable release readiness report that embeds the same manifest beside
driven: the default report is `ready: false` until the caller supplies observed the current gate results, sim-config inventory baseline, and blocked runtime
gate output such as `project_release_gate=ok` or explicit gate results. 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 This helper does not execute shell commands and does not inspect or implement
CNC behavior. It only packages project-level release evidence and blocked 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 `tests/host/verify_project_release_readiness_artifact.sh`. External tools can
load that JSON with `parseProjectReleaseReadinessArtifactJson()` and validate load that JSON with `parseProjectReleaseReadinessArtifactJson()` and validate
it with `createProjectReleaseReadinessArtifactValidation()` without copying the 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 ## OPFS/session persistence exports

View File

@@ -1,6 +1,7 @@
export { createLinuxCncIniSdk } from "./linuxcnc-ini.js"; export { createLinuxCncIniSdk } from "./linuxcnc-ini.js";
export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js"; export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js";
export { export {
createProjectReleaseGateManifest,
createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
parseProjectReleaseReadinessArtifactJson, parseProjectReleaseReadinessArtifactJson,

View File

@@ -98,6 +98,21 @@ function arrayOrEmpty(value) {
return Array.isArray(value) ? 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({ export function createProjectReleaseReadinessReport({
gateResults = {}, gateResults = {},
observedOutputs = [], observedOutputs = [],
@@ -106,7 +121,8 @@ export function createProjectReleaseReadinessReport({
promotedRuntimeFamilies = [], promotedRuntimeFamilies = [],
} = {}) { } = {}) {
const outputs = Array.isArray(observedOutputs) ? observedOutputs : []; 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 }); const passed = readGatePassed({ gate, gateResults, observedOutputs: outputs });
return { return {
id: gate.id, id: gate.id,
@@ -149,6 +165,7 @@ export function createProjectReleaseReadinessReport({
blockedRuntimeFamilies: blockedFamilies, blockedRuntimeFamilies: blockedFamilies,
promotedRuntimeFamilies: promotedFamilies, promotedRuntimeFamilies: promotedFamilies,
promotedBlockedFamilies, promotedBlockedFamilies,
gateManifest,
gates: gateRows, gates: gateRows,
rows: [ rows: [
{ {
@@ -215,6 +232,8 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
artifactApiName: artifactObject.apiName ?? null, artifactApiName: artifactObject.apiName ?? null,
artifactVersion: artifactObject.reportVersion ?? null, artifactVersion: artifactObject.reportVersion ?? null,
gateCount: gates.length, gateCount: gates.length,
expectedGateCount: REQUIRED_RELEASE_GATES.length,
expectedGateIds: createProjectReleaseGateManifest().gateIds,
blockedRuntimeFamilies: arrayOrEmpty(artifactObject.blockedRuntimeFamilies), blockedRuntimeFamilies: arrayOrEmpty(artifactObject.blockedRuntimeFamilies),
rows: [ rows: [
{ {

View File

@@ -31,6 +31,7 @@ for (const phrase of [
"docs/drift-report.md", "docs/drift-report.md",
"PROJECT_COMPLETION_TRACKER.md", "PROJECT_COMPLETION_TRACKER.md",
"runtime/sdk/src/index.js", "runtime/sdk/src/index.js",
"createProjectReleaseGateManifest()",
"createProjectReleaseReadinessReport()", "createProjectReleaseReadinessReport()",
"parseProjectReleaseReadinessArtifactJson()", "parseProjectReleaseReadinessArtifactJson()",
"createProjectReleaseReadinessArtifactValidation()", "createProjectReleaseReadinessArtifactValidation()",
@@ -96,6 +97,7 @@ for (const phrase of [
for (const phrase of [ for (const phrase of [
"OPFS/session persistence exports", "OPFS/session persistence exports",
"Project release readiness export", "Project release readiness export",
"createProjectReleaseGateManifest()",
"createProjectReleaseReadinessReport()", "createProjectReleaseReadinessReport()",
"parseProjectReleaseReadinessArtifactJson()", "parseProjectReleaseReadinessArtifactJson()",
"createProjectReleaseReadinessArtifactValidation()", "createProjectReleaseReadinessArtifactValidation()",

View File

@@ -21,5 +21,18 @@ assert.deepEqual(validation.missing, []);
assert.equal(validation.artifactApiName, "project-release-readiness-report"); assert.equal(validation.artifactApiName, "project-release-readiness-report");
assert.equal(validation.artifactVersion, 1); assert.equal(validation.artifactVersion, 1);
assert.equal(validation.gateCount, 10); 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"); console.log("project_release_readiness_artifact_node_smoke=ok");

View File

@@ -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");

View File

@@ -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"

View File

@@ -32,6 +32,7 @@ import {
createMachineSessionPersistenceDisplayViewModel, createMachineSessionPersistenceDisplayViewModel,
createMachineSessionPersistenceRenderState, createMachineSessionPersistenceRenderState,
createMachineSessionPersistenceSummary, createMachineSessionPersistenceSummary,
createProjectReleaseGateManifest,
createProjectReleaseReadinessArtifactValidation, createProjectReleaseReadinessArtifactValidation,
createProjectReleaseReadinessReport, createProjectReleaseReadinessReport,
parseProjectReleaseReadinessArtifactJson, parseProjectReleaseReadinessArtifactJson,
@@ -83,6 +84,7 @@ const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md
const requiredExports = [ const requiredExports = [
["createLinuxCncIniSdk", createLinuxCncIniSdk], ["createLinuxCncIniSdk", createLinuxCncIniSdk],
["createLinuxCncInterpSdk", createLinuxCncInterpSdk], ["createLinuxCncInterpSdk", createLinuxCncInterpSdk],
["createProjectReleaseGateManifest", createProjectReleaseGateManifest],
["createProjectReleaseReadinessArtifactValidation", createProjectReleaseReadinessArtifactValidation], ["createProjectReleaseReadinessArtifactValidation", createProjectReleaseReadinessArtifactValidation],
["createProjectReleaseReadinessReport", createProjectReleaseReadinessReport], ["createProjectReleaseReadinessReport", createProjectReleaseReadinessReport],
["parseProjectReleaseReadinessArtifactJson", parseProjectReleaseReadinessArtifactJson], ["parseProjectReleaseReadinessArtifactJson", parseProjectReleaseReadinessArtifactJson],
@@ -214,6 +216,7 @@ const releaseReadinessReady = createProjectReleaseReadinessReport({
}); });
assert.equal(releaseReadinessReady.ready, true); assert.equal(releaseReadinessReady.ready, true);
assert.equal(releaseReadinessReady.releaseGate.passed, true); assert.equal(releaseReadinessReady.releaseGate.passed, true);
assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest());
const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.stringify(createProjectReleaseReadinessReport({ const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.stringify(createProjectReleaseReadinessReport({
gateResults: { gateResults: {
"diff-check": true, "diff-check": true,
@@ -238,6 +241,8 @@ assert.deepEqual(createProjectReleaseReadinessArtifactValidation(releaseReadines
artifactApiName: "project-release-readiness-report", artifactApiName: "project-release-readiness-report",
artifactVersion: 1, artifactVersion: 1,
gateCount: 10, gateCount: 10,
expectedGateCount: 10,
expectedGateIds: createProjectReleaseGateManifest().gateIds,
blockedRuntimeFamilies: [ blockedRuntimeFamilies: [
"L4-USER-M-PROCESS", "L4-USER-M-PROCESS",
"L4-TOOL-DB", "L4-TOOL-DB",

View File

@@ -4,3 +4,4 @@ set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)" ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
node "$ROOT_DIR/tests/sdk/node/verify_sdk_surface.mjs" node "$ROOT_DIR/tests/sdk/node/verify_sdk_surface.mjs"
"$ROOT_DIR/tests/sdk/node/verify_project_release_gate_manifest.sh"