diff --git a/text4.txt b/text4.txt index a7eebbb..d8373c0 100644 --- a/text4.txt +++ b/text4.txt @@ -621,6 +621,354 @@ Batch 15: blocked runtime host preflight - Node inventory, browser smoke, and the boundary phase completion summary now require this host-preflight artifact. +Batch 16: blocked runtime host requirement summary + +- Complete as a generated normalized host requirement summary, still blocked + for execution and promotion on this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-requirement-summary.tsv + ``` + +- The artifact expands the host preflight `runtime_requirements` vectors into + one row per required host command/source/module, with: + - requirement name; + - requirement kind (`host_command` or `source_or_module`); + - current availability; + - blocked runtime families that require it; + - blocked runtime families for which it is missing; + - opt-in probe environment variables affected by it; + - whether the missing requirement currently blocks runtime execution. +- Current missing host commands are normalized as: + + ```text + halcmd available=0 required_by=L4-TOOL-DB,L4-USER-M-PROCESS + halrun available=0 required_by=L4-USER-M-PROCESS + linuxcnc available=0 required_by=L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS + milltask available=0 required_by=L4-TOOL-DB + ``` + +- Current available prerequisites remain recorded but non-promoting, including + `python3`, `tclsh`, `axis/db_demo/db_nonran.py`, `linuxcnc.so`, + `tooldb.py`, `interp_python.cc`, `python_plugin.cc`, and the selected + stop-lookahead Python modules. +- This summary does not run probes and does not relax promotion locks. It only + makes the host gap list machine-readable by requirement and affected blocked + family. +- Node inventory, browser smoke, and the boundary phase completion summary now + require this host requirement summary. + +Batch 17: blocked runtime host unblock plan + +- Complete as a generated host unblock plan, still blocked for execution and + promotion on this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-unblock-plan.tsv + ``` + +- The artifact filters the host requirement summary to only requirements that + are both unavailable and currently block runtime probes. Each row records: + - missing host command; + - affected blocked runtime families; + - affected target programs/families; + - affected opt-in native probe environment variables; + - exact affected opt-in native probe commands; + - remaining missing requirements after that one command becomes available; + - non-executing unblock status and next action. +- Current blocking host commands are: + + ```text + halcmd + halrun + linuxcnc + milltask + ``` + +- Every current row remains a partial unblock because each affected runtime + family still has at least one other missing host requirement after any single + command is provided: + + ```text + unblocks_when_available=partial_unblock_other_requirements_remain + next_action=provide_missing_host_requirement + execution_enabled=0 + promotion_allowed=0 + ``` + +- This plan does not install host commands, run probes, or relax promotion + locks. It only makes the next host-runtime unblock actions and affected + opt-in probes machine-readable. +- Node inventory, browser smoke, and the boundary phase completion summary now + require this host unblock plan. + +Batch 18: blocked runtime family host readiness + +- Complete as a generated per-family host readiness summary, still blocked for + execution and promotion on this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-family-host-readiness.tsv + ``` + +- The artifact records one row per blocked runtime family with: + - boundary class/kind/target; + - required LinuxCNC runtime family; + - missing host requirement count and list; + - currently available prerequisite count and list; + - opt-in native probe environment variable; + - exact opt-in native probe command; + - current probe and preflight status; + - per-family host status. +- Current rows remain: + + ```text + L4-USER-M-PROCESS missing=halrun,halcmd,linuxcnc + L4-TOOL-DB missing=linuxcnc,milltask,halcmd + L4-PYTHON-REMAP missing=linuxcnc + ``` + +- Current per-family status remains: + + ```text + preflight_status=blocked_missing_host_runtime + family_host_status=blocked_missing_host_requirements + execution_enabled=0 + promotion_allowed=0 + ``` + +- This summary does not run probes and does not relax promotion locks. It gives + each blocked family a single host-readiness row to check before running the + already declared opt-in native probe command. +- Node inventory, browser smoke, and the boundary phase completion summary now + require this family host readiness summary. + +Batch 19: blocked runtime host readiness rollup + +- Complete as a generated aggregate host readiness rollup, still blocked for + execution and promotion on this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv + ``` + +- The artifact reduces the per-family host readiness rows to one overall + decision for blocked runtime native probes: + - total blocked runtime family count; + - host-ready family count; + - host-blocked family count; + - blocked family list; + - unique missing host requirement list; + - ready opt-in command count/list; + - blocked opt-in command list; + - aggregate host readiness status. +- Current aggregate state remains: + + ```text + family_count=3 + host_ready_family_count=0 + host_blocked_family_count=3 + missing_host_requirements=halcmd,halrun,linuxcnc,milltask + ready_opt_in_command_count=0 + ready_opt_in_commands=- + host_readiness_status=host_blocked_for_all_opt_in_native_probes + execution_enabled=0 + promotion_allowed=0 + ``` + +- The rollup does not run probes and does not relax promotion locks. It gives + the browser, Node inventory, and future automation one single non-promoting + answer: this host is not ready to run any blocked runtime opt-in native + probe. +- Node inventory, browser smoke, and the boundary phase completion summary now + require this host readiness rollup. + +Batch 20: blocked runtime opt-in probe dispatch plan + +- Complete as a generated per-probe host dispatch plan, still blocked for + execution and promotion on this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-plan.tsv + ``` + +- The artifact turns the family host-readiness rows and aggregate host + readiness rollup into one row per blocked runtime opt-in native probe, with: + - boundary class/kind/target; + - opt-in environment variable; + - exact opt-in native probe command; + - per-family host status; + - missing host requirements; + - aggregate host readiness status; + - dispatch action and dispatch permission; + - next action. +- Current dispatch rows remain: + + ```text + L4-USER-M-PROCESS host_action=skip_missing_host_requirements missing=halrun,halcmd,linuxcnc + L4-TOOL-DB host_action=skip_missing_host_requirements missing=linuxcnc,milltask,halcmd + L4-PYTHON-REMAP host_action=skip_missing_host_requirements missing=linuxcnc + ``` + +- Current dispatch state remains: + + ```text + host_readiness_status=host_blocked_for_all_opt_in_native_probes + dispatch_allowed=0 + execution_enabled=0 + promotion_allowed=0 + ``` + +- This dispatch plan does not run probes and does not relax promotion locks. It + gives future automation a per-command action: skip each blocked runtime + native probe until the named host requirements are present, then require the + existing manual opt-in command before any promotion gates can move. +- Node inventory, browser smoke, and the boundary phase completion summary now + require this opt-in probe dispatch plan. + +Batch 21: blocked runtime opt-in probe dispatch rollup + +- Complete as a generated aggregate dispatch rollup, still blocked for + execution and promotion on this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-rollup.tsv + ``` + +- The artifact reduces the per-probe dispatch plan rows to one overall + dispatch switch for blocked runtime native probes: + - total opt-in probe count; + - dispatch-allowed probe count; + - dispatch-blocked probe count; + - blocked family list; + - dispatch action set; + - dispatch-allowed command list; + - dispatch-blocked command list; + - unique missing host requirement list; + - aggregate dispatch status. +- Current aggregate dispatch state remains: + + ```text + probe_count=3 + dispatch_allowed_count=0 + dispatch_blocked_count=3 + dispatch_actions=skip_missing_host_requirements + dispatch_allowed_commands=- + missing_host_requirements=halcmd,halrun,linuxcnc,milltask + dispatch_status=dispatch_blocked_for_all_opt_in_native_probes + execution_enabled=0 + promotion_allowed=0 + ``` + +- The rollup does not run probes and does not relax promotion locks. It gives + browser smoke, Node inventory, and future automation a single non-promoting + dispatch answer: no blocked runtime opt-in native probe may run on this host. +- Node inventory, browser smoke, and the boundary phase completion summary now + require this opt-in probe dispatch rollup. + +Batch 22: blocked runtime opt-in probe skip evidence contract + +- Complete as a generated per-probe skip/evidence contract, still blocked for + execution and promotion on this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-contract.tsv + ``` + +- The artifact joins the opt-in probe dispatch plan with the native pass + evidence contract so each currently skipped probe records: + - boundary class/kind/target; + - runtime probe id; + - opt-in environment variable; + - exact opt-in native probe command; + - host dispatch action; + - skip reason; + - missing host requirements; + - current probe status; + - whether native pass evidence is required now; + - observed native evidence readiness; + - skip/evidence status. +- Current skip/evidence rows remain: + + ```text + L4-USER-M-PROCESS skip_reason=missing_host_requirements missing=halrun,halcmd,linuxcnc + L4-TOOL-DB skip_reason=missing_host_requirements missing=linuxcnc,milltask,halcmd + L4-PYTHON-REMAP skip_reason=missing_host_requirements missing=linuxcnc + ``` + +- Current evidence state remains: + + ```text + current_probe_status=skipped_missing_host_runtime + evidence_required_now=0 + observed_evidence_ready=0 + native_evidence_status=pending_until_native_pass + skip_evidence_status=skip_valid_until_host_requirements_available + execution_enabled=0 + promotion_allowed=0 + ``` + +- This contract does not run probes and does not relax promotion locks. It + makes the non-executed state explicit: skipped opt-in native probes do not + satisfy native pass evidence until host requirements are present and the + existing manual opt-in probe command passes. +- Node inventory, browser smoke, and the boundary phase completion summary now + require this opt-in probe skip evidence contract. + +Batch 23: blocked runtime opt-in probe skip evidence rollup + +- Complete as a generated aggregate skip/evidence rollup, still blocked for + execution and promotion on this host. +- Generated artifact: + + ```text + wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv + ``` + +- The artifact reduces the per-probe skip/evidence contract rows to one + overall native evidence acceptance state for blocked runtime opt-in probes: + - total opt-in probe count; + - skipped probe count; + - current evidence-required count; + - observed evidence-ready count; + - skipped family list; + - skip reason list; + - unique missing host requirement list; + - current probe status list; + - native evidence status list; + - skip/evidence status list; + - aggregate evidence rollup status. +- Current aggregate skip/evidence state remains: + + ```text + probe_count=3 + skip_count=3 + evidence_required_now_count=0 + observed_evidence_ready_count=0 + skip_reasons=missing_host_requirements + missing_host_requirements=halcmd,halrun,linuxcnc,milltask + current_probe_statuses=skipped_missing_host_runtime + native_evidence_statuses=pending_until_native_pass + skip_evidence_statuses=skip_valid_until_host_requirements_available + evidence_rollup_status=no_native_pass_evidence_accepted_while_host_blocked + execution_enabled=0 + promotion_allowed=0 + ``` + +- The rollup does not run probes and does not relax promotion locks. It gives + browser smoke, Node inventory, and future automation a single non-promoting + evidence answer: no native pass evidence is accepted while every blocked + runtime opt-in probe is skipped for missing host requirements. +- Node inventory, browser smoke, and the boundary phase completion summary now + require this opt-in probe skip evidence rollup. + Current next implementation recommendations Generated recommendation source: diff --git a/text5.txt b/text5.txt new file mode 100644 index 0000000..3830843 --- /dev/null +++ b/text5.txt @@ -0,0 +1,6394 @@ +项目整体完成情况与后续执行计划 + +生成时间:2026-06-11 CST + +本文件接替 `text4.txt`。后续工作按 `text5.txt` 执行;除非本文件被更新, +不要再回到 `text1/text2/text3/text4` 的旧计划扩展范围。 + +一、项目纪律 + +1. LinuxCNC 仍是唯一 CNC 语义来源。 + G-code、remap、tool、parameter、planner、kinematics、user-M、tool DB、 + Python remap 等语义必须来自 LinuxCNC upstream 或 vendored LinuxCNC + C/C++/配置/脚本源码。 + +2. 本项目允许实现的范围是: + build、source sync、runtime shim、filesystem staging、OPFS、WASM/browser + boundary、测试胶水、文档、机器可读 gate/artifact。 + +3. 禁止事项: + - 不在 JS 中重写 G-code 或 CNC 语义。 + - 不修改 vendored LinuxCNC 源文件来让测试通过。 + - 不把 full-process/HAL/UI/Python/tool DB 依赖伪装成 standalone pass。 + - 不把 expected failure 硬改成 pass,除非已经有 LinuxCNC-owned runtime + proof,并且 native、Node、browser gate 都按顺序通过。 + +4. browser 不能依赖目录枚举。Node/browser staging 继续使用 + `tools/source-manifest.txt`、INI 文本和 SDK staging helper 生成计划。 + +二、当前整体完成情况 + +1. WASM port 基础架构已成型。 + `wasm-port` 已按独立移植工作区组织,包含 vendor、runtime/core、 + runtime/sdk、runtime/opfs、runtime/ui、native/wasm/browser tests、docs 和 + tools。`linuxcnc/` 作为 upstream source tree 使用,不在原地修改。 + +2. G-code file execution 状态输出已完成第一阶段。 + 已有 `run_step` 机器可解析输出: + - 当前 line; + - 当前 statement_uri; + - read/execute phase 与 step; + - X/Y/Z/A/B/C/U/V/W 轴位置; + - UI 优先解析 run_step,fallback 到 canonical motion event。 + +3. `linuxcnc/nc_files` 基础测试已固化。 + 当前 Layer 1 基线: + - 命令:`wasm-port/tests/native/verify_nc_files.sh` + - 结果:`total 107, pass 101, expected_fail 6, unexpected_fail 0` + - `3D_Chips.ngc` 等代表程序已纳入 context-aware smoke。 + +4. `linuxcnc/configs/sim` native strict harness 已建立。 + 当前 Layer 2 基线: + - 命令:`wasm-port/tests/native/verify_sim_configs.sh` + - 结果:`total 159, pass 151, expected_fail 8, unexpected_fail 0` + - class 统计: + - `main 48` + - `macro_load 46` + - `remap_subroutine 65` + - 8 个 expected failure 当前归因清楚: + - `axis/foam/foam.ngc`:native standalone 缺 U/V axis context; + - bridge-mill `5axisgui.ngc`:native standalone 缺 W axis context; + - `axis/geometry/xyzc.ngc`:native standalone 缺 M110 USER_M_PATH runtime; + - `axis/external_offsets/*.ngc`:native standalone 缺 M111 USER_M_PATH + runtime; + - `incremental_repetition_g533.ngc`:保留 upstream demo edge。 + +5. `configs/sim` 覆盖矩阵已完成当前定义。 + 已有 tracked matrix: + - `wasm-port/docs/sim-configs-coverage-matrix.md` + 已有 native generated artifacts: + - `wasm-port/build/native/sim-configs/summary.tsv` + - `wasm-port/build/native/sim-configs/class-summary.tsv` + - `wasm-port/build/native/sim-configs/path-matrix.tsv` + 159 条记录已映射到 class、native status、expected failure、Layer 3/4 + coverage、blocked dependency 或非目标分类。 + +6. INI-context staging helper 已制度化。 + `planIniFileContextStaging()` 和 `planSimConfigStaging()` 已负责根据 INI + 与 manifest 收集: + - `[DISPLAY]OPEN_FILE` + - `[EMCIO]TOOL_TABLE` + - `[RS274NGC]PARAMETER_FILE` + - `[RS274NGC]SUBROUTINE_PATH` + - `[RS274NGC]USER_M_PATH` + - remap-NGC files + 这些 helper 只做 host/filesystem staging plan,不读写 CNC 语义。 + +7. Node WASM sim-config inventory 已完成当前阶段。 + 当前 Node inventory: + - `executed 28` + - `passed 28` + - `skipped 131` + - `unexpected_fail 0` + 当前 skip/block 汇总: + - `ASSET-ONLY 65` + - `L4-PYTHON-REMAP 53` + - `L4-TOOL-DB 1` + - `L4-USER-M-PROCESS 1` + - `NON_MAIN_CLASS 10` + - `UPSTREAM-DEMO 1` + +8. HAL/UI/full-process boundary classifier 与 gate 已完成当前阶段。 + SDK 已能识别并记录: + - HAL/HALUI/UI process declarations; + - Python UI/DB/remap declarations; + - `[EMCIO]DB_PROGRAM` tool DB process; + - `[RS274NGC]USER_M_PATH` 与 unstaged external user-M process calls; + - L4 hard blocked recommendation。 + +9. 当前三个 hard blocked runtime family 已有非执行 proof/gate/artifact。 + 仍 blocked,不允许 promotion: + - `L4-USER-M-PROCESS`: + `axis/vismach/millturn/example.ngc` + - `L4-TOOL-DB`: + `axis/db_demo/base.ngc` + - `L4-PYTHON-REMAP`: + `axis/remap/stop-lookahead/nc_files` 作为最小生命周期 fixture + +10. blocked runtime host readiness 当前状态。 + 当前 host 不具备执行 blocked runtime opt-in probes 的条件: + - 缺 `halcmd` + - 缺 `halrun` + - 缺 `linuxcnc` + - 缺 `milltask` + 当前 rollup: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +11. 当前工作区状态说明。 + 已有未提交改动: + - `text4.txt` + - `wasm-port/tests/browser/interp_smoke.html` + - `wasm-port/tests/browser/verify_interp_browser.sh` + - `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` + 本文件只新增 `text5.txt`,不回退这些既有改动。 + +三、当前必须保持通过的验证入口 + +每一批后续改动后,至少运行: + +```bash +git diff --check +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +涉及 native baseline 或大范围 source/staging 改动时,再运行: + +```bash +wasm-port/tests/native/verify_nc_files.sh +wasm-port/tests/native/verify_sim_configs.sh +wasm-port/tests/native/verify_native_probes.sh +``` + +通过条件: + +- `git diff --check` clean。 +- vendor sync 通过。 +- no-standalone-CNC-semantics guard 通过。 +- Node/browser smoke 通过。 +- Layer 1 保持 `107 / 101 / expected_fail 6 / unexpected_fail 0`。 +- Layer 2 保持 `159 / 151 / expected_fail 8 / unexpected_fail 0`,除非有新的 + LinuxCNC-owned proof 和文档同步说明。 +- Node inventory 保持 `unexpected_fail 0`。 +- blocked runtime rows 保持 `execution_enabled=0` 和 `promotion_allowed=0`, + 直到 gate 明确允许。 + +四、后续执行顺序 + +1. 先稳定当前大批改动,形成可审查状态 + +目标: + +- 不继续扩大测试面。 +- 先确认当前 23 个 boundary/gate 批次、browser smoke 改动和 Node inventory + 改动可以稳定复跑。 +- 把当前工作集整理成可审查边界。 + +执行命令: + +```bash +git diff --check +git status --short +git diff --stat +git diff --name-only +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +产出: + +- 记录当前所有改动文件。 +- 按审查 batch 归类: + - Batch A:browser smoke gate/schema 扩展; + - Batch B:Node sim-config inventory artifact/gate 扩展; + - Batch C:`text4.txt` 边界记录; + - Batch D:`text5.txt` 后续执行计划。 +- 如果某个验证失败,优先修当前批次的 test/gate drift,不新增功能。 + +2. 对 `text4` 后半段新增 gate 做文档对账 + +目标: + +- 确认 `text4.txt` 中 Batch 15 到 Batch 23 的新增 artifact,都在 + `docs/compatibility-validation.md` 或 + `docs/sim-configs-coverage-matrix.md` 中有对应说明。 +- 文档不能只记录“生成了文件”,还要记录该 artifact 的 promotion 含义: + non-executing、non-promoting、blocked until host/runtime/native proof。 + +重点对账 artifact: + +```text +runtime-boundary-host-preflight.tsv +runtime-boundary-host-requirement-summary.tsv +runtime-boundary-host-unblock-plan.tsv +runtime-boundary-family-host-readiness.tsv +runtime-boundary-host-readiness-rollup.tsv +runtime-boundary-opt-in-probe-dispatch-plan.tsv +runtime-boundary-opt-in-probe-dispatch-rollup.tsv +runtime-boundary-opt-in-probe-skip-evidence-contract.tsv +runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv +``` + +验证: + +```bash +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +3. 固化 blocked runtime host preflight 的人工检查流程 + +目标: + +- 后续换到具备 LinuxCNC host runtime 的机器时,可以先跑 preflight/rollup, + 再决定是否允许执行 opt-in native probes。 +- 当前机器继续保持 blocked,不运行 opt-in probe。 + +当前缺失: + +```text +halcmd +halrun +linuxcnc +milltask +``` + +当前禁止直接执行: + +```bash +ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh +ENABLE_TOOL_DB_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_tool_db_runtime.sh +ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh +``` + +只有当对应 gate 显示 `runtime_ready=1`,并且 dispatch plan 显示 +`dispatch_allowed=1` 时,才允许人工 opt-in 执行。 + +执行时仍然不能自动 promotion。native pass 后还必须经过: + +1. native pass evidence contract; +2. Node inventory gate; +3. browser smoke gate; +4. manual promotion lock update; +5. 文档和 coverage matrix 同步。 + +4. 第一个可推进 blocked runtime:millturn M128/M129 + +前置条件: + +- host 具备 `tclsh`、`halrun`、`halcmd`、`linuxcnc`。 +- `user-m-process-native-runtime-probe-gate.tsv` 报告 `runtime_ready=1`。 +- `runtime-boundary-opt-in-probe-dispatch-plan.tsv` 对 + `L4-USER-M-PROCESS` 报告可 dispatch。 + +执行目标: + +- 用 LinuxCNC-owned Tcl/HAL runtime 证明 `M128/M129` 过程脚本真实更新 + `ini.[xyz].*` HAL pins。 +- 证明 `M428 -> M128` 和 `M429 -> M129` transition contract。 +- 不把该行为降级成 canonical user-M event。 + +允许执行的 opt-in 命令: + +```bash +ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh +``` + +native pass 期望: + +```text +runtime_state_probe_passed +millturn_user_m_M128_runtime_state_ok=1 +millturn_user_m_M129_runtime_state_ok=1 +``` + +后续要求: + +- 更新 native evidence artifact。 +- 更新 Node inventory gate。 +- 只在 Node proof 通过后,再考虑 browser/sdkwasm narrow boundary。 +- 不声明 full LinuxCNC task/HAL/UI process coverage。 + +5. 第二个可推进 blocked runtime:tool DB process + +前置条件: + +- host 具备 `python3`、`linuxcnc`、`milltask`、`halcmd`。 +- `tool-db-process-native-runtime-probe-gate.tsv` 报告 `runtime_ready=1`。 +- dispatch plan 对 `L4-TOOL-DB` 报告可 dispatch。 + +执行目标: + +- 证明 LinuxCNC `DB_PROGRAM` v2.1 protocol: + - startup handshake; + - `g` get-all; + - `p` put/update; + - `l` load; + - `u` unload; + - persistence state。 +- 禁止用 `.tbl` fallback 冒充 DB_PROGRAM coverage。 + +允许执行的 opt-in 命令: + +```bash +ENABLE_TOOL_DB_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_tool_db_runtime.sh +``` + +native pass 期望: + +```text +runtime_protocol_probe_passed +tool_db_protocol_version=v2.1 +tool_db_runtime_protocol_probe_ok=1 +tool_db_persistence_state_ok=1 +``` + +6. 第三个可推进 blocked runtime:Python remap lifecycle + +前置条件: + +- host 具备 `python3` 和 `linuxcnc`。 +- `python-remap-native-runtime-probe-gate.tsv` 报告 `runtime_ready=1`。 +- dispatch plan 对 `L4-PYTHON-REMAP` 报告可 dispatch。 + +执行目标: + +- 先只做最小 fixture: + `axis/remap/stop-lookahead/nc_files` +- 证明 LinuxCNC Python remap runtime lifecycle: + - demo.ini path/toplevel import; + - callable lookup; + - generator return; + - first `INTERP_EXECUTE_FINISH` yield。 +- 不把 53 个 Python-remap family 直接提升到 browser/full inventory。 + +允许执行的 opt-in 命令: + +```bash +ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh +``` + +native pass 期望: + +```text +runtime_lifecycle_probe_passed +python_remap_lifecycle_generator_first_yield=2 +python_remap_runtime_lifecycle_probe_ok=1 +``` + +7. native pass 后的 promotion 顺序 + +任何 blocked runtime family 即使 native opt-in probe 通过,也必须按下面顺序推进: + +1. pass evidence contract 变为 ready; +2. `runtime-probe-gate-alignment.tsv` 对齐; +3. `runtime-boundary-promotion-readiness.tsv` 更新; +4. Node inventory 新增 narrow proof,但 blocked row 不能直接变成 broad REP; +5. browser smoke 只补 narrow boundary proof; +6. 手动更新 promotion lock; +7. 更新: + - `docs/compatibility-validation.md` + - `docs/full-process-boundary-design.md` + - `docs/sim-configs-coverage-matrix.md` + - `text5.txt` + +8. 继续 upstream intake 的条件 + +只有当前工作集稳定,并且 blocked runtime gate 没有 drift 后,才允许继续小批量 +引入 upstream `tests/interp/*` 或 `tests/ccomp/*`。 + +候选原则: + +- 优先 pure interpreter file execution。 +- 需要 INI/tool/parameter/subroutine/user-M context 的,必须通过 staging helper。 +- Python remap、MDI/UI/full-process、HAL/task/linuxcncrsh 默认 blocked。 +- 每个 intake 必须先 native,再 Node WASM,再 browser。 +- 每次只做小批量,不用新增测试面掩盖当前 gate 问题。 + +五、近期不要做的事 + +1. 不继续扩大 browser 全量 inventory。 + browser 保持 class representative smoke,不追求一次性跑完 159 个 sim-config + rows。 + +2. 不在当前 host 上执行 blocked opt-in probes。 + 当前 host 缺 `halcmd/halrun/linuxcnc/milltask`,dispatch rollup 已显示全部 + blocked。 + +3. 不把 `axis/db_demo/base.ngc` 降级成 tool-table fallback。 + +4. 不把 `axis/vismach/millturn/example.ngc` 降级成 user-M canonical event。 + +5. 不把 Python remap family 降级成 JS/Python stub 语义。 + +6. 不把 `incremental_repetition_g533.ngc` 改成 pass。 + 这是保留 upstream demo edge,不修改 G-code 或 runner 语义。 + +六、下一次开始执行时的第一条命令 + +从这里继续: + +```bash +git diff --check +git status --short +git diff --stat +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +如果这些不通过,先修验证 drift;不要新增功能。 + +如果这些通过,优先做“第四部分第 2 步:对 `text4` 后半段新增 gate 做文档对账”。 + +七、2026-06-11 执行记录 + +本轮按“六、下一次开始执行时的第一条命令”继续执行。 + +1. 初始稳定性检查完成。 + +执行: + +```bash +git diff --check +git status --short +git diff --stat +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +结果: + +- `git diff --check` clean。 +- 当前工作区仍有既有改动,并新增/更新本轮文档: + - `text4.txt` + - `text5.txt` + - `wasm-port/tests/browser/interp_smoke.html` + - `wasm-port/tests/browser/verify_interp_browser.sh` + - `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` + - `wasm-port/docs/compatibility-validation.md` + - `wasm-port/docs/sim-configs-coverage-matrix.md` + - `wasm-port/docs/full-process-boundary-design.md` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` + +2. 第四部分第 2 步完成:`text4` 后半段新增 gate 文档对账。 + +更新: + +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +已补齐以下 artifact 的路径和 non-executing/non-promoting 语义说明: + +```text +runtime-boundary-host-preflight.tsv +runtime-boundary-host-requirement-summary.tsv +runtime-boundary-host-unblock-plan.tsv +runtime-boundary-family-host-readiness.tsv +runtime-boundary-host-readiness-rollup.tsv +runtime-boundary-opt-in-probe-dispatch-plan.tsv +runtime-boundary-opt-in-probe-dispatch-rollup.tsv +runtime-boundary-opt-in-probe-skip-evidence-contract.tsv +runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv +``` + +验证: + +```bash +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +结果全部通过。 + +3. 第四部分第 3 步完成:blocked runtime host preflight 人工检查流程固化。 + +更新: + +- `wasm-port/docs/full-process-boundary-design.md` + +新增 `Host Preflight Runbook`,明确: + +- 必须先检查 host preflight、family readiness、host readiness rollup、 + dispatch plan、dispatch rollup。 +- 当前 host 缺 `halcmd`、`halrun`、`linuxcnc`、`milltask`。 +- 当前 host 对所有 blocked runtime opt-in native probes 都保持 blocked。 +- 以下命令当前仍禁止执行: + +```bash +ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh +ENABLE_TOOL_DB_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_tool_db_runtime.sh +ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh +``` + +- 只有对应 family 的 `runtime_ready=1` 且 `dispatch_allowed=1` 后,才允许 + 人工 opt-in。 +- native opt-in probe pass 仍不等于 promotion;还必须经过 evidence contract、 + gate alignment、Node proof、browser proof 和 manual promotion lock update。 + +验证: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 第四部分第 4 步当前 blocked,不能执行。 + +原因: + +- `runtime-boundary-host-readiness-rollup.tsv` 当前仍为 + `host_blocked_for_all_opt_in_native_probes`。 +- `runtime-boundary-opt-in-probe-dispatch-plan.tsv` 对 + `L4-USER-M-PROCESS` 仍为 `dispatch_allowed=0`。 +- 当前缺失 `halrun`、`halcmd`、`linuxcnc`,因此不能运行 millturn + `M128/M129` opt-in native runtime probe。 + +下一步: + +- 暂停 blocked runtime promotion 执行。 +- 若继续在当前 host 工作,只能做不需要 host runtime 的文档、对账、 + source/gate consistency、或小批量 pure interpreter intake。 +- 若要推进第四部分第 4 步,需要先切换到具备 LinuxCNC host runtime 的环境, + 重新跑 host preflight/dispatch artifacts,确认 `runtime_ready=1` 和 + `dispatch_allowed=1`。 + +八、2026-06-11 继续执行记录:source/gate consistency 对账 + +本轮从第七部分记录的 blocker 继续执行。由于当前 host 仍缺 LinuxCNC runtime, +第四部分第 4 步的 millturn `M128/M129` opt-in probe 继续保持 blocked; +本轮只做不需要 host runtime 的 source/gate consistency 对账。 + +1. 当前 blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +结论: + +- 不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 +- 继续保持 blocked runtime promotion 暂停。 + +2. 生成 artifact 与文档引用对账完成。 + +执行: + +```bash +find wasm-port/build/wasm/sim-configs-inventory -maxdepth 1 -type f -name '*.tsv' -printf '%f\n' | sort > /tmp/generated.tsvs +rg -o "[A-Za-z0-9_.-]+\\.tsv" \ + wasm-port/docs/compatibility-validation.md \ + wasm-port/docs/sim-configs-coverage-matrix.md \ + wasm-port/docs/full-process-boundary-design.md \ + text5.txt | sed 's/^.*://' | sort -u > /tmp/documented.tsvs +comm -23 /tmp/generated.tsvs /tmp/documented.tsvs +``` + +初始发现 4 个已生成但未被当前文档集合引用的 artifact: + +```text +native-runtime-probe-execution-plan.tsv +next-boundary-recommendations.tsv +runtime-boundary-native-evidence-acceptance-gate.tsv +runtime-boundary-promotion-blockers.tsv +``` + +已更新: + +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` +- `wasm-port/docs/full-process-boundary-design.md` + +补充说明: + +- `native-runtime-probe-execution-plan.tsv` 是 guarded opt-in execution plan, + 记录 exact command、required proof、runtime readiness、expected pass status + 和 promotion prerequisites;它不是 probe runner,也不允许 promotion。 +- `next-boundary-recommendations.tsv` 是 generated priority handoff,不是执行 + 许可。 +- `runtime-boundary-native-evidence-acceptance-gate.tsv` 决定 native pass + evidence 是否可被接受;当前因 host runtime 缺失仍 blocked。 +- `runtime-boundary-promotion-blockers.tsv` 展开 promotion blocker keys: + host runtime 缺失、native probe 未通过、native evidence 未 ready、 + Node/browser gate 未完成、promotion lock active、manual lock update required。 + +复查结果: + +- `comm -23 /tmp/generated.tsvs /tmp/documented.tsvs` 现在无输出。 +- `sim-configs-inventory` 下当前生成的 TSV artifact 都已被文档集合引用。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 `text5.txt` 的 blocked runtime 执行仍不能继续,因为 host readiness +没有变化。下一步有两个合法方向: + +- 切换到具备 `halcmd`、`halrun`、`linuxcnc`、`milltask` 的 LinuxCNC host + runtime 环境,然后重新跑 preflight/dispatch,再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的工作:文档对账、gate consistency、 + source proof consistency,或小批量 pure interpreter intake。 + +九、2026-06-11 继续执行记录:blocked runtime 跨 artifact guard + +本轮继续按当前 host 可执行范围推进。blocked runtime host readiness 仍未变化, +不执行任何 opt-in native runtime probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- 当前 host 仍为: + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 新增 Node inventory 跨 artifact consistency guard。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` + +新增 `verifyBlockedRuntimeOptInGateConsistency()`,在每次 Node inventory +运行时检查以下 artifact 的 blocked runtime opt-in target 是否一致: + +```text +next-boundary-recommendations.tsv +native-runtime-probe-execution-plan.tsv +runtime-boundary-promotion-readiness.tsv +runtime-boundary-promotion-blockers.tsv +runtime-boundary-post-native-pass-gates.tsv +runtime-boundary-host-preflight.tsv +runtime-boundary-family-host-readiness.tsv +runtime-boundary-opt-in-probe-dispatch-plan.tsv +runtime-boundary-opt-in-probe-skip-evidence-contract.tsv +runtime-boundary-native-evidence-acceptance-gate.tsv +``` + +检查内容: + +- 3 个 opt-in target 的 class/order 必须一致: + - `L4-USER-M-PROCESS` + - `L4-TOOL-DB` + - `L4-PYTHON-REMAP` +- boundary kind、target、blocked kind 必须一致。 +- required native/Node/browser proof 必须与 recommendation 对齐。 +- opt-in env 和 execution command 必须一致。 +- current probe status 必须一致。 +- missing host/runtime requirements 必须一致。 +- dispatch 必须保持 `dispatch_allowed=0`。 +- evidence acceptance 必须保持 blocked。 +- promotion lock、manual lock、host runtime blocker 必须仍然存在。 +- 所有相关 artifact 必须保持 `execution_enabled=0` 和 + `promotion_allowed=0`。 + +第一次运行时发现 guard 对 `native-runtime-probe-execution-plan.tsv` 的 schema +假设过严:该 artifact 使用 `boundary_class` 表示 blocked kind,没有单独 +`blocked` 字段。已按实际 schema 修正为: + +```text +row.blocked ?? row.boundary_class +``` + +修正后 Node inventory 通过。 + +3. 文档同步。 + +更新: + +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +已记录 Node inventory 现在会执行 blocked runtime opt-in target 的跨 artifact +一致性检查,防止 recommendations、execution plan、host preflight、dispatch、 +skip/evidence、native evidence acceptance、promotion readiness/blockers 和 +post-native-pass gates 之间发生 drift。 + +4. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +5. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 +*** End of File + +十、2026-06-11 继续执行记录:artifact 文档覆盖 completion criteria + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 将 artifact 文档覆盖加入 boundary phase completion summary。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` + +新增到 `boundary-phase-completion-summary.tsv` 的 criterion: + +```text +wasm_inventory_artifact_documentation_coverage +native_artifact_documentation_coverage +``` + +当前生成结果: + +```text +wasm_inventory_artifact_documentation_coverage passed=1 count=54 +native_artifact_documentation_coverage passed=1 count=8 +``` + +含义: + +- WASM sim-config inventory 生成的 TSV artifact 必须被 compatibility、matrix + 或 full-process boundary docs 引用。 +- native generated TSV artifact 必须按相对路径被 compatibility、matrix 或 + full-process boundary docs 引用。 +- 这两个检查原本已经作为硬断言存在;本轮把它们也写入机器可读 completion + summary,方便后续 review/CI 对账。 + +3. 文档同步。 + +更新: + +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +已记录 `boundary-phase-completion-summary.tsv` 现在包含: + +- WASM sim-config inventory artifact 文档覆盖; +- native generated TSV artifact 文档覆盖。 + +4. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +5. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +十一、2026-06-11 继续执行记录:native TSV 文档覆盖 guard + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 新增 native generated TSV 文档覆盖自动检查。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` + +新增: + +- `generatedTsvArtifactTokens()` +- `verifyNativeGeneratedArtifactDocumentationCoverage()` + +检查内容: + +- `build/native/` 下生成的所有 TSV artifact,必须按相对路径被以下文档至少 + 引用一次: + - `wasm-port/docs/compatibility-validation.md` + - `wasm-port/docs/sim-configs-coverage-matrix.md` + - `wasm-port/docs/full-process-boundary-design.md` +- 使用相对路径检查,避免 `summary.tsv` 这类 basename 在 `nc-files` 与 + `sim-configs` 之间重名导致误判。 + +当前被纳入检查的 native TSV: + +```text +build/native/source-probes.tsv +build/native/native-source-proof-summary.tsv +build/native/native-runtime-probe-summary.tsv +build/native/nc-files/summary.tsv +build/native/sim-configs/summary.tsv +build/native/sim-configs/class-summary.tsv +build/native/sim-configs/path-matrix.tsv +build/native/sim-configs/skipped.tsv +``` + +3. 文档同步。 + +更新: + +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +已补充 native generated TSV artifact 列表和检查说明,包括: + +- native source proof summary; +- native runtime probe summary; +- native `nc_files` baseline summary; +- native sim-config summary/class/path/skipped artifacts; +- `source-probes.tsv`。 + +4. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +5. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +十二、2026-06-11 继续执行记录:native source proof consistency guard + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 新增 native source proof consistency guard。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` + +新增 `verifyNativeSourceProofRuntimeProbeConsistency()`,在每次 Node inventory +运行时检查: + +- `native-proof-alignment-summary.tsv` +- `native-runtime-probe-summary.tsv` +- `runtime-probe-gate-alignment.tsv` + +检查内容: + +- 三者必须覆盖同一组 blocked class: + - `L4-USER-M-PROCESS` + - `L4-TOOL-DB` + - `L4-PYTHON-REMAP` +- native source proof alignment 必须为 `alignment_ok=1`。 +- native runtime probe summary 的 `source_proof_ready` 必须与 source proof + alignment 一致。 +- runtime probe gate alignment 的 native/gate source proof readiness 必须与 + source proof alignment 一致。 +- source proof、runtime probe、gate alignment 都必须保持 + `execution_enabled=0` / `promotion_allowed=0`。 +- runtime probe gate alignment 必须保持 `alignment_ok=1`。 + +这样可以防止 native source proof summary、native runtime probe summary 与 +runtime probe gate alignment 之间发生 drift。 + +3. 文档同步。 + +更新: + +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +已记录 Node inventory 现在会检查 native source proof consistency,确保 source +proof alignment、native runtime probe summary 和 runtime probe gate alignment +一致,且不启用 execution/promotion。 + +4. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +5. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +十三、2026-06-11 继续执行记录:生成 artifact 文档覆盖 guard + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 新增生成 artifact 文档覆盖自动检查。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` + +新增: + +- `generatedSimConfigInventoryArtifactPaths` +- `verifyGeneratedArtifactDocumentationCoverage()` + +检查内容: + +- `build/wasm/sim-configs-inventory/` 下由 Node inventory 生成的每一个 TSV + artifact,都必须被以下文档至少引用一次: + - `wasm-port/docs/compatibility-validation.md` + - `wasm-port/docs/sim-configs-coverage-matrix.md` + - `wasm-port/docs/full-process-boundary-design.md` +- 如果新增 gate artifact 但没有文档说明,`verify_sim_configs_inventory_wasm.sh` + 会失败。 + +执行该检查前,docs-only 对账发现还缺 2 个引用: + +```text +python-remap-native-runtime-probe-gate.tsv +tool-db-process-native-runtime-probe-gate.tsv +``` + +已补充到: + +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +同时补充了该自动检查本身的文档说明。 + +3. 验证完成。 + +执行: + +```bash +find wasm-port/build/wasm/sim-configs-inventory -maxdepth 1 -type f -name '*.tsv' -printf '%f\n' | sort > /tmp/generated.tsvs +rg -o "[A-Za-z0-9_.-]+\\.tsv" \ + wasm-port/docs/compatibility-validation.md \ + wasm-port/docs/sim-configs-coverage-matrix.md \ + wasm-port/docs/full-process-boundary-design.md | sed 's/^.*://' | sort -u > /tmp/documented-docs-only.tsvs +comm -23 /tmp/generated.tsvs /tmp/documented-docs-only.tsvs +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- docs-only `comm` 对账无输出。 +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +十四、2026-06-11 继续执行记录:completion summary consistency criteria + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 将已有 consistency guard 暴露到 completion summary。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +新增到 `boundary-phase-completion-summary.tsv` 的 criterion: + +```text +blocked_runtime_opt_in_gate_consistency +native_source_proof_runtime_probe_consistency +``` + +当前生成结果: + +```text +blocked_runtime_opt_in_gate_consistency passed=1 count=3 +native_source_proof_runtime_probe_consistency passed=1 count=3 +``` + +含义: + +- blocked runtime opt-in target 的 recommendations、execution plan、host + preflight、dispatch、skip/evidence、native evidence acceptance、promotion + readiness/blockers 和 post-native-pass gates 必须保持同一 class/order、 + target、proof key、opt-in command、host blocking、dispatch blocking、 + evidence blocking 和 promotion lock 状态。 +- native source proof alignment、native runtime probe summary 和 runtime + probe gate alignment 必须覆盖同一 blocked class set,并保持 + `execution_enabled=0` 和 `promotion_allowed=0`。 +- 这只是把已有硬断言写入机器可读 completion summary;不执行 native runtime + probe,也不允许 promotion。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +十五、2026-06-11 继续执行记录:blocked runtime rollup consistency + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 新增 blocked runtime rollup consistency guard。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +新增: + +- `verifyBlockedRuntimeRollupConsistency()` +- `boundary-phase-completion-summary.tsv` criterion: + +```text +blocked_runtime_rollup_consistency +``` + +当前生成结果: + +```text +blocked_runtime_rollup_consistency passed=1 count=3 +``` + +检查内容: + +- `runtime-boundary-host-readiness-rollup.tsv` +- `runtime-boundary-opt-in-probe-dispatch-rollup.tsv` +- `runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv` + +三类 rollup 必须一致: + +- family/probe count; +- blocked family set; +- missing host requirements; +- blocked/allowed opt-in commands; +- host readiness、dispatch、skip/evidence 状态; +- `execution_enabled=0`; +- `promotion_allowed=0`。 + +该检查只做生成 artifact 一致性验证,不执行 native runtime probe,也不允许 +promotion。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +十六、2026-06-11 继续执行记录:blocked runtime host requirement consistency + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 新增 blocked runtime host requirement consistency guard。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +新增: + +- `verifyBlockedRuntimeHostRequirementConsistency()` +- `boundary-phase-completion-summary.tsv` criterion: + +```text +blocked_runtime_host_requirement_consistency +``` + +当前生成结果: + +```text +blocked_runtime_host_requirement_consistency passed=1 count=3 +``` + +检查内容: + +- `runtime-boundary-host-requirement-summary.tsv` +- `runtime-boundary-host-unblock-plan.tsv` +- `runtime-boundary-family-host-readiness.tsv` + +三类 host requirement artifact 必须一致: + +- 缺失 requirement set 与 unblock plan set; +- 每个 family 的 missing/available requirement set; +- 每个缺失 requirement 影响的 blocked family、target 和 opt-in command; +- 移除单个 requirement 后仍剩余的 missing requirements; +- `execution_enabled=0`; +- `promotion_allowed=0`。 + +该检查只做生成 artifact 一致性验证,不执行 native runtime probe,也不允许 +promotion。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +十七、2026-06-11 继续执行记录:blocked runtime promotion gate consistency + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 新增 blocked runtime promotion gate consistency guard。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +新增: + +- `verifyBlockedRuntimePromotionGateConsistency()` +- `boundary-phase-completion-summary.tsv` criterion: + +```text +blocked_runtime_promotion_gate_consistency +``` + +当前生成结果: + +```text +blocked_runtime_promotion_gate_consistency passed=1 count=3 +``` + +检查内容: + +- `blocked-runtime-promotion-lock.tsv` +- `runtime-boundary-promotion-readiness.tsv` +- `runtime-boundary-promotion-blockers.tsv` +- `runtime-boundary-post-native-pass-gates.tsv` +- `runtime-boundary-native-evidence-acceptance-gate.tsv` +- `native-runtime-probe-pass-evidence-contract.tsv` + +这些 promotion gate artifacts 必须一致: + +- target、blocked class、current probe status、expected pass status; +- native pass/evidence readiness; +- Node/browser promotion gate status; +- promotion lock 和 manual lock 状态; +- blocker keys; +- evidence acceptance 仍 blocked; +- `execution_enabled=0`; +- `promotion_allowed=0`。 + +该检查只做生成 artifact 一致性验证,不执行 native runtime probe,也不允许 +promotion。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +十八、2026-06-11 继续执行记录:blocked runtime worklist recommendation consistency + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 新增 blocked runtime worklist recommendation consistency guard。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +新增: + +- `verifyBlockedRuntimeWorklistRecommendationConsistency()` +- `boundary-phase-completion-summary.tsv` criterion: + +```text +blocked_runtime_worklist_recommendation_consistency +``` + +当前生成结果: + +```text +blocked_runtime_worklist_recommendation_consistency passed=1 count=3 +``` + +检查内容: + +- `next-boundary-worklist.tsv` +- `boundary-proof-gates.tsv` +- `next-boundary-recommendations.tsv` +- `blocked-runtime-promotion-lock.tsv` +- `runtime-boundary-contract-summary.tsv` + +这些 worklist/recommendation artifacts 必须一致: + +- recommendation target 必须有对应 worklist row; +- recommendation target 必须有 native/node/browser proof gates; +- recommendation 的 Node/browser proof 必须与 worklist/proof-gate 对齐; +- promotion lock 必须仍 active; +- runtime contract summary 必须仍 `contract_ok=1`; +- `execution_enabled=0`; +- `promotion_allowed=0`。 + +该检查只做生成 artifact 一致性验证,不执行 native runtime probe,也不允许 +promotion。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +十九、2026-06-12 继续执行记录:runtime family contract/alignment consistency + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 runtime family contract/alignment consistency guard。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +新增: + +- `verifyRuntimeFamilyContractAlignmentConsistency()` +- `boundary-phase-completion-summary.tsv` criterion: + +```text +runtime_family_contract_alignment_consistency +``` + +当前生成结果: + +```text +runtime_family_contract_alignment_consistency passed=1 count=3 +``` + +检查内容: + +- `L4-USER-M-PROCESS`: + - `user-m-process-transition-plan.tsv` + - `user-m-process-native-runtime-state-plan.tsv` + - `user-m-process-native-runtime-readiness.tsv` + - `user-m-process-native-runtime-probe-gate.tsv` + - `runtime-boundary-contract-summary.tsv` + - `runtime-boundary-native-alignment-summary.tsv` +- `L4-TOOL-DB`: + - `tool-db-process-transaction-plan.tsv` + - `tool-db-process-native-runtime-readiness.tsv` + - `tool-db-process-native-runtime-probe-gate.tsv` + - `runtime-boundary-contract-summary.tsv` + - `runtime-boundary-native-alignment-summary.tsv` +- `L4-PYTHON-REMAP`: + - `python-remap-runtime-contract.tsv` + - `python-remap-native-runtime-readiness.tsv` + - `python-remap-native-runtime-state-plan.tsv` + - `python-remap-native-runtime-fixture-plan.tsv` + - `python-remap-native-runtime-probe-gate.tsv` + - `runtime-boundary-contract-summary.tsv` + - `runtime-boundary-native-alignment-summary.tsv` + +这些 family-specific artifacts 必须与 aggregate runtime contract/native +alignment summaries 一致: + +- contract artifact、row count、runtime alignment artifact、alignment row count; +- user-M transition/state-plan/probe-gate M128/M129 字段; +- tool DB transaction/probe-gate protocol transaction set; +- Python family runtime contract/state-plan/fixture/probe-gate fields; +- missing host requirements; +- `source_proof_ready=1`; +- `proof_status=pending`; +- `execution_enabled=0`; +- `promotion_allowed=0`。 + +该检查只做生成 artifact 一致性验证,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- `boundary-phase-completion-summary.tsv` 现在包含 + `runtime_family_contract_alignment_consistency`。 +- 该 criterion 用于确保 user-M、tool DB、Python 三个 blocked runtime family + 的 contract/readiness/probe/fixture/state-plan artifacts 与 + `runtime-boundary-contract-summary.tsv` 和 + `runtime-boundary-native-alignment-summary.tsv` 对齐。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +二十、2026-06-12 继续执行记录:browser completion summary parity + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 browser completion summary parity 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- Node inventory 的 `boundary-phase-completion-summary.tsv` 已包含新增 + consistency criteria: + - `blocked_runtime_worklist_recommendation_consistency` + - `blocked_runtime_probe_execution_consistency` + - `blocked_runtime_promotion_gate_consistency` + - `blocked_runtime_host_requirement_consistency` + - `blocked_runtime_rollup_consistency` + - `blocked_runtime_opt_in_gate_consistency` + - `native_source_proof_runtime_probe_consistency` + - `runtime_family_contract_alignment_consistency` + - `wasm_inventory_artifact_documentation_coverage` + - `native_artifact_documentation_coverage` +- browser smoke 已读取 `boundary-phase-completion-summary.tsv`,但 + `expectedCriteria` 列表落后于 Node verifier。 + +本轮补齐 browser 侧 criterion 顺序,使 browser smoke 与 Node inventory +对同一组 boundary-phase completion criteria 做 drift 检查。 + +该检查只读取 generated summary,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在会镜像 `boundary-phase-completion-summary.tsv` criterion + 列表; +- 如果 Node inventory 新增 completion criterion 但 browser smoke 未同步, + browser smoke 会因 criterion drift 失败; +- 该检查是 browser-side parity check,不改变 blocked runtime 状态。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +二十一、2026-06-12 继续执行记录:browser runtime contract/native alignment parity + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 browser runtime contract/native alignment summary parity 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- Node inventory 已通过 `runtime_family_contract_alignment_consistency` + 检查 family-specific contract/readiness/probe artifacts 与 + aggregate runtime contract/native alignment summaries 的一致性。 +- browser smoke 已分别检查 runtime contract summary 和 native alignment + summary,但没有直接确认两者之间的 artifact/count/kind 对齐。 + +本轮新增 browser-side parity: + +- `verifyBrowserRuntimeBoundaryContractSummary()` 现在返回 parsed rows; +- 新增 `verifyBrowserRuntimeContractNativeAlignmentParity()`; +- browser smoke 会检查: + - `boundary_kind` 一致; + - `runtime_alignment_artifact` 与 native alignment `artifact` 一致; + - `runtime_alignment_row_count` 与 native alignment `row_count` 一致; + - `runtime_alignment_ok` 与 native alignment `summary_ok` 一致; + - contract/native alignment summary 均保持 OK; + - execution/promotion disabled counts 与各自 row counts 一致。 + +该检查只读取 generated artifacts,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在会检查 `runtime-boundary-contract-summary.tsv` 与 + `runtime-boundary-native-alignment-summary.tsv` 的 browser-side parity; +- 该检查确保 browser 侧看到的 aggregate contract/native alignment summary + 与 Node inventory 生成的 boundary gate 状态一致。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +二十二、2026-06-12 继续执行记录:browser native source proof/runtime probe parity + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 browser native source proof/runtime probe/gate parity 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- Node inventory 已通过 `native_source_proof_runtime_probe_consistency` + 检查: + - `native-proof-alignment-summary.tsv` + - `native-runtime-probe-summary.tsv` + - `runtime-probe-gate-alignment.tsv` +- browser smoke 已分别检查这三个 artifact,但没有直接确认三者之间的 + source-proof/runtime/gate 对齐。 + +本轮新增 browser-side parity: + +- `verifyBrowserNativeProofAlignment()` 现在返回 parsed rows; +- `verifyBrowserNativeRuntimeProbeSummary()` 现在返回 parsed rows; +- `verifyBrowserRuntimeProbeGateAlignment()` 现在返回 parsed rows; +- 新增 `verifyBrowserNativeSourceProofRuntimeProbeParity()`; +- browser smoke 会检查: + - 三个 artifact 覆盖同一 blocked class 顺序; + - `boundary_kind` 一致; + - proof/runtime target 一致,Python wildcard proof 例外; + - runtime/gate target 一致; + - native source proof alignment 为 ready; + - runtime `source_proof_ready` 与 gate source proof readiness 一致; + - required native proof 一致; + - runtime readiness、missing requirements、status compatibility 一致; + - execution/promotion disabled 状态一致。 + +该检查只读取 generated artifacts,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在会交叉检查 `native-proof-alignment-summary.tsv`、 + `native-runtime-probe-summary.tsv` 和 `runtime-probe-gate-alignment.tsv`; +- 该检查确保 browser 侧看到的 source proof readiness、runtime readiness、 + required native proof、target 和 disabled execution/promotion 状态与 + Node inventory 一致。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +二十三、2026-06-12 继续执行记录:browser blocked runtime probe execution parity + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 browser blocked runtime probe execution parity 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- Node inventory 已通过 `blocked_runtime_probe_execution_consistency` 检查: + - `native-runtime-probe-execution-plan.tsv` + - `native-runtime-probe-pass-evidence-contract.tsv` + - `runtime-boundary-host-preflight.tsv` + - `runtime-boundary-opt-in-probe-dispatch-plan.tsv` + - `runtime-boundary-opt-in-probe-skip-evidence-contract.tsv` +- browser smoke 已分别检查这些 artifact,但没有直接确认 probe execution、 + pass evidence、host preflight、dispatch 和 skip-evidence 之间的对齐。 + +本轮新增 browser-side parity: + +- `verifyBrowserNativeRuntimeProbeExecutionPlan()` 现在返回 parsed rows; +- `verifyBrowserNativeRuntimeProbePassEvidenceContract()` 现在返回 parsed rows; +- `verifyBrowserRuntimeBoundaryHostPreflight()` 现在返回 parsed rows; +- `verifyBrowserRuntimeBoundaryOptInProbeDispatchPlan()` 现在返回 parsed rows; +- `verifyBrowserRuntimeBoundaryOptInProbeSkipEvidenceContract()` 现在返回 + parsed rows; +- 新增 `verifyBrowserBlockedRuntimeProbeExecutionParity()`; +- browser smoke 会检查: + - 五个 artifact 覆盖同一 blocked class 顺序; + - `boundary_kind`、target、blocked class 一致; + - runtime probe、expected pass status、required native proof 一致; + - opt-in env 和 exact execution command 一致; + - current probe status 一致; + - runtime readiness、source proof readiness、missing requirements 一致; + - 当前 host 仍保持 `runtime_ready=0`、`skipped_missing_host_runtime`、 + `blocked_missing_host_runtime`、`dispatch_allowed=0`; + - pass/skip evidence 仍未 required/ready; + - execution/promotion disabled 状态一致。 + +该检查只读取 generated artifacts,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在会交叉检查 native runtime probe execution plan、pass + evidence contract、host preflight、dispatch plan 和 skip-evidence contract; +- 该检查确保 browser 侧看到的 host-blocked opt-in command、missing + requirements、evidence status 和 disabled execution/promotion 状态与 + Node inventory 一致。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +二十四、2026-06-12 继续执行记录:browser blocked runtime rollup parity + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 browser blocked runtime rollup parity 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- Node inventory 已通过 `blocked_runtime_rollup_consistency` 检查: + - `runtime-boundary-host-readiness-rollup.tsv` + - `runtime-boundary-opt-in-probe-dispatch-rollup.tsv` + - `runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv` +- browser smoke 已分别检查这三个 rollup,但没有直接确认 host readiness、 + dispatch 和 skip-evidence rollup 之间的对齐。 + +本轮新增 browser-side parity: + +- `verifyBrowserRuntimeBoundaryFamilyHostReadiness()` 现在返回 parsed rows; +- `verifyBrowserRuntimeBoundaryHostReadinessRollup()` 现在返回 parsed rows; +- `verifyBrowserRuntimeBoundaryOptInProbeDispatchRollup()` 现在返回 parsed + rows; +- `verifyBrowserRuntimeBoundaryOptInProbeSkipEvidenceRollup()` 现在返回 + parsed rows; +- 新增 `verifyBrowserBlockedRuntimeRollupParity()`; +- browser smoke 会检查: + - host family count、dispatch probe count、skip evidence probe count 一致; + - ready/blocked family count 与 dispatch allowed/blocked count 一致; + - dispatch blocked count 与 skip count 一致; + - blocked families 一致; + - missing host requirements 一致; + - ready/blocked opt-in commands 一致; + - host readiness、dispatch、evidence rollup status 一致; + - current probe/native evidence/skip evidence status 保持 blocked; + - execution/promotion disabled 状态一致。 + +该检查只读取 generated artifacts,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在会交叉检查 host-readiness、dispatch 和 skip-evidence + rollups; +- 该检查确保 browser 侧看到的 counts、blocked families、missing + requirements、blocked commands、host/dispatch/evidence status 和 disabled + execution/promotion 状态与 Node inventory 一致。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +二十五、2026-06-12 继续执行记录:browser blocked runtime promotion/opt-in gate parity + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 browser blocked runtime promotion/opt-in gate parity 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- Node inventory 已通过: + - `blockedRuntimePromotionGateConsistency` + - `blockedRuntimeOptInGateConsistency` +- browser smoke 已分别检查 promotion lock、promotion readiness、promotion + blockers、post-native-pass gates、native evidence acceptance gate、 + recommendations、host preflight、family readiness、dispatch plan 和 + skip-evidence contract,但此前没有直接确认这些 artifact 之间的 promotion + gate 与 opt-in gate 对齐。 + +本轮新增 browser-side parity: + +- `verifyBrowserBlockedRuntimePromotionLock()` 现在返回 parsed rows; +- `verifyBrowserRuntimeBoundaryPromotionReadiness()` 现在返回 parsed rows; +- `verifyBrowserRuntimeBoundaryPromotionBlockers()` 现在返回 parsed rows; +- `verifyBrowserRuntimeBoundaryPostNativePassGates()` 现在返回 parsed rows; +- `verifyBrowserRuntimeBoundaryNativeEvidenceAcceptanceGate()` 现在返回 + parsed rows; +- `verifyBrowserNextBoundaryRecommendations()` 现在返回 parsed rows; +- 新增 `verifyBrowserBlockedRuntimePromotionGateParity()`; +- 新增 `verifyBrowserBlockedRuntimeOptInGateParity()`。 + +browser smoke 会检查: + +- promotion lock、promotion readiness、promotion blockers、 + post-native-pass gates、native evidence acceptance gate 和 pass-evidence + contract 对齐; +- recommendation、execution plan、promotion readiness/blockers、 + post-native-pass gates、host preflight、family readiness、dispatch plan、 + skip-evidence contract 和 evidence acceptance gate 对齐; +- target、boundary kind、blocked class、proof key、opt-in env、exact command、 + current probe status、missing requirements 一致; +- 当前 host 仍保持 host-blocked、dispatch disabled、evidence acceptance + blocked、promotion lock active、manual lock update required; +- execution/promotion disabled 状态一致。 + +该检查只读取 generated artifacts,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在会交叉检查 promotion lock、promotion readiness、 + promotion blockers、post-native-pass gates、native evidence acceptance gate + 和 pass-evidence contract; +- browser smoke 现在会交叉检查 recommendations、execution plans、host + preflight、family readiness、dispatch plans、skip-evidence contracts 和 + evidence acceptance gates; +- 该检查确保 browser 侧看到的 locked promotion state、opt-in command、 + proof、missing requirements 和 disabled execution/promotion 状态与 Node + inventory 一致。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +二十六、2026-06-12 继续执行记录:browser boundary phase completion count parity + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 browser boundary phase completion count parity 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- browser smoke 已检查 `boundary-phase-completion-summary.tsv` 的 criterion + 顺序、`passed=1`、count 格式和 evidence 非空; +- 但 browser 侧没有直接确认该 completion summary 中的 blocked-runtime + criterion count 与浏览器已经读取的 generated artifact row 数一致。 + +本轮新增 browser-side parity: + +- `verifyBrowserBoundaryPhaseCompletion()` 现在返回 parsed rows; +- `verifyBrowserRuntimeBoundaryHostRequirementSummary()` 现在返回 parsed + rows; +- `verifyBrowserRuntimeBoundaryHostUnblockPlan()` 现在返回 parsed rows; +- 新增 `verifyBrowserBoundaryPhaseCompletionCountParity()`; +- browser smoke 会检查 selected blocked-runtime completion criteria 的 + `count` 与对应 browser artifact row 数一致,包括: + - native proof alignment; + - runtime/native alignment summary; + - native runtime probe summary; + - runtime probe gate alignment; + - native runtime execution/pass-evidence artifacts; + - promotion readiness/blockers/post-native-pass gates; + - host preflight、host requirement summary、host unblock plan、family + readiness; + - dispatch/skip-evidence plan 与 rollup; + - native evidence acceptance gate; + - runtime contract summary; + - blocked runtime consistency criteria。 + +该检查只读取 generated artifacts,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在会把 completion summary 的 selected blocked-runtime + criterion count 与已加载 artifact row 数对齐; +- 该检查确保 browser 侧看到的 machine-readable completion summary 不会与 + 实际 generated gate artifact 计数漂移。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +二十七、2026-06-12 继续执行记录:browser family-specific completion count parity + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 扩展 browser boundary phase completion count parity 覆盖面。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 二十六已让 browser smoke 校验 selected blocked-runtime completion + criteria 的 `count` 与已加载 generated artifact row 数一致; +- 但 user-M、tool DB、Python runtime 的 family-specific contract/readiness/ + probe criteria 还没有进入 browser count parity。 + +本轮扩展 browser-side count parity: + +- 让以下 verifier 返回 parsed rows: + - `verifyBrowserUserMTransitionPlan()` + - `verifyBrowserUserMNativeRuntimeStatePlan()` + - `verifyBrowserUserMNativeRuntimeReadiness()` + - `verifyBrowserUserMNativeRuntimeProbeGate()` + - `verifyBrowserToolDbTransactionPlan()` + - `verifyBrowserToolDbNativeRuntimeReadiness()` + - `verifyBrowserToolDbNativeRuntimeProbeGate()` + - `verifyBrowserPythonRuntimeContract()` + - `verifyBrowserPythonNativeRuntimeReadiness()` + - `verifyBrowserPythonNativeRuntimeProbeGate()` + - `verifyBrowserPythonNativeRuntimeStatePlan()` + - `verifyBrowserPythonNativeRuntimeFixturePlan()` +- `verifyBrowserBoundaryPhaseCompletionCountParity()` 现在还会检查: + - `user_m_transition_contract` + - `user_m_native_runtime_state_plan` + - `user_m_native_runtime_readiness` + - `user_m_native_runtime_probe_gate` + - `tool_db_transaction_contract` + - `tool_db_native_runtime_readiness` + - `tool_db_native_runtime_probe_gate` + - `python_runtime_contract` + - `python_native_runtime_readiness` + - `python_native_runtime_probe_gate` + - `python_native_runtime_state_plan` + - `python_native_runtime_fixture_plan` + +该检查只读取 generated artifacts,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 的 completion-summary count parity 现在覆盖 blocked-runtime + 和 family-specific artifact rows; +- 该检查确保 machine-readable completion summary 不会与 browser 已读取的 + user-M、tool DB、Python runtime contract/readiness/probe artifacts 计数 + 漂移。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +二十八、2026-06-12 继续执行记录:browser runtime family contract alignment parity + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 browser runtime family contract alignment parity 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- Node inventory 已通过 `runtimeFamilyContractAlignmentConsistency`; +- browser smoke 已逐个检查 user-M、tool DB、Python runtime family-specific + contract/readiness/probe/state/fixture artifacts,并在二十七中把这些 + artifact count 纳入 completion summary parity; +- 但 browser 侧此前没有直接把这些 family-specific artifacts 与 + `runtime-boundary-contract-summary.tsv` 做跨 artifact 对齐。 + +本轮新增 browser-side parity: + +- 新增 `verifyBrowserRuntimeFamilyContractAlignmentParity()`; +- browser smoke 会检查: + - `runtime-boundary-contract-summary.tsv` 的 contract artifact、row count、 + pending/disabled/promotion-disabled 状态与 family artifacts 一致; + - user-M transition plan、native runtime state plan、readiness、probe gate + 在 remap code、user-M code、switchkins、G5x、work offset、expected state + pins、missing requirements 和 proof key 上一致; + - tool DB transaction plan、runtime readiness、probe gate 在 DB_PROGRAM、 + protocol transactions、missing requirements 和 proof key 上一致; + - Python runtime contract、state plan、readiness、fixture plan、probe gate + 在 family、runtime phases、modules、callables、process assumptions、 + missing requirements 和 proof key 上一致; + - 所有相关 rows 继续保持 `proof_status=pending`、`execution_enabled=0` + 和 `promotion_allowed=0`。 + +该检查只读取 generated artifacts,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在会把 user-M、tool DB、Python family-specific + contract/readiness/probe artifacts 与 `runtime-boundary-contract-summary.tsv` + 交叉对齐; +- 该检查确保 browser 侧看到的 runtime-family contract alignment 与 Node + inventory 一致,同时仍保持 non-executing、non-promoting。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +二十九、2026-06-12 继续执行记录:browser next-boundary recommendation count parity + +本轮继续在当前 host 可执行范围内推进。blocked runtime host readiness 仍为 +blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 browser next-boundary recommendation count parity 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- browser smoke 已检查 `next-boundary-recommendations.tsv` 的 priority、 + target、blocked kind、recommendation、primary artifact、proof fields 和 + disabled execution/promotion 状态; +- 但 browser 侧此前只确认 `artifact_row_count` 是正数,没有直接确认它与 + 生成该 recommendation 的 source artifact row 数一致; +- 当前 generated recommendation 中 tool DB 的 primary artifact 是 + `tool-db-process-native-runtime-probe-gate.tsv`,browser 期望同步为该 + artifact。 + +本轮新增 browser-side parity: + +- 修正 tool DB recommendation primary artifact 期望为 + `tool-db-process-native-runtime-probe-gate.tsv`; +- 新增 `verifyBrowserNextBoundaryRecommendationCountParity()`; +- browser smoke 会检查: + - user-M recommendation `artifact_row_count` 等于 + `user-m-process-native-runtime-probe-gate.tsv` row 数; + - tool DB recommendation `artifact_row_count` 等于 + `tool-db-process-native-runtime-probe-gate.tsv` row 数; + - Python recommendation `artifact_row_count` 等于 + `python-remap-runtime-contract.tsv` row 数; + - recommendation `source_artifacts` 包含对应 source artifact; + - recommendation rows 继续保持 `execution_enabled=0` 和 + `promotion_allowed=0`。 + +该检查只读取 generated artifacts,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在会把 `next-boundary-recommendations.tsv` 的 artifact row + count 与 user-M probe-gate、tool DB probe-gate、Python runtime-contract + source rows 对齐; +- 该检查确保 priority handoff 不会与 visible source artifacts 计数漂移。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +三十、2026-06-12 继续执行记录:browser host requirement artifact parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 browser host requirement artifact parity 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- browser smoke 已分别检查 host preflight、host requirement summary、 + host unblock plan、family host readiness、host readiness rollup; +- 但 browser 侧此前没有直接把 requirement summary / unblock plan / + family readiness / preflight 做逐 requirement 归属对齐; +- 该 drift 会影响 future host 上是否错误判断 opt-in native probe 可执行。 + +本轮新增 browser-side parity: + +- 新增 `verifyBrowserBlockedRuntimeHostRequirementParity()`; +- browser smoke 会检查: + - 每个 family host readiness row 与对应 host preflight row 的 target、 + blocked kind、required runtime、opt-in env、execution command、 + current probe status、preflight status 和 missing requirements 一致; + - family missing/available requirement count 与列表长度一致; + - 每个 missing requirement 在 requirement summary 中为 unavailable、 + 阻塞对应 family,并记录对应 opt-in env; + - 每个 missing requirement 在 unblock plan 中记录 affected target、 + affected execution command,并且 remaining list 不再包含该 requirement; + - 每个 available prerequisite 在 requirement summary 中为 available、 + 不产生 missing block,并仍能追溯到对应 family 与 opt-in env; + - 所有相关 rows 继续保持 `execution_enabled=0` 和 + `promotion_allowed=0`。 + +该检查只读取 generated artifacts,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在会把 `runtime-boundary-host-requirement-summary.tsv`、 + `runtime-boundary-host-unblock-plan.tsv`、 + `runtime-boundary-family-host-readiness.tsv` 与 + `runtime-boundary-host-preflight.tsv` 交叉对齐; +- 该检查覆盖 missing/available host requirement ownership、affected + opt-in env 和 exact probe command traceability。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +三十一、2026-06-12 继续执行记录:browser artifact documentation coverage parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 browser generated artifact documentation coverage 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- Node inventory 已通过 `wasm_inventory_artifact_documentation_coverage` + 和 `native_artifact_documentation_coverage`; +- browser smoke 已检查 completion summary criteria 和 count parity; +- 但 browser 侧此前没有直接 fetch 文档并确认 generated artifact names / + native artifact tokens 被文档引用。 + +本轮新增 browser-side parity: + +- 新增 `verifyBrowserGeneratedArtifactDocumentationCoverage()`; +- browser smoke 会 fetch: + - `docs/compatibility-validation.md` + - `docs/sim-configs-coverage-matrix.md` + - `docs/full-process-boundary-design.md` +- browser smoke 会检查: + - 54 个 `build/wasm/sim-configs-inventory/*.tsv` artifact name 均在当前 + 文档中出现; + - 8 个 `build/native/**/*.tsv` relative artifact token 均在当前文档中出现; + - `boundary-phase-completion-summary.tsv` 中 + `wasm_inventory_artifact_documentation_coverage` 的 count 等于 54; + - `native_artifact_documentation_coverage` 的 count 等于 8。 + +该检查只读取 generated artifacts 和文档,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在会直接镜像 Node artifact documentation coverage gate; +- 该检查确保 generated gate artifact 出现时不会只更新 Node inventory,而漏掉 + browser 可见的文档覆盖验证。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +三十二、2026-06-12 继续执行记录:browser recommendation source artifact coverage parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 browser recommendation source artifact coverage 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- browser smoke 已检查 `next-boundary-recommendations.tsv` 的 priority、 + target、blocked kind、recommendation、primary artifact、proof fields、 + artifact row count 和 disabled execution/promotion 状态; +- browser smoke 也已能看到 generated WASM inventory artifact name set; +- 但 browser 侧此前没有确认 recommendation 的每个 `source_artifacts` + token 都属于 browser 可见的 generated artifact set。 + +本轮新增 browser-side parity: + +- 新增 `verifyBrowserNextBoundaryRecommendationSourceArtifactCoverage()`; +- browser smoke 会检查: + - 每条 recommendation 都有 source artifact 列表; + - source artifact 列表没有重复项; + - `primary_artifact` 必须出现在 `source_artifacts` 中; + - `primary_artifact` 和每个 `source_artifacts` token 都存在于 browser + generated WASM inventory artifact set; + - recommendation rows 继续保持 `execution_enabled=0` 和 + `promotion_allowed=0`。 + +该检查只读取 generated artifacts,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在会把 recommendation primary/source artifacts 与 generated + WASM inventory artifact set 对齐; +- 该检查防止 priority handoff 引用不存在、重复或未追踪的 machine-readable + source artifact。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +三十三、2026-06-12 继续执行记录:browser recommendation exact source artifact set parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 收紧 browser recommendation source artifact coverage 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 三十二中 browser smoke 已确认 recommendation 的 `primary_artifact` 和 + `source_artifacts` 都属于 browser 可见的 generated WASM inventory artifact set; +- 但该检查允许 source artifact 列表增加额外 artifact; +- priority handoff 的 source artifact 应该是每个 blocked family 的精确来源集合。 + +本轮扩展 browser-side parity: + +- `verifyBrowserNextBoundaryRecommendationSourceArtifactCoverage()` 现在会检查 + 每个 recommendation 的 exact `source_artifacts` 顺序和集合: + - user-M: + `next-boundary-worklist.tsv`, + `blocked-runtime-promotion-lock.tsv`, + `user-m-process-transition-plan.tsv`, + `user-m-process-native-runtime-state-plan.tsv`, + `user-m-process-native-runtime-probe-gate.tsv` + - tool DB: + `next-boundary-worklist.tsv`, + `blocked-runtime-promotion-lock.tsv`, + `tool-db-process-transaction-plan.tsv`, + `tool-db-process-native-runtime-readiness.tsv`, + `tool-db-process-native-runtime-probe-gate.tsv` + - Python remap: + `next-boundary-worklist.tsv`, + `blocked-runtime-promotion-lock.tsv`, + `python-remap-runtime-contract.tsv`, + `python-remap-native-runtime-fixture-plan.tsv`, + `python-remap-native-runtime-probe-gate.tsv` +- 检查仍保留: + - source artifact 列表非空; + - 无重复 source artifact; + - `primary_artifact` 出现在 source artifact 列表中; + - 所有 source artifact 都存在于 browser generated WASM inventory artifact set; + - recommendation rows 继续保持 `execution_enabled=0` 和 + `promotion_allowed=0`。 + +该检查只读取 generated artifacts,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在会把 recommendation primary/source artifacts 与 generated + WASM inventory artifact set 和 exact per-family source artifact set 对齐; +- 该检查防止 priority handoff 引用额外、错误、重复或未追踪的 + machine-readable source artifact。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +三十四、2026-06-12 继续执行记录:Node recommendation exact source artifact set parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 将 recommendation exact source artifact set 检查补到 Node inventory。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 三十三中 browser smoke 已检查每个 recommendation 的 exact + `source_artifacts` 顺序和集合; +- Node inventory 此前只要求 recommendation source artifacts 包含 + `next-boundary-worklist.tsv` 和 `blocked-runtime-promotion-lock.tsv`; +- 生成端也应阻止 extra / wrong / duplicated source artifact。 + +本轮新增 Node-side parity: + +- `verifyNextBoundaryRecommendationRows()` 现在会检查: + - 每个 recommendation 的 exact `source_artifacts` 顺序和集合; + - source artifact 列表无重复; + - `primary_artifact` 出现在 source artifact 列表中; + - 原有 priority、target、blocked kind、recommendation、primary artifact、 + proof fields、promotion lock、contract、disabled execution/promotion 检查保持不变。 + +该检查只校验 generated TSV rows,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- Node inventory 和 browser smoke 现在都校验 recommendation exact + per-family source artifact set; +- recommendation priority handoff 的 machine-readable source trace 在生成端 + 和 browser 端保持一致。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +三十五、2026-06-12 继续执行记录:Node recommendation generated source artifact existence + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 收紧 Node recommendation source artifact trace 检查。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 三十四中 Node inventory 已检查每个 recommendation 的 exact + `source_artifacts` 顺序和集合; +- 但还可以进一步确认这些 source artifact token 实际对应 + `build/wasm/sim-configs-inventory/` 下的 generated artifact 文件。 + +本轮新增 Node-side parity: + +- `verifyNextBoundaryRecommendationRows()` 现在会检查: + - `primary_artifact` 文件存在于 generated sim-config inventory artifact + directory; + - 每个 `source_artifacts` token 对应的 generated artifact 文件存在; + - 原有 exact source set、无重复、primary traceability、proof fields、 + promotion lock、contract、disabled execution/promotion 检查保持不变。 + +该检查只校验 generated TSV rows 和 artifact 文件存在性,不执行 native runtime +probe,也不允许 promotion。 + +2. 文档同步。 + +已记录: + +- Node inventory 现在要求 recommendation source trace 不仅语义上精确, + 还必须指向实际生成的 machine-readable artifact; +- browser 端仍继续校验 browser-visible generated artifact set、exact source set + 和 disabled execution/promotion 状态。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +三十六、2026-06-12 继续执行记录:browser generated artifact fetchability parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 browser generated artifact fetchability 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 三十一中 browser smoke 已 fetch 文档并确认 generated artifact names / + native artifact tokens 被文档引用; +- 三十五中 Node inventory 已确认 recommendation source trace 指向实际生成 + artifact; +- browser 侧也应确认文档覆盖对象在 browser HTTP smoke 中实际可 fetch。 + +本轮新增 browser-side parity: + +- `verifyBrowserGeneratedArtifactDocumentationCoverage()` 现在会检查: + - 54 个 `build/wasm/sim-configs-inventory/*.tsv` artifact 都能通过 browser + smoke fetch; + - 8 个 `build/native/**/*.tsv` artifact token 都能通过 browser smoke fetch; + - 每个 fetched TSV 有非空 header 且包含 TSV delimiter; + - 原有文档覆盖和 completion-summary count parity 保持不变。 + +该检查只读取 generated artifacts 和文档,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在不仅检查文档覆盖,也检查 generated WASM/native TSV + artifact 在浏览器 smoke 中可读取; +- 该检查是 artifact fetchability parity,不改变 blocked runtime 状态。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +三十七、2026-06-12 继续执行记录:browser generated artifact list uniqueness parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 收紧 browser generated artifact fetchability 检查。 + +更新: + +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 三十六中 browser smoke 已 fetch generated WASM/native TSV artifacts; +- 如果 browser expected artifact list 中出现重复项,同时漏掉另一个 artifact, + 单纯 fetch 和 count parity 不够直接暴露 expected list drift; +- browser 侧应显式检查 expected artifact/token list 唯一性和固定数量。 + +本轮新增 browser-side parity: + +- `verifyBrowserGeneratedArtifactDocumentationCoverage()` 现在会检查: + - expected WASM inventory artifact list 固定为 54 项; + - expected WASM inventory artifact list 无重复; + - expected native artifact token list 固定为 8 项; + - expected native artifact token list 无重复; + - 原有 fetchability、non-empty TSV header、文档覆盖和 + completion-summary count parity 保持不变。 + +该检查只读取 generated artifacts 和文档,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- browser smoke 现在不仅检查 generated artifact fetchability,也检查 expected + artifact/token list duplicate-free 和 fixed count; +- 该检查是 artifact list parity,不改变 blocked runtime 状态。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +三十八、2026-06-12 继续执行记录:Node/browser native skipped artifact empty allowance + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 将 generated artifact fetchability 检查补到 Node,并同步 browser 空文件例外。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/tests/browser/interp_smoke.html` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 三十六/三十七中 browser smoke 已检查 generated artifact fetchability、 + fixed count 和 duplicate-free expected artifact list; +- Node documentation coverage gate 此前只检查文档引用; +- 本轮先把 Node coverage gate 收紧为文件存在性和 TSV header 检查; +- 复跑时发现 `build/native/sim-configs/skipped.tsv` 当前为 0 字节,这是 native + strict sim-config baseline 没有 skipped rows 时的合法 artifact。 + +本轮新增/修正: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在会检查每个 generated + WASM sim-config inventory artifact 文件存在,且有非空 TSV header; +- `verifyNativeGeneratedArtifactDocumentationCoverage()` 现在会检查每个 native + generated TSV artifact 文件存在; +- native generated TSV artifact 除 + `build/native/sim-configs/skipped.tsv` 外必须有非空 TSV header; +- browser generated artifact fetchability 使用同一个空文件例外: + `build/native/sim-configs/skipped.tsv` 可以为空; +- 文档明确该空文件只表示 native strict sim-config baseline 当前没有 skipped + rows,不是 runtime promotion 证据。 + +该检查只读取 generated artifacts 和文档,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- Node 和 browser 都检查 generated artifact 可见性; +- `build/native/sim-configs/skipped.tsv` 是唯一允许为空的 native TSV artifact。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +三十九、2026-06-12 继续执行记录:Node generated artifact list fixed-count parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 将 generated artifact list fixed-count parity 补到 Node。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 三十七中 browser smoke 已检查 expected WASM/native artifact list 的固定数量 + 和无重复; +- 三十八中 Node coverage gate 已检查 generated artifact 文件存在性和 TSV + header; +- Node 端也应明确锁定 generated artifact list 的当前数量,防止生成端新增或 + 丢失 artifact 时只靠 browser hardcoded list 才暴露。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在要求 generated WASM + sim-config inventory artifact list: + - 无重复; + - 固定为 54 项; + - 每个 artifact 文件存在; + - 每个 artifact 有非空 TSV header。 +- `verifyNativeGeneratedArtifactDocumentationCoverage()` 现在要求 native + generated TSV token list: + - 无重复; + - 固定为 8 项; + - 每个 artifact 文件存在; + - 除 `build/native/sim-configs/skipped.tsv` 外必须有非空 TSV header。 + +该检查只读取 generated artifacts 和文档,不执行 native runtime probe,也不允许 +promotion。 + +2. 文档同步。 + +已记录: + +- Node 和 browser 都锁定当前 artifact list 数量: + - WASM inventory artifacts:54; + - native TSV artifacts:8; +- generated artifact list drift 必须通过 gate 和文档审查处理。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +四十、2026-06-12 继续执行记录:Node native artifact exact token set parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 将 native generated TSV artifact list 从 fixed-count 收紧为 exact set。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 三十九中 Node coverage gate 已检查 native generated TSV token list 固定为 + 8 项且无重复; +- 但如果 8 项中替换了一个 token,单纯 fixed-count 不能表达 intended + baseline; +- Node 端应直接锁定当前 native generated TSV token exact set。 + +本轮新增 Node-side parity: + +- `verifyNativeGeneratedArtifactDocumentationCoverage()` 现在要求 native + generated TSV token list 精确等于: + - `build/native/native-runtime-probe-summary.tsv` + - `build/native/native-source-proof-summary.tsv` + - `build/native/nc-files/summary.tsv` + - `build/native/sim-configs/class-summary.tsv` + - `build/native/sim-configs/path-matrix.tsv` + - `build/native/sim-configs/skipped.tsv` + - `build/native/sim-configs/summary.tsv` + - `build/native/source-probes.tsv` +- 原有无重复、fixed count、文件存在性、header/empty-skipped 例外、文档覆盖 + 检查保持不变。 + +该检查只读取 generated native artifacts 和文档,不执行 native runtime probe, +也不允许 promotion。 + +2. 文档同步。 + +已记录: + +- Node coverage gate 的 native generated TSV token list 是 exact + duplicate-free 8-token baseline; +- native generated artifact drift 必须通过 gate 和文档审查处理。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +四十一、2026-06-12 继续执行记录:Node WASM inventory artifact exact set parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 将 WASM inventory artifact list 从 fixed-count 收紧为 exact set。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 三十九中 Node coverage gate 已检查 generated WASM inventory artifact list + 固定为 54 项且无重复; +- 但如果 54 项中替换了一个 artifact,单纯 fixed-count 不能表达 intended + baseline; +- browser smoke 已有 hardcoded expected list,Node 端也应锁定 exact set。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在要求 generated WASM + sim-config inventory artifact list 精确等于当前 54-entry baseline; +- 原有无重复、fixed count、文件存在性、非空 TSV header、文档覆盖检查保持 + 不变; +- native generated TSV token exact set 检查继续保持。 + +该检查只读取 generated WASM inventory artifacts 和文档,不执行 native runtime +probe,也不允许 promotion。 + +2. 文档同步。 + +已记录: + +- Node coverage gate 的 generated WASM inventory artifact list 是 exact + duplicate-free 54-entry baseline; +- WASM inventory artifact drift 必须通过 gate 和文档审查处理。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +四十二、2026-06-12 继续执行记录:Node/browser artifact token surface parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 新增 Node 对 browser smoke artifact token 列表的 cross-surface 检查。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 四十/四十一中 Node 已锁定 native exact 8-token baseline 和 WASM exact + 54-entry baseline; +- browser smoke 也有 hardcoded expected artifact list 并执行 fetchability; +- 但如果 Node exact set 更新而 browser hardcoded list 漏同步,需要在 Node + 侧直接暴露 cross-surface drift。 + +本轮新增 Node-side parity: + +- 新增 `browserInterpSmokePath`; +- `verifyGeneratedArtifactDocumentationCoverage()` 现在会检查 browser smoke + source 中包含全部 54 个 generated WASM inventory artifact names; +- `verifyNativeGeneratedArtifactDocumentationCoverage()` 现在会检查 browser + smoke source 中包含全部 8 个 native generated TSV artifact tokens; +- 原有 exact set、fixed count、无重复、文件存在性、header/empty-skipped 例外、 + 文档覆盖检查保持不变。 + +该检查只读取 Node/browser test source、generated artifacts 和文档,不执行 native +runtime probe,也不允许 promotion。 + +2. 文档同步。 + +已记录: + +- Node coverage gate 现在会检查 browser smoke source 中列出同一批 WASM/native + artifact tokens; +- generated artifact coverage 的 Node/browser surface 必须同步。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +四十三、2026-06-12 继续执行记录:Node/browser artifact fixed-count guard surface parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 收紧 Node 对 browser smoke artifact coverage surface 的检查。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 四十二中 Node 已检查 browser smoke source 中列出同一批 WASM/native artifact + tokens; +- 但如果 browser smoke 保留 token 列表、移除 fixed-count guard,browser 侧仍 + 可能少一层 expected list drift 检查; +- Node 应确认 browser smoke 保留同一组 fixed-count guards。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke + source 中保留 `wasmArtifactNames.length !== 54` guard; +- `verifyNativeGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke + source 中保留 `nativeArtifactTokens.length !== 8` guard; +- 原有 exact set、fixed count、无重复、文件存在性、header/empty-skipped 例外、 + 文档覆盖和 browser token surface 检查保持不变。 + +该检查只读取 Node/browser test source、generated artifacts 和文档,不执行 native +runtime probe,也不允许 promotion。 + +2. 文档同步。 + +已记录: + +- Node coverage gate 现在确认 browser smoke 不仅列出同一批 artifact tokens, + 也保留对应 fixed-count guards; +- generated artifact coverage 的 Node/browser surface 必须同步。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +四十四、2026-06-12 继续执行记录:Node/browser artifact duplicate-free guard surface parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 继续收紧 Node 对 browser smoke artifact coverage surface 的检查。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 四十三中 Node 已确认 browser smoke 保留 WASM/native artifact fixed-count + guards; +- 但 browser smoke 的 duplicate-free guards 也属于 expected artifact list drift + 防线; +- Node 应确认 browser smoke 同时保留 duplicate-free guards。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke + source 中保留 `new Set(wasmArtifactNames).size !== wasmArtifactNames.length` + guard; +- `verifyNativeGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke + source 中保留 + `new Set(nativeArtifactTokens).size !== nativeArtifactTokens.length` guard; +- 原有 exact set、fixed count、无重复、文件存在性、header/empty-skipped 例外、 + 文档覆盖、browser token surface 和 browser fixed-count guard 检查保持不变。 + +该检查只读取 Node/browser test source、generated artifacts 和文档,不执行 native +runtime probe,也不允许 promotion。 + +2. 文档同步。 + +已记录: + +- Node coverage gate 现在确认 browser smoke 不仅列出同一批 artifact tokens、 + 保留 fixed-count guards,也保留 duplicate-free guards; +- generated artifact coverage 的 Node/browser surface 必须同步。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +四十五、2026-06-12 继续执行记录:Node/browser artifact fetchability guard surface parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 继续收紧 Node 对 browser smoke artifact coverage surface 的检查。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 四十二到四十四中 Node 已确认 browser smoke 保留同一批 artifact tokens、 + fixed-count guards 和 duplicate-free guards; +- browser smoke 的 fetchability/header guards 和 native skipped 空文件例外也是 + artifact surface contract 的一部分; +- Node 应确认 browser smoke 保留这些 guard,防止 browser 侧只保留 token list。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke + source 中保留 `browser_generated_artifact_fetchability` guard; +- `verifyNativeGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke + source 中保留 `browser_native_artifact_fetchability` guard; +- `verifyNativeGeneratedArtifactDocumentationCoverage()` 还检查 browser smoke + source 中保留 `emptyNativeArtifactTokensAllowed.has(artifactToken)`,确保 + `build/native/sim-configs/skipped.tsv` 的合法空文件例外同步存在; +- 原有 exact set、fixed count、无重复、文件存在性、header/empty-skipped 例外、 + 文档覆盖、browser token surface、fixed-count guard 和 duplicate-free guard + 检查保持不变。 + +该检查只读取 Node/browser test source、generated artifacts 和文档,不执行 native +runtime probe,也不允许 promotion。 + +2. 文档同步。 + +已记录: + +- Node coverage gate 现在确认 browser smoke 保留 token list、fixed-count guards、 + duplicate-free guards、fetchability/header guards,以及 native skipped 空文件 + 例外; +- generated artifact coverage 的 Node/browser surface 必须同步。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +4. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +四十六、2026-06-12 继续执行记录:Node/browser artifact documentation guard surface parity + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 继续收紧 Node 对 browser smoke artifact coverage surface 的检查。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 四十二到四十五中 Node 已确认 browser smoke 保留同一批 artifact tokens、 + fixed-count guards、duplicate-free guards、fetchability/header guards,以及 + native skipped 空文件例外; +- browser smoke 对文档覆盖的 missing-artifact guard 也是 artifact surface + contract 的一部分; +- Node 应确认 browser smoke 保留这些 guard,防止 browser 侧只保留 token 和 + fetchability 检查。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke + source 中保留 `browser_generated_artifact_documentation_coverage: missing` + guard; +- `verifyNativeGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke + source 中保留 `browser_native_artifact_documentation_coverage: missing` + guard; +- 原有 exact set、fixed count、无重复、文件存在性、header/empty-skipped 例外、 + 文档覆盖、browser token surface、fixed-count guard、duplicate-free guard 和 + fetchability/header guard 检查保持不变。 + +该检查只读取 Node/browser test source、generated artifacts 和文档,不执行 native +runtime probe,也不允许 promotion。 + +3. 文档同步。 + +已记录: + +- Node coverage gate 现在确认 browser smoke 保留 token list、fixed-count guards、 + duplicate-free guards、fetchability/header guards、documentation-missing guards, + 以及 native skipped 空文件例外; +- generated artifact coverage 的 Node/browser surface 必须同步。 + +4. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` + +5. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +四十七、2026-06-12 继续执行记录:Node/browser artifact completion count-parity surface guard + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 继续收紧 Node 对 browser smoke artifact coverage surface 的检查。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 四十二到四十六中 Node 已确认 browser smoke 保留同一批 artifact tokens、 + fixed-count guards、duplicate-free guards、fetchability/header guards、 + documentation-missing guards,以及 native skipped 空文件例外; +- browser smoke 还会把 `boundary-phase-completion-summary.tsv` 中的 artifact + documentation coverage count 与 browser 已加载的 token list 长度对齐; +- Node 应确认 browser smoke 保留该 completion count-parity 调用。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke + source 中保留 `wasm_inventory_artifact_documentation_coverage` 使用 + `generatedArtifactDocumentationCoverage.wasmArtifactNames.length` 的 count + parity guard; +- `verifyNativeGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke + source 中保留 `native_artifact_documentation_coverage` 使用 + `generatedArtifactDocumentationCoverage.nativeArtifactTokens.length` 的 count + parity guard; +- 原有 exact set、fixed count、无重复、文件存在性、header/empty-skipped 例外、 + 文档覆盖、browser token surface、fixed-count guard、duplicate-free guard、 + fetchability/header guard 和 documentation-missing guard 检查保持不变。 + +该检查只读取 Node/browser test source、generated artifacts 和文档,不执行 native +runtime probe,也不允许 promotion。 + +3. 文档同步。 + +已记录: + +- Node coverage gate 现在确认 browser smoke 保留 token list、fixed-count guards、 + duplicate-free guards、fetchability/header guards、documentation-missing guards、 + completion count-parity guards,以及 native skipped 空文件例外; +- generated artifact coverage 的 Node/browser surface 必须同步。 + +4. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` + +5. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +四十八、2026-06-12 继续执行记录:Node/browser artifact review-document fetch surface guard + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 继续收紧 Node 对 browser smoke artifact coverage surface 的检查。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 四十二到四十七中 Node 已确认 browser smoke 保留同一批 artifact tokens、 + fixed-count guards、duplicate-free guards、fetchability/header guards、 + documentation-missing guards、completion count-parity guards,以及 native + skipped 空文件例外; +- browser smoke 的 artifact documentation coverage 必须实际 fetch 三份 review + docs,而不是只检查内联 token list; +- Node 应确认 browser smoke 保留 review-document fetch surface。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke + source 中保留以下 fetch token: + - `../../docs/compatibility-validation.md` + - `../../docs/sim-configs-coverage-matrix.md` + - `../../docs/full-process-boundary-design.md` +- 原有 exact set、fixed count、无重复、文件存在性、header/empty-skipped 例外、 + 文档覆盖、browser token surface、fixed-count guard、duplicate-free guard、 + fetchability/header guard、documentation-missing guard 和 completion + count-parity guard 检查保持不变。 + +该检查只读取 Node/browser test source、generated artifacts 和文档,不执行 native +runtime probe,也不允许 promotion。 + +3. 文档同步。 + +已记录: + +- Node coverage gate 现在确认 browser smoke 保留 token list、fixed-count guards、 + duplicate-free guards、fetchability/header guards、documentation-missing guards、 + completion count-parity guards、review-document fetches,以及 native skipped + 空文件例外; +- generated artifact coverage 的 Node/browser surface 必须同步。 + +4. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` + +5. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +四十九、2026-06-12 继续执行记录:Node/browser artifact review-document join surface guard + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 继续收紧 Node 对 browser smoke artifact coverage surface 的检查。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 四十八中 Node 已确认 browser smoke 保留三份 review-document fetch token; +- 仅保留 token 还不够,browser smoke 必须把 fetch 结果收集成 + `documentationText`,并在 missing-reference 检查前 join; +- Node 应确认 browser smoke 保留这个 review-document join surface。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke + source 中保留 `const documentationText = [`; +- 同一检查还要求 browser smoke 保留 `].join("\\n");`; +- 这保证 browser smoke 的 artifact documentation coverage 仍使用三份 review + docs 的合并文本,而不是只保留内联 artifact token list。 + +原有 exact set、fixed count、无重复、文件存在性、header/empty-skipped 例外、 +文档覆盖、browser token surface、fixed-count guard、duplicate-free guard、 +fetchability/header guard、documentation-missing guard、completion count-parity +guard 和 review-document fetch token 检查保持不变。 + +该检查只读取 Node/browser test source、generated artifacts 和文档,不执行 native +runtime probe,也不允许 promotion。 + +3. 文档同步。 + +已记录: + +- Node coverage gate 现在确认 browser smoke 保留 review-document fetches,并把 + fetched docs join 成用于 missing-reference 检查的 review text; +- generated artifact coverage 的 Node/browser surface 必须同步。 + +4. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` + +5. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +五十、2026-06-12 继续执行记录:completion-summary artifact documentation count baseline guard + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 收紧 Node completion-summary artifact documentation count。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- Node 已锁定 generated WASM artifact list 是 54 项,native generated TSV token + list 是 8 项; +- browser smoke 已把 documentation coverage completion count 与 browser 已加载 + token list 长度做 count-parity; +- Node 自身也应直接检查 `boundary-phase-completion-summary.tsv` 中这两个 + documentation coverage criterion 的 count 与当前 baseline 一致。 + +本轮新增 Node-side guard: + +- `verifyBoundaryPhaseCompletionSummaryRows()` 现在要求 + `wasm_inventory_artifact_documentation_coverage.count === "54"`; +- 同一函数要求 `native_artifact_documentation_coverage.count === "8"`; +- 这样可以防止 completion summary 的 documentation coverage count 与生成的 + artifact baseline 脱节。 + +该检查只读取生成的 completion rows,不执行 native runtime probe,也不允许 +promotion。 + +3. 文档同步。 + +已记录: + +- Node coverage gate 现在锁定 `boundary-phase-completion-summary.tsv` 中 artifact + documentation coverage 的两个 baseline count: + - WASM sim-config inventory artifact count `54` + - native generated TSV artifact count `8` + +4. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` + +5. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +五十一、2026-06-12 继续执行记录:browser completion evidence reviewability surface guard + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 收紧 browser completion count parity 的 evidence surface。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- browser smoke 已检查 artifact documentation coverage completion count 与 + browser 已加载 token list 一致; +- 该 count parity 还必须保留 reviewable evidence 检查,避免 completion summary + 只给出 count 而没有可审查 evidence; +- Node 应确认 browser smoke 源码保留该 evidence reviewability guard。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke source + 中保留 `row.evidence === "-" || row.evidence.length < 12`; +- 同一检查还要求保留 `completion evidence is not reviewable` 错误文本; +- 这样可以防止 browser-side completion count parity 去掉 evidence 可审查性约束。 + +该检查只读取 Node/browser test source、generated artifacts 和文档,不执行 native +runtime probe,也不允许 promotion。 + +3. 文档同步。 + +已记录: + +- Node coverage gate 现在确认 browser smoke 在 completion count parity 中保留 + reviewable evidence 检查; +- generated artifact coverage 的 Node/browser surface 必须同步。 + +4. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` + +5. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +五十二、2026-06-12 继续执行记录:browser completion count-parity invocation guard + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 收紧 browser completion count parity 调用面。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- 五十一中 Node 已确认 browser smoke 的 count-parity helper 保留 reviewable + evidence 检查; +- 仅保留 helper 内容还不够,browser smoke 必须实际用 generated + `boundaryPhaseCompletionRows` 调用该 helper; +- Node 应确认 browser smoke 保留这个调用面。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke source + 中保留: + `verifyBrowserBoundaryPhaseCompletionCountParity(\n boundaryPhaseCompletionRows,` +- 这样可以防止 browser smoke 只保留 helper 定义或字符串,但不再用实际生成的 + completion rows 进行 count parity。 + +该检查只读取 Node/browser test source、generated artifacts 和文档,不执行 native +runtime probe,也不允许 promotion。 + +3. 文档同步。 + +已记录: + +- Node coverage gate 现在确认 browser smoke 实际用 generated completion rows + 调用 completion count-parity helper; +- generated artifact coverage 的 Node/browser surface 必须同步。 + +4. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` + +5. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +五十三、2026-06-12 继续执行记录:browser artifact documentation coverage invocation guard + +本轮继续选择当前 host 可执行的非 runtime 方向。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. blocker 复查。 + +执行: + +```bash +git diff --check +git status --short +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +``` + +结果: + +- `git diff --check` clean。 +- `runtime-boundary-host-readiness-rollup.tsv` 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` + +2. 收紧 browser artifact documentation coverage 调用面。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- Node 已确认 browser smoke 保留 artifact documentation coverage helper 的 + token list、review-document fetch、join、missing guard 和 count parity; +- 仅保留 helper 内容还不够,browser smoke 必须实际 await 执行该 helper; +- Node 应确认 browser smoke 保留这个调用面。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke source + 中保留 `await verifyBrowserGeneratedArtifactDocumentationCoverage();`; +- 这样可以防止 browser smoke 只保留 helper 定义,但不再实际执行 generated + artifact documentation coverage。 + +该检查只读取 Node/browser test source、generated artifacts 和文档,不执行 native +runtime probe,也不允许 promotion。 + +3. 文档同步。 + +已记录: + +- Node coverage gate 现在确认 browser smoke 实际执行 artifact documentation + coverage helper; +- generated artifact coverage 的 Node/browser surface 必须同步。 + +4. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` + +5. 下一步状态。 + +当前 host 仍不能推进第四部分第 4 步。后续继续有两个合法方向: + +- 切换到具备 LinuxCNC host runtime 的环境,重新跑 preflight/dispatch, + 再决定是否执行 opt-in probe。 +- 留在当前 host,继续做不需要 runtime 的 source proof/gate consistency, + 或小批量 pure interpreter intake。 + +五十四、2026-06-12 继续执行记录:当前 gate 复跑与下一步建议 + +本轮按当前 `text5.txt` 最新状态继续执行。blocked runtime host readiness +仍为 blocked,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 基础 gate 复跑。 + +执行: + +```bash +git diff --check +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +结果: + +- `git diff --check` clean。 +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- `sim_configs_wasm_node_smoke=ok` +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` + +2. host preflight artifact 复查。 + +读取: + +```bash +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-preflight.tsv +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-requirement-summary.tsv +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-unblock-plan.tsv +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-plan.tsv +``` + +当前结论: + +- `L4-USER-M-PROCESS` 仍缺 `halrun,halcmd,linuxcnc`。 +- `L4-TOOL-DB` 仍缺 `linuxcnc,milltask,halcmd`。 +- `L4-PYTHON-REMAP` 仍缺 `linuxcnc`。 +- rollup 仍为: + - `family_count=3` + - `host_ready_family_count=0` + - `host_blocked_family_count=3` + - `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` + - `ready_opt_in_command_count=0` + - `host_readiness_status=host_blocked_for_all_opt_in_native_probes` + - `execution_enabled=0` + - `promotion_allowed=0` +- dispatch plan 对三个 blocked family 均为 `dispatch_allowed=0`。 + +3. 下一步工作建议。 + +当前最推荐的下一步不是继续扩 runtime,而是在当前 host 上继续做 +non-runtime gate consistency,优先级如下: + +1. 继续收紧 Node/browser 对 generated artifact 文档覆盖的双向一致性: + 确认新增 artifact 不仅出现在文档 token list,也在 browser smoke 中被实际 + 读取和断言,并由 Node gate 检查调用面。 +2. 若需要更接近 coverage 增量,则做小批量 pure interpreter intake: + 只选不依赖 HAL/task/Python remap/tool DB/user-M process 的 upstream + `tests/interp/*` fixture,严格按 native -> Node WASM -> browser representative + 顺序推进。 +3. blocked runtime 的 millturn/tool DB/Python remap opt-in probe 暂不推进; + 只有切换到具备 `halcmd/halrun/linuxcnc/milltask` 的 host,并重新跑 + preflight/dispatch 确认 `runtime_ready=1` 与 `dispatch_allowed=1` 后, + 才能人工执行对应 `ENABLE_*_RUNTIME_PROBE=1` 命令。 + +本轮建议优先执行第 1 项。它不需要 LinuxCNC host runtime,风险低,且能继续 +防止 artifact/gate 文档与 browser 实际检查面漂移。 + +五十五、2026-06-12 继续执行记录:browser generated artifact fetch invocation guard + +本轮继续执行五十四建议的第 1 项:在当前 host 上做 non-runtime gate +consistency,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 收紧 browser artifact fetch 调用面。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- browser smoke 已经会执行 `verifyBrowserGeneratedArtifactDocumentationCoverage()`; +- Node gate 也已经确认 browser 保留 artifact list、fixed count、duplicate-free、 + fetchability/header、documentation-missing 和 completion count-parity guard; +- 但 Node 还应显式锁定 browser 必须按 artifact name/token 动态 fetch 生成的 + TSV,而不是只保留错误标签或静态字符串。 + +本轮新增 Node-side parity: + +- 对 WASM inventory artifacts,Node 现在检查 browser smoke source 中保留: + `await fetchText(\`../../build/wasm/sim-configs-inventory/${artifactName}\`)` +- 对 native generated TSV artifacts,Node 现在检查 browser smoke source 中保留: + `await fetchText(\`../../${artifactToken}\`)` + +该检查只读取 Node/browser test source、generated artifacts 和文档,不执行 native +runtime probe,也不允许 promotion。 + +2. 文档同步。 + +已记录: + +- Node coverage gate 现在确认 browser smoke 按 artifact name fetch generated + WASM TSV; +- Node coverage gate 现在确认 browser smoke 按 relative artifact token fetch + native TSV; +- 这仍是 documentation/artifact fetchability parity,不是 runtime proof。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` + +4. 下一步工作建议。 + +当前 host 仍不能推进 blocked runtime opt-in probe。下一步继续优先做 +non-runtime consistency: + +1. 检查 Node/browser 是否都锁定 generated artifact coverage 的 + documentation source set,也就是 compatibility、matrix、full-process 三份文档 + 必须都被读取并合并后再做 missing-reference check。 +2. 若该面已经足够稳,再考虑小批量 pure interpreter intake;仍需避免 + HAL/task/Python remap/tool DB/user-M process 依赖。 +3. blocked runtime 只有在具备 `halcmd/halrun/linuxcnc/milltask` 的 host 上重新跑 + preflight/dispatch 并确认可 dispatch 后,才允许人工 opt-in。 + +五十六、2026-06-12 继续执行记录:browser documentation source-set invocation guard + +本轮继续执行五十五建议的第 1 项:检查并收紧 generated artifact coverage 的 +documentation source set。仍不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native +probe。 + +1. 收紧 browser documentation source-set 调用面。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- browser smoke 已读取三份 review docs: + - `../../docs/compatibility-validation.md` + - `../../docs/sim-configs-coverage-matrix.md` + - `../../docs/full-process-boundary-design.md` +- Node gate 已逐个检查这些 token,也检查了 `const documentationText = [` 和 + `].join("\\n");`; +- 但为了防止 token 分散存在却不再作为同一个 missing-reference source set 使用, + Node 应锁定完整 fetch-and-join 片段。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke source + 中保留完整三文档 fetch-and-join source set: + - compatibility validation; + - sim-configs coverage matrix; + - full-process boundary design; + - `].join("\\n");` + +该检查只读取 Node/browser test source 和文档,不执行 native runtime probe,也不 +允许 promotion。 + +2. 文档同步。 + +已记录: + +- Node coverage gate 现在确认 browser smoke fetches and joins the exact + compatibility/matrix/full-process boundary review-document set; +- 该 source set 用于 generated artifact missing-reference checks; +- 这仍是 documentation/artifact parity,不是 runtime proof。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` + +4. 下一步工作建议。 + +当前 host 仍不能推进 blocked runtime opt-in probe。下一步有两个高效方向: + +1. 继续 non-runtime consistency:检查 Node/browser 是否都锁定 + generated artifact coverage 的 missing-reference loop 必须遍历 + `wasmArtifactNames` 和 `nativeArtifactTokens`,并且使用同一个 joined + `documentationText`。 +2. 如果不继续收紧 artifact 文档 gate,则切换到小批量 pure interpreter intake; + 仍只选不依赖 HAL/task/Python remap/tool DB/user-M process 的 fixture。 + +五十七、2026-06-12 继续执行记录:browser documentation missing-reference loop guard + +本轮继续执行五十六建议的第 1 项:收紧 generated artifact coverage 的 +missing-reference loop。仍不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native +probe。 + +1. 收紧 browser missing-reference loop 调用面。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- browser smoke 已经 fetch/join 三份 review docs 到同一个 `documentationText`; +- browser smoke 也已经遍历 `wasmArtifactNames` 和 `nativeArtifactTokens` 做 + missing-reference checks; +- Node gate 需要明确锁定这两个 loop 使用同一个 joined `documentationText`, + 防止只保留错误标签或列表 token 而不再实际做覆盖检查。 + +本轮新增 Node-side parity: + +- 对 WASM inventory artifacts,Node 现在检查 browser smoke source 中保留: + `for (const artifactName of wasmArtifactNames) {` + 和 `if (!documentationText.includes(artifactName)) {` +- 对 native generated TSV artifacts,Node 现在检查 browser smoke source 中保留: + `for (const artifactToken of nativeArtifactTokens) {` + 和 `if (!documentationText.includes(artifactToken)) {` + +该检查只读取 Node/browser test source 和文档,不执行 native runtime probe,也不 +允许 promotion。 + +2. 文档同步。 + +已记录: + +- Node coverage gate 现在确认 browser missing-reference loops 遍历 + `wasmArtifactNames` 和 `nativeArtifactTokens`; +- 两个 loop 都必须 against joined `documentationText`; +- 这仍是 documentation/artifact parity,不是 runtime proof。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` + +4. 下一步工作建议。 + +当前 host 仍不能推进 blocked runtime opt-in probe。下一步建议: + +1. artifact documentation coverage 的 Node/browser 调用面已经较完整;优先考虑 + 做一次小批量 pure interpreter intake,但必须先筛 fixture,排除 HAL/task、 + Python remap、tool DB、user-M process、UI/full-process 依赖。 +2. 如果继续做 non-runtime consistency,可检查 browser 和 Node 对 + `boundary-phase-completion-summary.tsv` 的 artifact documentation count parity + 是否都绑定到同一组 returned artifact arrays,而不是硬编码独立计数。 +3. blocked runtime 仍只在具备 `halcmd/halrun/linuxcnc/milltask` 的 host 上重新跑 + preflight/dispatch 并确认可 dispatch 后,才允许人工 opt-in。 + +五十八、2026-06-12 继续执行记录:small pure interpreter intake - do-while-break bug fixture + +本轮执行五十七建议的第 1 项:小批量 pure interpreter intake。仍不执行任何 +`ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. fixture 筛选。 + +候选: + +- `wasm-port/vendor/linuxcnc/tests/interp/do-while-break/bug.ngc` + +筛选结论: + +- 同目录 `test.ngc` 已在 Node/browser 覆盖; +- `bug.ngc` 是一个更小的 `do` / `break` / `while` 回归 fixture; +- 不依赖 INI、tool table、HAL/task、Python remap、tool DB、user-M process、 + UI/full-process; +- 只通过 vendored LinuxCNC interpreter 执行,不引入 JS CNC 语义。 + +2. 探测。 + +用 SDK 直接运行 `bug.ngc`,确认当前 WASM output: + +- `file_open=0` +- `file_read_count=7` +- `file_execute_count=7` +- `file_saw_error=0` +- `canon_event=PROGRAM_END` +- `@start of do-while-loop` 出现在 print output; +- `@end of do-while-loop` 不出现,证明 `break` 后不会执行 loop 尾部 print。 + +3. 更新。 + +更新: + +- `wasm-port/tests/wasm/node/verify_interp_wasm.mjs` +- `wasm-port/tests/browser/interp_smoke.html` + +新增检查: + +- Node fixture name:`interp_do_while_break_bug_wasm` +- Browser fixture name:`browser_interp_do_while_break_bug` +- 两侧都断言: + - `file_open=0` + - `file_read_count=7` + - `file_execute_count=7` + - `file_saw_error=0` + - `canon_event=PROGRAM_END` + - `absent=@end of do-while-loop` + - `absent=file_saw_error=1` + +4. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_interp_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +结果: + +- `git diff --check` clean。 +- `interp_wasm_node_smoke=ok` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- Host aggregate 中 Node inventory 仍保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` + +5. 下一步工作建议。 + +继续小批量 pure interpreter intake,但每次只加 1-2 个 fixture,避免扩大 +review 面。下一步优先做覆盖对账后选择: + +1. `do-while-break/bug.ngc` 已完成;继续找未被 Node/browser 双侧覆盖的 + pure interpreter `.ngc`。 +2. 候选应优先来自 upstream `tests/interp/good` 或 `tests/interp/bad` 中不依赖 + INI/tool/remap/HAL/task 的单文件 arc/error fixture。 +3. 若候选需要 INI、tool table、subroutine path 或 remap,必须先走 staging + helper 和 blocked dependency 分类,不要直接纳入 pure interpreter intake。 + +五十九、2026-06-12 继续执行记录:good/bad pure interpreter fixture coverage guard + +本轮继续执行五十八建议,优先从 upstream `tests/interp/good` 和 +`tests/interp/bad` 做 pure interpreter intake 对账。仍不执行任何 +`ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 覆盖对账。 + +对账结果: + +- `tests/interp/good` 当前 6 个 `.ngc` 全部已在 Node/browser 覆盖; +- `tests/interp/bad` 当前 21 个 `.ngc` 全部已在 Node/browser 覆盖; +- 因此本轮不重复添加 fixture,而是把“已全覆盖”变成可执行 guard。 + +2. 更新。 + +更新: + +- `wasm-port/tests/wasm/node/verify_interp_wasm.mjs` +- `wasm-port/tests/browser/interp_smoke.html` + +Node 新增: + +- `interpRegressionNgcFiles(name)` 枚举 upstream `tests/interp/` 下的 + `.ngc`; +- `badInterpFixtures` 必须与 vendored `tests/interp/bad/*.ngc` 完全一致; +- `goodArcFixtures` 必须与 vendored `tests/interp/good/*.ngc` 完全一致; +- 如果 upstream good/bad 新增 `.ngc`,Node smoke 会失败,要求显式纳入或分类。 + +Browser 新增: + +- `badInterpFixtures` 固定 `length=21` 且 file list duplicate-free; +- `goodArcFixtures` 固定 `length=6` 且 file list duplicate-free; +- browser representative 不枚举目录,但会防止代表列表被静默删减或重复。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_interp_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +结果: + +- `git diff --check` clean。 +- `interp_wasm_node_smoke=ok` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- Host aggregate 中 Node inventory 仍保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` + +4. 下一步工作建议。 + +继续小批量 pure interpreter intake,但不要再从 `good` / `bad` 目录找增量, +这两个目录已有 coverage guard。下一步建议: + +1. 对 `tests/interp/m98m99` 做同类覆盖对账,优先确认已覆盖文件、未覆盖文件、 + 以及哪些需要 fanuc-mode INI 或 expected-error 分类; +2. 只纳入 1 个明确 pure interpreter 且 Node/browser 都可稳定断言的未覆盖 + fixture; +3. 如果 `m98m99` 已经足够完整,则回到 non-runtime consistency,检查 + `boundary-phase-completion-summary.tsv` 的 count parity 是否完全绑定到返回的 + artifact arrays。 + +六十、2026-06-12 继续执行记录:m98m99 pure interpreter fixture coverage guard + +本轮执行五十九建议,对 upstream `tests/interp/m98m99` 做覆盖对账。仍不执行任何 +`ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 覆盖对账。 + +对账结果: + +- `tests/interp/m98m99` 当前 24 个 `.ngc` 已全部在 Node/browser 覆盖; +- 覆盖包含: + - basic Fanuc and RS274 sub calls; + - variable scope; + - M98 expected errors; + - mixed sub-style expected errors; + - nested/sub-follow-main; + - fanuc-mode INI enable/disable cases; + - main-program O-word legal and expected-error cases; + - named/numbered program and O-expression call cases; +- 因此本轮不重复添加 fixture,而是把“已全覆盖”变成可执行 guard。 + +2. 更新。 + +更新: + +- `wasm-port/tests/wasm/node/verify_interp_wasm.mjs` +- `wasm-port/tests/browser/interp_smoke.html` + +Node 新增: + +- `interpRegressionNgcFilesRecursive(name)` 递归枚举 upstream + `tests/interp//**/*.ngc`; +- `m98m99RegressionFiles` 固定 24 个当前 upstream `.ngc`; +- Node smoke 要求该 manifest 与 vendored `tests/interp/m98m99/**/*.ngc` 完全一致; +- 如果 upstream m98m99 新增 `.ngc`,Node smoke 会失败,要求显式纳入或分类。 + +Browser 新增: + +- `m98m99RegressionFiles` 固定 `length=24` 且 duplicate-free; +- browser representative 不枚举目录,但会防止 m98m99 manifest 被静默删减或重复。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_interp_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +结果: + +- `git diff --check` clean。 +- `interp_wasm_node_smoke=ok` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` +- Host aggregate 中 Node inventory 仍保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` + +4. 下一步工作建议。 + +`good`、`bad`、`m98m99` 三个 pure interpreter fixture family 已有覆盖 guard。 +下一步建议回到 non-runtime consistency: + +1. 检查 `boundary-phase-completion-summary.tsv` 的 artifact documentation count + parity 是否完全绑定到 `verifyBrowserGeneratedArtifactDocumentationCoverage()` + 返回的 `wasmArtifactNames` / `nativeArtifactTokens`,以及 Node 是否锁定这个 + 调用面; +2. 如果还要继续 interpreter intake,优先对 `magic_comments` 或 `rotation` 这类 + 已有少量代表的纯解释器 family 做覆盖对账,不要碰 HAL/task/remap/tool DB/ + user-M process; +3. blocked runtime 仍只在具备 `halcmd/halrun/linuxcnc/milltask` 的 host 上重新跑 + preflight/dispatch 并确认可 dispatch 后,才允许人工 opt-in。 + +六十一、2026-06-12 继续执行记录:artifact documentation count parity returned-array guard + +本轮执行六十建议的第 1 项,回到 non-runtime consistency,检查 +`boundary-phase-completion-summary.tsv` 的 artifact documentation count parity +是否绑定到 `verifyBrowserGeneratedArtifactDocumentationCoverage()` 返回的 arrays。 +仍不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 收紧 browser count-parity 调用面。 + +更新: + +- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- `wasm-port/docs/compatibility-validation.md` +- `wasm-port/docs/sim-configs-coverage-matrix.md` + +背景: + +- browser smoke 已执行 `verifyBrowserGeneratedArtifactDocumentationCoverage()`; +- `verifyBrowserBoundaryPhaseCompletionCountParity()` 已用 generated completion rows; +- WASM documentation count 已使用 + `generatedArtifactDocumentationCoverage.wasmArtifactNames.length`; +- native documentation count 已使用 + `generatedArtifactDocumentationCoverage.nativeArtifactTokens.length`; +- 但 Node gate 应锁定这两个 entries 同时位于同一个 count-parity Map 中,防止 + 后续被拆成独立硬编码计数。 + +本轮新增 Node-side parity: + +- `verifyGeneratedArtifactDocumentationCoverage()` 现在检查 browser smoke source + 中保留 compound count-parity snippet: + - `wasm_inventory_artifact_documentation_coverage` + - `generatedArtifactDocumentationCoverage.wasmArtifactNames.length` + - `native_artifact_documentation_coverage` + - `generatedArtifactDocumentationCoverage.nativeArtifactTokens.length` + +该检查只读取 Node/browser test source 和文档,不执行 native runtime probe,也不 +允许 promotion。 + +2. 文档同步。 + +已记录: + +- browser documentation-coverage completion counts 必须绑定到 returned + `wasmArtifactNames` 和 `nativeArtifactTokens` arrays; +- 这仍是 documentation/artifact count-parity,不是 runtime proof。 + +3. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +结果: + +- `git diff --check` clean。 +- `sim_configs_wasm_node_inventory=ok` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` + +4. 下一步工作建议。 + +当前 artifact documentation coverage 的 Node/browser 调用面已较完整。下一步建议: + +1. 继续 interpreter family coverage 对账,优先 `magic_comments` 或 `rotation`; + 只加 coverage guard 或 1 个明确 pure interpreter fixture; +2. 如果不继续 intake,则做一次当前大工作集整理:`git diff --stat`、 + `git diff --name-only`,按 browser gate、Node inventory、interp intake、 + docs/text 归 batch,准备 review; +3. blocked runtime 仍只在具备 `halcmd/halrun/linuxcnc/milltask` 的 host 上重新跑 + preflight/dispatch 并确认可 dispatch 后,才允许人工 opt-in。 + +六十二、2026-06-12 继续执行记录:magic_comments/rotation pure interpreter fixture coverage guard + +本轮执行六十一建议的第 1 项,对 `magic_comments` 和 `rotation` 做 pure +interpreter family coverage 对账。仍不执行任何 +`ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 覆盖对账。 + +对账结果: + +- `tests/interp/magic_comments` 当前只有 1 个 `.ngc`: + `param_format_printing/test.ngc`; +- `tests/interp/rotation` 当前有 3 个 `.ngc`: + `abs-pts/test.ngc`、`g28/g28.ngc`、`g53/g53.ngc`; +- 这 4 个文件此前已在 Node/browser 中执行覆盖; +- 因此本轮不新增 fixture,只把“已全覆盖”变成可执行 guard。 + +2. 更新。 + +更新: + +- `wasm-port/tests/wasm/node/verify_interp_wasm.mjs` +- `wasm-port/tests/browser/interp_smoke.html` + +Node 新增: + +- `rotationRegressionFiles` 固定 3 个当前 upstream `.ngc`; +- `magicCommentsRegressionFiles` 固定 1 个当前 upstream `.ngc`; +- Node smoke 要求这两个 manifest 与 vendored upstream 递归 `.ngc` 列表完全一致。 + +Browser 新增: + +- `rotationRegressionFiles` 固定 `length=3` 且 duplicate-free; +- `magicCommentsRegressionFiles` 固定 `length=1` 且 duplicate-free; +- browser representative 不枚举目录,但会防止这两个 manifest 被静默删减或重复。 + +3. 下一步工作建议。 + +验证通过后,下一步优先做当前大工作集整理: + +1. `git diff --stat`、`git diff --name-only`; +2. 按 browser gate、Node inventory、interp intake、docs/text 归 batch; +3. 只补 review 边界说明,不继续扩大 interpreter intake; +4. blocked runtime 仍只在具备 `halcmd/halrun/linuxcnc/milltask` 的 host 上重新跑 + preflight/dispatch 并确认可 dispatch 后,才允许人工 opt-in。 + +六十三、2026-06-12 继续执行记录:当前大工作集 review batch 整理 + +本轮执行六十二建议的第 1 到第 3 项,只整理当前工作集的 review 边界,不新增 +interpreter fixture,不扩大 browser inventory,也不执行任何 +`ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 当前工作区。 + +当前 tracked 改动: + +```text +text4.txt +wasm-port/docs/compatibility-validation.md +wasm-port/docs/full-process-boundary-design.md +wasm-port/docs/sim-configs-coverage-matrix.md +wasm-port/tests/browser/interp_smoke.html +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/wasm/node/verify_interp_wasm.mjs +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs +``` + +当前 untracked 文件: + +```text +text5.txt +``` + +`git diff --stat` 当前不包含 untracked `text5.txt`,review/提交时必须单独把 +`text5.txt` 纳入范围。 + +2. Review batch 划分。 + +Batch A:browser gate/schema 扩展。 + +- 文件: + - `wasm-port/tests/browser/interp_smoke.html` + - `wasm-port/tests/browser/verify_interp_browser.sh` +- 内容边界: + - browser 端校验 generated artifact 文档覆盖、fetchability、唯一性和固定数量; + - browser 端校验 boundary phase completion count parity; + - browser 端校验 blocked runtime host readiness、dispatch、skip-evidence、 + promotion/opt-in gate parity; + - browser 端补 pure interpreter representative 的 fixed-count/duplicate-free + guard; + - browser 仍不枚举目录,不跑 full 159 sim-config inventory。 +- 非目标: + - 不执行 blocked runtime probe; + - 不声明 full-process/HAL/task/UI coverage; + - 不把 blocked row promotion 成 pass。 + +Batch B:Node inventory / generated artifact gate 扩展。 + +- 文件: + - `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` +- 内容边界: + - 生成并校验 runtime host preflight、requirement summary、unblock plan、 + family readiness、host readiness rollup; + - 生成并校验 opt-in probe dispatch plan/rollup、skip-evidence contract/rollup; + - 校验 generated artifact exact set、documentation coverage、review document + fetch/join surface; + - 锁定 Node/browser artifact token surface parity 和 completion count parity。 +- 当前预期状态: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - blocked runtime 仍保持 `execution_enabled=0`、`promotion_allowed=0`。 + +Batch C:pure interpreter intake / coverage guard。 + +- 文件: + - `wasm-port/tests/wasm/node/verify_interp_wasm.mjs` + - `wasm-port/tests/browser/interp_smoke.html` +- 内容边界: + - 新增 `do-while-break/bug.ngc` 作为小批量 pure interpreter fixture; + - `good`、`bad`、`m98m99`、`magic_comments`、`rotation` 已有覆盖变成 + executable coverage guard; + - Node 对可递归 family 使用 upstream `.ngc` manifest 完全对账; + - browser 对 representative list 做固定数量和 duplicate-free guard。 +- 非目标: + - 不继续 intake `HAL/task/remap/tool DB/user-M process`; + - 不用 JS 重写 G-code 语义; + - 不修改 vendored LinuxCNC 源文件或 fixture 内容。 + +Batch D:docs/text review 边界。 + +- 文件: + - `text4.txt` + - `text5.txt` + - `wasm-port/docs/compatibility-validation.md` + - `wasm-port/docs/full-process-boundary-design.md` + - `wasm-port/docs/sim-configs-coverage-matrix.md` +- 内容边界: + - 记录 Batch 16 到 Batch 23 的 blocked runtime host/dispatch/skip evidence + artifacts; + - 说明这些 artifacts 是 non-executing、non-promoting proof/gate; + - 明确当前 host 缺 `halcmd`、`halrun`、`linuxcnc`、`milltask`; + - 记录 native/Node/browser promotion 顺序和禁止事项。 + +3. 本轮不处理事项。 + +- 不运行: + +```bash +ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh +ENABLE_TOOL_DB_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_tool_db_runtime.sh +ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh +``` + +- 不继续扩大 interpreter intake; +- 不新增 browser 全量 inventory; +- 不修改 `wasm-port/vendor/linuxcnc/`; +- 不把 expected failure 或 blocked runtime row 改成 pass。 + +4. 下一步工作建议。 + +先复跑最小 review gate: + +```bash +git diff --check +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/wasm/node/verify_interp_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +如果全部通过,下一步只做 review 准备: + +1. 确认 `text5.txt` 是否需要加入版本控制; +2. 按 Batch A/B/C/D 分开审查 diff; +3. 暂停新增 coverage,除非 review 发现 drift 或 gate 失败。 + +5. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/wasm/node/verify_interp_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +结果: + +- `git diff --check` clean。 +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `interp_wasm_node_smoke=ok` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` + +6. 下一步工作建议。 + +当前工作集已经进入 review 准备状态。下一步建议不要继续新增测试面,改为: + +1. 确认 `text5.txt` 加入版本控制; +2. 按 Batch A/B/C/D 顺序做人工 review; +3. review 时优先检查: + - generated artifact list 是否有重复/遗漏; + - completion count parity 是否仍绑定 generated arrays; + - blocked runtime 是否始终 `execution_enabled=0`、`promotion_allowed=0`; + - pure interpreter coverage guard 是否只覆盖 upstream `.ngc` manifest; +4. review 通过后再考虑提交或拆分提交。 + +六十四、2026-06-12 继续执行记录:review risk audit 静态核查 + +本轮执行六十三建议的 review 重点检查,只做静态核查和生成物读取,不新增 +coverage,不扩大 browser inventory,不执行任何 +`ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. Artifact list 精确集合核查。 + +核查内容: + +- browser `wasmArtifactNames` 与当前 + `wasm-port/build/wasm/sim-configs-inventory/*.tsv` 精确一致; +- browser `nativeArtifactTokens` 与当前 `wasm-port/build/native/**/*.tsv` + 精确一致; +- 当前 WASM generated artifact 数量仍为 54; +- 当前 native TSV artifact token 数量仍为 8。 + +结果: + +```text +browser_wasm_artifact_exact_set=ok count=54 +browser_native_artifact_exact_set=ok count=8 +``` + +2. Completion count parity 绑定核查。 + +核查内容: + +- browser `verifyBrowserGeneratedArtifactDocumentationCoverage()` 返回 + `wasmArtifactNames` / `nativeArtifactTokens`; +- browser `verifyBrowserBoundaryPhaseCompletionCountParity()` 的 + `wasm_inventory_artifact_documentation_coverage` 使用 + `generatedArtifactDocumentationCoverage.wasmArtifactNames.length`; +- `native_artifact_documentation_coverage` 使用 + `generatedArtifactDocumentationCoverage.nativeArtifactTokens.length`; +- Node gate 仍锁定这段 compound snippet,防止后续改成硬编码独立计数。 + +结果: + +- count parity 仍绑定 generated arrays; +- 未发现独立硬编码计数替代 returned arrays 的 drift。 + +3. Blocked runtime 禁止提升核查。 + +读取当前 rollup: + +```bash +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-rollup.tsv +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv +``` + +结果: + +- `family_count=3` +- `host_ready_family_count=0` +- `host_blocked_family_count=3` +- `missing_host_requirements=halcmd,halrun,linuxcnc,milltask` +- `ready_opt_in_command_count=0` +- `dispatch_allowed_count=0` +- `dispatch_blocked_count=3` +- `skip_count=3` +- `execution_enabled=0` +- `promotion_allowed=0` + +源码静态检索也未发现 `node:child_process`、`spawn()`、`execFile()` 或 `exec()` +用于自动执行 opt-in probe。 + +4. Pure interpreter manifest guard 核查。 + +核查内容: + +- Node `badInterpFixtures` 对账 `interpRegressionNgcFiles("bad")`; +- Node `goodArcFixtures` 对账 `interpRegressionNgcFiles("good")`; +- Node `m98m99RegressionFiles` 对账 + `interpRegressionNgcFilesRecursive("m98m99")`; +- Node `magicCommentsRegressionFiles` 对账 + `interpRegressionNgcFilesRecursive("magic_comments")`; +- Node `rotationRegressionFiles` 对账 + `interpRegressionNgcFilesRecursive("rotation")`; +- browser 对上述 representative list 保持 fixed-count 和 duplicate-free guard。 + +结果: + +- pure interpreter coverage guard 仍只绑定 upstream `.ngc` manifest; +- 未发现对 HAL/task/remap/tool DB/user-M process 的新增 intake。 + +5. 下一步工作建议。 + +当前 review risk audit 通过。下一步建议: + +1. 复跑一次最小 review gate; +2. 如果通过,停止继续修改代码,进入人工 review/提交准备; +3. `text5.txt` 仍为 untracked,提交前需要明确加入或有意排除; +4. 如果要拆分提交,优先按 Batch A/B/C/D 拆: + - A:browser gate/schema; + - B:Node inventory/generated artifact gate; + - C:pure interpreter intake/coverage guard; + - D:docs/text。 + +6. 验证完成。 + +执行: + +```bash +git diff --check +wasm-port/tools/verify_vendor_sync.sh +wasm-port/tools/verify_no_standalone_cnc_semantics.sh +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +wasm-port/tests/wasm/node/verify_interp_wasm.sh +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/host/verify_host_smokes.sh +``` + +结果: + +- `git diff --check` clean。 +- `vendor sync up to date` +- `standalone CNC semantics guard complete` +- Node inventory 保持: + - `executed=28` + - `passed=28` + - `skipped=131` + - `unexpected_fail=0` + - `ASSET-ONLY=65` + - `L4-PYTHON-REMAP=53` + - `L4-TOOL-DB=1` + - `L4-USER-M-PROCESS=1` + - `NON_MAIN_CLASS=10` + - `UPSTREAM-DEMO=1` +- `interp_wasm_node_smoke=ok` +- `browser_interp_smoke=ok` +- `host_wasm_opfs_browser_smokes=ok` + +7. 下一步工作建议。 + +当前工作集已满足 review 前置条件。下一步建议停止继续修改代码,进入人工 +review/提交准备: + +1. 明确 `text5.txt` 是否加入版本控制; +2. 如果提交,按 Batch A/B/C/D 拆分或至少按该顺序 review; +3. 提交前最后复核 `git status --short`,确认没有遗漏 untracked 文件; +4. 除非 review 发现 gate drift,不再继续新增 coverage。 + +六十五、2026-06-12 继续执行记录:提交拆分准备 + +本轮执行“进入 review/提交准备”的建议,只做提交边界判断,不修改功能代码, +不新增 coverage,不执行任何 `ENABLE_*_RUNTIME_PROBE=1` opt-in native probe。 + +1. 当前提交候选状态。 + +当前工作区包含 9 个文件: + +```text +text4.txt +text5.txt +wasm-port/docs/compatibility-validation.md +wasm-port/docs/full-process-boundary-design.md +wasm-port/docs/sim-configs-coverage-matrix.md +wasm-port/tests/browser/interp_smoke.html +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/wasm/node/verify_interp_wasm.mjs +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs +``` + +`text5.txt` 已用 `git add --intent-to-add text5.txt` 纳入 `git status` 视图, +但当前没有实际 staged 内容,`git diff --cached --stat` 为空。 + +2. 拆分风险判断。 + +原计划的 Batch A/B/C/D 是 review 顺序,不是天然的文件级提交边界: + +- Batch A 和 Batch C 都修改 `wasm-port/tests/browser/interp_smoke.html`; +- Batch A/B/D 的 docs 与 generated artifact documentation coverage gate 相互依赖; +- 若强行按 4 个提交拆,需要交互式 hunk staging,容易把 browser smoke 的 + runtime gate 和 pure interpreter guard 拆错; +- 其中部分中间提交可能无法单独通过 browser documentation coverage gate。 + +因此当前低风险提交策略是: + +1. 做人工 review 时继续按 Batch A/B/C/D 顺序看; +2. 实际提交优先采用 2 个文件级提交,避免交互式 hunk 拆分风险; +3. 如必须拆成更细提交,再单独做 hunk-level staging,并在每个提交后复跑 + browser/inventory gate。 + +3. 推荐提交拆分。 + +推荐 Commit 1:runtime/browser/Node/interpreter gate code。 + +文件: + +```text +wasm-port/tests/browser/interp_smoke.html +wasm-port/tests/browser/verify_interp_browser.sh +wasm-port/tests/wasm/node/verify_interp_wasm.mjs +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs +``` + +建议 message: + +```text +Expand WASM runtime boundary review gates +``` + +注意:这个提交如果不同时带 docs,browser documentation coverage 可能依赖后续 +提交,因此更适合作为 review unit;若要求每个提交都独立通过 gate,应把相关 +docs 一并放入 Commit 1。 + +推荐 Commit 2:documentation and execution log。 + +文件: + +```text +text4.txt +text5.txt +wasm-port/docs/compatibility-validation.md +wasm-port/docs/full-process-boundary-design.md +wasm-port/docs/sim-configs-coverage-matrix.md +``` + +建议 message: + +```text +Document blocked runtime gate review state +``` + +4. 更稳妥的一提交方案。 + +如果要求提交后立即可复跑全部 gate,当前最稳妥方案是单提交包含全部 9 个文件: + +```text +Add blocked runtime gate review coverage +``` + +理由: + +- browser generated artifact documentation coverage 与 docs 同步提交; +- Node/browser artifact surface parity 与 docs 同步提交; +- pure interpreter guard 与 browser smoke 同步提交; +- `text5.txt` 执行记录不会遗漏。 + +5. 下一步工作建议。 + +当前建议优先选择: + +1. 若目标是“可 bisect、每个提交都通过 gate”:做单提交; +2. 若目标是“review 逻辑更清楚”:按上面的 2 个 review unit 审查,但提交时 + 可以合并为单提交; +3. 不建议现在做 4 个 hunk-level 提交,除非明确愿意承担拆分和重复验证成本。 diff --git a/wasm-port/docs/compatibility-validation.md b/wasm-port/docs/compatibility-validation.md index 67ec2ce..21bc87d 100644 --- a/wasm-port/docs/compatibility-validation.md +++ b/wasm-port/docs/compatibility-validation.md @@ -141,14 +141,28 @@ The native harness also emits machine-readable derived artifacts without changing the `summary.tsv` schema: ```text +wasm-port/build/native/source-probes.tsv +wasm-port/build/native/native-source-proof-summary.tsv +wasm-port/build/native/native-runtime-probe-summary.tsv +wasm-port/build/native/nc-files/summary.tsv +wasm-port/build/native/sim-configs/summary.tsv wasm-port/build/native/sim-configs/class-summary.tsv wasm-port/build/native/sim-configs/path-matrix.tsv +wasm-port/build/native/sim-configs/skipped.tsv ``` `class-summary.tsv` aggregates class/status/expected-failure counts. `path-matrix.tsv` records each path, class, native status, expected-failure reason, INI, tool table, runtime family, and blocked kind for CI and Node inventory reconciliation. +`source-probes.tsv` records the native source-probe entry points used by +`verify_native_probes.sh`. `native-source-proof-summary.tsv` records +LinuxCNC-owned source proof for user-M process, tool DB process, and Python +remap runtime blockers without enabling execution or promotion. +`native-runtime-probe-summary.tsv` records the guarded native opt-in runtime +probe status and missing host requirements. `nc-files/summary.tsv` records the +Layer 1 native `nc_files` baseline, and `sim-configs/skipped.tsv` records the +Layer 2 native sim-config skipped or expected-skip accounting. The Node inventory layer writes its own machine-readable artifacts: @@ -163,19 +177,34 @@ wasm-port/build/wasm/sim-configs-inventory/user-m-process-state-targets.tsv wasm-port/build/wasm/sim-configs-inventory/user-m-process-native-state-alignment.tsv wasm-port/build/wasm/sim-configs-inventory/tool-db-process-protocol-gates.tsv wasm-port/build/wasm/sim-configs-inventory/tool-db-process-native-protocol-alignment.tsv +wasm-port/build/wasm/sim-configs-inventory/tool-db-process-native-runtime-probe-gate.tsv wasm-port/build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv wasm-port/build/wasm/sim-configs-inventory/python-remap-runtime-gates.tsv wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-alignment.tsv wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-readiness.tsv +wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-probe-gate.tsv wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-state-plan.tsv wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-fixture-plan.tsv wasm-port/build/wasm/sim-configs-inventory/python-remap-family-summary.tsv wasm-port/build/wasm/sim-configs-inventory/boundary-phase-completion-summary.tsv wasm-port/build/wasm/sim-configs-inventory/native-proof-alignment-summary.tsv wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-native-alignment-summary.tsv +wasm-port/build/wasm/sim-configs-inventory/native-runtime-probe-execution-plan.tsv wasm-port/build/wasm/sim-configs-inventory/blocked-runtime-promotion-lock.tsv wasm-port/build/wasm/sim-configs-inventory/next-boundary-worklist.tsv +wasm-port/build/wasm/sim-configs-inventory/next-boundary-recommendations.tsv wasm-port/build/wasm/sim-configs-inventory/boundary-proof-gates.tsv +wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-native-evidence-acceptance-gate.tsv +wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-promotion-blockers.tsv +wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-preflight.tsv +wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-requirement-summary.tsv +wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-unblock-plan.tsv +wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-family-host-readiness.tsv +wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-plan.tsv +wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-rollup.tsv +wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-contract.tsv +wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv ``` The current skip/block summary is `ASSET-ONLY=65`, `L4-PYTHON-REMAP=53`, @@ -243,6 +272,133 @@ blocked dependency row, records the required native/Node/browser proof, and keeps `execution_enabled=0` until the corresponding LinuxCNC-owned runtime boundary exists. The tracked matrix and inventory guard also require those two rows to remain non-representative in Node/browser until that proof exists. +`runtime-boundary-host-preflight.tsv` is the host-facing preflight for the +blocked runtime probes. It records each blocked family, the required LinuxCNC +runtime commands, the opt-in environment variable, the exact native probe +command, the current probe status, and keeps `execution_enabled=0` and +`promotion_allowed=0`. It does not run probes or relax promotion locks. +`runtime-boundary-host-requirement-summary.tsv` normalizes those preflight +requirements into one row per host command or source/module prerequisite, so +missing `halcmd`, `halrun`, `linuxcnc`, and `milltask` can be audited by the +blocked families they affect. `runtime-boundary-host-unblock-plan.tsv` +filters that table to unavailable requirements and records the affected +opt-in commands and remaining missing requirements; it is a planning artifact +only. +`runtime-boundary-family-host-readiness.tsv` reduces the same host evidence to +one row per blocked runtime family, and +`runtime-boundary-host-readiness-rollup.tsv` gives the aggregate decision. On +this host the rollup remains `host_blocked_for_all_opt_in_native_probes` with +zero ready opt-in commands. These rows are non-executing and non-promoting. +`runtime-boundary-opt-in-probe-dispatch-plan.tsv` turns each family readiness +row into a per-probe dispatch action. `runtime-boundary-opt-in-probe-dispatch-rollup.tsv` +then gives the aggregate dispatch switch; currently every blocked runtime +probe is skipped for missing host requirements. Dispatch permission only means +the guarded native probe may be manually run on a ready host, not that the row +is promoted. +`runtime-boundary-opt-in-probe-skip-evidence-contract.tsv` records why each +non-dispatched probe is skipped and whether native pass evidence is required +yet. `runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv` gives the +aggregate evidence decision. While probes are skipped for missing host +requirements, no native pass evidence is accepted and every blocked family +remains `execution_enabled=0` and `promotion_allowed=0`. +`native-runtime-probe-execution-plan.tsv` records the exact opt-in command, +required native proof key, current runtime readiness, expected pass status, +missing requirements, and promotion prerequisites for the three guarded native +runtime probes. On this host all rows remain +`plan_status=blocked_missing_host_runtime`. +`runtime-boundary-native-evidence-acceptance-gate.tsv` decides whether native +pass evidence may be accepted for each blocked runtime family. It currently +blocks evidence acceptance until host requirements are available and the +native opt-in probe passes. `runtime-boundary-promotion-blockers.tsv` expands +each non-ready promotion decision into concrete blocker keys such as missing +host runtime, native probe not passed, Node/browser gates incomplete, +promotion lock active, and manual lock update required. +`next-boundary-recommendations.tsv` is the generated priority handoff for the +next runtime boundary work. It ranks millturn user-M, tool DB, and Python +runtime lifecycle work, while keeping `execution_enabled=0` and +`promotion_allowed=0`. The Node inventory verifies the exact per-family +`source_artifacts` set, duplicate-free source lists, and `primary_artifact` +traceability for each recommendation, and requires every referenced artifact +token to exist under the generated sim-config inventory artifact directory. +The Node inventory also runs a cross-artifact guard over the blocked runtime +opt-in target set. It requires recommendations, execution plan, host preflight, +dispatch plan, skip/evidence contract, native evidence acceptance gate, +promotion readiness, promotion blockers, and post-native-pass gates to agree +on the same target, proof keys, opt-in command, missing runtime requirements, +active promotion lock, and non-executing/non-promoting state. +The browser smoke mirrors the promotion and opt-in gate consistency checks by +cross-checking the promotion lock, readiness, blocker, post-native-pass, native +evidence acceptance, pass-evidence, recommendation, host-preflight, family +readiness, dispatch, and skip-evidence artifacts. This browser-side parity is +non-executing and keeps every blocked runtime family locked until native, +Node, browser, and manual promotion gates all agree. +It also cross-checks `runtime-boundary-host-requirement-summary.tsv`, +`runtime-boundary-host-unblock-plan.tsv`, +`runtime-boundary-family-host-readiness.tsv`, and +`runtime-boundary-host-preflight.tsv` so missing and available host +requirements, affected opt-in commands, and family probe commands cannot drift +between the host-readiness artifacts. +It also checks that `boundary-phase-completion-summary.tsv` counts match the +blocked-runtime and family-specific artifact rows the browser has already +loaded, so completion criteria cannot drift from the generated gate artifacts +seen by browser smoke. +It also cross-checks the user-M, tool DB, and Python family-specific +contract/readiness/probe artifacts against `runtime-boundary-contract-summary.tsv` +so browser validation sees the same runtime-family contract alignment as the +Node inventory, without running any blocked runtime process. +It also checks `next-boundary-recommendations.tsv` row counts against the +generated user-M probe-gate, tool DB probe-gate, and Python runtime-contract +source rows, so recommendation priority handoffs cannot drift from their +machine-readable source artifacts. +It also checks each recommendation's `primary_artifact` and `source_artifacts` +against the browser-visible generated artifact set, including duplicate +detection, primary-artifact traceability, and exact per-family source artifact +sets, while keeping execution and promotion disabled. The Node coverage gate +also confirms the browser recommendation source-artifact check uses the same +`wasmArtifactNames` returned by the artifact documentation coverage helper. +It also fetches the compatibility, matrix, and full-process boundary docs in +browser smoke and checks the documented artifact names/tokens for the generated +WASM inventory TSVs and native TSVs. The same browser check fetches those TSV +artifacts, requires a non-empty TSV header, and verifies the expected +artifact/token lists are duplicate-free with fixed counts, mirroring the Node +documentation coverage gate without executing native runtime probes. The native +`build/native/sim-configs/skipped.tsv` artifact is allowed to be empty when the +native strict sim-config baseline has no skipped rows. +The same Node inventory run also checks generated artifact documentation +coverage: every TSV emitted under +`build/wasm/sim-configs-inventory/` must be named in +`compatibility-validation.md`, `sim-configs-coverage-matrix.md`, or +`full-process-boundary-design.md`. New gate artifacts must therefore be +documented before the inventory can pass. +The Node coverage gate also requires the generated WASM inventory artifact list +to remain the exact duplicate-free 54-entry baseline and the native generated +TSV token list to remain the exact duplicate-free 8-token baseline. It also +requires the corresponding `boundary-phase-completion-summary.tsv` +documentation-coverage counts to remain `54` and `8`, respectively. It also +checks that the browser smoke source lists the same generated WASM and native +artifact tokens and preserves the same fixed-count, duplicate-free, +fetchability/header, documentation-missing, and completion count-parity guards +before browser-side artifact coverage can pass. It also checks that browser +smoke actually executes the artifact documentation coverage helper, fetches +each generated WASM artifact by artifact name, fetches each native TSV by +relative artifact token, calls the completion count-parity helper with the +generated completion rows, still fetches and joins the exact compatibility, +matrix, and full-process boundary review-document set for that coverage check +before checking for missing artifact references, requires the browser +missing-reference loops to iterate `wasmArtifactNames` and +`nativeArtifactTokens` against that joined `documentationText`, keeps +completion evidence reviewable for browser count parity, binds the +documentation-coverage completion counts to the returned `wasmArtifactNames` +and `nativeArtifactTokens` arrays, and preserves the native skipped-artifact +empty allowance for `build/native/sim-configs/skipped.tsv`. +Native generated TSVs under `build/native/` are checked the same way by their +relative artifact path. This catches native baseline/source-proof/runtime-probe +artifacts that are generated but not described in the compatibility, coverage, +or full-process boundary documentation. +It also checks native source proof consistency: the native source proof +alignment rows, native runtime probe summary, and runtime probe gate alignment +must cover the same blocked classes and agree that source proof is ready while +execution and promotion remain disabled. `user-m-process-state-targets.tsv` expands the `millturn` M128/M129 process boundary into per-pin proof targets: two user-M codes, three axes, and four `ini.[xyz]` HAL pins per axis. Each row records the source Tcl file, remap @@ -302,6 +458,10 @@ keeping `proof_status=pending`, `execution_enabled=0`, and vendored `DB_PROGRAM`, drives the LinuxCNC `tooldb.py` `v2.1`/`g`/`p`/`l`/`u` protocol, verifies nonrandom startup/update/load/unload state and persistence, and reports `runtime_protocol_probe_passed` without enabling promotion. +`tool-db-process-native-runtime-probe-gate.tsv` combines the DB source proof +and host readiness into the execution gate for that guarded native probe. It +records `runtime_ready`, missing requirements, source proof readiness, and the +non-promoting gate status before any manual opt-in run is allowed. `python-remap-boundary-summary.tsv` is the machine-readable companion for the Python remap inventory batch. It has one inventory-only row for each `L4-PYTHON-REMAP` path, records Python modules, remap/prolog/epilog functions, @@ -323,6 +483,11 @@ prerequisites for guarded probes by family: `python3`, `linuxcnc`, LinuxCNC's `interp_python.cc` / `python_plugin.cc` owner source files, and the configured Python modules. It is a readiness gate only and keeps every row `proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`. +`python-remap-native-runtime-probe-gate.tsv` combines the selected Python +runtime fixture, source proof, and host readiness into the execution gate for +the guarded native lifecycle probe. It remains non-executing and +non-promoting until `linuxcnc` is available and the dispatch gate permits the +manual opt-in probe. `python-remap-native-runtime-state-plan.tsv` records the next native runtime probe target set by family: LinuxCNC Python phases, configured modules, callables, NGC-only subpaths, process assumptions, readiness counts, and @@ -352,6 +517,50 @@ disabled, and proof that blocked runtime families carry LinuxCNC-owned user-M, tool DB, and Python runtime evidence fields. It also records native source proof alignment when the native proof summary is available, plus the aggregate native stdout alignment summary for user-M, tool DB, and Python runtime gates. +The same completion summary now includes documentation-coverage criteria for +generated WASM sim-config inventory TSV artifacts and native generated TSV +artifacts, so artifact generation and review documentation stay in lockstep. +It also exposes the blocked runtime worklist/recommendation consistency guard, +the blocked runtime promotion gate consistency guard, the blocked runtime host +requirement consistency guard, the blocked runtime host/dispatch/skip-evidence +rollup consistency guard, the blocked runtime opt-in cross-artifact consistency +guard, the native source proof/runtime probe consistency guard, and the runtime +family contract/alignment consistency guard as completion criteria, so review +can see those gates without reopening every individual TSV. The family +contract/alignment guard checks that the user-M, tool DB, and Python-specific +contract, readiness, probe-gate, and fixture/state-plan artifacts agree with +`runtime-boundary-contract-summary.tsv` and +`runtime-boundary-native-alignment-summary.tsv` while keeping execution and +promotion disabled. +The browser smoke consumes the same `boundary-phase-completion-summary.tsv` +criterion list and fails on criterion drift. This is a browser-side parity +check for generated gate artifacts only; it does not execute blocked native +runtime probes or allow promotion. +The browser smoke also checks that `runtime-boundary-contract-summary.tsv` +points at the same runtime alignment artifact, row count, boundary kind, and +disabled execution/promotion state reported by +`runtime-boundary-native-alignment-summary.tsv`. +It also cross-checks `native-proof-alignment-summary.tsv`, +`native-runtime-probe-summary.tsv`, and `runtime-probe-gate-alignment.tsv` so +browser validation sees the same source-proof readiness, runtime readiness, +required native proof, target, and disabled execution/promotion state as the +Node inventory. +The browser smoke also cross-checks `native-runtime-probe-execution-plan.tsv`, +`native-runtime-probe-pass-evidence-contract.tsv`, +`runtime-boundary-host-preflight.tsv`, +`runtime-boundary-opt-in-probe-dispatch-plan.tsv`, and +`runtime-boundary-opt-in-probe-skip-evidence-contract.tsv`, keeping the +current host-blocked probe status, missing requirements, opt-in command, +evidence status, and disabled execution/promotion state aligned. +It also checks the host requirement summary and unblock plan against family +host-readiness and preflight rows, including unavailable requirement +ownership, available prerequisite ownership, opt-in environment variables, and +execution command traceability. +It also cross-checks `runtime-boundary-host-readiness-rollup.tsv`, +`runtime-boundary-opt-in-probe-dispatch-rollup.tsv`, and +`runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv` for family/probe +counts, blocked families, missing requirements, blocked opt-in commands, +host/dispatch/evidence status, and disabled execution/promotion parity. `native-proof-alignment-summary.tsv` aligns native source proof rows with the generated worklist and native proof gates for user-M, tool DB, and Python runtime blockers. It is proof-consumption accounting only and keeps execution diff --git a/wasm-port/docs/full-process-boundary-design.md b/wasm-port/docs/full-process-boundary-design.md index 979ecca..062c076 100644 --- a/wasm-port/docs/full-process-boundary-design.md +++ b/wasm-port/docs/full-process-boundary-design.md @@ -487,6 +487,78 @@ this phase. It combines the worklist, native/Node/browser proof gates, and runtime alignment summary so every blocked target has `lock_active=1` until a LinuxCNC-owned runtime boundary replaces the pending gates. +## Host Preflight Runbook + +The blocked runtime probes are manual opt-in probes. They must not be run just +because the probe script exists. A future host must first pass the generated +preflight and dispatch checks for the specific blocked family. + +Before attempting any opt-in probe, inspect: + +```bash +wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-preflight.tsv +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-family-host-readiness.tsv +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-plan.tsv +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-rollup.tsv +cat wasm-port/build/wasm/sim-configs-inventory/native-runtime-probe-execution-plan.tsv +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-native-evidence-acceptance-gate.tsv +cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-promotion-blockers.tsv +``` + +The current host is not ready for any blocked runtime opt-in probe. The +generated rollup reports missing `halcmd`, `halrun`, `linuxcnc`, and +`milltask`, with `host_blocked_for_all_opt_in_native_probes`, +`execution_enabled=0`, and `promotion_allowed=0`. + +The opt-in commands remain forbidden while their dispatch rows have +`dispatch_allowed=0`: + +```bash +ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh +ENABLE_TOOL_DB_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_tool_db_runtime.sh +ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh +``` + +A probe may be attempted only when all of these are true for that family: + +- the corresponding runtime probe gate reports `runtime_ready=1`; +- `runtime-boundary-family-host-readiness.tsv` reports the family ready; +- `runtime-boundary-opt-in-probe-dispatch-plan.tsv` reports + `dispatch_allowed=1`; +- `runtime-boundary-opt-in-probe-dispatch-rollup.tsv` does not block that + command; +- the probe is run manually with its explicit `ENABLE_*_RUNTIME_PROBE=1` + environment variable. + +Passing a native opt-in probe is not promotion. After a native pass, the row +must still satisfy: + +- `native-runtime-probe-pass-evidence-contract.tsv`; +- `runtime-probe-gate-alignment.tsv`; +- `runtime-boundary-promotion-readiness.tsv`; +- `runtime-boundary-post-native-pass-gates.tsv`; +- `runtime-boundary-native-evidence-acceptance-gate.tsv`; +- `runtime-boundary-promotion-blockers.tsv`; +- Node inventory proof; +- browser smoke proof where applicable; +- manual update of `blocked-runtime-promotion-lock.tsv`; +- documentation updates in this file, `compatibility-validation.md`, and + `sim-configs-coverage-matrix.md`. + +While the skip/evidence artifacts report +`skip_valid_until_host_requirements_available` and +`no_native_pass_evidence_accepted_while_host_blocked`, skipped probes provide +no native pass evidence. They only prove that the current host is correctly +blocked. + +`native-runtime-probe-execution-plan.tsv` and +`next-boundary-recommendations.tsv` are planning handoffs, not permission to +run probes. They name the next recommended boundary work and exact opt-in +commands, but the host preflight and dispatch rows still decide whether those +commands are allowed on the current machine. + The first two priorities are the current designed-but-disabled full-process blocks: diff --git a/wasm-port/docs/sim-configs-coverage-matrix.md b/wasm-port/docs/sim-configs-coverage-matrix.md index 24d4dc6..01fe78d 100644 --- a/wasm-port/docs/sim-configs-coverage-matrix.md +++ b/wasm-port/docs/sim-configs-coverage-matrix.md @@ -36,6 +36,8 @@ Current baseline: `wasm-port/build/wasm/sim-configs-inventory/tool-db-process-boundary-summary.tsv` - Node tool DB process transaction contract artifact: `wasm-port/build/wasm/sim-configs-inventory/tool-db-process-transaction-plan.tsv` +- Node tool DB native runtime probe gate artifact: + `wasm-port/build/wasm/sim-configs-inventory/tool-db-process-native-runtime-probe-gate.tsv` - Node Python-remap boundary inventory artifact: `wasm-port/build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv` - Node Python-remap family inventory artifact: @@ -44,6 +46,8 @@ Current baseline: `wasm-port/build/wasm/sim-configs-inventory/python-remap-runtime-contract.tsv` - Node Python-remap native runtime readiness artifact: `wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-readiness.tsv` +- Node Python-remap native runtime probe gate artifact: + `wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-probe-gate.tsv` - Node Python-remap native runtime state-plan artifact: `wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-state-plan.tsv` - Node Python-remap native runtime fixture-plan artifact: @@ -52,8 +56,34 @@ Current baseline: `wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-contract-summary.tsv` - Node boundary-phase completion artifact: `wasm-port/build/wasm/sim-configs-inventory/boundary-phase-completion-summary.tsv` +- Node native runtime probe execution-plan artifact: + `wasm-port/build/wasm/sim-configs-inventory/native-runtime-probe-execution-plan.tsv` - Node next-boundary worklist artifact: `wasm-port/build/wasm/sim-configs-inventory/next-boundary-worklist.tsv` +- Node next-boundary recommendation artifact: + `wasm-port/build/wasm/sim-configs-inventory/next-boundary-recommendations.tsv` +- Node native evidence acceptance gate artifact: + `wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-native-evidence-acceptance-gate.tsv` +- Node promotion blocker summary artifact: + `wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-promotion-blockers.tsv` +- Node blocked-runtime host preflight artifact: + `wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-preflight.tsv` +- Node blocked-runtime host requirement summary artifact: + `wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-requirement-summary.tsv` +- Node blocked-runtime host unblock plan artifact: + `wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-unblock-plan.tsv` +- Node blocked-runtime family host readiness artifact: + `wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-family-host-readiness.tsv` +- Node blocked-runtime aggregate host readiness artifact: + `wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv` +- Node blocked-runtime opt-in probe dispatch plan artifact: + `wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-plan.tsv` +- Node blocked-runtime opt-in probe dispatch rollup artifact: + `wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-rollup.tsv` +- Node blocked-runtime opt-in probe skip/evidence contract artifact: + `wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-contract.tsv` +- Node blocked-runtime opt-in probe skip/evidence rollup artifact: + `wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv` - Current Node inventory: `executed 28`, `passed 28`, `skipped 131`, `unexpected_fail 0` - Current Node skip/block counts: `ASSET-ONLY 65`, `L4-PYTHON-REMAP 53`, @@ -160,6 +190,10 @@ Legend: `ENABLE_TOOL_DB_RUNTIME_PROBE=1` proves the LinuxCNC-owned `DB_PROGRAM` `v2.1`/`g`/`p`/`l`/`u` protocol, nonrandom state updates, and flat-file persistence. +- `tool-db-process-native-runtime-probe-gate.tsv`: the execution gate for the + guarded native DB process protocol probe. It combines source proof and host + readiness, records missing runtime requirements, and keeps the DB boundary + non-executing and non-promotable until the manual opt-in probe is allowed. - `python-remap-boundary-summary.tsv`: one row for each `L4-PYTHON-REMAP` path. It records Python modules, remap/prolog/epilog functions, NGC-only subpaths, HAL/UI/HALUI assumptions, the LinuxCNC Python owner set, LinuxCNC @@ -179,6 +213,11 @@ Legend: `linuxcnc`, LinuxCNC's Python interpreter/plugin owner source files, and the configured Python modules for each blocked family, while keeping every row pending, non-executing, and non-promotable. +- `python-remap-native-runtime-probe-gate.tsv`: the execution gate for the + selected Python remap lifecycle fixture. It combines source proof and host + readiness, records missing runtime requirements, and keeps Python remap + execution blocked until the LinuxCNC host runtime and dispatch gate allow a + manual opt-in probe. - `python-remap-native-runtime-state-plan.tsv`: one row per blocked Python-remap family. It records the runtime phases, modules, callables, NGC-only subpaths, process assumptions, readiness counts, source-alignment @@ -208,7 +247,68 @@ Legend: LinuxCNC-owned runtime proof fields for user-M, tool DB, and Python runtime blockers. When native source proof is available, it also requires that proof to be consumed by generated worklist/native proof-gate rows without enabling - execution or promotion. + execution or promotion. It also records WASM sim-config inventory artifact + documentation coverage and native generated TSV documentation coverage as + explicit completion criteria. It also records the blocked runtime + worklist/recommendation consistency guard, the blocked runtime promotion gate + consistency guard, the blocked runtime host requirement consistency guard, + the blocked runtime host/dispatch/skip-evidence rollup consistency guard, the + blocked runtime opt-in cross-artifact consistency guard, the native source + proof/runtime probe consistency guard, and the runtime family + contract/alignment consistency guard as explicit completion criteria. The + family contract/alignment check keeps the user-M, tool DB, and Python + contract/readiness/probe artifacts aligned with the aggregate runtime + contract and native-alignment summaries without enabling execution or + promotion. + Browser smoke mirrors this completion-summary criterion list and fails on + criterion drift, so browser-side validation sees the same boundary-phase + gate set as the Node inventory without executing blocked runtime probes. + It also checks selected completion-summary counts against the blocked-runtime + and family-specific artifact rows already loaded by browser smoke, so the + machine-readable completion summary cannot claim a different row count from + the visible gate artifacts. + The Node coverage gate also checks that browser smoke keeps the same + generated WASM/native artifact tokens, fixed-count guards, duplicate-free + guards, fetchability/header guards, documentation-missing guards, completion + count-parity guards, review-document fetches, and native skipped-artifact + empty allowance as the Node inventory gate. + It also checks that `runtime-boundary-contract-summary.tsv` and + `runtime-boundary-native-alignment-summary.tsv` agree on boundary kind, + runtime alignment artifact, alignment row count, and disabled execution and + promotion state. + It also cross-checks user-M, tool DB, and Python family-specific + contract/readiness/probe artifacts against `runtime-boundary-contract-summary.tsv` + so browser validation sees the same runtime-family contract alignment as + Node inventory without executing blocked runtime processes. + Browser smoke also cross-checks `native-proof-alignment-summary.tsv`, + `native-runtime-probe-summary.tsv`, and `runtime-probe-gate-alignment.tsv` + for source-proof readiness, runtime readiness, required native proof, target, + and disabled execution/promotion parity. + It also cross-checks the native runtime probe execution plan, pass evidence + contract, host preflight, dispatch plan, and skip-evidence contract so the + browser smoke sees the same host-blocked opt-in command, missing + requirements, evidence status, and disabled execution/promotion state as the + Node inventory. + It also cross-checks the host-readiness, dispatch, and skip-evidence rollups + for counts, blocked families, missing requirements, blocked commands, + host/dispatch/evidence status, and disabled execution/promotion parity. + It also cross-checks the promotion lock, promotion readiness, promotion + blockers, post-native-pass gates, native evidence acceptance gate, and pass + evidence contract so browser validation sees the same locked promotion state + as Node inventory. + It also cross-checks recommendations, execution plans, host preflight, + family readiness, dispatch plans, skip-evidence contracts, and evidence + acceptance gates so browser validation sees the same opt-in command, proof, + missing requirement, and disabled execution/promotion state as Node inventory. + It also checks recommendation artifact row counts against the generated + user-M probe-gate, tool DB probe-gate, and Python runtime-contract source + rows, so priority handoffs cannot drift from the visible source artifacts. + It also checks each recommendation primary artifact and source artifact list + against the generated WASM inventory artifact set, including duplicate + detection, primary-artifact traceability, and exact per-family source + artifact sets. The Node coverage gate also verifies that browser smoke passes + the `wasmArtifactNames` returned by artifact documentation coverage into this + recommendation source-artifact coverage check. - `native-proof-alignment-summary.tsv`: one row per native source proof class. It aligns the native source proof summary with generated worklist/native proof-gate consumers for user-M, tool DB, and Python runtime blockers. @@ -217,6 +317,115 @@ Legend: LinuxCNC owner set and runtime owner evidence, records native, Node, and browser proof requirements, and names the next boundary-design action before any blocked family can be promoted. +- `next-boundary-recommendations.tsv`: the generated priority queue for the + next blocked runtime-boundary work. It currently ranks millturn `M128/M129` + state proof, tool DB protocol proof, and Python remap lifecycle proof, while + preserving active promotion locks and disabled execution. The Node inventory + verifies exact per-family source artifact sets, duplicate-free source lists, + primary-artifact traceability, and generated artifact existence for each + recommendation. +- `native-runtime-probe-execution-plan.tsv`: the guarded opt-in execution + plan for blocked runtime native probes. It records the exact command, + required native proof key, current runtime readiness, expected pass status, + missing requirements, gate alignment, and promotion prerequisites. It is not + a probe runner and does not promote rows. +- `runtime-boundary-native-evidence-acceptance-gate.tsv`: one row per blocked + runtime family deciding whether native pass evidence may be accepted. While + probes are skipped for missing host requirements, evidence acceptance is + blocked and Node/browser gates remain blocked. +- `runtime-boundary-promotion-blockers.tsv`: the expanded blocker summary for + non-ready promotions. Current rows record missing host runtime, native + probe not passed, native evidence not ready, Node/browser gates incomplete, + active promotion lock, and manual lock update required. +- The Node inventory also performs a cross-artifact consistency guard across + the blocked runtime opt-in target set. The guard requires the + recommendations, execution plan, host preflight, family readiness, dispatch + plan, skip/evidence contract, native evidence acceptance gate, promotion + readiness, promotion blockers, and post-native-pass gates to agree on the + target, proof keys, opt-in command, missing host requirements, active + promotion lock, and disabled execution/promotion fields. +- The Node inventory also checks documentation coverage for generated + sim-config inventory TSVs. Every artifact emitted under + `build/wasm/sim-configs-inventory/` must be referenced by the current + compatibility, matrix, or full-process boundary docs, so generated gate + artifacts cannot silently appear without review text. The generated WASM + inventory artifact list must remain the exact duplicate-free 54-entry + baseline, and every token plus the fixed-count, duplicate-free, and + fetchability guards must also be listed by browser smoke. +- The same inventory check covers native generated TSV artifacts under + `build/native/` by relative path, including native source-proof, + runtime-probe, `nc_files`, and sim-config baseline artifacts. The native + generated TSV token list must remain the exact duplicate-free 8-token + baseline, and every token plus the fixed-count, duplicate-free, fetchability, + and empty-skipped allowance guards must also be listed by browser smoke. +- Browser smoke mirrors this documentation coverage by fetching the current + compatibility, matrix, and full-process boundary docs and checking the + generated WASM inventory artifact names plus native TSV tokens against the + same completion-summary counts. The Node coverage gate also locks the two + documentation-coverage completion counts to the current `54` WASM inventory + artifacts and `8` native TSV artifacts. It verifies that browser smoke + executes the artifact documentation coverage helper, calls the completion + count-parity helper with the generated completion rows, fetches and joins the + exact compatibility, matrix, and full-process boundary review-document set + used for the missing-reference checks, requires those checks to iterate + `wasmArtifactNames` and `nativeArtifactTokens` against the joined + `documentationText`, fetches generated WASM TSVs by artifact name, fetches + native TSVs by relative artifact token, binds the documentation-coverage + completion counts to the returned `wasmArtifactNames` and + `nativeArtifactTokens` arrays, and keeps completion evidence reviewable when + browser-side count parity is checked. Browser smoke also requires a + non-empty TSV header, duplicate-free expected lists, and fixed artifact/token + counts. This is + documentation/artifact fetchability parity only and does not execute blocked + runtime probes. The native + `build/native/sim-configs/skipped.tsv` artifact may be empty when the native + strict sim-config baseline has no skipped rows. +- The Node inventory also checks native source proof consistency. Native + source proof alignment rows, the native runtime probe summary, and runtime + probe gate alignment must cover the same blocked classes, agree that source + proof is ready, and keep execution and promotion disabled. +- `runtime-boundary-host-preflight.tsv`: the host-facing preflight for the + guarded blocked-runtime native probes. It records each blocked family, + required host/runtime commands, opt-in environment variable, exact native + probe command, current probe status, and keeps execution and promotion + disabled. It does not run the probes. +- `runtime-boundary-host-requirement-summary.tsv`: a normalized requirement + table for the blocked runtime probes. It records each required host command + or source/module, its availability, affected blocked families, affected + opt-in probe environment variables, and whether the missing requirement + currently blocks execution. +- `runtime-boundary-host-unblock-plan.tsv`: the filtered plan for unavailable + host requirements. It records the affected blocked families, affected + opt-in commands, and remaining missing requirements after each single + requirement is provided. It is advisory only and does not install anything + or allow promotion. +- `runtime-boundary-family-host-readiness.tsv`: one host-readiness row per + blocked runtime family. It records missing requirements, available + prerequisites, the opt-in command, current probe/preflight status, and keeps + `execution_enabled=0` and `promotion_allowed=0`. +- Browser smoke cross-checks the host requirement summary, unblock plan, + family host readiness, and host preflight rows. The check verifies + unavailable requirement ownership, available prerequisite ownership, + affected opt-in environments, and exact probe command traceability while + remaining non-executing and non-promoting. +- `runtime-boundary-host-readiness-rollup.tsv`: the aggregate host-readiness + decision for all blocked runtime families. On this host it records zero + ready families, missing `halcmd`, `halrun`, `linuxcnc`, and `milltask`, and + `host_blocked_for_all_opt_in_native_probes`. +- `runtime-boundary-opt-in-probe-dispatch-plan.tsv`: one dispatch row per + guarded native runtime probe. It turns host readiness into a dispatch action + and remains non-promoting; a dispatchable row only means the manual opt-in + native probe may be attempted on a ready host. +- `runtime-boundary-opt-in-probe-dispatch-rollup.tsv`: the aggregate dispatch + switch for blocked runtime probes. Current rows are blocked for missing host + requirements, so no opt-in native probe is dispatchable on this host. +- `runtime-boundary-opt-in-probe-skip-evidence-contract.tsv`: the per-probe + skip/evidence contract. It records the skip reason, missing host + requirements, current probe status, whether native pass evidence is required + now, and keeps every skipped probe non-executing and non-promoting. +- `runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv`: the aggregate + evidence decision for skipped opt-in probes. While all probes are skipped for + missing host requirements, no native pass evidence is accepted. ## Summary diff --git a/wasm-port/tests/browser/interp_smoke.html b/wasm-port/tests/browser/interp_smoke.html index 2ef119b..351bd72 100644 --- a/wasm-port/tests/browser/interp_smoke.html +++ b/wasm-port/tests/browser/interp_smoke.html @@ -77,6 +77,119 @@ }); } + async function verifyBrowserGeneratedArtifactDocumentationCoverage() { + const documentationText = [ + await fetchText("../../docs/compatibility-validation.md"), + await fetchText("../../docs/sim-configs-coverage-matrix.md"), + await fetchText("../../docs/full-process-boundary-design.md"), + ].join("\n"); + const wasmArtifactNames = [ + "blocked-dependency-summary.tsv", + "blocked-runtime-promotion-lock.tsv", + "boundary-phase-completion-summary.tsv", + "boundary-proof-gates.tsv", + "boundary-summary.tsv", + "full-process-boundary-summary.tsv", + "ini-boundary-summary.tsv", + "native-proof-alignment-summary.tsv", + "native-runtime-probe-execution-plan.tsv", + "native-runtime-probe-pass-evidence-contract.tsv", + "native-runtime-probe-summary.tsv", + "next-boundary-recommendations.tsv", + "next-boundary-worklist.tsv", + "python-remap-boundary-summary.tsv", + "python-remap-family-summary.tsv", + "python-remap-native-runtime-alignment.tsv", + "python-remap-native-runtime-fixture-plan.tsv", + "python-remap-native-runtime-probe-gate.tsv", + "python-remap-native-runtime-readiness.tsv", + "python-remap-native-runtime-state-plan.tsv", + "python-remap-runtime-contract.tsv", + "python-remap-runtime-gates.tsv", + "runtime-boundary-contract-summary.tsv", + "runtime-boundary-family-host-readiness.tsv", + "runtime-boundary-host-preflight.tsv", + "runtime-boundary-host-readiness-rollup.tsv", + "runtime-boundary-host-requirement-summary.tsv", + "runtime-boundary-host-unblock-plan.tsv", + "runtime-boundary-native-alignment-summary.tsv", + "runtime-boundary-native-evidence-acceptance-gate.tsv", + "runtime-boundary-opt-in-probe-dispatch-plan.tsv", + "runtime-boundary-opt-in-probe-dispatch-rollup.tsv", + "runtime-boundary-opt-in-probe-skip-evidence-contract.tsv", + "runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv", + "runtime-boundary-post-native-pass-gates.tsv", + "runtime-boundary-promotion-blockers.tsv", + "runtime-boundary-promotion-readiness.tsv", + "runtime-probe-gate-alignment.tsv", + "skip-summary.tsv", + "summary.tsv", + "tool-db-process-boundary-summary.tsv", + "tool-db-process-native-protocol-alignment.tsv", + "tool-db-process-native-runtime-probe-gate.tsv", + "tool-db-process-native-runtime-readiness.tsv", + "tool-db-process-protocol-gates.tsv", + "tool-db-process-transaction-plan.tsv", + "user-m-process-boundary-summary.tsv", + "user-m-process-native-runtime-probe-gate.tsv", + "user-m-process-native-runtime-readiness.tsv", + "user-m-process-native-runtime-state-plan.tsv", + "user-m-process-native-state-alignment.tsv", + "user-m-process-native-transition-alignment.tsv", + "user-m-process-state-targets.tsv", + "user-m-process-transition-plan.tsv", + ].sort(); + const nativeArtifactTokens = [ + "build/native/native-runtime-probe-summary.tsv", + "build/native/native-source-proof-summary.tsv", + "build/native/nc-files/summary.tsv", + "build/native/sim-configs/class-summary.tsv", + "build/native/sim-configs/path-matrix.tsv", + "build/native/sim-configs/skipped.tsv", + "build/native/sim-configs/summary.tsv", + "build/native/source-probes.tsv", + ]; + + if (wasmArtifactNames.length !== 54 || new Set(wasmArtifactNames).size !== wasmArtifactNames.length) { + throw new Error("browser_generated_artifact_documentation_coverage: WASM artifact list drift"); + } + if (nativeArtifactTokens.length !== 8 || new Set(nativeArtifactTokens).size !== nativeArtifactTokens.length) { + throw new Error("browser_native_artifact_documentation_coverage: native artifact token list drift"); + } + + const emptyNativeArtifactTokensAllowed = new Set([ + "build/native/sim-configs/skipped.tsv", + ]); + for (const artifactName of wasmArtifactNames) { + const artifactText = await fetchText(`../../build/wasm/sim-configs-inventory/${artifactName}`); + if (!artifactText.includes("\t") || artifactText.trim().split("\n")[0].length === 0) { + throw new Error(`browser_generated_artifact_fetchability: invalid ${artifactName}`); + } + } + for (const artifactToken of nativeArtifactTokens) { + const artifactText = await fetchText(`../../${artifactToken}`); + if (artifactText.length === 0 && emptyNativeArtifactTokensAllowed.has(artifactToken)) { + continue; + } + if (!artifactText.includes("\t") || artifactText.trim().split("\n")[0].length === 0) { + throw new Error(`browser_native_artifact_fetchability: invalid ${artifactToken}`); + } + } + + for (const artifactName of wasmArtifactNames) { + if (!documentationText.includes(artifactName)) { + throw new Error(`browser_generated_artifact_documentation_coverage: missing ${artifactName}`); + } + } + for (const artifactToken of nativeArtifactTokens) { + if (!documentationText.includes(artifactToken)) { + throw new Error(`browser_native_artifact_documentation_coverage: missing ${artifactToken}`); + } + } + + return { nativeArtifactTokens, wasmArtifactNames }; + } + async function verifyBrowserBoundaryPhaseCompletion() { const rows = parseTsv( await fetchText("../../build/wasm/sim-configs-inventory/boundary-phase-completion-summary.tsv"), @@ -91,12 +204,28 @@ "runtime_native_alignment_artifacts", "native_runtime_probe_summary", "runtime_probe_gate_alignment", + "blocked_runtime_worklist_recommendation_consistency", "native_runtime_probe_execution_plan", "native_runtime_probe_pass_evidence_contract", + "blocked_runtime_probe_execution_consistency", "runtime_boundary_promotion_readiness", "runtime_boundary_promotion_blockers", "runtime_boundary_post_native_pass_gates", + "blocked_runtime_promotion_gate_consistency", "runtime_boundary_host_preflight", + "runtime_boundary_host_requirement_summary", + "runtime_boundary_host_unblock_plan", + "runtime_boundary_family_host_readiness", + "blocked_runtime_host_requirement_consistency", + "runtime_boundary_host_readiness_rollup", + "runtime_boundary_opt_in_probe_dispatch_plan", + "runtime_boundary_opt_in_probe_dispatch_rollup", + "runtime_boundary_opt_in_probe_skip_evidence_contract", + "runtime_boundary_opt_in_probe_skip_evidence_rollup", + "blocked_runtime_rollup_consistency", + "runtime_boundary_native_evidence_acceptance_gate", + "blocked_runtime_opt_in_gate_consistency", + "native_source_proof_runtime_probe_consistency", "user_m_transition_contract", "user_m_native_runtime_state_plan", "user_m_native_runtime_readiness", @@ -110,6 +239,9 @@ "python_native_runtime_state_plan", "python_native_runtime_fixture_plan", "runtime_contract_summary", + "runtime_family_contract_alignment_consistency", + "wasm_inventory_artifact_documentation_coverage", + "native_artifact_documentation_coverage", ]; const actualCriteria = rows.map((row) => row.criterion).join(","); if (actualCriteria !== expectedCriteria.join(",")) { @@ -126,6 +258,28 @@ throw new Error(`${row.criterion}: missing completion evidence`); } } + + return rows; + } + + function verifyBrowserBoundaryPhaseCompletionCountParity(completionRows, expectedCounts) { + const completionByCriterion = new Map(completionRows.map((row) => [row.criterion, row])); + + for (const [criterion, expectedCount] of expectedCounts) { + const row = completionByCriterion.get(criterion); + if (!row) { + throw new Error(`browser_boundary_phase_completion_count_parity: missing ${criterion}`); + } + if (row.passed !== "1") { + throw new Error(`${criterion}: completion criterion must pass before count parity`); + } + if (row.count !== String(expectedCount)) { + throw new Error(`${criterion}: completion count ${row.count} does not match browser artifact count ${expectedCount}`); + } + if (row.evidence === "-" || row.evidence.length < 12) { + throw new Error(`${criterion}: completion evidence is not reviewable`); + } + } } async function verifyBrowserIniBoundarySummary() { @@ -224,6 +378,143 @@ throw new Error(`${row.target}: Python browser proof gate must avoid full-process claim`); } } + + return rows; + } + + async function verifyBrowserNextBoundaryWorklist() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/next-boundary-worklist.tsv"), + ); + const expectedFirstRows = [ + ["external_user_m_process", "axis/vismach/millturn/example.ngc", "L4-USER-M-PROCESS"], + ["tool_database_process", "axis/db_demo/base.ngc", "L4-TOOL-DB"], + ]; + + if (rows.length < expectedFirstRows.length) { + throw new Error(`browser_next_boundary_worklist: too few rows ${rows.length}`); + } + const priorities = rows.map((row) => Number(row.priority)); + for (let index = 1; index < priorities.length; index += 1) { + if (priorities[index] < priorities[index - 1]) { + throw new Error("browser_next_boundary_worklist: priority order drift"); + } + } + + for (const [index, expected] of expectedFirstRows.entries()) { + const row = rows[index]; + if ( + row.boundary_kind !== expected[0] || + row.target !== expected[1] || + row.blocked !== expected[2] + ) { + throw new Error(`browser_next_boundary_worklist: row ${index + 1} identity drift`); + } + } + + for (const row of rows) { + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${row.target}: next boundary worklist must remain disabled`); + } + if ( + row.linuxcnc_owner === "-" || + row.runtime_owner_evidence === "-" || + row.required_native_proof === "-" || + row.required_node_proof === "-" || + row.required_browser_proof === "-" || + row.next_action === "-" + ) { + throw new Error(`${row.target}: next boundary worklist lacks traceability`); + } + if (row.boundary_kind === "python_runtime") { + if ( + row.blocked !== "L4-PYTHON-REMAP" || + row.design_status !== "inventory_only" || + row.next_action !== "design_linuxcnc_python_runtime_boundary" || + !row.notes.includes("dependency_inventory_only_no_execution") + ) { + throw new Error(`${row.target}: Python worklist row must remain inventory-only`); + } + } + } + + return rows; + } + + function verifyBrowserNextBoundaryWorklistProofGateParity({ + worklistRows, + proofGateRows, + promotionLockRows, + }) { + const expectedLayers = ["browser", "native", "node"]; + const proofRowsByTarget = new Map(); + for (const row of proofGateRows) { + const targetRows = proofRowsByTarget.get(row.target) || []; + targetRows.push(row); + proofRowsByTarget.set(row.target, targetRows); + } + const lockByTarget = new Map(promotionLockRows.map((row) => [row.target, row])); + + if (proofGateRows.length !== worklistRows.length * expectedLayers.length) { + throw new Error("browser_next_boundary_worklist_proof_gate_parity: proof gate count drift"); + } + + for (const worklist of worklistRows) { + const proofRows = proofRowsByTarget.get(worklist.target) || []; + const layers = proofRows.map((row) => row.proof_layer).sort(); + if (layers.join(",") !== expectedLayers.join(",")) { + throw new Error(`${worklist.target}: proof gate layer set drift`); + } + const lock = lockByTarget.get(worklist.target); + if (!lock) { + throw new Error(`${worklist.target}: promotion lock row missing`); + } + if ( + lock.boundary_kind !== worklist.boundary_kind || + lock.blocked !== worklist.blocked || + lock.design_status !== worklist.design_status || + lock.next_action !== worklist.next_action || + lock.lock_active !== "1" + ) { + throw new Error(`${worklist.target}: worklist/promotion lock drift`); + } + if (lock.execution_enabled !== "0" || lock.promotion_allowed !== "0") { + throw new Error(`${worklist.target}: promotion lock must remain disabled`); + } + + for (const proof of proofRows) { + if ( + proof.priority !== worklist.priority || + proof.boundary_kind !== worklist.boundary_kind || + proof.blocked !== worklist.blocked || + proof.design_status !== worklist.design_status || + proof.linuxcnc_owner !== worklist.linuxcnc_owner || + proof.runtime_owner_evidence !== worklist.runtime_owner_evidence || + proof.next_action !== worklist.next_action + ) { + throw new Error(`${worklist.target}/${proof.proof_layer}: worklist/proof gate metadata drift`); + } + if ( + proof.proof_status !== "pending" || + proof.execution_enabled !== "0" || + proof.promotion_allowed !== "0" + ) { + throw new Error(`${worklist.target}/${proof.proof_layer}: proof gate must remain pending and disabled`); + } + if (proof.proof_layer === "native" && proof.required_proof !== worklist.required_native_proof) { + throw new Error(`${worklist.target}: native proof gate drift`); + } + if (proof.proof_layer === "node" && proof.required_proof !== worklist.required_node_proof) { + throw new Error(`${worklist.target}: Node proof gate drift`); + } + if ( + proof.proof_layer === "browser" && + !proof.required_proof.split(",").includes(worklist.required_browser_proof.split(",")[0]) + ) { + throw new Error(`${worklist.target}: browser proof gate drift`); + } + } + } } async function verifyBrowserBoundarySummary() { @@ -395,6 +686,8 @@ throw new Error(`${code}: transition plan must record non-execution policy`); } } + + return rows; } async function verifyBrowserUserMNativeTransitionAlignment() { @@ -534,6 +827,8 @@ throw new Error(`${remapCode}: runtime state plan must keep millturn blocked`); } } + + return rows; } async function verifyBrowserUserMNativeRuntimeReadiness() { @@ -584,6 +879,8 @@ throw new Error(`${row.requirement}: runtime readiness must keep blocked state`); } } + + return rows; } async function verifyBrowserUserMNativeRuntimeProbeGate() { @@ -644,6 +941,8 @@ } } } + + return rows; } async function verifyBrowserToolDbTransactionPlan() { @@ -715,6 +1014,8 @@ throw new Error(`${row.transaction_phase}: tool DB transaction must record blocked execution policy`); } } + + return rows; } async function verifyBrowserToolDbNativeRuntimeReadiness() { @@ -776,6 +1077,8 @@ throw new Error(`${row.requirement}: tool DB readiness must keep blocked state`); } } + + return rows; } async function verifyBrowserToolDbNativeRuntimeProbeGate() { @@ -867,6 +1170,8 @@ ) { throw new Error("missing-runtime tool DB probe gate drift"); } + + return rows; } async function verifyBrowserPythonRuntimeContract() { @@ -930,6 +1235,8 @@ throw new Error(`${row.family}: Python runtime contract must record non-execution policy`); } } + + return rows; } async function verifyBrowserPythonNativeRuntimeReadiness() { @@ -1018,6 +1325,8 @@ throw new Error(`${family}: Python readiness lacks configured module rows`); } } + + return rows; } async function verifyBrowserPythonNativeRuntimeProbeGate() { @@ -1103,6 +1412,8 @@ ) { throw new Error(`${row.family}: missing-runtime Python probe gate drift`); } + + return rows; } async function verifyBrowserPythonNativeRuntimeStatePlan() { @@ -1195,6 +1506,8 @@ throw new Error(`${row.family}: Python state plan must keep blocked state`); } } + + return rows; } async function verifyBrowserPythonNativeRuntimeFixturePlan() { @@ -1275,6 +1588,8 @@ if (!row.notes.includes("fixture_plan_only_no_python_execution_no_promotion")) { throw new Error(`${row.family}: Python fixture must remain non-executing`); } + + return rows; } async function verifyBrowserNativeProofAlignment() { @@ -1338,6 +1653,8 @@ throw new Error(`${boundaryClass}: native proof lacks generated gate consumer`); } } + + return rows; } async function verifyBrowserRuntimeNativeAlignmentSummary() { @@ -1508,6 +1825,8 @@ throw new Error(`${row.boundary_class}: native runtime probe lacks stdout evidence`); } } + + return rows; } async function verifyBrowserRuntimeProbeGateAlignment() { @@ -1587,6 +1906,82 @@ throw new Error(`${row.boundary_class}: runtime probe gate alignment notes drift`); } } + + return rows; + } + + function verifyBrowserNativeSourceProofRuntimeProbeParity( + nativeProofRows, + nativeRuntimeProbeRows, + runtimeProbeGateAlignmentRows, + ) { + const expectedClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + const proofByClass = new Map(nativeProofRows.map((row) => [row.boundary_class, row])); + const runtimeByClass = new Map(nativeRuntimeProbeRows.map((row) => [row.boundary_class, row])); + const gateByClass = new Map(runtimeProbeGateAlignmentRows.map((row) => [row.boundary_class, row])); + + if ( + nativeProofRows.map((row) => row.boundary_class).join(",") !== expectedClasses.join(",") || + nativeRuntimeProbeRows.map((row) => row.boundary_class).join(",") !== expectedClasses.join(",") || + runtimeProbeGateAlignmentRows.map((row) => row.boundary_class).join(",") !== expectedClasses.join(",") + ) { + throw new Error("browser_native_source_proof_runtime_probe_parity: class order drift"); + } + + for (const boundaryClass of expectedClasses) { + const proof = proofByClass.get(boundaryClass); + const runtime = runtimeByClass.get(boundaryClass); + const gate = gateByClass.get(boundaryClass); + if (!proof || !runtime || !gate) { + throw new Error(`${boundaryClass}: missing native proof/runtime/gate parity row`); + } + if (runtime.boundary_kind !== proof.boundary_kind || gate.boundary_kind !== runtime.boundary_kind) { + throw new Error(`${boundaryClass}: native proof/runtime/gate boundary kind drift`); + } + if (proof.target !== "*" && runtime.target !== proof.target) { + throw new Error(`${boundaryClass}: native proof/runtime target drift`); + } + if (gate.target !== runtime.target) { + throw new Error(`${boundaryClass}: runtime gate target drift`); + } + if ( + proof.alignment_ok !== "1" || + runtime.source_proof_ready !== proof.alignment_ok || + gate.native_source_proof_ready !== proof.alignment_ok || + gate.gate_source_proof_ready !== proof.alignment_ok + ) { + throw new Error(`${boundaryClass}: native source proof readiness drift`); + } + if ( + runtime.required_native_proof !== gate.native_required_native_proof || + gate.required_native_proof_match !== "1" + ) { + throw new Error(`${boundaryClass}: native runtime proof requirement drift`); + } + if ( + runtime.runtime_ready !== gate.native_runtime_ready || + gate.gate_runtime_ready !== gate.native_runtime_ready || + gate.missing_requirements_match !== "1" || + gate.status_compatible !== "1" + ) { + throw new Error(`${boundaryClass}: native runtime readiness/status drift`); + } + if ( + proof.execution_enabled !== "0" || + proof.promotion_allowed !== "0" || + runtime.execution_enabled !== "0" || + runtime.promotion_allowed !== "0" || + gate.execution_disabled_match !== "1" || + gate.promotion_disabled_match !== "1" || + gate.alignment_ok !== "1" + ) { + throw new Error(`${boundaryClass}: native proof/runtime/gate parity must remain disabled`); + } + } } async function verifyBrowserNativeRuntimeProbeExecutionPlan() { @@ -1688,6 +2083,8 @@ throw new Error(`${row.boundary_class}: native runtime execution plan notes drift`); } } + + return rows; } async function verifyBrowserNativeRuntimeProbePassEvidenceContract() { @@ -1790,6 +2187,8 @@ throw new Error(`${row.boundary_class}: native pass evidence notes drift`); } } + + return rows; } async function verifyBrowserRuntimeBoundaryContractSummary() { @@ -1803,6 +2202,7 @@ boundaryKind: "external_user_m_process", contractArtifact: "user-m-process-transition-plan.tsv", rowCount: "2", + runtimeAlignmentArtifact: "user-m-process-native-state-alignment.tsv", }, ], [ @@ -1811,6 +2211,7 @@ boundaryKind: "tool_database_process", contractArtifact: "tool-db-process-transaction-plan.tsv", rowCount: "5", + runtimeAlignmentArtifact: "tool-db-process-native-protocol-alignment.tsv", }, ], [ @@ -1819,6 +2220,7 @@ boundaryKind: "python_runtime", contractArtifact: "python-remap-runtime-contract.tsv", rowCount: "12", + runtimeAlignmentArtifact: "python-remap-native-runtime-alignment.tsv", }, ], ]); @@ -1835,7 +2237,8 @@ if ( row.boundary_kind !== expectedRow.boundaryKind || row.contract_artifact !== expectedRow.contractArtifact || - row.contract_row_count !== expectedRow.rowCount + row.contract_row_count !== expectedRow.rowCount || + row.runtime_alignment_artifact !== expectedRow.runtimeAlignmentArtifact ) { throw new Error(`${row.boundary_class}: runtime contract summary metadata drift`); } @@ -1855,6 +2258,220 @@ throw new Error(`${row.boundary_class}: runtime contract summary must remain non-executing`); } } + + return rows; + } + + function verifyBrowserRuntimeContractNativeAlignmentParity(contractRows, nativeAlignmentRows) { + const nativeByClass = new Map(nativeAlignmentRows.map((row) => [row.boundary_class, row])); + if (contractRows.length !== nativeAlignmentRows.length) { + throw new Error("browser_runtime_contract_native_alignment_parity: row count drift"); + } + + for (const contract of contractRows) { + const native = nativeByClass.get(contract.boundary_class); + if (!native) { + throw new Error(`${contract.boundary_class}: missing native alignment summary row`); + } + if ( + contract.boundary_kind !== native.boundary_kind || + contract.runtime_alignment_artifact !== native.artifact || + contract.runtime_alignment_row_count !== native.row_count || + contract.runtime_alignment_ok !== native.summary_ok + ) { + throw new Error(`${contract.boundary_class}: contract/native alignment summary drift`); + } + if (contract.contract_ok !== "1" || native.summary_ok !== "1") { + throw new Error(`${contract.boundary_class}: contract/native alignment summary must remain OK`); + } + if ( + contract.execution_disabled_count !== contract.contract_row_count || + contract.promotion_disabled_count !== contract.contract_row_count || + native.execution_disabled_count !== native.row_count || + native.promotion_disabled_count !== native.row_count + ) { + throw new Error(`${contract.boundary_class}: contract/native alignment must remain disabled`); + } + } + } + + function verifyBrowserRuntimeFamilyContractAlignmentParity({ + userMTransitionRows, + userMRuntimeStatePlanRows, + userMRuntimeReadinessRows, + userMRuntimeProbeGateRows, + toolDbTransactionRows, + toolDbRuntimeReadinessRows, + toolDbRuntimeProbeGateRows, + pythonRuntimeContractRows, + pythonRuntimeReadinessRows, + pythonRuntimeProbeGateRows, + pythonRuntimeStatePlanRows, + pythonRuntimeFixturePlanRows, + runtimeContractSummaryRows, + }) { + const unavailableRequirements = (rows) => + rows.filter((row) => row.available === "0").map((row) => row.requirement).join(","); + const contractByClass = new Map(runtimeContractSummaryRows.map((row) => [row.boundary_class, row])); + const requireDisabled = (row, label) => { + if (row.proof_status !== "pending" || row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${label}: family contract alignment must remain pending and disabled`); + } + }; + + const userMContract = contractByClass.get("L4-USER-M-PROCESS"); + if ( + !userMContract || + userMContract.contract_artifact !== "user-m-process-transition-plan.tsv" || + userMContract.contract_row_count !== String(userMTransitionRows.length) || + userMContract.contract_ok !== "1" + ) { + throw new Error("browser_runtime_family_contract_alignment: user-M contract summary drift"); + } + if ( + userMRuntimeStatePlanRows.length !== userMTransitionRows.length || + userMRuntimeProbeGateRows.length !== userMTransitionRows.length + ) { + throw new Error("browser_runtime_family_contract_alignment: user-M row count drift"); + } + const userMTransitionByRemap = new Map(userMTransitionRows.map((row) => [row.remap_code, row])); + const userMProbeByRemap = new Map(userMRuntimeProbeGateRows.map((row) => [row.remap_code, row])); + const userMMissingRequirements = unavailableRequirements(userMRuntimeReadinessRows); + for (const plan of userMRuntimeStatePlanRows) { + const transition = userMTransitionByRemap.get(plan.remap_code); + const probe = userMProbeByRemap.get(plan.remap_code); + if (!transition || !probe) { + throw new Error(`${plan.remap_code}: user-M family alignment missing transition/probe row`); + } + for (const field of [ + "path", + "ini", + "blocked", + "user_m_code", + "switchkins_target", + "active_g5x", + "work_offset_pocket", + "expected_state_pins", + ]) { + if (plan[field] !== transition[field]) { + throw new Error(`${plan.remap_code}: user-M family alignment ${field} drift`); + } + } + if ( + probe.required_runtime !== plan.required_runtime || + probe.required_native_proof !== plan.proof_kind || + probe.source_proof_ready !== "1" || + probe.missing_requirements !== userMMissingRequirements + ) { + throw new Error(`${plan.remap_code}: user-M family probe/readiness drift`); + } + requireDisabled(transition, `${plan.remap_code}: user-M transition`); + requireDisabled(plan, `${plan.remap_code}: user-M runtime plan`); + requireDisabled(probe, `${plan.remap_code}: user-M probe gate`); + } + + const toolDbContract = contractByClass.get("L4-TOOL-DB"); + if ( + !toolDbContract || + toolDbContract.contract_artifact !== "tool-db-process-transaction-plan.tsv" || + toolDbContract.contract_row_count !== String(toolDbTransactionRows.length) || + toolDbContract.contract_ok !== "1" || + toolDbRuntimeProbeGateRows.length !== 1 + ) { + throw new Error("browser_runtime_family_contract_alignment: tool DB contract summary drift"); + } + const toolDbProbe = toolDbRuntimeProbeGateRows[0]; + if (toolDbProbe.missing_requirements !== unavailableRequirements(toolDbRuntimeReadinessRows)) { + throw new Error("browser_runtime_family_contract_alignment: tool DB missing requirements drift"); + } + for (const transaction of toolDbTransactionRows) { + if ( + transaction.db_program !== toolDbProbe.db_program || + !toolDbProbe.protocol_transactions.includes(`${transaction.transaction_phase}:${transaction.protocol_message}`) + ) { + throw new Error(`${transaction.transaction_phase}: tool DB family transaction/probe drift`); + } + requireDisabled(transaction, `${transaction.transaction_phase}: tool DB transaction`); + } + if ( + toolDbProbe.required_native_proof !== "native_db_process_protocol_probe_required" || + toolDbProbe.source_proof_ready !== "1" + ) { + throw new Error("browser_runtime_family_contract_alignment: tool DB proof drift"); + } + requireDisabled(toolDbProbe, "tool DB probe gate"); + + const pythonContract = contractByClass.get("L4-PYTHON-REMAP"); + if ( + !pythonContract || + pythonContract.contract_artifact !== "python-remap-runtime-contract.tsv" || + pythonContract.contract_row_count !== String(pythonRuntimeContractRows.length) || + pythonContract.contract_ok !== "1" || + pythonRuntimeStatePlanRows.length !== pythonRuntimeContractRows.length || + pythonRuntimeProbeGateRows.length !== 1 || + pythonRuntimeFixturePlanRows.length !== 1 + ) { + throw new Error("browser_runtime_family_contract_alignment: Python contract summary drift"); + } + const pythonContractByFamily = new Map(pythonRuntimeContractRows.map((row) => [row.family, row])); + const pythonStateByFamily = new Map(pythonRuntimeStatePlanRows.map((row) => [row.family, row])); + for (const contract of pythonRuntimeContractRows) { + const statePlan = pythonStateByFamily.get(contract.family); + if (!statePlan) { + throw new Error(`${contract.family}: Python family state plan missing`); + } + for (const field of [ + "blocked", + "runtime_phases", + "python_modules", + "python_callables", + "ngc_only_subpaths", + "process_assumptions", + ]) { + if (statePlan[field] !== contract[field]) { + throw new Error(`${contract.family}: Python family ${field} drift`); + } + } + requireDisabled(contract, `${contract.family}: Python contract`); + requireDisabled(statePlan, `${contract.family}: Python state plan`); + } + const pythonProbe = pythonRuntimeProbeGateRows[0]; + const pythonFixture = pythonRuntimeFixturePlanRows[0]; + const pythonFixtureContract = pythonContractByFamily.get(pythonFixture.family); + const pythonFixtureState = pythonStateByFamily.get(pythonFixture.family); + if (!pythonFixtureContract || !pythonFixtureState || pythonProbe.family !== pythonFixture.family) { + throw new Error("browser_runtime_family_contract_alignment: Python fixture/probe family drift"); + } + if ( + pythonFixture.runtime_phases !== pythonFixtureContract.runtime_phases || + pythonFixture.python_modules !== pythonFixtureContract.python_modules || + pythonFixture.process_assumptions !== pythonFixtureContract.process_assumptions || + pythonProbe.required_native_proof !== pythonFixture.proof_kind || + pythonProbe.source_proof_ready !== "1" + ) { + throw new Error("browser_runtime_family_contract_alignment: Python fixture/probe proof drift"); + } + const pythonFixtureMissingRequirements = pythonRuntimeReadinessRows + .filter((row) => row.family === pythonProbe.family && row.available === "0") + .map((row) => row.requirement) + .join(","); + if (pythonProbe.missing_requirements !== pythonFixtureMissingRequirements) { + throw new Error("browser_runtime_family_contract_alignment: Python missing requirements drift"); + } + requireDisabled(pythonFixture, "Python fixture plan"); + requireDisabled(pythonProbe, "Python probe gate"); + + for (const summary of runtimeContractSummaryRows) { + if ( + summary.pending_count !== summary.contract_row_count || + summary.execution_disabled_count !== summary.contract_row_count || + summary.promotion_disabled_count !== summary.contract_row_count || + summary.runtime_alignment_ok !== "1" || + summary.contract_ok !== "1" + ) { + throw new Error(`${summary.boundary_class}: runtime family contract summary status drift`); + } + } } async function verifyBrowserRuntimeNativeAlignmentDetails(summaryRows) { @@ -1934,6 +2551,8 @@ throw new Error(`${row.target}: Python promotion lock must remain inventory-only`); } } + + return rows; } async function verifyBrowserRuntimeBoundaryPromotionReadiness() { @@ -2013,6 +2632,8 @@ throw new Error(`${row.boundary_class}: promotion readiness notes drift`); } } + + return rows; } async function verifyBrowserRuntimeBoundaryPromotionBlockers() { @@ -2094,6 +2715,8 @@ throw new Error(`${row.boundary_class}: promotion blocker notes drift`); } } + + return rows; } async function verifyBrowserRuntimeBoundaryPostNativePassGates() { @@ -2183,6 +2806,8 @@ throw new Error(`${row.boundary_class}: post-native-pass notes drift`); } } + + return rows; } async function verifyBrowserRuntimeBoundaryHostPreflight() { @@ -2274,6 +2899,857 @@ throw new Error(`${row.boundary_class}: host preflight notes drift`); } } + + return rows; + } + + async function verifyBrowserRuntimeBoundaryHostRequirementSummary() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-host-requirement-summary.tsv"), + ); + const byRequirement = new Map(rows.map((row) => [row.requirement, row])); + const required = new Map([ + ["linuxcnc", { available: "0", requiredByCount: "3", missingFor: "L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS" }], + ["halcmd", { available: "0", requiredByCount: "2", missingFor: "L4-TOOL-DB,L4-USER-M-PROCESS" }], + ["milltask", { available: "0", requiredByCount: "1", missingFor: "L4-TOOL-DB" }], + ["python3", { available: "1", requiredByCount: "2", missingFor: "-" }], + ["tclsh", { available: "1", requiredByCount: "1", missingFor: "-" }], + ]); + + if (rows.length < 8) { + throw new Error(`browser_runtime_boundary_host_requirement_summary: row count drift ${rows.length}`); + } + + for (const [requirement, expected] of required) { + const row = byRequirement.get(requirement); + if (!row) { + throw new Error(`browser_runtime_boundary_host_requirement_summary: missing ${requirement}`); + } + if ( + row.available !== expected.available || + row.required_by_count !== expected.requiredByCount || + row.missing_for !== expected.missingFor + ) { + throw new Error(`${requirement}: host requirement summary drift`); + } + } + + for (const row of rows) { + if (!["host_command", "source_or_module", "runtime_input"].includes(row.requirement_kind)) { + throw new Error(`${row.requirement}: invalid host requirement kind`); + } + if (!["0", "1"].includes(row.available)) { + throw new Error(`${row.requirement}: invalid host requirement availability`); + } + if (!/^[1-9][0-9]*$/.test(row.required_by_count)) { + throw new Error(`${row.requirement}: invalid host requirement count`); + } + if (row.required_by === "-" || row.blocked_families === "-" || row.opt_in_envs === "-") { + throw new Error(`${row.requirement}: host requirement summary lacks traceability`); + } + if (row.available === "0") { + if (row.missing_for === "-" || row.missing_blocks_runtime !== "1") { + throw new Error(`${row.requirement}: unavailable host requirement must block runtime`); + } + } else if (row.missing_for !== "-" || row.missing_blocks_runtime !== "0") { + throw new Error(`${row.requirement}: available host requirement must not block runtime`); + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${row.requirement}: host requirement summary must not enable execution or promotion`); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error(`${row.requirement}: host requirement summary notes drift`); + } + } + + return rows; + } + + async function verifyBrowserRuntimeBoundaryHostUnblockPlan() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-host-unblock-plan.tsv"), + ); + const expected = new Map([ + ["linuxcnc", { missingFor: "L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS", remaining: "halcmd,halrun,milltask" }], + ["halcmd", { missingFor: "L4-TOOL-DB,L4-USER-M-PROCESS", remaining: "halrun,linuxcnc,milltask" }], + ["halrun", { missingFor: "L4-USER-M-PROCESS", remaining: "halcmd,linuxcnc" }], + ["milltask", { missingFor: "L4-TOOL-DB", remaining: "halcmd,linuxcnc" }], + ]); + + if (rows.length !== expected.size) { + throw new Error(`browser_runtime_boundary_host_unblock_plan: row count drift ${rows.length}`); + } + + for (const row of rows) { + const expectedRow = expected.get(row.requirement); + if (!expectedRow) { + throw new Error(`browser_runtime_boundary_host_unblock_plan: unexpected ${row.requirement}`); + } + if ( + row.requirement_kind !== "host_command" || + row.available !== "0" || + row.missing_for !== expectedRow.missingFor || + row.remaining_missing_after_requirement !== expectedRow.remaining + ) { + throw new Error(`${row.requirement}: host unblock plan metadata drift`); + } + if ( + row.affected_targets === "-" || + row.opt_in_envs === "-" || + row.affected_execution_commands === "-" + ) { + throw new Error(`${row.requirement}: host unblock plan lacks traceability`); + } + if ( + row.unblocks_when_available !== "partial_unblock_other_requirements_remain" || + row.next_action !== "provide_missing_host_requirement" + ) { + throw new Error(`${row.requirement}: host unblock plan status drift`); + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${row.requirement}: host unblock plan must not enable execution or promotion`); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error(`${row.requirement}: host unblock plan notes drift`); + } + } + + return rows; + } + + async function verifyBrowserRuntimeBoundaryFamilyHostReadiness() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-family-host-readiness.tsv"), + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + target: "axis/vismach/millturn/example.ngc", + missingCount: "3", + missing: "halrun,halcmd,linuxcnc", + available: "tclsh", + optInEnv: "ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1", + }, + ], + [ + "L4-TOOL-DB", + { + target: "axis/db_demo/base.ngc", + missingCount: "3", + missing: "linuxcnc,milltask,halcmd", + available: "python3,axis/db_demo/db_nonran.py,linuxcnc.so,tooldb.py", + optInEnv: "ENABLE_TOOL_DB_RUNTIME_PROBE=1", + }, + ], + [ + "L4-PYTHON-REMAP", + { + target: "axis/remap/stop-lookahead/nc_files", + missingCount: "1", + missing: "linuxcnc", + available: "python3,src/emc/rs274ngc/interp_python.cc,src/emc/pythonplugin/python_plugin.cc,axis/remap/stop-lookahead/python/remap.py,axis/remap/stop-lookahead/python/toplevel.py", + optInEnv: "ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1", + }, + ], + ]); + + if (rows.length !== expected.size) { + throw new Error(`browser_runtime_boundary_family_host_readiness: row count drift ${rows.length}`); + } + + for (const row of rows) { + const expectedRow = expected.get(row.boundary_class); + if (!expectedRow) { + throw new Error(`browser_runtime_boundary_family_host_readiness: unexpected ${row.boundary_class}`); + } + if ( + row.target !== expectedRow.target || + row.missing_requirement_count !== expectedRow.missingCount || + row.missing_host_requirements !== expectedRow.missing || + row.available_requirements !== expectedRow.available || + row.opt_in_env !== expectedRow.optInEnv + ) { + throw new Error(`${row.boundary_class}: family host readiness metadata drift`); + } + if ( + row.preflight_status !== "blocked_missing_host_runtime" || + row.family_host_status !== "blocked_missing_host_requirements" + ) { + throw new Error(`${row.boundary_class}: family host readiness status drift`); + } + if (!row.execution_command.includes(row.opt_in_env)) { + throw new Error(`${row.boundary_class}: family host readiness command drift`); + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${row.boundary_class}: family host readiness must not enable execution or promotion`); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error(`${row.boundary_class}: family host readiness notes drift`); + } + } + + return rows; + } + + function verifyBrowserBlockedRuntimeHostRequirementParity({ + hostPreflightRows, + requirementSummaryRows, + unblockPlanRows, + familyHostReadinessRows, + }) { + const splitList = (value) => value === "-" ? [] : value.split(","); + const preflightByClass = new Map(hostPreflightRows.map((row) => [row.boundary_class, row])); + const summaryByRequirement = new Map(requirementSummaryRows.map((row) => [row.requirement, row])); + const unblockByRequirement = new Map(unblockPlanRows.map((row) => [row.requirement, row])); + + for (const family of familyHostReadinessRows) { + const preflight = preflightByClass.get(family.boundary_class); + if (!preflight) { + throw new Error(`${family.boundary_class}: missing host preflight parity row`); + } + if ( + preflight.target !== family.target || + preflight.blocked !== family.blocked || + preflight.required_runtime !== family.required_runtime || + preflight.opt_in_env !== family.opt_in_env || + preflight.execution_command !== family.execution_command || + preflight.current_probe_status !== family.current_probe_status || + preflight.preflight_status !== family.preflight_status || + preflight.missing_requirements !== family.missing_host_requirements + ) { + throw new Error(`${family.boundary_class}: host preflight/family readiness parity drift`); + } + + const missingRequirements = splitList(family.missing_host_requirements); + const availableRequirements = splitList(family.available_requirements); + if (family.missing_requirement_count !== String(missingRequirements.length)) { + throw new Error(`${family.boundary_class}: missing host requirement count drift`); + } + if (family.available_requirement_count !== String(availableRequirements.length)) { + throw new Error(`${family.boundary_class}: available host requirement count drift`); + } + + for (const requirement of missingRequirements) { + const summary = summaryByRequirement.get(requirement); + const unblock = unblockByRequirement.get(requirement); + if (!summary || !unblock) { + throw new Error(`${family.boundary_class}: missing host requirement parity source ${requirement}`); + } + if ( + summary.available !== "0" || + summary.missing_blocks_runtime !== "1" || + !splitList(summary.missing_for).includes(family.boundary_class) || + !splitList(summary.blocked_families).includes(family.boundary_class) || + !splitList(summary.opt_in_envs).includes(family.opt_in_env) + ) { + throw new Error(`${family.boundary_class}/${requirement}: host requirement summary parity drift`); + } + if ( + unblock.available !== "0" || + !splitList(unblock.missing_for).includes(family.boundary_class) || + !splitList(unblock.affected_targets).includes(family.target) || + !splitList(unblock.opt_in_envs).includes(family.opt_in_env) || + !splitList(unblock.affected_execution_commands).includes(family.execution_command) || + splitList(unblock.remaining_missing_after_requirement).includes(requirement) + ) { + throw new Error(`${family.boundary_class}/${requirement}: host unblock plan parity drift`); + } + } + + for (const requirement of availableRequirements) { + const summary = summaryByRequirement.get(requirement); + if (!summary) { + throw new Error(`${family.boundary_class}: missing available requirement summary ${requirement}`); + } + if ( + summary.available !== "1" || + summary.missing_for !== "-" || + summary.missing_blocks_runtime !== "0" || + !splitList(summary.required_by).includes(family.boundary_class) || + !splitList(summary.blocked_families).includes(family.boundary_class) || + !splitList(summary.opt_in_envs).includes(family.opt_in_env) + ) { + throw new Error(`${family.boundary_class}/${requirement}: available host requirement parity drift`); + } + } + + for (const row of [preflight, family]) { + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${family.boundary_class}: host requirement parity must remain disabled`); + } + } + } + + for (const summary of requirementSummaryRows) { + if (summary.required_by_count !== String(splitList(summary.required_by).length)) { + throw new Error(`${summary.requirement}: host requirement required_by_count drift`); + } + if (summary.available === "0" && !unblockByRequirement.has(summary.requirement)) { + throw new Error(`${summary.requirement}: unavailable host requirement lacks unblock plan row`); + } + if (summary.available === "1" && unblockByRequirement.has(summary.requirement)) { + throw new Error(`${summary.requirement}: available host requirement must not have unblock plan row`); + } + if (summary.execution_enabled !== "0" || summary.promotion_allowed !== "0") { + throw new Error(`${summary.requirement}: host requirement parity must remain disabled`); + } + } + } + + async function verifyBrowserRuntimeBoundaryHostReadinessRollup() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv"), + ); + if (rows.length !== 1) { + throw new Error(`browser_runtime_boundary_host_readiness_rollup: row count drift ${rows.length}`); + } + + const row = rows[0]; + if ( + row.scope !== "blocked_runtime_host_readiness" || + row.family_count !== "3" || + row.host_ready_family_count !== "0" || + row.host_blocked_family_count !== "3" || + row.missing_host_requirement_count !== "4" || + row.ready_opt_in_command_count !== "0" || + row.ready_opt_in_commands !== "-" || + row.host_readiness_status !== "host_blocked_for_all_opt_in_native_probes" + ) { + throw new Error("host readiness rollup state drift"); + } + for (const requirement of ["halcmd", "halrun", "linuxcnc", "milltask"]) { + if (!row.missing_host_requirements.includes(requirement)) { + throw new Error(`host readiness rollup missing ${requirement}`); + } + } + for (const boundaryClass of ["L4-PYTHON-REMAP", "L4-TOOL-DB", "L4-USER-M-PROCESS"]) { + if (!row.blocked_families.includes(boundaryClass)) { + throw new Error(`host readiness rollup missing ${boundaryClass}`); + } + } + for (const command of [ + "ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh", + "ENABLE_TOOL_DB_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_tool_db_runtime.sh", + "ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh", + ]) { + if (!row.blocked_opt_in_commands.includes(command)) { + throw new Error(`host readiness rollup missing blocked command ${command}`); + } + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error("host readiness rollup must not enable execution or promotion"); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error("host readiness rollup notes drift"); + } + + return rows; + } + + async function verifyBrowserRuntimeBoundaryOptInProbeDispatchPlan() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-plan.tsv"), + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + target: "axis/vismach/millturn/example.ngc", + missing: "halrun,halcmd,linuxcnc", + optInEnv: "ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1", + }, + ], + [ + "L4-TOOL-DB", + { + target: "axis/db_demo/base.ngc", + missing: "linuxcnc,milltask,halcmd", + optInEnv: "ENABLE_TOOL_DB_RUNTIME_PROBE=1", + }, + ], + [ + "L4-PYTHON-REMAP", + { + target: "axis/remap/stop-lookahead/nc_files", + missing: "linuxcnc", + optInEnv: "ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1", + }, + ], + ]); + + if (rows.length !== expected.size) { + throw new Error(`browser_runtime_boundary_opt_in_probe_dispatch_plan: row count drift ${rows.length}`); + } + + for (const row of rows) { + const expectedRow = expected.get(row.boundary_class); + if (!expectedRow) { + throw new Error(`browser_runtime_boundary_opt_in_probe_dispatch_plan: unexpected ${row.boundary_class}`); + } + if ( + row.target !== expectedRow.target || + row.missing_host_requirements !== expectedRow.missing || + row.opt_in_env !== expectedRow.optInEnv + ) { + throw new Error(`${row.boundary_class}: opt-in probe dispatch metadata drift`); + } + if (!row.execution_command.includes(row.opt_in_env)) { + throw new Error(`${row.boundary_class}: opt-in probe dispatch command drift`); + } + if ( + row.family_host_status !== "blocked_missing_host_requirements" || + row.host_readiness_status !== "host_blocked_for_all_opt_in_native_probes" || + row.host_action !== "skip_missing_host_requirements" || + row.dispatch_allowed !== "0" + ) { + throw new Error(`${row.boundary_class}: opt-in probe dispatch status drift`); + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${row.boundary_class}: opt-in probe dispatch must not enable execution or promotion`); + } + if (!row.next_action.includes(row.missing_host_requirements)) { + throw new Error(`${row.boundary_class}: opt-in probe dispatch next action must name missing requirements`); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error(`${row.boundary_class}: opt-in probe dispatch notes drift`); + } + } + + return rows; + } + + async function verifyBrowserRuntimeBoundaryOptInProbeDispatchRollup() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-rollup.tsv"), + ); + if (rows.length !== 1) { + throw new Error(`browser_runtime_boundary_opt_in_probe_dispatch_rollup: row count drift ${rows.length}`); + } + + const row = rows[0]; + if ( + row.scope !== "blocked_runtime_opt_in_probe_dispatch" || + row.probe_count !== "3" || + row.dispatch_allowed_count !== "0" || + row.dispatch_blocked_count !== "3" || + row.dispatch_actions !== "skip_missing_host_requirements" || + row.dispatch_allowed_commands !== "-" || + row.dispatch_status !== "dispatch_blocked_for_all_opt_in_native_probes" + ) { + throw new Error("opt-in probe dispatch rollup state drift"); + } + for (const boundaryClass of ["L4-PYTHON-REMAP", "L4-TOOL-DB", "L4-USER-M-PROCESS"]) { + if (!row.blocked_families.includes(boundaryClass)) { + throw new Error(`opt-in probe dispatch rollup missing ${boundaryClass}`); + } + } + for (const requirement of ["halcmd", "halrun", "linuxcnc", "milltask"]) { + if (!row.missing_host_requirements.includes(requirement)) { + throw new Error(`opt-in probe dispatch rollup missing ${requirement}`); + } + } + for (const command of [ + "ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh", + "ENABLE_TOOL_DB_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_tool_db_runtime.sh", + "ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh", + ]) { + if (!row.dispatch_blocked_commands.includes(command)) { + throw new Error(`opt-in probe dispatch rollup missing blocked command ${command}`); + } + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error("opt-in probe dispatch rollup must not enable execution or promotion"); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error("opt-in probe dispatch rollup notes drift"); + } + + return rows; + } + + async function verifyBrowserRuntimeBoundaryOptInProbeSkipEvidenceContract() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-contract.tsv"), + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + target: "axis/vismach/millturn/example.ngc", + runtimeProbe: "linuxcnc_millturn_user_m_runtime_probe", + missing: "halrun,halcmd,linuxcnc", + optInEnv: "ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1", + }, + ], + [ + "L4-TOOL-DB", + { + target: "axis/db_demo/base.ngc", + runtimeProbe: "linuxcnc_tool_db_runtime_probe", + missing: "linuxcnc,milltask,halcmd", + optInEnv: "ENABLE_TOOL_DB_RUNTIME_PROBE=1", + }, + ], + [ + "L4-PYTHON-REMAP", + { + target: "axis/remap/stop-lookahead/nc_files", + runtimeProbe: "linuxcnc_python_remap_runtime_probe", + missing: "linuxcnc", + optInEnv: "ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1", + }, + ], + ]); + + if (rows.length !== expected.size) { + throw new Error(`browser_runtime_boundary_opt_in_probe_skip_evidence_contract: row count drift ${rows.length}`); + } + + for (const row of rows) { + const expectedRow = expected.get(row.boundary_class); + if (!expectedRow) { + throw new Error(`browser_runtime_boundary_opt_in_probe_skip_evidence_contract: unexpected ${row.boundary_class}`); + } + if ( + row.target !== expectedRow.target || + row.runtime_probe !== expectedRow.runtimeProbe || + row.missing_host_requirements !== expectedRow.missing || + row.opt_in_env !== expectedRow.optInEnv + ) { + throw new Error(`${row.boundary_class}: opt-in probe skip evidence metadata drift`); + } + if (!row.execution_command.includes(row.opt_in_env)) { + throw new Error(`${row.boundary_class}: opt-in probe skip evidence command drift`); + } + if ( + row.host_action !== "skip_missing_host_requirements" || + row.skip_reason !== "missing_host_requirements" || + row.current_probe_status !== "skipped_missing_host_runtime" || + row.evidence_required_now !== "0" || + row.observed_evidence_ready !== "0" || + row.native_evidence_status !== "pending_until_native_pass" || + row.skip_evidence_status !== "skip_valid_until_host_requirements_available" + ) { + throw new Error(`${row.boundary_class}: opt-in probe skip evidence status drift`); + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${row.boundary_class}: opt-in probe skip evidence must not enable execution or promotion`); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error(`${row.boundary_class}: opt-in probe skip evidence notes drift`); + } + } + + return rows; + } + + function verifyBrowserBlockedRuntimeProbeExecutionParity({ + executionPlanRows, + passEvidenceRows, + hostPreflightRows, + dispatchPlanRows, + skipEvidenceRows, + }) { + const expectedClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + const passByClass = new Map(passEvidenceRows.map((row) => [row.boundary_class, row])); + const preflightByClass = new Map(hostPreflightRows.map((row) => [row.boundary_class, row])); + const dispatchByClass = new Map(dispatchPlanRows.map((row) => [row.boundary_class, row])); + const skipByClass = new Map(skipEvidenceRows.map((row) => [row.boundary_class, row])); + + for (const rows of [ + executionPlanRows, + passEvidenceRows, + hostPreflightRows, + dispatchPlanRows, + skipEvidenceRows, + ]) { + if (rows.map((row) => row.boundary_class).join(",") !== expectedClasses.join(",")) { + throw new Error("browser_blocked_runtime_probe_execution_parity: class order drift"); + } + } + + for (const execution of executionPlanRows) { + const pass = passByClass.get(execution.boundary_class); + const preflight = preflightByClass.get(execution.boundary_class); + const dispatch = dispatchByClass.get(execution.boundary_class); + const skip = skipByClass.get(execution.boundary_class); + if (!pass || !preflight || !dispatch || !skip) { + throw new Error(`${execution.boundary_class}: missing probe execution parity row`); + } + + for (const row of [pass, preflight, dispatch, skip]) { + if (row.boundary_kind !== execution.boundary_kind || row.target !== execution.target) { + throw new Error(`${execution.boundary_class}: probe execution kind/target drift`); + } + } + if ( + preflight.blocked !== execution.boundary_class || + dispatch.blocked !== execution.boundary_class || + skip.blocked !== execution.boundary_class + ) { + throw new Error(`${execution.boundary_class}: probe execution blocked kind drift`); + } + if ( + pass.runtime_probe !== execution.runtime_probe || + skip.runtime_probe !== execution.runtime_probe || + pass.expected_pass_status !== execution.expected_pass_status + ) { + throw new Error(`${execution.boundary_class}: probe execution runtime/pass status drift`); + } + if (preflight.required_native_proof !== execution.required_native_proof) { + throw new Error(`${execution.boundary_class}: probe execution required native proof drift`); + } + for (const row of [preflight, dispatch, skip]) { + if ( + row.opt_in_env !== execution.opt_in_env || + row.execution_command !== execution.execution_command + ) { + throw new Error(`${execution.boundary_class}: probe execution opt-in command drift`); + } + } + if ( + pass.current_probe_status !== execution.current_probe_status || + preflight.current_probe_status !== execution.current_probe_status || + skip.current_probe_status !== execution.current_probe_status + ) { + throw new Error(`${execution.boundary_class}: probe execution current status drift`); + } + if ( + preflight.runtime_ready !== execution.runtime_ready || + preflight.source_proof_ready !== execution.source_proof_ready || + preflight.missing_requirements !== execution.missing_requirements || + dispatch.missing_host_requirements !== execution.missing_requirements || + skip.missing_host_requirements !== execution.missing_requirements + ) { + throw new Error(`${execution.boundary_class}: probe execution readiness/missing requirement drift`); + } + + if ( + execution.runtime_ready !== "0" || + execution.source_proof_ready !== "1" || + execution.current_probe_status !== "skipped_missing_host_runtime" || + execution.plan_status !== "blocked_missing_host_runtime" || + preflight.preflight_status !== "blocked_missing_host_runtime" || + dispatch.host_readiness_status !== "host_blocked_for_all_opt_in_native_probes" || + dispatch.host_action !== "skip_missing_host_requirements" || + dispatch.dispatch_allowed !== "0" || + skip.host_action !== "skip_missing_host_requirements" || + skip.skip_reason !== "missing_host_requirements" + ) { + throw new Error(`${execution.boundary_class}: probe execution host-blocked status drift`); + } + if ( + pass.evidence_required_now !== "0" || + skip.evidence_required_now !== "0" || + pass.observed_evidence_ready !== "0" || + skip.observed_evidence_ready !== "0" || + pass.evidence_status !== "pending_until_native_pass" || + skip.native_evidence_status !== "pending_until_native_pass" || + skip.skip_evidence_status !== "skip_valid_until_host_requirements_available" + ) { + throw new Error(`${execution.boundary_class}: probe execution evidence status drift`); + } + for (const row of [execution, pass, preflight, dispatch, skip]) { + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${execution.boundary_class}: probe execution parity must remain disabled`); + } + } + } + } + + async function verifyBrowserRuntimeBoundaryOptInProbeSkipEvidenceRollup() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv"), + ); + if (rows.length !== 1) { + throw new Error(`browser_runtime_boundary_opt_in_probe_skip_evidence_rollup: row count drift ${rows.length}`); + } + + const row = rows[0]; + if ( + row.scope !== "blocked_runtime_opt_in_probe_skip_evidence" || + row.probe_count !== "3" || + row.skip_count !== "3" || + row.evidence_required_now_count !== "0" || + row.observed_evidence_ready_count !== "0" || + row.skip_reasons !== "missing_host_requirements" || + row.current_probe_statuses !== "skipped_missing_host_runtime" || + row.native_evidence_statuses !== "pending_until_native_pass" || + row.skip_evidence_statuses !== "skip_valid_until_host_requirements_available" || + row.evidence_rollup_status !== "no_native_pass_evidence_accepted_while_host_blocked" + ) { + throw new Error("opt-in probe skip evidence rollup state drift"); + } + for (const boundaryClass of ["L4-PYTHON-REMAP", "L4-TOOL-DB", "L4-USER-M-PROCESS"]) { + if (!row.skipped_families.includes(boundaryClass)) { + throw new Error(`opt-in probe skip evidence rollup missing ${boundaryClass}`); + } + } + for (const requirement of ["halcmd", "halrun", "linuxcnc", "milltask"]) { + if (!row.missing_host_requirements.includes(requirement)) { + throw new Error(`opt-in probe skip evidence rollup missing ${requirement}`); + } + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error("opt-in probe skip evidence rollup must not enable execution or promotion"); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error("opt-in probe skip evidence rollup notes drift"); + } + + return rows; + } + + function verifyBrowserBlockedRuntimeRollupParity({ + hostReadinessRollupRows, + dispatchRollupRows, + skipEvidenceRollupRows, + familyHostReadinessRows, + dispatchPlanRows, + skipEvidenceRows, + }) { + if ( + hostReadinessRollupRows.length !== 1 || + dispatchRollupRows.length !== 1 || + skipEvidenceRollupRows.length !== 1 + ) { + throw new Error("browser_blocked_runtime_rollup_parity: expected one row per rollup"); + } + + const host = hostReadinessRollupRows[0]; + const dispatch = dispatchRollupRows[0]; + const skip = skipEvidenceRollupRows[0]; + + if ( + host.family_count !== String(familyHostReadinessRows.length) || + dispatch.probe_count !== String(dispatchPlanRows.length) || + skip.probe_count !== String(skipEvidenceRows.length) || + host.family_count !== dispatch.probe_count || + dispatch.probe_count !== skip.probe_count + ) { + throw new Error("browser_blocked_runtime_rollup_parity: count drift"); + } + if ( + host.host_ready_family_count !== dispatch.dispatch_allowed_count || + host.host_blocked_family_count !== dispatch.dispatch_blocked_count || + dispatch.dispatch_blocked_count !== skip.skip_count + ) { + throw new Error("browser_blocked_runtime_rollup_parity: blocked/allowed count drift"); + } + if ( + host.blocked_families !== dispatch.blocked_families || + dispatch.blocked_families !== skip.skipped_families + ) { + throw new Error("browser_blocked_runtime_rollup_parity: blocked family drift"); + } + if ( + host.missing_host_requirements !== dispatch.missing_host_requirements || + dispatch.missing_host_requirements !== skip.missing_host_requirements + ) { + throw new Error("browser_blocked_runtime_rollup_parity: missing requirement drift"); + } + if ( + host.ready_opt_in_commands !== dispatch.dispatch_allowed_commands || + host.blocked_opt_in_commands !== dispatch.dispatch_blocked_commands + ) { + throw new Error("browser_blocked_runtime_rollup_parity: opt-in command drift"); + } + if ( + host.host_readiness_status !== "host_blocked_for_all_opt_in_native_probes" || + dispatch.dispatch_status !== "dispatch_blocked_for_all_opt_in_native_probes" || + skip.evidence_rollup_status !== "no_native_pass_evidence_accepted_while_host_blocked" || + skip.current_probe_statuses !== "skipped_missing_host_runtime" || + skip.native_evidence_statuses !== "pending_until_native_pass" || + skip.skip_evidence_statuses !== "skip_valid_until_host_requirements_available" || + skip.evidence_required_now_count !== "0" || + skip.observed_evidence_ready_count !== "0" + ) { + throw new Error("browser_blocked_runtime_rollup_parity: host-blocked status drift"); + } + for (const row of [host, dispatch, skip]) { + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error("browser_blocked_runtime_rollup_parity: rollup must remain disabled"); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error("browser_blocked_runtime_rollup_parity: rollup notes drift"); + } + } + } + + async function verifyBrowserRuntimeBoundaryNativeEvidenceAcceptanceGate() { + const rows = parseTsv( + await fetchText("../../build/wasm/sim-configs-inventory/runtime-boundary-native-evidence-acceptance-gate.tsv"), + ); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + target: "axis/vismach/millturn/example.ngc", + runtimeProbe: "linuxcnc_millturn_user_m_runtime_probe", + missing: "halrun,halcmd,linuxcnc", + }, + ], + [ + "L4-TOOL-DB", + { + target: "axis/db_demo/base.ngc", + runtimeProbe: "linuxcnc_tool_db_runtime_probe", + missing: "linuxcnc,milltask,halcmd", + }, + ], + [ + "L4-PYTHON-REMAP", + { + target: "axis/remap/stop-lookahead/nc_files", + runtimeProbe: "linuxcnc_python_remap_runtime_probe", + missing: "linuxcnc", + }, + ], + ]); + + if (rows.length !== expected.size) { + throw new Error(`browser_runtime_boundary_native_evidence_acceptance_gate: row count drift ${rows.length}`); + } + + for (const row of rows) { + const expectedRow = expected.get(row.boundary_class); + if (!expectedRow) { + throw new Error(`browser_runtime_boundary_native_evidence_acceptance_gate: unexpected ${row.boundary_class}`); + } + if (row.target !== expectedRow.target || row.runtime_probe !== expectedRow.runtimeProbe) { + throw new Error(`${row.boundary_class}: native evidence acceptance gate metadata drift`); + } + if ( + row.current_probe_status !== "skipped_missing_host_runtime" || + row.native_evidence_status !== "pending_until_native_pass" || + row.evidence_required_now !== "0" || + row.observed_evidence_ready !== "0" || + row.skip_evidence_status !== "skip_valid_until_host_requirements_available" || + row.promotion_ready !== "0" || + row.node_gate_status !== "blocked_until_native_pass_evidence" || + row.browser_gate_status !== "blocked_until_node_gate_complete" || + row.native_evidence_gate !== "blocked_until_host_requirements_available" || + row.evidence_acceptance_allowed !== "0" + ) { + throw new Error(`${row.boundary_class}: native evidence acceptance gate status drift`); + } + if (!row.next_action.includes(expectedRow.missing)) { + throw new Error(`${row.boundary_class}: native evidence acceptance gate next action must name missing requirements`); + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${row.boundary_class}: native evidence acceptance gate must not enable execution or promotion`); + } + if (!row.notes.includes("no_automatic_promotion")) { + throw new Error(`${row.boundary_class}: native evidence acceptance gate notes drift`); + } + } + + return rows; } async function verifyBrowserNextBoundaryRecommendations() { @@ -2297,7 +3773,7 @@ target: "axis/db_demo/base.ngc", blocked: "L4-TOOL-DB", recommendation: "implement_tooldata_db_protocol_probe", - primaryArtifact: "tool-db-process-transaction-plan.tsv", + primaryArtifact: "tool-db-process-native-runtime-probe-gate.tsv", proofToken: "not_tbl_fallback", }, ], @@ -2358,6 +3834,393 @@ throw new Error(`${row.boundary_kind}: recommendation lacks source artifact trace`); } } + + return rows; + } + + function verifyBrowserNextBoundaryRecommendationCountParity({ + recommendationRows, + userMRuntimeProbeGateRows, + toolDbRuntimeProbeGateRows, + pythonRuntimeContractRows, + }) { + const expectedCounts = new Map([ + ["external_user_m_process", userMRuntimeProbeGateRows.length], + ["tool_database_process", toolDbRuntimeProbeGateRows.length], + ["python_runtime", pythonRuntimeContractRows.length], + ]); + + for (const row of recommendationRows) { + const expectedCount = expectedCounts.get(row.boundary_kind); + if (expectedCount === undefined) { + throw new Error(`${row.boundary_kind}: recommendation count parity unexpected kind`); + } + if (row.artifact_row_count !== String(expectedCount)) { + throw new Error(`${row.boundary_kind}: recommendation artifact count ${row.artifact_row_count} drift from generated source count ${expectedCount}`); + } + if (row.boundary_kind === "external_user_m_process" && !row.source_artifacts.includes("user-m-process-native-runtime-probe-gate.tsv")) { + throw new Error("user-M recommendation lacks runtime probe gate source artifact"); + } + if (row.boundary_kind === "tool_database_process" && !row.source_artifacts.includes("tool-db-process-native-runtime-probe-gate.tsv")) { + throw new Error("tool DB recommendation lacks runtime probe gate source artifact"); + } + if (row.boundary_kind === "python_runtime" && !row.source_artifacts.includes("python-remap-runtime-contract.tsv")) { + throw new Error("Python recommendation lacks runtime contract source artifact"); + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${row.boundary_kind}: recommendation count parity must remain disabled`); + } + } + } + + function verifyBrowserNextBoundaryRecommendationSourceArtifactCoverage({ + recommendationRows, + wasmArtifactNames, + }) { + const artifactNameSet = new Set(wasmArtifactNames); + const expectedSourceArtifacts = new Map([ + [ + "external_user_m_process", + [ + "next-boundary-worklist.tsv", + "blocked-runtime-promotion-lock.tsv", + "user-m-process-transition-plan.tsv", + "user-m-process-native-runtime-state-plan.tsv", + "user-m-process-native-runtime-probe-gate.tsv", + ], + ], + [ + "tool_database_process", + [ + "next-boundary-worklist.tsv", + "blocked-runtime-promotion-lock.tsv", + "tool-db-process-transaction-plan.tsv", + "tool-db-process-native-runtime-readiness.tsv", + "tool-db-process-native-runtime-probe-gate.tsv", + ], + ], + [ + "python_runtime", + [ + "next-boundary-worklist.tsv", + "blocked-runtime-promotion-lock.tsv", + "python-remap-runtime-contract.tsv", + "python-remap-native-runtime-fixture-plan.tsv", + "python-remap-native-runtime-probe-gate.tsv", + ], + ], + ]); + + for (const row of recommendationRows) { + const sourceArtifacts = row.source_artifacts === "-" ? [] : row.source_artifacts.split(","); + const expectedSources = expectedSourceArtifacts.get(row.boundary_kind); + if (!expectedSources) { + throw new Error(`${row.boundary_kind}: unexpected recommendation source artifact kind`); + } + if (sourceArtifacts.length === 0) { + throw new Error(`${row.boundary_kind}: recommendation lacks source artifacts`); + } + if (sourceArtifacts.join(",") !== expectedSources.join(",")) { + throw new Error(`${row.boundary_kind}: recommendation source artifact set drift`); + } + if (new Set(sourceArtifacts).size !== sourceArtifacts.length) { + throw new Error(`${row.boundary_kind}: duplicate recommendation source artifact`); + } + if (!sourceArtifacts.includes(row.primary_artifact)) { + throw new Error(`${row.boundary_kind}: recommendation primary artifact missing from source artifacts`); + } + for (const artifactName of [row.primary_artifact, ...sourceArtifacts]) { + if (!artifactNameSet.has(artifactName)) { + throw new Error(`${row.boundary_kind}: unknown recommendation source artifact ${artifactName}`); + } + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${row.boundary_kind}: recommendation source artifact coverage must remain disabled`); + } + } + } + + function verifyBrowserBlockedRuntimePromotionGateParity({ + promotionLockRows, + promotionReadinessRows, + promotionBlockerRows, + postNativePassGateRows, + nativeEvidenceAcceptanceGateRows, + passEvidenceRows, + }) { + const expectedClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + const lockByTarget = new Map(promotionLockRows.map((row) => [row.target, row])); + const readinessByClass = new Map(promotionReadinessRows.map((row) => [row.boundary_class, row])); + const blockerByClass = new Map(promotionBlockerRows.map((row) => [row.boundary_class, row])); + const postNativeByClass = new Map(postNativePassGateRows.map((row) => [row.boundary_class, row])); + const evidenceGateByClass = new Map( + nativeEvidenceAcceptanceGateRows.map((row) => [row.boundary_class, row]), + ); + const passEvidenceByClass = new Map(passEvidenceRows.map((row) => [row.boundary_class, row])); + + for (const rows of [ + promotionReadinessRows, + promotionBlockerRows, + postNativePassGateRows, + nativeEvidenceAcceptanceGateRows, + passEvidenceRows, + ]) { + if (rows.map((row) => row.boundary_class).join(",") !== expectedClasses.join(",")) { + throw new Error("browser_blocked_runtime_promotion_gate_parity: class order drift"); + } + } + + for (const boundaryClass of expectedClasses) { + const readiness = readinessByClass.get(boundaryClass); + const blocker = blockerByClass.get(boundaryClass); + const postNative = postNativeByClass.get(boundaryClass); + const evidenceGate = evidenceGateByClass.get(boundaryClass); + const passEvidence = passEvidenceByClass.get(boundaryClass); + if (!readiness || !blocker || !postNative || !evidenceGate || !passEvidence) { + throw new Error(`${boundaryClass}: missing promotion gate parity row`); + } + + const lock = lockByTarget.get(readiness.target); + if (!lock) { + throw new Error(`${boundaryClass}: missing promotion lock row for ${readiness.target}`); + } + + for (const row of [blocker, postNative, evidenceGate, passEvidence]) { + if (row.boundary_kind !== readiness.boundary_kind || row.target !== readiness.target) { + throw new Error(`${boundaryClass}: promotion gate kind/target drift`); + } + } + for (const row of [blocker, postNative, evidenceGate]) { + if (row.blocked !== readiness.blocked) { + throw new Error(`${boundaryClass}: promotion gate blocked kind drift`); + } + } + + if ( + readiness.current_probe_status !== passEvidence.current_probe_status || + readiness.expected_pass_status !== passEvidence.expected_pass_status || + readiness.native_evidence_status !== passEvidence.evidence_status || + readiness.native_evidence_ready !== passEvidence.observed_evidence_ready || + readiness.promotion_lock_active !== lock.lock_active || + readiness.promotion_ready !== blocker.promotion_ready || + readiness.promotion_ready !== evidenceGate.promotion_ready + ) { + throw new Error(`${boundaryClass}: promotion readiness/pass/lock drift`); + } + if ( + postNative.current_probe_status !== readiness.current_probe_status || + postNative.expected_pass_status !== readiness.expected_pass_status || + postNative.native_pass_ready !== readiness.native_pass_ready || + postNative.native_evidence_ready !== readiness.native_evidence_ready || + postNative.manual_lock_update_required !== readiness.manual_lock_update_required || + postNative.promotion_lock_active !== readiness.promotion_lock_active + ) { + throw new Error(`${boundaryClass}: post-native promotion gate drift`); + } + if ( + evidenceGate.current_probe_status !== readiness.current_probe_status || + evidenceGate.native_evidence_status !== readiness.native_evidence_status || + evidenceGate.evidence_required_now !== passEvidence.evidence_required_now || + evidenceGate.observed_evidence_ready !== passEvidence.observed_evidence_ready || + evidenceGate.node_gate_status !== postNative.node_gate_status || + evidenceGate.browser_gate_status !== postNative.browser_gate_status + ) { + throw new Error(`${boundaryClass}: native evidence acceptance parity drift`); + } + + if ( + readiness.native_pass_ready !== "0" || + readiness.native_evidence_ready !== "0" || + readiness.node_inventory_gate_complete !== "0" || + readiness.browser_smoke_gate_complete !== "0" || + readiness.promotion_lock_active !== "1" || + readiness.manual_lock_update_required !== "1" || + readiness.promotion_ready !== "0" || + postNative.node_gate_status !== "blocked_until_native_pass_evidence" || + postNative.browser_gate_status !== "blocked_until_node_gate_complete" || + postNative.gate_status !== "blocked_before_native_pass" || + evidenceGate.native_evidence_gate !== "blocked_until_host_requirements_available" || + evidenceGate.evidence_acceptance_allowed !== "0" + ) { + throw new Error(`${boundaryClass}: promotion gate must remain host-blocked`); + } + for (const blockerKey of [ + "native_runtime_probe_not_passed", + "native_pass_evidence_not_ready", + "node_inventory_gate_not_complete", + "browser_smoke_gate_not_complete", + "promotion_lock_active", + "manual_lock_update_required", + ]) { + if (!blocker.blocker_keys.includes(blockerKey)) { + throw new Error(`${boundaryClass}: promotion blocker missing ${blockerKey}`); + } + } + for (const row of [readiness, blocker, postNative, evidenceGate, passEvidence, lock]) { + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${boundaryClass}: promotion gate parity must remain disabled`); + } + } + } + } + + function verifyBrowserBlockedRuntimeOptInGateParity({ + recommendationRows, + executionPlanRows, + promotionReadinessRows, + promotionBlockerRows, + postNativePassGateRows, + hostPreflightRows, + familyHostReadinessRows, + dispatchPlanRows, + skipEvidenceRows, + evidenceAcceptanceGateRows, + }) { + const expectedClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + const classToKind = new Map([ + ["L4-USER-M-PROCESS", "external_user_m_process"], + ["L4-TOOL-DB", "tool_database_process"], + ["L4-PYTHON-REMAP", "python_runtime"], + ]); + const recommendationByClass = new Map(recommendationRows.map((row) => [row.blocked, row])); + const byClass = (rows) => new Map(rows.map((row) => [row.boundary_class, row])); + const executionByClass = byClass(executionPlanRows); + const readinessByClass = byClass(promotionReadinessRows); + const blockerByClass = byClass(promotionBlockerRows); + const postNativeByClass = byClass(postNativePassGateRows); + const preflightByClass = byClass(hostPreflightRows); + const familyReadinessByClass = byClass(familyHostReadinessRows); + const dispatchByClass = byClass(dispatchPlanRows); + const skipEvidenceByClass = byClass(skipEvidenceRows); + const evidenceGateByClass = byClass(evidenceAcceptanceGateRows); + + for (const rows of [ + executionPlanRows, + promotionReadinessRows, + promotionBlockerRows, + postNativePassGateRows, + hostPreflightRows, + familyHostReadinessRows, + dispatchPlanRows, + skipEvidenceRows, + evidenceAcceptanceGateRows, + ]) { + if (rows.map((row) => row.boundary_class).join(",") !== expectedClasses.join(",")) { + throw new Error("browser_blocked_runtime_opt_in_gate_parity: class order drift"); + } + } + + for (const boundaryClass of expectedClasses) { + const expectedKind = classToKind.get(boundaryClass); + const recommendation = recommendationByClass.get(boundaryClass); + const execution = executionByClass.get(boundaryClass); + const readiness = readinessByClass.get(boundaryClass); + const blocker = blockerByClass.get(boundaryClass); + const postNative = postNativeByClass.get(boundaryClass); + const preflight = preflightByClass.get(boundaryClass); + const familyReadiness = familyReadinessByClass.get(boundaryClass); + const dispatch = dispatchByClass.get(boundaryClass); + const skipEvidence = skipEvidenceByClass.get(boundaryClass); + const evidenceGate = evidenceGateByClass.get(boundaryClass); + for (const [label, row] of [ + ["recommendation", recommendation], + ["execution", execution], + ["readiness", readiness], + ["blocker", blocker], + ["postNative", postNative], + ["preflight", preflight], + ["familyReadiness", familyReadiness], + ["dispatch", dispatch], + ["skipEvidence", skipEvidence], + ["evidenceGate", evidenceGate], + ]) { + if (!row) { + throw new Error(`${boundaryClass}: missing ${label} row for opt-in parity`); + } + } + + if (recommendation.boundary_kind !== expectedKind) { + throw new Error(`${boundaryClass}: recommendation kind drift`); + } + for (const row of [ + execution, + readiness, + blocker, + postNative, + preflight, + familyReadiness, + dispatch, + skipEvidence, + evidenceGate, + ]) { + if ( + row.boundary_kind !== expectedKind || + row.target !== recommendation.target || + (row.blocked || row.boundary_class) !== recommendation.blocked + ) { + throw new Error(`${boundaryClass}: opt-in gate identity drift`); + } + if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") { + throw new Error(`${boundaryClass}: opt-in gate parity must remain disabled`); + } + } + + if ( + execution.required_native_proof !== recommendation.required_native_proof || + postNative.required_node_proof !== recommendation.required_node_proof || + postNative.required_browser_proof !== recommendation.required_browser_proof || + preflight.required_native_proof !== execution.required_native_proof + ) { + throw new Error(`${boundaryClass}: opt-in gate proof drift`); + } + if ( + preflight.opt_in_env !== execution.opt_in_env || + dispatch.opt_in_env !== execution.opt_in_env || + skipEvidence.opt_in_env !== execution.opt_in_env || + preflight.execution_command !== execution.execution_command || + dispatch.execution_command !== execution.execution_command || + skipEvidence.execution_command !== execution.execution_command + ) { + throw new Error(`${boundaryClass}: opt-in gate command drift`); + } + if ( + preflight.current_probe_status !== execution.current_probe_status || + readiness.current_probe_status !== execution.current_probe_status || + postNative.current_probe_status !== execution.current_probe_status || + evidenceGate.current_probe_status !== execution.current_probe_status || + familyReadiness.current_probe_status !== execution.current_probe_status + ) { + throw new Error(`${boundaryClass}: opt-in gate probe status drift`); + } + if ( + preflight.missing_requirements !== execution.missing_requirements || + dispatch.missing_host_requirements !== execution.missing_requirements || + skipEvidence.missing_host_requirements !== execution.missing_requirements || + blocker.missing_runtime_requirements !== execution.missing_requirements + ) { + throw new Error(`${boundaryClass}: opt-in gate missing requirement drift`); + } + if ( + evidenceGate.evidence_acceptance_allowed !== "0" || + dispatch.dispatch_allowed !== "0" || + readiness.promotion_lock_active !== "1" || + postNative.promotion_lock_active !== "1" || + evidenceGate.native_evidence_gate.indexOf("blocked") === -1 || + !blocker.blocker_keys.includes("promotion_lock_active") || + !blocker.blocker_keys.includes("manual_lock_update_required") || + !blocker.blocker_keys.includes("host_runtime_requirements_missing") + ) { + throw new Error(`${boundaryClass}: opt-in gate must remain blocked`); + } + } } function verifyExpectedFileError(fixtureName, output, expectedText) { @@ -2909,38 +4772,218 @@ try { verifyBrowserIniContextStagingPlan(); - await verifyBrowserBoundaryPhaseCompletion(); + const boundaryPhaseCompletionRows = + await verifyBrowserBoundaryPhaseCompletion(); + const generatedArtifactDocumentationCoverage = + await verifyBrowserGeneratedArtifactDocumentationCoverage(); await verifyBrowserIniBoundarySummary(); - await verifyBrowserBoundaryProofGates(); + const nextBoundaryWorklistRows = + await verifyBrowserNextBoundaryWorklist(); + const boundaryProofGateRows = + await verifyBrowserBoundaryProofGates(); await verifyBrowserBoundarySummary(); - await verifyBrowserUserMTransitionPlan(); + const userMTransitionPlanRows = + await verifyBrowserUserMTransitionPlan(); await verifyBrowserUserMNativeTransitionAlignment(); - await verifyBrowserUserMNativeRuntimeStatePlan(); - await verifyBrowserUserMNativeRuntimeReadiness(); - await verifyBrowserUserMNativeRuntimeProbeGate(); - await verifyBrowserToolDbTransactionPlan(); - await verifyBrowserToolDbNativeRuntimeReadiness(); - await verifyBrowserToolDbNativeRuntimeProbeGate(); - await verifyBrowserPythonRuntimeContract(); - await verifyBrowserPythonNativeRuntimeReadiness(); - await verifyBrowserPythonNativeRuntimeProbeGate(); - await verifyBrowserPythonNativeRuntimeStatePlan(); - await verifyBrowserPythonNativeRuntimeFixturePlan(); - await verifyBrowserNativeProofAlignment(); + const userMNativeRuntimeStatePlanRows = + await verifyBrowserUserMNativeRuntimeStatePlan(); + const userMNativeRuntimeReadinessRows = + await verifyBrowserUserMNativeRuntimeReadiness(); + const userMNativeRuntimeProbeGateRows = + await verifyBrowserUserMNativeRuntimeProbeGate(); + const toolDbTransactionPlanRows = + await verifyBrowserToolDbTransactionPlan(); + const toolDbNativeRuntimeReadinessRows = + await verifyBrowserToolDbNativeRuntimeReadiness(); + const toolDbNativeRuntimeProbeGateRows = + await verifyBrowserToolDbNativeRuntimeProbeGate(); + const pythonRuntimeContractRows = + await verifyBrowserPythonRuntimeContract(); + const pythonNativeRuntimeReadinessRows = + await verifyBrowserPythonNativeRuntimeReadiness(); + const pythonNativeRuntimeProbeGateRows = + await verifyBrowserPythonNativeRuntimeProbeGate(); + const pythonNativeRuntimeStatePlanRows = + await verifyBrowserPythonNativeRuntimeStatePlan(); + const pythonNativeRuntimeFixturePlanRows = + await verifyBrowserPythonNativeRuntimeFixturePlan(); + const nativeProofAlignmentRows = await verifyBrowserNativeProofAlignment(); const runtimeNativeAlignmentSummaryRows = await verifyBrowserRuntimeNativeAlignmentSummary(); - await verifyBrowserNativeRuntimeProbeSummary(); - await verifyBrowserRuntimeProbeGateAlignment(); - await verifyBrowserNativeRuntimeProbeExecutionPlan(); - await verifyBrowserNativeRuntimeProbePassEvidenceContract(); - await verifyBrowserRuntimeBoundaryContractSummary(); + const nativeRuntimeProbeSummaryRows = + await verifyBrowserNativeRuntimeProbeSummary(); + const runtimeProbeGateAlignmentRows = + await verifyBrowserRuntimeProbeGateAlignment(); + verifyBrowserNativeSourceProofRuntimeProbeParity( + nativeProofAlignmentRows, + nativeRuntimeProbeSummaryRows, + runtimeProbeGateAlignmentRows, + ); + const nativeRuntimeProbeExecutionPlanRows = + await verifyBrowserNativeRuntimeProbeExecutionPlan(); + const nativeRuntimeProbePassEvidenceRows = + await verifyBrowserNativeRuntimeProbePassEvidenceContract(); + const runtimeBoundaryContractSummaryRows = + await verifyBrowserRuntimeBoundaryContractSummary(); + verifyBrowserRuntimeContractNativeAlignmentParity( + runtimeBoundaryContractSummaryRows, + runtimeNativeAlignmentSummaryRows, + ); + verifyBrowserRuntimeFamilyContractAlignmentParity({ + userMTransitionRows: userMTransitionPlanRows, + userMRuntimeStatePlanRows: userMNativeRuntimeStatePlanRows, + userMRuntimeReadinessRows: userMNativeRuntimeReadinessRows, + userMRuntimeProbeGateRows: userMNativeRuntimeProbeGateRows, + toolDbTransactionRows: toolDbTransactionPlanRows, + toolDbRuntimeReadinessRows: toolDbNativeRuntimeReadinessRows, + toolDbRuntimeProbeGateRows: toolDbNativeRuntimeProbeGateRows, + pythonRuntimeContractRows, + pythonRuntimeReadinessRows: pythonNativeRuntimeReadinessRows, + pythonRuntimeProbeGateRows: pythonNativeRuntimeProbeGateRows, + pythonRuntimeStatePlanRows: pythonNativeRuntimeStatePlanRows, + pythonRuntimeFixturePlanRows: pythonNativeRuntimeFixturePlanRows, + runtimeContractSummaryRows: runtimeBoundaryContractSummaryRows, + }); await verifyBrowserRuntimeNativeAlignmentDetails(runtimeNativeAlignmentSummaryRows); - await verifyBrowserBlockedRuntimePromotionLock(); - await verifyBrowserRuntimeBoundaryPromotionReadiness(); - await verifyBrowserRuntimeBoundaryPromotionBlockers(); - await verifyBrowserRuntimeBoundaryPostNativePassGates(); - await verifyBrowserRuntimeBoundaryHostPreflight(); - await verifyBrowserNextBoundaryRecommendations(); + const blockedRuntimePromotionLockRows = + await verifyBrowserBlockedRuntimePromotionLock(); + verifyBrowserNextBoundaryWorklistProofGateParity({ + worklistRows: nextBoundaryWorklistRows, + proofGateRows: boundaryProofGateRows, + promotionLockRows: blockedRuntimePromotionLockRows, + }); + const runtimeBoundaryPromotionReadinessRows = + await verifyBrowserRuntimeBoundaryPromotionReadiness(); + const runtimeBoundaryPromotionBlockerRows = + await verifyBrowserRuntimeBoundaryPromotionBlockers(); + const runtimeBoundaryPostNativePassGateRows = + await verifyBrowserRuntimeBoundaryPostNativePassGates(); + const runtimeBoundaryHostPreflightRows = + await verifyBrowserRuntimeBoundaryHostPreflight(); + const runtimeBoundaryHostRequirementSummaryRows = + await verifyBrowserRuntimeBoundaryHostRequirementSummary(); + const runtimeBoundaryHostUnblockPlanRows = + await verifyBrowserRuntimeBoundaryHostUnblockPlan(); + const runtimeBoundaryFamilyHostReadinessRows = + await verifyBrowserRuntimeBoundaryFamilyHostReadiness(); + verifyBrowserBlockedRuntimeHostRequirementParity({ + hostPreflightRows: runtimeBoundaryHostPreflightRows, + requirementSummaryRows: runtimeBoundaryHostRequirementSummaryRows, + unblockPlanRows: runtimeBoundaryHostUnblockPlanRows, + familyHostReadinessRows: runtimeBoundaryFamilyHostReadinessRows, + }); + const runtimeBoundaryHostReadinessRollupRows = + await verifyBrowserRuntimeBoundaryHostReadinessRollup(); + const runtimeBoundaryOptInProbeDispatchPlanRows = + await verifyBrowserRuntimeBoundaryOptInProbeDispatchPlan(); + const runtimeBoundaryOptInProbeDispatchRollupRows = + await verifyBrowserRuntimeBoundaryOptInProbeDispatchRollup(); + const runtimeBoundaryOptInProbeSkipEvidenceRows = + await verifyBrowserRuntimeBoundaryOptInProbeSkipEvidenceContract(); + verifyBrowserBlockedRuntimeProbeExecutionParity({ + executionPlanRows: nativeRuntimeProbeExecutionPlanRows, + passEvidenceRows: nativeRuntimeProbePassEvidenceRows, + hostPreflightRows: runtimeBoundaryHostPreflightRows, + dispatchPlanRows: runtimeBoundaryOptInProbeDispatchPlanRows, + skipEvidenceRows: runtimeBoundaryOptInProbeSkipEvidenceRows, + }); + const runtimeBoundaryOptInProbeSkipEvidenceRollupRows = + await verifyBrowserRuntimeBoundaryOptInProbeSkipEvidenceRollup(); + verifyBrowserBlockedRuntimeRollupParity({ + hostReadinessRollupRows: runtimeBoundaryHostReadinessRollupRows, + dispatchRollupRows: runtimeBoundaryOptInProbeDispatchRollupRows, + skipEvidenceRollupRows: runtimeBoundaryOptInProbeSkipEvidenceRollupRows, + familyHostReadinessRows: runtimeBoundaryFamilyHostReadinessRows, + dispatchPlanRows: runtimeBoundaryOptInProbeDispatchPlanRows, + skipEvidenceRows: runtimeBoundaryOptInProbeSkipEvidenceRows, + }); + const runtimeBoundaryNativeEvidenceAcceptanceGateRows = + await verifyBrowserRuntimeBoundaryNativeEvidenceAcceptanceGate(); + verifyBrowserBlockedRuntimePromotionGateParity({ + promotionLockRows: blockedRuntimePromotionLockRows, + promotionReadinessRows: runtimeBoundaryPromotionReadinessRows, + promotionBlockerRows: runtimeBoundaryPromotionBlockerRows, + postNativePassGateRows: runtimeBoundaryPostNativePassGateRows, + nativeEvidenceAcceptanceGateRows: runtimeBoundaryNativeEvidenceAcceptanceGateRows, + passEvidenceRows: nativeRuntimeProbePassEvidenceRows, + }); + const nextBoundaryRecommendationRows = + await verifyBrowserNextBoundaryRecommendations(); + verifyBrowserNextBoundaryRecommendationCountParity({ + recommendationRows: nextBoundaryRecommendationRows, + userMRuntimeProbeGateRows: userMNativeRuntimeProbeGateRows, + toolDbRuntimeProbeGateRows: toolDbNativeRuntimeProbeGateRows, + pythonRuntimeContractRows, + }); + verifyBrowserNextBoundaryRecommendationSourceArtifactCoverage({ + recommendationRows: nextBoundaryRecommendationRows, + wasmArtifactNames: generatedArtifactDocumentationCoverage.wasmArtifactNames, + }); + verifyBrowserBlockedRuntimeOptInGateParity({ + recommendationRows: nextBoundaryRecommendationRows, + executionPlanRows: nativeRuntimeProbeExecutionPlanRows, + promotionReadinessRows: runtimeBoundaryPromotionReadinessRows, + promotionBlockerRows: runtimeBoundaryPromotionBlockerRows, + postNativePassGateRows: runtimeBoundaryPostNativePassGateRows, + hostPreflightRows: runtimeBoundaryHostPreflightRows, + familyHostReadinessRows: runtimeBoundaryFamilyHostReadinessRows, + dispatchPlanRows: runtimeBoundaryOptInProbeDispatchPlanRows, + skipEvidenceRows: runtimeBoundaryOptInProbeSkipEvidenceRows, + evidenceAcceptanceGateRows: runtimeBoundaryNativeEvidenceAcceptanceGateRows, + }); + verifyBrowserBoundaryPhaseCompletionCountParity( + boundaryPhaseCompletionRows, + new Map([ + ["native_source_proof_alignment", nativeProofAlignmentRows.length], + ["runtime_native_alignment_artifacts", runtimeNativeAlignmentSummaryRows.length], + ["native_runtime_probe_summary", nativeRuntimeProbeSummaryRows.length], + ["runtime_probe_gate_alignment", runtimeProbeGateAlignmentRows.length], + ["blocked_runtime_worklist_recommendation_consistency", nextBoundaryRecommendationRows.length], + ["native_runtime_probe_execution_plan", nativeRuntimeProbeExecutionPlanRows.length], + ["native_runtime_probe_pass_evidence_contract", nativeRuntimeProbePassEvidenceRows.length], + ["blocked_runtime_probe_execution_consistency", nativeRuntimeProbeExecutionPlanRows.length], + ["runtime_boundary_promotion_readiness", runtimeBoundaryPromotionReadinessRows.length], + ["runtime_boundary_promotion_blockers", runtimeBoundaryPromotionBlockerRows.length], + ["runtime_boundary_post_native_pass_gates", runtimeBoundaryPostNativePassGateRows.length], + ["blocked_runtime_promotion_gate_consistency", runtimeBoundaryPromotionReadinessRows.length], + ["runtime_boundary_host_preflight", runtimeBoundaryHostPreflightRows.length], + ["runtime_boundary_host_requirement_summary", runtimeBoundaryHostRequirementSummaryRows.length], + ["runtime_boundary_host_unblock_plan", runtimeBoundaryHostUnblockPlanRows.length], + ["runtime_boundary_family_host_readiness", runtimeBoundaryFamilyHostReadinessRows.length], + ["blocked_runtime_host_requirement_consistency", runtimeBoundaryFamilyHostReadinessRows.length], + ["runtime_boundary_host_readiness_rollup", runtimeBoundaryHostReadinessRollupRows.length], + ["runtime_boundary_opt_in_probe_dispatch_plan", runtimeBoundaryOptInProbeDispatchPlanRows.length], + ["runtime_boundary_opt_in_probe_dispatch_rollup", runtimeBoundaryOptInProbeDispatchRollupRows.length], + ["runtime_boundary_opt_in_probe_skip_evidence_contract", runtimeBoundaryOptInProbeSkipEvidenceRows.length], + ["runtime_boundary_opt_in_probe_skip_evidence_rollup", runtimeBoundaryOptInProbeSkipEvidenceRollupRows.length], + ["blocked_runtime_rollup_consistency", runtimeBoundaryFamilyHostReadinessRows.length], + ["runtime_boundary_native_evidence_acceptance_gate", runtimeBoundaryNativeEvidenceAcceptanceGateRows.length], + ["blocked_runtime_opt_in_gate_consistency", nextBoundaryRecommendationRows.length], + ["native_source_proof_runtime_probe_consistency", nativeProofAlignmentRows.length], + ["user_m_transition_contract", userMTransitionPlanRows.length], + ["user_m_native_runtime_state_plan", userMNativeRuntimeStatePlanRows.length], + ["user_m_native_runtime_readiness", userMNativeRuntimeReadinessRows.length], + ["user_m_native_runtime_probe_gate", userMNativeRuntimeProbeGateRows.length], + ["tool_db_transaction_contract", toolDbTransactionPlanRows.length], + ["tool_db_native_runtime_readiness", toolDbNativeRuntimeReadinessRows.length], + ["tool_db_native_runtime_probe_gate", toolDbNativeRuntimeProbeGateRows.length], + ["python_runtime_contract", pythonRuntimeContractRows.length], + ["python_native_runtime_readiness", pythonNativeRuntimeReadinessRows.length], + ["python_native_runtime_probe_gate", pythonNativeRuntimeProbeGateRows.length], + ["python_native_runtime_state_plan", pythonNativeRuntimeStatePlanRows.length], + ["python_native_runtime_fixture_plan", pythonNativeRuntimeFixturePlanRows.length], + ["runtime_contract_summary", runtimeBoundaryContractSummaryRows.length], + ["runtime_family_contract_alignment_consistency", runtimeBoundaryContractSummaryRows.length], + [ + "wasm_inventory_artifact_documentation_coverage", + generatedArtifactDocumentationCoverage.wasmArtifactNames.length, + ], + [ + "native_artifact_documentation_coverage", + generatedArtifactDocumentationCoverage.nativeArtifactTokens.length, + ], + ]), + ); const interp = await createLinuxCncInterpSdk({ locateFile(path) { @@ -4240,6 +6283,25 @@ ].join("\n"), ); + const doWhileBreakBugPath = await writeInterpRegressionFile( + interp, + "do-while-break", + "bug.ngc", + ); + verifyExpectedOutput( + "browser_interp_do_while_break_bug", + interp.runFile(doWhileBreakBugPath), + [ + "file_open=0", + "file_read_count=7", + "file_execute_count=7", + "file_saw_error=0", + "canon_event=PROGRAM_END", + "absent=@end of do-while-loop", + "absent=file_saw_error=1", + ].join("\n"), + ); + const owordBug315Path = await writeInterpRegressionFile( interp, "oword-bug315", @@ -4635,7 +6697,7 @@ ].join("\n"), ); - for (const badFixture of [ + const badInterpFixtures = [ { name: "a_in_canned_cycle", file: "a-in-canned-cycle.ngc", @@ -4783,7 +6845,11 @@ executeCount: 1, errorText: "All axes missing with motion code", }, - ]) { + ]; + if (badInterpFixtures.length !== 21 || new Set(badInterpFixtures.map((fixture) => fixture.file)).size !== badInterpFixtures.length) { + throw new Error("browser_interp_bad_fixture_coverage: list drift"); + } + for (const badFixture of badInterpFixtures) { const badPath = await writeInterpRegressionFile(interp, "bad", badFixture.file); verifyExpectedOutput( `browser_interp_bad_${badFixture.name}`, @@ -4818,7 +6884,7 @@ ].join("\n"), ); - for (const goodArcFixture of [ + const goodArcFixtures = [ { name: "big_imperial_center_format", file: "good-arc.big.imperial.center-format.ngc", @@ -4861,7 +6927,11 @@ firstEnd: 50, firstAxis: 24.986, }, - ]) { + ]; + if (goodArcFixtures.length !== 6 || new Set(goodArcFixtures.map((fixture) => fixture.file)).size !== goodArcFixtures.length) { + throw new Error("browser_interp_good_fixture_coverage: list drift"); + } + for (const goodArcFixture of goodArcFixtures) { const goodArcPath = await writeInterpRegressionFile(interp, "good", goodArcFixture.file); verifyExpectedOutput( `browser_interp_good_arc_${goodArcFixture.name}`, @@ -5044,6 +7114,14 @@ "rotation/abs-pts", "test.ngc", ); + const rotationRegressionFiles = [ + "abs-pts/test.ngc", + "g28/g28.ngc", + "g53/g53.ngc", + ]; + if (rotationRegressionFiles.length !== 3 || new Set(rotationRegressionFiles).size !== rotationRegressionFiles.length) { + throw new Error("browser_interp_rotation_fixture_coverage: list drift"); + } const rotationAbsPtsOutput = interp.runFile(rotationAbsPtsPath); verifyExpectedOutput( "browser_interp_rotation_abs_pts", @@ -5295,6 +7373,12 @@ "magic_comments/param_format_printing", "test.ngc", ); + const magicCommentsRegressionFiles = [ + "param_format_printing/test.ngc", + ]; + if (magicCommentsRegressionFiles.length !== 1 || new Set(magicCommentsRegressionFiles).size !== magicCommentsRegressionFiles.length) { + throw new Error("browser_interp_magic_comments_fixture_coverage: list drift"); + } verifyExpectedOutput( "browser_interp_magic_comments_param_format", interp.runFile(magicCommentsParamFormatPath), @@ -5315,6 +7399,36 @@ ].join("\n"), ); + const m98m99RegressionFiles = [ + "01-basics/test.ngc", + "02-variables/test.ngc", + "03-error-M98-no-P-word/test.ngc", + "04-M98-but-no-sub/test.ngc", + "05-M98-loops/test.ngc", + "06-error-mixed-sub-styles/O...-called-with-O..._call.ngc", + "06-error-mixed-sub-styles/O...-ended-with-O..._endsub.ngc", + "06-error-mixed-sub-styles/O...-sub-called-with-M98.ngc", + "06-error-mixed-sub-styles/O...-sub-ended-with-M99.ngc", + "07-nested-subs/test.ngc", + "08-sub-follows-main/test.ngc", + "09-disable-fanuc-subs/test-fanuc.ngc", + "09-disable-fanuc-subs/test-rs274ngc.ngc", + "10-M98-P001/test.ngc", + "11-main-program-oword/test-illegal-end-main-with-eof.ngc", + "11-main-program-oword/test-illegal-no-m30-before-osub.ngc", + "11-main-program-oword/test-illegal-sub-after-percent.ngc", + "11-main-program-oword/test-legal-end-main-with-m02.ngc", + "11-main-program-oword/test-legal-end-main-with-m2.ngc", + "11-main-program-oword/test-legal-end-main-with-m30.ngc", + "11-main-program-oword/test-legal-end-main-with-percent.ngc", + "13-named-program/test-named.ngc", + "13-named-program/test-numbered.ngc", + "14-o-expression-call/test.ngc", + ]; + if (m98m99RegressionFiles.length !== 24 || new Set(m98m99RegressionFiles).size !== m98m99RegressionFiles.length) { + throw new Error("browser_interp_m98m99_fixture_coverage: list drift"); + } + const m98m99BasicsPath = await writeInterpRegressionFile( interp, "m98m99/01-basics", diff --git a/wasm-port/tests/browser/verify_interp_browser.sh b/wasm-port/tests/browser/verify_interp_browser.sh index 8f15fae..cef8b09 100755 --- a/wasm-port/tests/browser/verify_interp_browser.sh +++ b/wasm-port/tests/browser/verify_interp_browser.sh @@ -34,7 +34,17 @@ RUNTIME_BOUNDARY_PROMOTION_READINESS="$ROOT_DIR/build/wasm/sim-configs-inventory RUNTIME_BOUNDARY_PROMOTION_BLOCKERS="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-promotion-blockers.tsv" RUNTIME_BOUNDARY_POST_NATIVE_PASS_GATES="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-post-native-pass-gates.tsv" RUNTIME_BOUNDARY_HOST_PREFLIGHT="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-host-preflight.tsv" +RUNTIME_BOUNDARY_HOST_REQUIREMENT_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-host-requirement-summary.tsv" +RUNTIME_BOUNDARY_HOST_UNBLOCK_PLAN="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-host-unblock-plan.tsv" +RUNTIME_BOUNDARY_FAMILY_HOST_READINESS="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-family-host-readiness.tsv" +RUNTIME_BOUNDARY_HOST_READINESS_ROLLUP="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv" +RUNTIME_BOUNDARY_OPT_IN_PROBE_DISPATCH_PLAN="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-plan.tsv" +RUNTIME_BOUNDARY_OPT_IN_PROBE_DISPATCH_ROLLUP="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-rollup.tsv" +RUNTIME_BOUNDARY_OPT_IN_PROBE_SKIP_EVIDENCE_CONTRACT="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-contract.tsv" +RUNTIME_BOUNDARY_OPT_IN_PROBE_SKIP_EVIDENCE_ROLLUP="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv" +RUNTIME_BOUNDARY_NATIVE_EVIDENCE_ACCEPTANCE_GATE="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-native-evidence-acceptance-gate.tsv" BOUNDARY_PHASE_COMPLETION_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/boundary-phase-completion-summary.tsv" +NEXT_BOUNDARY_WORKLIST="$ROOT_DIR/build/wasm/sim-configs-inventory/next-boundary-worklist.tsv" NEXT_BOUNDARY_RECOMMENDATIONS="$ROOT_DIR/build/wasm/sim-configs-inventory/next-boundary-recommendations.tsv" INVENTORY_SCRIPT="$ROOT_DIR/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs" NATIVE_SOURCE_PROOF_SUMMARY="$ROOT_DIR/build/native/native-source-proof-summary.tsv" @@ -84,7 +94,17 @@ for artifact in \ "$RUNTIME_BOUNDARY_PROMOTION_BLOCKERS" \ "$RUNTIME_BOUNDARY_POST_NATIVE_PASS_GATES" \ "$RUNTIME_BOUNDARY_HOST_PREFLIGHT" \ + "$RUNTIME_BOUNDARY_HOST_REQUIREMENT_SUMMARY" \ + "$RUNTIME_BOUNDARY_HOST_UNBLOCK_PLAN" \ + "$RUNTIME_BOUNDARY_FAMILY_HOST_READINESS" \ + "$RUNTIME_BOUNDARY_HOST_READINESS_ROLLUP" \ + "$RUNTIME_BOUNDARY_OPT_IN_PROBE_DISPATCH_PLAN" \ + "$RUNTIME_BOUNDARY_OPT_IN_PROBE_DISPATCH_ROLLUP" \ + "$RUNTIME_BOUNDARY_OPT_IN_PROBE_SKIP_EVIDENCE_CONTRACT" \ + "$RUNTIME_BOUNDARY_OPT_IN_PROBE_SKIP_EVIDENCE_ROLLUP" \ + "$RUNTIME_BOUNDARY_NATIVE_EVIDENCE_ACCEPTANCE_GATE" \ "$BOUNDARY_PHASE_COMPLETION_SUMMARY" \ + "$NEXT_BOUNDARY_WORKLIST" \ "$NEXT_BOUNDARY_RECOMMENDATIONS"; do if [[ ! -f "$artifact" || "$INVENTORY_SCRIPT" -nt "$artifact" ]]; then INVENTORY_REQUIRED=1 @@ -117,6 +137,33 @@ fi if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_HOST_PREFLIGHT" ]]; then INVENTORY_REQUIRED=1 fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_HOST_REQUIREMENT_SUMMARY" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_HOST_UNBLOCK_PLAN" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_FAMILY_HOST_READINESS" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_HOST_READINESS_ROLLUP" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_OPT_IN_PROBE_DISPATCH_PLAN" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_OPT_IN_PROBE_DISPATCH_ROLLUP" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_OPT_IN_PROBE_SKIP_EVIDENCE_CONTRACT" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_OPT_IN_PROBE_SKIP_EVIDENCE_ROLLUP" ]]; then + INVENTORY_REQUIRED=1 +fi +if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$RUNTIME_BOUNDARY_NATIVE_EVIDENCE_ACCEPTANCE_GATE" ]]; then + INVENTORY_REQUIRED=1 +fi if [[ "$INVENTORY_REQUIRED" == "1" ]]; then SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_sim_configs_inventory_wasm.sh" fi diff --git a/wasm-port/tests/wasm/node/verify_interp_wasm.mjs b/wasm-port/tests/wasm/node/verify_interp_wasm.mjs index 8e4d0a6..9939eb5 100644 --- a/wasm-port/tests/wasm/node/verify_interp_wasm.mjs +++ b/wasm-port/tests/wasm/node/verify_interp_wasm.mjs @@ -266,6 +266,35 @@ function writeInterpRegressionFiles(name, files) { return wasmDir; } +function interpRegressionNgcFiles(name) { + const sourceDir = resolve(rootDir, "vendor/linuxcnc/tests/interp", name); + return readdirSync(sourceDir) + .filter((filename) => filename.endsWith(".ngc")) + .sort(); +} + +function interpRegressionNgcFilesRecursive(name) { + const sourceDir = resolve(rootDir, "vendor/linuxcnc/tests/interp", name); + const files = []; + + function walk(relativeDir) { + const directory = resolve(sourceDir, relativeDir); + for (const entry of readdirSync(directory, { withFileTypes: true })) { + const relativePath = relativeDir ? `${relativeDir}/${entry.name}` : entry.name; + if (entry.isDirectory()) { + walk(relativePath); + continue; + } + if (entry.isFile() && entry.name.endsWith(".ngc")) { + files.push(relativePath); + } + } + } + + walk(""); + return files.sort(); +} + function writeCcompRegressionFiles(name) { const sourceDir = resolve(rootDir, "vendor/linuxcnc/tests/ccomp", name); const wasmDir = `/work/ccomp/${name}`; @@ -1210,6 +1239,21 @@ verifyExpectedOutput( ].join("\n"), ); +const doWhileBreakBugPath = writeInterpRegressionFile("do-while-break", "bug.ngc"); +verifyExpectedOutput( + "interp_do_while_break_bug_wasm", + interp.runFile(doWhileBreakBugPath), + [ + "file_open=0", + "file_read_count=7", + "file_execute_count=7", + "file_saw_error=0", + "canon_event=PROGRAM_END", + "absent=@end of do-while-loop", + "absent=file_saw_error=1", + ].join("\n"), +); + const owordBug315Path = writeInterpRegressionFile("oword-bug315", "test.ngc"); verifyExpectedOutput( "interp_oword_bug315_wasm", @@ -1550,7 +1594,7 @@ verifyExpectedOutput( ].join("\n"), ); -for (const badFixture of [ +const badInterpFixtures = [ { name: "a_in_canned_cycle", file: "a-in-canned-cycle.ngc", @@ -1698,7 +1742,13 @@ for (const badFixture of [ executeCount: 1, errorText: "All axes missing with motion code", }, -]) { +]; +assert.deepEqual( + badInterpFixtures.map((fixture) => fixture.file).sort(), + interpRegressionNgcFiles("bad"), + "interp bad fixture coverage drift", +); +for (const badFixture of badInterpFixtures) { const badPath = writeInterpRegressionFile("bad", badFixture.file); verifyExpectedOutput( `interp_bad_${badFixture.name}_wasm`, @@ -1733,7 +1783,7 @@ verifyExpectedOutput( ].join("\n"), ); -for (const goodArcFixture of [ +const goodArcFixtures = [ { name: "big_imperial_center_format", file: "good-arc.big.imperial.center-format.ngc", @@ -1776,7 +1826,13 @@ for (const goodArcFixture of [ firstEnd: 50, firstAxis: 24.986, }, -]) { +]; +assert.deepEqual( + goodArcFixtures.map((fixture) => fixture.file).sort(), + interpRegressionNgcFiles("good"), + "interp good fixture coverage drift", +); +for (const goodArcFixture of goodArcFixtures) { const goodArcPath = writeInterpRegressionFile("good", goodArcFixture.file); verifyExpectedOutput( `interp_good_arc_${goodArcFixture.name}_wasm`, @@ -1952,6 +2008,16 @@ verifyExpectedOutput( ); const rotationAbsPtsPath = writeInterpRegressionFile("rotation/abs-pts", "test.ngc"); +const rotationRegressionFiles = [ + "abs-pts/test.ngc", + "g28/g28.ngc", + "g53/g53.ngc", +]; +assert.deepEqual( + rotationRegressionFiles, + interpRegressionNgcFilesRecursive("rotation"), + "interp_rotation_fixture_coverage: upstream .ngc manifest", +); const rotationAbsPtsOutput = interp.runFile(rotationAbsPtsPath); verifyExpectedOutput( "interp_rotation_abs_pts_wasm", @@ -2198,6 +2264,14 @@ const magicCommentsParamFormatPath = writeInterpRegressionFile( "magic_comments/param_format_printing", "test.ngc", ); +const magicCommentsRegressionFiles = [ + "param_format_printing/test.ngc", +]; +assert.deepEqual( + magicCommentsRegressionFiles, + interpRegressionNgcFilesRecursive("magic_comments"), + "interp_magic_comments_fixture_coverage: upstream .ngc manifest", +); verifyExpectedOutput( "interp_magic_comments_param_format_wasm", interp.runFile(magicCommentsParamFormatPath), @@ -2218,6 +2292,38 @@ verifyExpectedOutput( ].join("\n"), ); +const m98m99RegressionFiles = [ + "01-basics/test.ngc", + "02-variables/test.ngc", + "03-error-M98-no-P-word/test.ngc", + "04-M98-but-no-sub/test.ngc", + "05-M98-loops/test.ngc", + "06-error-mixed-sub-styles/O...-called-with-O..._call.ngc", + "06-error-mixed-sub-styles/O...-ended-with-O..._endsub.ngc", + "06-error-mixed-sub-styles/O...-sub-called-with-M98.ngc", + "06-error-mixed-sub-styles/O...-sub-ended-with-M99.ngc", + "07-nested-subs/test.ngc", + "08-sub-follows-main/test.ngc", + "09-disable-fanuc-subs/test-fanuc.ngc", + "09-disable-fanuc-subs/test-rs274ngc.ngc", + "10-M98-P001/test.ngc", + "11-main-program-oword/test-illegal-end-main-with-eof.ngc", + "11-main-program-oword/test-illegal-no-m30-before-osub.ngc", + "11-main-program-oword/test-illegal-sub-after-percent.ngc", + "11-main-program-oword/test-legal-end-main-with-m02.ngc", + "11-main-program-oword/test-legal-end-main-with-m2.ngc", + "11-main-program-oword/test-legal-end-main-with-m30.ngc", + "11-main-program-oword/test-legal-end-main-with-percent.ngc", + "13-named-program/test-named.ngc", + "13-named-program/test-numbered.ngc", + "14-o-expression-call/test.ngc", +]; +assert.deepEqual( + m98m99RegressionFiles, + interpRegressionNgcFilesRecursive("m98m99"), + "interp m98m99 fixture coverage drift", +); + const m98m99BasicsPath = writeInterpRegressionFile("m98m99/01-basics", "test.ngc"); verifyExpectedOutput( "interp_m98m99_01_basics_wasm", diff --git a/wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs b/wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs index 972ae1d..7a5a508 100644 --- a/wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs +++ b/wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs @@ -56,8 +56,10 @@ const nativeClassSummaryPath = resolve(rootDir, "build/native/sim-configs/class- const nativePathMatrixPath = resolve(rootDir, "build/native/sim-configs/path-matrix.tsv"); const nativeSourceProofSummaryPath = resolve(rootDir, "build/native/native-source-proof-summary.tsv"); const nativeRuntimeProbeSummarySourcePath = resolve(rootDir, "build/native/native-runtime-probe-summary.tsv"); +const compatibilityValidationPath = resolve(rootDir, "docs/compatibility-validation.md"); const trackedMatrixPath = resolve(rootDir, "docs/sim-configs-coverage-matrix.md"); const fullProcessBoundaryDesignPath = resolve(rootDir, "docs/full-process-boundary-design.md"); +const browserInterpSmokePath = resolve(rootDir, "tests/browser/interp_smoke.html"); const buildDir = resolve(rootDir, "build/wasm/sim-configs-inventory"); const summaryPath = resolve(buildDir, "summary.tsv"); const skipSummaryPath = resolve(buildDir, "skip-summary.tsv"); @@ -103,7 +105,72 @@ const runtimeBoundaryPromotionReadinessPath = resolve(buildDir, "runtime-boundar const runtimeBoundaryPromotionBlockersPath = resolve(buildDir, "runtime-boundary-promotion-blockers.tsv"); const runtimeBoundaryPostNativePassGatesPath = resolve(buildDir, "runtime-boundary-post-native-pass-gates.tsv"); const runtimeBoundaryHostPreflightPath = resolve(buildDir, "runtime-boundary-host-preflight.tsv"); +const runtimeBoundaryHostRequirementSummaryPath = resolve(buildDir, "runtime-boundary-host-requirement-summary.tsv"); +const runtimeBoundaryHostUnblockPlanPath = resolve(buildDir, "runtime-boundary-host-unblock-plan.tsv"); +const runtimeBoundaryFamilyHostReadinessPath = resolve(buildDir, "runtime-boundary-family-host-readiness.tsv"); +const runtimeBoundaryHostReadinessRollupPath = resolve(buildDir, "runtime-boundary-host-readiness-rollup.tsv"); +const runtimeBoundaryOptInProbeDispatchPlanPath = resolve(buildDir, "runtime-boundary-opt-in-probe-dispatch-plan.tsv"); +const runtimeBoundaryOptInProbeDispatchRollupPath = resolve(buildDir, "runtime-boundary-opt-in-probe-dispatch-rollup.tsv"); +const runtimeBoundaryOptInProbeSkipEvidenceContractPath = resolve(buildDir, "runtime-boundary-opt-in-probe-skip-evidence-contract.tsv"); +const runtimeBoundaryOptInProbeSkipEvidenceRollupPath = resolve(buildDir, "runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv"); +const runtimeBoundaryNativeEvidenceAcceptanceGatePath = resolve(buildDir, "runtime-boundary-native-evidence-acceptance-gate.tsv"); const nextBoundaryRecommendationsPath = resolve(buildDir, "next-boundary-recommendations.tsv"); +const generatedSimConfigInventoryArtifactPaths = [ + summaryPath, + skipSummaryPath, + boundarySummaryPath, + iniBoundarySummaryPath, + blockedDependencySummaryPath, + fullProcessBoundarySummaryPath, + userMProcessBoundarySummaryPath, + userMProcessStateTargetsPath, + userMProcessTransitionPlanPath, + userMProcessNativeStateAlignmentPath, + userMProcessNativeTransitionAlignmentPath, + userMProcessNativeRuntimeStatePlanPath, + userMProcessNativeRuntimeReadinessPath, + userMProcessNativeRuntimeProbeGatePath, + toolDbProcessBoundarySummaryPath, + toolDbProcessProtocolGatesPath, + toolDbProcessTransactionPlanPath, + toolDbProcessNativeProtocolAlignmentPath, + toolDbProcessNativeRuntimeReadinessPath, + toolDbProcessNativeRuntimeProbeGatePath, + pythonRemapBoundarySummaryPath, + pythonRemapRuntimeGatesPath, + pythonRemapNativeRuntimeAlignmentPath, + pythonRemapNativeRuntimeReadinessPath, + pythonRemapNativeRuntimeProbeGatePath, + pythonRemapNativeRuntimeStatePlanPath, + pythonRemapNativeRuntimeFixturePlanPath, + pythonRemapFamilySummaryPath, + pythonRemapRuntimeContractPath, + runtimeBoundaryContractSummaryPath, + boundaryPhaseCompletionSummaryPath, + nextBoundaryWorklistPath, + boundaryProofGatesPath, + nativeProofAlignmentSummaryPath, + nativeRuntimeProbeSummaryPath, + runtimeBoundaryNativeAlignmentSummaryPath, + runtimeProbeGateAlignmentPath, + nativeRuntimeProbeExecutionPlanPath, + nativeRuntimeProbePassEvidenceContractPath, + blockedRuntimePromotionLockPath, + runtimeBoundaryPromotionReadinessPath, + runtimeBoundaryPromotionBlockersPath, + runtimeBoundaryPostNativePassGatesPath, + runtimeBoundaryHostPreflightPath, + runtimeBoundaryHostRequirementSummaryPath, + runtimeBoundaryHostUnblockPlanPath, + runtimeBoundaryFamilyHostReadinessPath, + runtimeBoundaryHostReadinessRollupPath, + runtimeBoundaryOptInProbeDispatchPlanPath, + runtimeBoundaryOptInProbeDispatchRollupPath, + runtimeBoundaryOptInProbeSkipEvidenceContractPath, + runtimeBoundaryOptInProbeSkipEvidenceRollupPath, + runtimeBoundaryNativeEvidenceAcceptanceGatePath, + nextBoundaryRecommendationsPath, +]; mkdirSync(buildDir, { recursive: true }); @@ -4968,6 +5035,8 @@ function verifyRuntimeBoundaryContractSummaryRows(rows) { } function boundaryPhaseCompletionSummaryRows({ + generatedArtifactPaths, + documentationPaths, boundaryRows, iniBoundaryRows, blockedRows, @@ -4986,16 +5055,32 @@ function boundaryPhaseCompletionSummaryRows({ pythonRuntimeStatePlanRows, pythonRuntimeFixturePlanRows, runtimeContractSummaryRows, + runtimeFamilyContractAlignmentConsistencyOk, nativeProofAlignmentRows, nativeRuntimeProbeRows, runtimeNativeAlignmentRows, runtimeProbeGateAlignmentRows, + blockedRuntimeWorklistRecommendationConsistencyOk, nativeRuntimeProbeExecutionPlanRows, nativeRuntimeProbePassEvidenceRows, + blockedRuntimeProbeExecutionConsistencyOk, runtimeBoundaryPromotionReadinessRows, runtimeBoundaryPromotionBlockerRows, runtimeBoundaryPostNativePassGateRows, + blockedRuntimePromotionGateConsistencyOk, runtimeBoundaryHostPreflightRows, + runtimeBoundaryHostRequirementSummaryRows, + runtimeBoundaryHostUnblockPlanRows, + runtimeBoundaryFamilyHostReadinessRows, + runtimeBoundaryHostReadinessRollupRows, + blockedRuntimeHostRequirementConsistencyOk, + runtimeBoundaryOptInProbeDispatchPlanRows, + runtimeBoundaryOptInProbeDispatchRollupRows, + runtimeBoundaryOptInProbeSkipEvidenceContractRows, + runtimeBoundaryOptInProbeSkipEvidenceRollupRows, + runtimeBoundaryNativeEvidenceAcceptanceGateRows, + nextBoundaryRecommendationRows, + blockedRuntimeRollupConsistencyOk, trackedRows, inventoryRows, }) { @@ -5017,6 +5102,20 @@ function boundaryPhaseCompletionSummaryRows({ const [path, inventoryStatus, reason] = row.split("\t"); return [path, { inventoryStatus, reason }]; })); + const documentationText = documentationPaths + .map((documentationPath) => readFileSync(documentationPath, "utf8")) + .join("\n"); + const wasmArtifactNames = [...new Set(generatedArtifactPaths.map((artifactPath) => basename(artifactPath)))].sort(); + const wasmArtifactDocumentationOk = ( + wasmArtifactNames.length === generatedArtifactPaths.length && + wasmArtifactNames.length > 0 && + wasmArtifactNames.every((artifactName) => documentationText.includes(artifactName)) + ); + const nativeArtifactTokens = generatedTsvArtifactTokens(resolve(rootDir, "build/native")); + const nativeArtifactDocumentationOk = ( + nativeArtifactTokens.length > 0 && + nativeArtifactTokens.every((artifactToken) => documentationText.includes(artifactToken)) + ); const vendoredBoundaryOk = vendoredBoundaryRows.every((row) => row.report_available === "1"); const hardBlockedEvidenceOk = hardBlockedRows.every((row) => { @@ -5402,6 +5501,200 @@ function boundaryPhaseCompletionSummaryRows({ row.promotion_allowed === "0", ) ); + const runtimeBoundaryHostRequirementSummaryOk = ( + runtimeBoundaryHostRequirementSummaryRows.length >= 8 && + runtimeBoundaryHostRequirementSummaryRows.every((row) => + ["host_command", "source_or_module", "runtime_input"].includes(row.requirement_kind) && + ["0", "1"].includes(row.available) && + /^[1-9][0-9]*$/.test(row.required_by_count) && + row.required_by !== "-" && + row.blocked_families !== "-" && + row.opt_in_envs !== "-" && + ( + (row.available === "0" && + row.missing_for !== "-" && + row.missing_blocks_runtime === "1") || + (row.available === "1" && + row.missing_for === "-" && + row.missing_blocks_runtime === "0") + ) && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); + const runtimeBoundaryHostUnblockPlanOk = ( + runtimeBoundaryHostUnblockPlanRows.length === 4 && + runtimeBoundaryHostUnblockPlanRows.every((row) => + row.requirement_kind === "host_command" && + row.available === "0" && + row.missing_for !== "-" && + row.affected_targets !== "-" && + row.opt_in_envs !== "-" && + row.affected_execution_commands !== "-" && + row.remaining_missing_after_requirement !== "-" && + row.unblocks_when_available === "partial_unblock_other_requirements_remain" && + row.next_action === "provide_missing_host_requirement" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); + const runtimeBoundaryFamilyHostReadinessOk = ( + runtimeBoundaryFamilyHostReadinessRows.length === 3 && + runtimeBoundaryFamilyHostReadinessRows.every((row) => + /^[1-9][0-9]*$/.test(row.missing_requirement_count) && + row.missing_host_requirements !== "-" && + /^[1-9][0-9]*$/.test(row.available_requirement_count) && + row.available_requirements !== "-" && + /^ENABLE_[A-Z0-9_]+=1$/.test(row.opt_in_env) && + row.execution_command.includes(row.opt_in_env) && + row.preflight_status === "blocked_missing_host_runtime" && + row.family_host_status === "blocked_missing_host_requirements" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); + const runtimeBoundaryHostReadinessRollupOk = ( + runtimeBoundaryHostReadinessRollupRows.length === 1 && + runtimeBoundaryHostReadinessRollupRows.every((row) => + row.scope === "blocked_runtime_host_readiness" && + row.family_count === "3" && + row.host_ready_family_count === "0" && + row.host_blocked_family_count === "3" && + row.blocked_families !== "-" && + row.missing_host_requirements !== "-" && + row.missing_host_requirement_count === "4" && + row.ready_opt_in_command_count === "0" && + row.ready_opt_in_commands === "-" && + row.blocked_opt_in_commands !== "-" && + row.host_readiness_status === "host_blocked_for_all_opt_in_native_probes" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); + const runtimeBoundaryOptInProbeDispatchPlanOk = ( + runtimeBoundaryOptInProbeDispatchPlanRows.length === 3 && + runtimeBoundaryOptInProbeDispatchPlanRows.every((row) => + /^ENABLE_[A-Z0-9_]+=1$/.test(row.opt_in_env) && + row.execution_command.includes(row.opt_in_env) && + row.family_host_status === "blocked_missing_host_requirements" && + row.missing_host_requirements !== "-" && + row.host_action === "skip_missing_host_requirements" && + row.dispatch_allowed === "0" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); + const runtimeBoundaryOptInProbeDispatchRollupOk = ( + runtimeBoundaryOptInProbeDispatchRollupRows.length === 1 && + runtimeBoundaryOptInProbeDispatchRollupRows.every((row) => + row.scope === "blocked_runtime_opt_in_probe_dispatch" && + row.probe_count === "3" && + row.dispatch_allowed_count === "0" && + row.dispatch_blocked_count === "3" && + row.dispatch_actions === "skip_missing_host_requirements" && + row.dispatch_status === "dispatch_blocked_for_all_opt_in_native_probes" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); + const runtimeBoundaryOptInProbeSkipEvidenceContractOk = ( + runtimeBoundaryOptInProbeSkipEvidenceContractRows.length === 3 && + runtimeBoundaryOptInProbeSkipEvidenceContractRows.every((row) => + row.host_action === "skip_missing_host_requirements" && + row.skip_reason === "missing_host_requirements" && + row.missing_host_requirements !== "-" && + row.current_probe_status === "skipped_missing_host_runtime" && + row.evidence_required_now === "0" && + row.observed_evidence_ready === "0" && + row.skip_evidence_status === "skip_valid_until_host_requirements_available" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); + const runtimeBoundaryOptInProbeSkipEvidenceRollupOk = ( + runtimeBoundaryOptInProbeSkipEvidenceRollupRows.length === 1 && + runtimeBoundaryOptInProbeSkipEvidenceRollupRows.every((row) => + row.scope === "blocked_runtime_opt_in_probe_skip_evidence" && + row.probe_count === "3" && + row.skip_count === "3" && + row.evidence_required_now_count === "0" && + row.observed_evidence_ready_count === "0" && + row.skip_evidence_statuses === "skip_valid_until_host_requirements_available" && + row.evidence_rollup_status === "no_native_pass_evidence_accepted_while_host_blocked" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); + const runtimeBoundaryNativeEvidenceAcceptanceGateOk = ( + runtimeBoundaryNativeEvidenceAcceptanceGateRows.length === 3 && + runtimeBoundaryNativeEvidenceAcceptanceGateRows.every((row) => + row.current_probe_status === "skipped_missing_host_runtime" && + row.native_evidence_status === "pending_until_native_pass" && + row.evidence_required_now === "0" && + row.observed_evidence_ready === "0" && + row.native_evidence_gate === "blocked_until_host_requirements_available" && + row.evidence_acceptance_allowed === "0" && + row.promotion_ready === "0" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) + ); + const expectedBlockedRuntimeClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + const classOrderMatches = (rows, field = "boundary_class") => + rows.map((row) => row[field]).join(",") === expectedBlockedRuntimeClasses.join(","); + const blockedRuntimeOptInGateConsistencyOk = ( + classOrderMatches(nextBoundaryRecommendationRows, "blocked") && + classOrderMatches(nativeRuntimeProbeExecutionPlanRows) && + classOrderMatches(runtimeBoundaryPromotionReadinessRows) && + classOrderMatches(runtimeBoundaryPromotionBlockerRows) && + classOrderMatches(runtimeBoundaryPostNativePassGateRows) && + classOrderMatches(runtimeBoundaryHostPreflightRows) && + classOrderMatches(runtimeBoundaryFamilyHostReadinessRows) && + classOrderMatches(runtimeBoundaryOptInProbeDispatchPlanRows) && + classOrderMatches(runtimeBoundaryOptInProbeSkipEvidenceContractRows) && + classOrderMatches(runtimeBoundaryNativeEvidenceAcceptanceGateRows) && + runtimeBoundaryOptInProbeDispatchPlanRows.every((row) => + row.dispatch_allowed === "0" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) && + runtimeBoundaryNativeEvidenceAcceptanceGateRows.every((row) => + row.evidence_acceptance_allowed === "0" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) && + runtimeBoundaryPromotionBlockerRows.every((row) => + row.blocker_keys.includes("host_runtime_requirements_missing") && + row.blocker_keys.includes("promotion_lock_active") && + row.blocker_keys.includes("manual_lock_update_required"), + ) + ); + const nativeSourceProofRuntimeProbeConsistencyOk = ( + classOrderMatches(nativeProofAlignmentRows) && + classOrderMatches(nativeRuntimeProbeRows) && + classOrderMatches(runtimeProbeGateAlignmentRows) && + nativeProofAlignmentRows.every((row) => + row.alignment_ok === "1" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) && + nativeRuntimeProbeRows.every((row) => + row.source_proof_ready === "1" && + row.execution_enabled === "0" && + row.promotion_allowed === "0", + ) && + runtimeProbeGateAlignmentRows.every((row) => + row.native_source_proof_ready === "1" && + row.gate_source_proof_ready === "1" && + row.execution_disabled_match === "1" && + row.promotion_disabled_match === "1" && + row.alignment_ok === "1", + ) + ); return [ [ @@ -5458,6 +5751,12 @@ function boundaryPhaseCompletionSummaryRows({ String(runtimeProbeGateAlignmentRows.length), "native runtime probe summary agrees with generated runtime probe gates before any blocked boundary can be promoted", ], + [ + "blocked_runtime_worklist_recommendation_consistency", + flagValue(blockedRuntimeWorklistRecommendationConsistencyOk), + String(nextBoundaryRecommendationRows.length), + "blocked runtime recommendations agree with next-boundary worklist, proof gates, promotion locks, and runtime contract summaries", + ], [ "native_runtime_probe_execution_plan", flagValue(nativeRuntimeProbeExecutionPlanOk), @@ -5470,6 +5769,12 @@ function boundaryPhaseCompletionSummaryRows({ String(nativeRuntimeProbePassEvidenceRows.length), "native runtime probe pass evidence contract records stdout evidence required after each native pass before Node/browser promotion work", ], + [ + "blocked_runtime_probe_execution_consistency", + flagValue(blockedRuntimeProbeExecutionConsistencyOk), + String(nativeRuntimeProbeExecutionPlanRows.length), + "blocked runtime execution plan, pass evidence, preflight, dispatch, and skip-evidence contracts agree while probes remain host-blocked", + ], [ "runtime_boundary_promotion_readiness", flagValue(runtimeBoundaryPromotionReadinessOk), @@ -5488,12 +5793,96 @@ function boundaryPhaseCompletionSummaryRows({ String(runtimeBoundaryPostNativePassGateRows.length), "runtime boundary post-native-pass gates require Node/browser proof and manual lock updates before any promotion", ], + [ + "blocked_runtime_promotion_gate_consistency", + flagValue(blockedRuntimePromotionGateConsistencyOk), + String(runtimeBoundaryPromotionReadinessRows.length), + "blocked runtime promotion lock, readiness, blockers, post-native gates, evidence acceptance, and pass evidence contracts agree while promotion remains disabled", + ], [ "runtime_boundary_host_preflight", flagValue(runtimeBoundaryHostPreflightOk), String(runtimeBoundaryHostPreflightRows.length), "runtime boundary host preflight summarizes required runtimes, missing host requirements, and opt-in native probe commands without execution", ], + [ + "runtime_boundary_host_requirement_summary", + flagValue(runtimeBoundaryHostRequirementSummaryOk), + String(runtimeBoundaryHostRequirementSummaryRows.length), + "runtime boundary host requirement summary normalizes missing and available host requirements across blocked runtime families", + ], + [ + "runtime_boundary_host_unblock_plan", + flagValue(runtimeBoundaryHostUnblockPlanOk), + String(runtimeBoundaryHostUnblockPlanRows.length), + "runtime boundary host unblock plan records which missing host commands affect which opt-in native probes without executing them", + ], + [ + "runtime_boundary_family_host_readiness", + flagValue(runtimeBoundaryFamilyHostReadinessOk), + String(runtimeBoundaryFamilyHostReadinessRows.length), + "runtime boundary family host readiness records per-family missing and available host prerequisites before opt-in native probes", + ], + [ + "blocked_runtime_host_requirement_consistency", + flagValue(blockedRuntimeHostRequirementConsistencyOk), + String(runtimeBoundaryFamilyHostReadinessRows.length), + "blocked runtime host requirement summary, unblock plan, and family readiness agree on missing requirements, targets, commands, and disabled execution/promotion", + ], + [ + "runtime_boundary_host_readiness_rollup", + flagValue(runtimeBoundaryHostReadinessRollupOk), + String(runtimeBoundaryHostReadinessRollupRows.length), + "runtime boundary host readiness rollup records the aggregate host readiness decision for all blocked runtime opt-in probes", + ], + [ + "runtime_boundary_opt_in_probe_dispatch_plan", + flagValue(runtimeBoundaryOptInProbeDispatchPlanOk), + String(runtimeBoundaryOptInProbeDispatchPlanRows.length), + "runtime boundary opt-in probe dispatch plan records the per-probe host action before any native runtime probe can run", + ], + [ + "runtime_boundary_opt_in_probe_dispatch_rollup", + flagValue(runtimeBoundaryOptInProbeDispatchRollupOk), + String(runtimeBoundaryOptInProbeDispatchRollupRows.length), + "runtime boundary opt-in probe dispatch rollup records the aggregate dispatch switch for blocked runtime native probes", + ], + [ + "runtime_boundary_opt_in_probe_skip_evidence_contract", + flagValue(runtimeBoundaryOptInProbeSkipEvidenceContractOk), + String(runtimeBoundaryOptInProbeSkipEvidenceContractRows.length), + "runtime boundary opt-in probe skip evidence contract records why skipped probes do not provide native pass evidence yet", + ], + [ + "runtime_boundary_opt_in_probe_skip_evidence_rollup", + flagValue(runtimeBoundaryOptInProbeSkipEvidenceRollupOk), + String(runtimeBoundaryOptInProbeSkipEvidenceRollupRows.length), + "runtime boundary opt-in probe skip evidence rollup records the aggregate native evidence acceptance state while probes are skipped", + ], + [ + "blocked_runtime_rollup_consistency", + flagValue(blockedRuntimeRollupConsistencyOk), + String(expectedBlockedRuntimeClasses.length), + "blocked runtime host readiness, dispatch, and skip-evidence rollups agree on counts, families, commands, missing requirements, and disabled execution/promotion", + ], + [ + "runtime_boundary_native_evidence_acceptance_gate", + flagValue(runtimeBoundaryNativeEvidenceAcceptanceGateOk), + String(runtimeBoundaryNativeEvidenceAcceptanceGateRows.length), + "runtime boundary native evidence acceptance gate keeps native evidence and downstream promotion gates closed while opt-in probes are host-blocked", + ], + [ + "blocked_runtime_opt_in_gate_consistency", + flagValue(blockedRuntimeOptInGateConsistencyOk), + String(expectedBlockedRuntimeClasses.length), + "blocked runtime opt-in target artifacts agree on class order, target gates, host blocking, dispatch blocking, evidence blocking, and promotion locks", + ], + [ + "native_source_proof_runtime_probe_consistency", + flagValue(nativeSourceProofRuntimeProbeConsistencyOk), + String(expectedBlockedRuntimeClasses.length), + "native source proof alignment, native runtime probe summary, and runtime probe gate alignment agree while execution and promotion remain disabled", + ], [ "user_m_transition_contract", flagValue(userMTransitionContractOk), @@ -5572,6 +5961,24 @@ function boundaryPhaseCompletionSummaryRows({ String(runtimeContractSummaryRows.length), "Runtime boundary contract summary unifies user-M, tool DB, and Python contracts with pending disabled proof state", ], + [ + "runtime_family_contract_alignment_consistency", + flagValue(runtimeFamilyContractAlignmentConsistencyOk), + String(runtimeContractSummaryRows.length), + "Family-specific user-M, tool DB, and Python contract/readiness/probe artifacts agree with runtime contract and native alignment summaries", + ], + [ + "wasm_inventory_artifact_documentation_coverage", + flagValue(wasmArtifactDocumentationOk), + String(wasmArtifactNames.length), + "Every generated WASM sim-config inventory TSV artifact is referenced by the compatibility, matrix, or full-process boundary docs", + ], + [ + "native_artifact_documentation_coverage", + flagValue(nativeArtifactDocumentationOk), + String(nativeArtifactTokens.length), + "Every generated native TSV artifact is referenced by relative path in the compatibility, matrix, or full-process boundary docs", + ], ].map((row) => row.map(tsvValue).join("\t")); } @@ -5591,12 +5998,28 @@ function verifyBoundaryPhaseCompletionSummaryRows(rows) { "runtime_native_alignment_artifacts", "native_runtime_probe_summary", "runtime_probe_gate_alignment", + "blocked_runtime_worklist_recommendation_consistency", "native_runtime_probe_execution_plan", "native_runtime_probe_pass_evidence_contract", + "blocked_runtime_probe_execution_consistency", "runtime_boundary_promotion_readiness", "runtime_boundary_promotion_blockers", "runtime_boundary_post_native_pass_gates", + "blocked_runtime_promotion_gate_consistency", "runtime_boundary_host_preflight", + "runtime_boundary_host_requirement_summary", + "runtime_boundary_host_unblock_plan", + "runtime_boundary_family_host_readiness", + "blocked_runtime_host_requirement_consistency", + "runtime_boundary_host_readiness_rollup", + "runtime_boundary_opt_in_probe_dispatch_plan", + "runtime_boundary_opt_in_probe_dispatch_rollup", + "runtime_boundary_opt_in_probe_skip_evidence_contract", + "runtime_boundary_opt_in_probe_skip_evidence_rollup", + "blocked_runtime_rollup_consistency", + "runtime_boundary_native_evidence_acceptance_gate", + "blocked_runtime_opt_in_gate_consistency", + "native_source_proof_runtime_probe_consistency", "user_m_transition_contract", "user_m_native_runtime_state_plan", "user_m_native_runtime_readiness", @@ -5610,6 +6033,9 @@ function verifyBoundaryPhaseCompletionSummaryRows(rows) { "python_native_runtime_state_plan", "python_native_runtime_fixture_plan", "runtime_contract_summary", + "runtime_family_contract_alignment_consistency", + "wasm_inventory_artifact_documentation_coverage", + "native_artifact_documentation_coverage", ]; assert.deepEqual( @@ -5622,6 +6048,17 @@ function verifyBoundaryPhaseCompletionSummaryRows(rows) { assert.ok(/^[1-9][0-9]*$/.test(row.count), `${row.criterion}: invalid criterion count`); assert.notEqual(row.evidence, "-", `${row.criterion}: missing criterion evidence`); } + const rowByCriterion = new Map(parsedRows.map((row) => [row.criterion, row])); + assert.equal( + rowByCriterion.get("wasm_inventory_artifact_documentation_coverage")?.count, + "54", + "WASM artifact documentation completion count must match generated artifact baseline", + ); + assert.equal( + rowByCriterion.get("native_artifact_documentation_coverage")?.count, + "8", + "native artifact documentation completion count must match generated artifact baseline", + ); return parsedRows; } @@ -6661,6 +7098,1771 @@ function verifyRuntimeBoundaryHostPreflightRows({ return parsedRows; } +function runtimeRequirementKind(requirement) { + if (["python3", "tclsh", "halrun", "halcmd", "linuxcnc", "milltask"].includes(requirement)) { + return "host_command"; + } + if (requirement.endsWith(".cc") || requirement.endsWith(".py") || requirement.endsWith(".so")) { + return "source_or_module"; + } + return "runtime_input"; +} + +function runtimeBoundaryHostRequirementSummaryRows(preflightRows) { + const byRequirement = new Map(); + + for (const row of preflightRows) { + for (const token of splitSummaryList(row.runtime_requirements)) { + const separator = token.lastIndexOf(":"); + assert.notEqual(separator, -1, `${row.boundary_class}: malformed runtime requirement ${token}`); + const requirement = token.slice(0, separator); + const available = token.slice(separator + 1); + const existing = byRequirement.get(requirement) ?? { + requirement, + available, + requiredBy: [], + missingFor: [], + blockedFamilies: [], + optInEnvs: [], + }; + assert.equal(existing.available, available, `${requirement}: availability drift across preflight rows`); + existing.requiredBy.push(row.boundary_class); + existing.blockedFamilies.push(row.blocked); + existing.optInEnvs.push(row.opt_in_env); + if (splitSummaryList(row.missing_requirements).includes(requirement)) { + existing.missingFor.push(row.boundary_class); + } + byRequirement.set(requirement, existing); + } + } + + return [...byRequirement.values()] + .sort((left, right) => { + const missingCompare = Number(right.missingFor.length > 0) - Number(left.missingFor.length > 0); + if (missingCompare !== 0) { + return missingCompare; + } + return left.requirement.localeCompare(right.requirement); + }) + .map((row) => [ + row.requirement, + runtimeRequirementKind(row.requirement), + row.available, + String(uniqueSorted(row.requiredBy).length), + listValue(uniqueSorted(row.requiredBy)), + listValue(uniqueSorted(row.missingFor)), + listValue(uniqueSorted(row.blockedFamilies)), + listValue(uniqueSorted(row.optInEnvs)), + flagValue(row.missingFor.length > 0), + "0", + "0", + "host_requirement_summary_only_no_runtime_execution_no_automatic_promotion", + ].map(tsvValue).join("\t")); +} + +function verifyRuntimeBoundaryHostRequirementSummaryRows(rows, preflightRows) { + const expectedHeaders = [ + "requirement", + "requirement_kind", + "available", + "required_by_count", + "required_by", + "missing_for", + "blocked_families", + "opt_in_envs", + "missing_blocks_runtime", + "execution_enabled", + "promotion_allowed", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const expectedRequirements = uniqueSorted( + preflightRows.flatMap((row) => + splitSummaryList(row.runtime_requirements).map((token) => token.slice(0, token.lastIndexOf(":"))), + ), + ); + + verifySamePathSet( + "runtime_boundary_host_requirement_summary", + expectedRequirements, + parsedRows.map((row) => row.requirement).sort(), + ); + + const byRequirement = new Map(parsedRows.map((row) => [row.requirement, row])); + for (const required of [ + ["linuxcnc", "0", "3", "L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS"], + ["halcmd", "0", "2", "L4-TOOL-DB,L4-USER-M-PROCESS"], + ["milltask", "0", "1", "L4-TOOL-DB"], + ["python3", "1", "2", "-"], + ["tclsh", "1", "1", "-"], + ]) { + const [requirement, available, requiredByCount, missingFor] = required; + const row = byRequirement.get(requirement); + assert.ok(row, `host requirement summary missing ${requirement}`); + assert.equal(row.available, available, `${requirement}: availability drift`); + assert.equal(row.required_by_count, requiredByCount, `${requirement}: required-by count drift`); + assert.equal(row.missing_for, missingFor, `${requirement}: missing-for drift`); + } + + for (const row of parsedRows) { + assert.ok(["host_command", "source_or_module", "runtime_input"].includes(row.requirement_kind), `${row.requirement}: invalid requirement kind`); + assert.ok(["0", "1"].includes(row.available), `${row.requirement}: invalid availability`); + assert.ok(/^[1-9][0-9]*$/.test(row.required_by_count), `${row.requirement}: invalid required-by count`); + assert.notEqual(row.required_by, "-", `${row.requirement}: host requirement lacks required-by classes`); + assert.notEqual(row.blocked_families, "-", `${row.requirement}: host requirement lacks blocked families`); + assert.notEqual(row.opt_in_envs, "-", `${row.requirement}: host requirement lacks opt-in envs`); + assert.equal(row.execution_enabled, "0", `${row.requirement}: host requirement summary must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.requirement}: host requirement summary must not allow promotion`); + if (row.available === "0") { + assert.notEqual(row.missing_for, "-", `${row.requirement}: unavailable requirement must list missing families`); + assert.equal(row.missing_blocks_runtime, "1", `${row.requirement}: unavailable requirement must block runtime`); + } else { + assert.equal(row.missing_for, "-", `${row.requirement}: available requirement must not list missing families`); + assert.equal(row.missing_blocks_runtime, "0", `${row.requirement}: available requirement must not block runtime`); + } + assert.ok(row.notes.includes("no_automatic_promotion"), `${row.requirement}: host requirement note drift`); + } + + return parsedRows; +} + +function runtimeBoundaryHostUnblockPlanRows({ + requirementSummaryRows, + preflightRows, +}) { + const preflightByClass = new Map(preflightRows.map((row) => [row.boundary_class, row])); + + return requirementSummaryRows + .filter((row) => row.missing_blocks_runtime === "1") + .map((row) => { + const affectedClasses = splitSummaryList(row.missing_for); + const affectedTargets = affectedClasses.map((boundaryClass) => + preflightByClass.get(boundaryClass)?.target ?? boundaryClass, + ); + const affectedCommands = affectedClasses.map((boundaryClass) => + preflightByClass.get(boundaryClass)?.execution_command ?? "-", + ); + const afterAvailableStillMissing = affectedClasses.flatMap((boundaryClass) => + splitSummaryList(preflightByClass.get(boundaryClass)?.missing_requirements ?? "-") + .filter((requirement) => requirement !== row.requirement), + ); + const unblocksWhenAvailable = afterAvailableStillMissing.length === 0 + ? "runtime_preflight_ready_for_affected_families" + : "partial_unblock_other_requirements_remain"; + + return [ + row.requirement, + row.requirement_kind, + row.available, + row.missing_for, + listValue(uniqueSorted(affectedTargets)), + row.opt_in_envs, + listValue(uniqueSorted(affectedCommands)), + listValue(uniqueSorted(afterAvailableStillMissing)), + unblocksWhenAvailable, + "provide_missing_host_requirement", + "0", + "0", + "host_unblock_plan_summary_only_no_runtime_execution_no_automatic_promotion", + ].map(tsvValue).join("\t"); + }); +} + +function verifyRuntimeBoundaryHostUnblockPlanRows({ + rows, + requirementSummaryRows, + preflightRows, +}) { + const expectedHeaders = [ + "requirement", + "requirement_kind", + "available", + "missing_for", + "affected_targets", + "opt_in_envs", + "affected_execution_commands", + "remaining_missing_after_requirement", + "unblocks_when_available", + "next_action", + "execution_enabled", + "promotion_allowed", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const blockingRequirements = requirementSummaryRows.filter((row) => row.missing_blocks_runtime === "1"); + const blockingRequirementNames = blockingRequirements.map((row) => row.requirement).sort(); + const summaryByRequirement = new Map(requirementSummaryRows.map((row) => [row.requirement, row])); + const preflightByClass = new Map(preflightRows.map((row) => [row.boundary_class, row])); + + verifySamePathSet( + "runtime_boundary_host_unblock_plan", + blockingRequirementNames, + parsedRows.map((row) => row.requirement).sort(), + ); + + const expected = new Map([ + ["linuxcnc", { missingFor: "L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS", remaining: "halcmd,halrun,milltask" }], + ["halcmd", { missingFor: "L4-TOOL-DB,L4-USER-M-PROCESS", remaining: "halrun,linuxcnc,milltask" }], + ["halrun", { missingFor: "L4-USER-M-PROCESS", remaining: "halcmd,linuxcnc" }], + ["milltask", { missingFor: "L4-TOOL-DB", remaining: "halcmd,linuxcnc" }], + ]); + + for (const row of parsedRows) { + const summary = summaryByRequirement.get(row.requirement); + const expectedRow = expected.get(row.requirement); + assert.ok(summary, `${row.requirement}: unblock plan lacks requirement summary`); + assert.ok(expectedRow, `${row.requirement}: unexpected unblock requirement`); + assert.equal(row.requirement_kind, summary.requirement_kind, `${row.requirement}: unblock kind drift`); + assert.equal(row.available, "0", `${row.requirement}: unblock plan only covers unavailable requirements`); + assert.equal(row.missing_for, expectedRow.missingFor, `${row.requirement}: unblock missing-for drift`); + assert.equal(row.missing_for, summary.missing_for, `${row.requirement}: unblock summary missing-for drift`); + assert.equal(row.remaining_missing_after_requirement, expectedRow.remaining, `${row.requirement}: unblock remaining requirement drift`); + assert.notEqual(row.affected_targets, "-", `${row.requirement}: unblock plan lacks affected targets`); + assert.notEqual(row.opt_in_envs, "-", `${row.requirement}: unblock plan lacks opt-in envs`); + assert.notEqual(row.affected_execution_commands, "-", `${row.requirement}: unblock plan lacks commands`); + for (const boundaryClass of splitSummaryList(row.missing_for)) { + const preflight = preflightByClass.get(boundaryClass); + assert.ok(preflight, `${row.requirement}: unblock plan lacks preflight for ${boundaryClass}`); + assert.ok(row.affected_targets.includes(preflight.target), `${row.requirement}: unblock plan missing target ${preflight.target}`); + assert.ok(row.affected_execution_commands.includes(preflight.execution_command), `${row.requirement}: unblock plan missing command for ${boundaryClass}`); + } + assert.equal(row.unblocks_when_available, "partial_unblock_other_requirements_remain", `${row.requirement}: current host should still require other missing runtime commands`); + assert.equal(row.next_action, "provide_missing_host_requirement", `${row.requirement}: unblock next action drift`); + assert.equal(row.execution_enabled, "0", `${row.requirement}: unblock plan must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.requirement}: unblock plan must not allow promotion`); + assert.ok(row.notes.includes("no_automatic_promotion"), `${row.requirement}: unblock plan note drift`); + } + + return parsedRows; +} + +function runtimeBoundaryFamilyHostReadinessRows(preflightRows) { + return preflightRows.map((row) => { + const availableRequirements = splitSummaryList(row.runtime_requirements) + .filter((token) => token.endsWith(":1")) + .map((token) => token.slice(0, token.lastIndexOf(":"))); + const missingRequirements = splitSummaryList(row.missing_requirements); + const familyHostStatus = missingRequirements.length === 0 + ? "host_ready_to_run_opt_in_probe" + : "blocked_missing_host_requirements"; + + return [ + row.boundary_class, + row.boundary_kind, + row.target, + row.blocked, + row.required_runtime, + String(missingRequirements.length), + listValue(missingRequirements), + String(availableRequirements.length), + listValue(availableRequirements), + row.opt_in_env, + row.execution_command, + row.current_probe_status, + row.preflight_status, + familyHostStatus, + "0", + "0", + "family_host_readiness_summary_only_no_runtime_execution_no_automatic_promotion", + ].map(tsvValue).join("\t"); + }); +} + +function verifyRuntimeBoundaryFamilyHostReadinessRows(rows, preflightRows) { + const expectedHeaders = [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "required_runtime", + "missing_requirement_count", + "missing_host_requirements", + "available_requirement_count", + "available_requirements", + "opt_in_env", + "execution_command", + "current_probe_status", + "preflight_status", + "family_host_status", + "execution_enabled", + "promotion_allowed", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const preflightByClass = new Map(preflightRows.map((row) => [row.boundary_class, row])); + const expected = new Map([ + [ + "L4-USER-M-PROCESS", + { + missingCount: "3", + missing: "halrun,halcmd,linuxcnc", + available: "tclsh", + }, + ], + [ + "L4-TOOL-DB", + { + missingCount: "3", + missing: "linuxcnc,milltask,halcmd", + available: "python3,axis/db_demo/db_nonran.py,linuxcnc.so,tooldb.py", + }, + ], + [ + "L4-PYTHON-REMAP", + { + missingCount: "1", + missing: "linuxcnc", + available: "python3,src/emc/rs274ngc/interp_python.cc,src/emc/pythonplugin/python_plugin.cc,axis/remap/stop-lookahead/python/remap.py,axis/remap/stop-lookahead/python/toplevel.py", + }, + ], + ]); + + assert.equal(parsedRows.length, expected.size, "family host readiness row count drift"); + assert.deepEqual( + parsedRows.map((row) => row.boundary_class), + [...expected.keys()], + "family host readiness order drift", + ); + + for (const row of parsedRows) { + const preflight = preflightByClass.get(row.boundary_class); + const expectedRow = expected.get(row.boundary_class); + assert.ok(preflight, `${row.boundary_class}: family host readiness lacks preflight row`); + assert.ok(expectedRow, `${row.boundary_class}: unexpected family host readiness row`); + assert.equal(row.boundary_kind, preflight.boundary_kind, `${row.boundary_class}: family host readiness kind drift`); + assert.equal(row.target, preflight.target, `${row.boundary_class}: family host readiness target drift`); + assert.equal(row.blocked, preflight.blocked, `${row.boundary_class}: family host readiness blocked drift`); + assert.equal(row.required_runtime, preflight.required_runtime, `${row.boundary_class}: family host readiness runtime drift`); + assert.equal(row.opt_in_env, preflight.opt_in_env, `${row.boundary_class}: family host readiness opt-in drift`); + assert.equal(row.execution_command, preflight.execution_command, `${row.boundary_class}: family host readiness command drift`); + assert.equal(row.current_probe_status, preflight.current_probe_status, `${row.boundary_class}: family host readiness probe status drift`); + assert.equal(row.preflight_status, preflight.preflight_status, `${row.boundary_class}: family host readiness preflight status drift`); + assert.equal(row.missing_requirement_count, expectedRow.missingCount, `${row.boundary_class}: family host readiness missing count drift`); + assert.equal(row.missing_host_requirements, expectedRow.missing, `${row.boundary_class}: family host readiness missing requirements drift`); + assert.equal(row.available_requirements, expectedRow.available, `${row.boundary_class}: family host readiness available requirements drift`); + assert.equal(row.available_requirement_count, String(splitSummaryList(expectedRow.available).length), `${row.boundary_class}: family host readiness available count drift`); + assert.equal(row.family_host_status, "blocked_missing_host_requirements", `${row.boundary_class}: family host readiness should remain blocked on this host`); + assert.equal(row.execution_enabled, "0", `${row.boundary_class}: family host readiness must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: family host readiness must not allow promotion`); + assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: family host readiness note drift`); + } + + return parsedRows; +} + +function runtimeBoundaryHostReadinessRollupRows({ + familyHostReadinessRows, + requirementSummaryRows, +}) { + const readyFamilies = familyHostReadinessRows.filter((row) => + row.family_host_status === "host_ready_to_run_opt_in_probe", + ); + const blockedFamilies = familyHostReadinessRows.filter((row) => + row.family_host_status !== "host_ready_to_run_opt_in_probe", + ); + const missingHostRequirements = uniqueSorted( + requirementSummaryRows + .filter((row) => row.missing_blocks_runtime === "1") + .map((row) => row.requirement), + ); + const readyOptInCommands = readyFamilies.map((row) => row.execution_command); + const blockedOptInCommands = blockedFamilies.map((row) => row.execution_command); + const hostReadinessStatus = readyFamilies.length === familyHostReadinessRows.length + ? "host_ready_for_all_opt_in_native_probes" + : readyFamilies.length > 0 + ? "host_partially_ready_for_opt_in_native_probes" + : "host_blocked_for_all_opt_in_native_probes"; + + return [[ + "blocked_runtime_host_readiness", + String(familyHostReadinessRows.length), + String(readyFamilies.length), + String(blockedFamilies.length), + listValue(uniqueSorted(blockedFamilies.map((row) => row.boundary_class))), + listValue(missingHostRequirements), + String(missingHostRequirements.length), + String(readyOptInCommands.length), + listValue(uniqueSorted(readyOptInCommands)), + listValue(uniqueSorted(blockedOptInCommands)), + hostReadinessStatus, + "0", + "0", + "host_readiness_rollup_summary_only_no_runtime_execution_no_automatic_promotion", + ].map(tsvValue).join("\t")]; +} + +function verifyRuntimeBoundaryHostReadinessRollupRows({ + rows, + familyHostReadinessRows, + requirementSummaryRows, +}) { + const expectedHeaders = [ + "scope", + "family_count", + "host_ready_family_count", + "host_blocked_family_count", + "blocked_families", + "missing_host_requirements", + "missing_host_requirement_count", + "ready_opt_in_command_count", + "ready_opt_in_commands", + "blocked_opt_in_commands", + "host_readiness_status", + "execution_enabled", + "promotion_allowed", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const row = parsedRows[0]; + const expectedMissingRequirements = uniqueSorted( + requirementSummaryRows + .filter((requirement) => requirement.missing_blocks_runtime === "1") + .map((requirement) => requirement.requirement), + ); + + assert.equal(parsedRows.length, 1, "host readiness rollup row count drift"); + assert.equal(row.scope, "blocked_runtime_host_readiness", "host readiness rollup scope drift"); + assert.equal(row.family_count, String(familyHostReadinessRows.length), "host readiness rollup family count drift"); + assert.equal(row.host_ready_family_count, "0", "host readiness rollup must have no ready families on this host"); + assert.equal(row.host_blocked_family_count, String(familyHostReadinessRows.length), "host readiness rollup blocked family count drift"); + assert.equal(row.blocked_families, "L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS", "host readiness rollup blocked families drift"); + assert.equal(row.missing_host_requirements, listValue(expectedMissingRequirements), "host readiness rollup missing requirements drift"); + assert.equal(row.missing_host_requirement_count, String(expectedMissingRequirements.length), "host readiness rollup missing requirement count drift"); + assert.equal(row.ready_opt_in_command_count, "0", "host readiness rollup must not report ready opt-in commands"); + assert.equal(row.ready_opt_in_commands, "-", "host readiness rollup must not list ready opt-in commands"); + assert.notEqual(row.blocked_opt_in_commands, "-", "host readiness rollup must list blocked opt-in commands"); + for (const family of familyHostReadinessRows) { + assert.ok(row.blocked_opt_in_commands.includes(family.execution_command), `${family.boundary_class}: host readiness rollup missing blocked command`); + } + assert.equal(row.host_readiness_status, "host_blocked_for_all_opt_in_native_probes", "host readiness rollup status drift"); + assert.equal(row.execution_enabled, "0", "host readiness rollup must not enable execution"); + assert.equal(row.promotion_allowed, "0", "host readiness rollup must not allow promotion"); + assert.ok(row.notes.includes("no_automatic_promotion"), "host readiness rollup note drift"); + + return parsedRows; +} + +function runtimeBoundaryOptInProbeDispatchPlanRows({ + familyHostReadinessRows, + hostReadinessRollupRows, +}) { + const rollup = hostReadinessRollupRows[0]; + + return familyHostReadinessRows.map((row) => { + const hostReady = row.family_host_status === "host_ready_to_run_opt_in_probe"; + const hostAction = hostReady + ? "ready_for_manual_opt_in_native_probe" + : "skip_missing_host_requirements"; + const nextAction = hostReady + ? `manually_run_opt_in_probe:${row.execution_command}` + : `provide_missing_host_requirements:${row.missing_host_requirements}`; + + return [ + row.boundary_class, + row.boundary_kind, + row.target, + row.blocked, + row.opt_in_env, + row.execution_command, + row.family_host_status, + row.missing_host_requirements, + rollup?.host_readiness_status ?? "-", + hostAction, + flagValue(hostReady), + "0", + "0", + nextAction, + "opt_in_probe_dispatch_plan_summary_only_no_runtime_execution_no_automatic_promotion", + ].map(tsvValue).join("\t"); + }); +} + +function verifyRuntimeBoundaryOptInProbeDispatchPlanRows({ + rows, + familyHostReadinessRows, + hostReadinessRollupRows, +}) { + const expectedHeaders = [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "opt_in_env", + "execution_command", + "family_host_status", + "missing_host_requirements", + "host_readiness_status", + "host_action", + "dispatch_allowed", + "execution_enabled", + "promotion_allowed", + "next_action", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const familyReadinessByClass = new Map( + familyHostReadinessRows.map((row) => [row.boundary_class, row]), + ); + const rollup = hostReadinessRollupRows[0]; + const expectedClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + + assert.equal(parsedRows.length, expectedClasses.length, "opt-in probe dispatch plan row count drift"); + assert.deepEqual( + parsedRows.map((row) => row.boundary_class), + expectedClasses, + "opt-in probe dispatch plan order drift", + ); + assert.ok(rollup, "opt-in probe dispatch plan lacks host readiness rollup"); + + for (const row of parsedRows) { + const readiness = familyReadinessByClass.get(row.boundary_class); + assert.ok(readiness, `${row.boundary_class}: dispatch plan lacks family readiness row`); + assert.equal(row.boundary_kind, readiness.boundary_kind, `${row.boundary_class}: dispatch kind drift`); + assert.equal(row.target, readiness.target, `${row.boundary_class}: dispatch target drift`); + assert.equal(row.blocked, readiness.blocked, `${row.boundary_class}: dispatch blocked drift`); + assert.equal(row.opt_in_env, readiness.opt_in_env, `${row.boundary_class}: dispatch opt-in drift`); + assert.equal(row.execution_command, readiness.execution_command, `${row.boundary_class}: dispatch command drift`); + assert.equal(row.family_host_status, readiness.family_host_status, `${row.boundary_class}: dispatch host status drift`); + assert.equal(row.missing_host_requirements, readiness.missing_host_requirements, `${row.boundary_class}: dispatch missing requirement drift`); + assert.equal(row.host_readiness_status, rollup.host_readiness_status, `${row.boundary_class}: dispatch rollup status drift`); + assert.ok(row.execution_command.includes(row.opt_in_env), `${row.boundary_class}: dispatch command must include opt-in env`); + assert.equal(row.family_host_status, "blocked_missing_host_requirements", `${row.boundary_class}: current host must remain blocked`); + assert.notEqual(row.missing_host_requirements, "-", `${row.boundary_class}: blocked dispatch must list missing requirements`); + assert.equal(row.host_readiness_status, "host_blocked_for_all_opt_in_native_probes", `${row.boundary_class}: dispatch host readiness status drift`); + assert.equal(row.host_action, "skip_missing_host_requirements", `${row.boundary_class}: current host dispatch action drift`); + assert.equal(row.dispatch_allowed, "0", `${row.boundary_class}: current host dispatch must not be allowed`); + assert.equal(row.execution_enabled, "0", `${row.boundary_class}: dispatch plan must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: dispatch plan must not allow promotion`); + assert.ok(row.next_action.includes(row.missing_host_requirements), `${row.boundary_class}: dispatch next action must name missing requirements`); + assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: dispatch plan note drift`); + } + + return parsedRows; +} + +function runtimeBoundaryOptInProbeDispatchRollupRows(dispatchPlanRows) { + const dispatchAllowedRows = dispatchPlanRows.filter((row) => row.dispatch_allowed === "1"); + const dispatchBlockedRows = dispatchPlanRows.filter((row) => row.dispatch_allowed !== "1"); + const dispatchActions = uniqueSorted(dispatchPlanRows.map((row) => row.host_action)); + const dispatchStatus = dispatchAllowedRows.length === dispatchPlanRows.length + ? "dispatch_allowed_for_all_opt_in_native_probes" + : dispatchAllowedRows.length > 0 + ? "dispatch_partially_allowed_for_opt_in_native_probes" + : "dispatch_blocked_for_all_opt_in_native_probes"; + + return [[ + "blocked_runtime_opt_in_probe_dispatch", + String(dispatchPlanRows.length), + String(dispatchAllowedRows.length), + String(dispatchBlockedRows.length), + listValue(uniqueSorted(dispatchBlockedRows.map((row) => row.boundary_class))), + listValue(dispatchActions), + listValue(uniqueSorted(dispatchAllowedRows.map((row) => row.execution_command))), + listValue(uniqueSorted(dispatchBlockedRows.map((row) => row.execution_command))), + listValue(uniqueSorted(dispatchBlockedRows.flatMap((row) => + splitSummaryList(row.missing_host_requirements), + ))), + dispatchStatus, + "0", + "0", + "opt_in_probe_dispatch_rollup_summary_only_no_runtime_execution_no_automatic_promotion", + ].map(tsvValue).join("\t")]; +} + +function verifyRuntimeBoundaryOptInProbeDispatchRollupRows({ + rows, + dispatchPlanRows, +}) { + const expectedHeaders = [ + "scope", + "probe_count", + "dispatch_allowed_count", + "dispatch_blocked_count", + "blocked_families", + "dispatch_actions", + "dispatch_allowed_commands", + "dispatch_blocked_commands", + "missing_host_requirements", + "dispatch_status", + "execution_enabled", + "promotion_allowed", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const row = parsedRows[0]; + const dispatchBlockedRows = dispatchPlanRows.filter((plan) => plan.dispatch_allowed !== "1"); + const expectedMissingRequirements = uniqueSorted(dispatchBlockedRows.flatMap((plan) => + splitSummaryList(plan.missing_host_requirements), + )); + + assert.equal(parsedRows.length, 1, "opt-in probe dispatch rollup row count drift"); + assert.equal(row.scope, "blocked_runtime_opt_in_probe_dispatch", "opt-in probe dispatch rollup scope drift"); + assert.equal(row.probe_count, String(dispatchPlanRows.length), "opt-in probe dispatch rollup probe count drift"); + assert.equal(row.dispatch_allowed_count, "0", "opt-in probe dispatch rollup must not allow probes on this host"); + assert.equal(row.dispatch_blocked_count, String(dispatchPlanRows.length), "opt-in probe dispatch rollup blocked count drift"); + assert.equal(row.blocked_families, "L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS", "opt-in probe dispatch rollup blocked families drift"); + assert.equal(row.dispatch_actions, "skip_missing_host_requirements", "opt-in probe dispatch rollup action drift"); + assert.equal(row.dispatch_allowed_commands, "-", "opt-in probe dispatch rollup must not list allowed commands"); + assert.notEqual(row.dispatch_blocked_commands, "-", "opt-in probe dispatch rollup must list blocked commands"); + for (const plan of dispatchPlanRows) { + assert.ok(row.dispatch_blocked_commands.includes(plan.execution_command), `${plan.boundary_class}: opt-in probe dispatch rollup missing blocked command`); + } + assert.equal(row.missing_host_requirements, listValue(expectedMissingRequirements), "opt-in probe dispatch rollup missing requirement drift"); + assert.equal(row.dispatch_status, "dispatch_blocked_for_all_opt_in_native_probes", "opt-in probe dispatch rollup status drift"); + assert.equal(row.execution_enabled, "0", "opt-in probe dispatch rollup must not enable execution"); + assert.equal(row.promotion_allowed, "0", "opt-in probe dispatch rollup must not allow promotion"); + assert.ok(row.notes.includes("no_automatic_promotion"), "opt-in probe dispatch rollup note drift"); + + return parsedRows; +} + +function runtimeBoundaryOptInProbeSkipEvidenceContractRows({ + dispatchPlanRows, + passEvidenceRows, +}) { + const evidenceByClass = new Map(passEvidenceRows.map((row) => [row.boundary_class, row])); + + return dispatchPlanRows.map((row) => { + const evidence = evidenceByClass.get(row.boundary_class); + const skipReason = row.dispatch_allowed === "1" ? "-" : "missing_host_requirements"; + const skipEvidenceStatus = row.dispatch_allowed === "1" + ? "skip_contract_not_applicable_dispatch_allowed" + : "skip_valid_until_host_requirements_available"; + + return [ + row.boundary_class, + row.boundary_kind, + row.target, + row.blocked, + evidence?.runtime_probe ?? "-", + row.opt_in_env, + row.execution_command, + row.host_action, + skipReason, + row.missing_host_requirements, + evidence?.current_probe_status ?? "-", + evidence?.evidence_required_now ?? "0", + evidence?.observed_evidence_ready ?? "0", + evidence?.evidence_status ?? "-", + skipEvidenceStatus, + "0", + "0", + "opt_in_probe_skip_evidence_contract_only_no_runtime_execution_no_automatic_promotion", + ].map(tsvValue).join("\t"); + }); +} + +function verifyRuntimeBoundaryOptInProbeSkipEvidenceContractRows({ + rows, + dispatchPlanRows, + passEvidenceRows, +}) { + const expectedHeaders = [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "runtime_probe", + "opt_in_env", + "execution_command", + "host_action", + "skip_reason", + "missing_host_requirements", + "current_probe_status", + "evidence_required_now", + "observed_evidence_ready", + "native_evidence_status", + "skip_evidence_status", + "execution_enabled", + "promotion_allowed", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const dispatchByClass = new Map(dispatchPlanRows.map((row) => [row.boundary_class, row])); + const evidenceByClass = new Map(passEvidenceRows.map((row) => [row.boundary_class, row])); + const expectedClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + + assert.equal(parsedRows.length, expectedClasses.length, "opt-in probe skip evidence contract row count drift"); + assert.deepEqual( + parsedRows.map((row) => row.boundary_class), + expectedClasses, + "opt-in probe skip evidence contract order drift", + ); + + for (const row of parsedRows) { + const dispatch = dispatchByClass.get(row.boundary_class); + const evidence = evidenceByClass.get(row.boundary_class); + assert.ok(dispatch, `${row.boundary_class}: skip evidence contract lacks dispatch row`); + assert.ok(evidence, `${row.boundary_class}: skip evidence contract lacks pass evidence row`); + assert.equal(row.boundary_kind, dispatch.boundary_kind, `${row.boundary_class}: skip evidence kind drift`); + assert.equal(row.target, dispatch.target, `${row.boundary_class}: skip evidence target drift`); + assert.equal(row.blocked, dispatch.blocked, `${row.boundary_class}: skip evidence blocked drift`); + assert.equal(row.runtime_probe, evidence.runtime_probe, `${row.boundary_class}: skip evidence runtime probe drift`); + assert.equal(row.opt_in_env, dispatch.opt_in_env, `${row.boundary_class}: skip evidence opt-in drift`); + assert.equal(row.execution_command, dispatch.execution_command, `${row.boundary_class}: skip evidence command drift`); + assert.equal(row.host_action, "skip_missing_host_requirements", `${row.boundary_class}: current host skip action drift`); + assert.equal(row.host_action, dispatch.host_action, `${row.boundary_class}: skip evidence host action drift`); + assert.equal(row.skip_reason, "missing_host_requirements", `${row.boundary_class}: skip evidence reason drift`); + assert.equal(row.missing_host_requirements, dispatch.missing_host_requirements, `${row.boundary_class}: skip evidence missing requirements drift`); + assert.notEqual(row.missing_host_requirements, "-", `${row.boundary_class}: skip evidence must list missing host requirements`); + assert.equal(row.current_probe_status, "skipped_missing_host_runtime", `${row.boundary_class}: skip evidence current probe status drift`); + assert.equal(row.current_probe_status, evidence.current_probe_status, `${row.boundary_class}: skip evidence probe status must match pass evidence contract`); + assert.equal(row.evidence_required_now, "0", `${row.boundary_class}: skipped probe must not require evidence now`); + assert.equal(row.evidence_required_now, evidence.evidence_required_now, `${row.boundary_class}: skip evidence required-now drift`); + assert.equal(row.observed_evidence_ready, "0", `${row.boundary_class}: skipped probe must not report evidence ready`); + assert.equal(row.observed_evidence_ready, evidence.observed_evidence_ready, `${row.boundary_class}: skip evidence readiness drift`); + assert.equal(row.native_evidence_status, "pending_until_native_pass", `${row.boundary_class}: skip evidence status drift`); + assert.equal(row.native_evidence_status, evidence.evidence_status, `${row.boundary_class}: skip evidence native status drift`); + assert.equal(row.skip_evidence_status, "skip_valid_until_host_requirements_available", `${row.boundary_class}: skip evidence contract status drift`); + assert.equal(row.execution_enabled, "0", `${row.boundary_class}: skip evidence contract must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: skip evidence contract must not allow promotion`); + assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: skip evidence contract note drift`); + } + + return parsedRows; +} + +function runtimeBoundaryOptInProbeSkipEvidenceRollupRows(skipEvidenceContractRows) { + const skippedRows = skipEvidenceContractRows.filter((row) => row.skip_reason !== "-"); + const evidenceRequiredRows = skipEvidenceContractRows.filter((row) => row.evidence_required_now === "1"); + const evidenceReadyRows = skipEvidenceContractRows.filter((row) => row.observed_evidence_ready === "1"); + const missingHostRequirements = uniqueSorted(skippedRows.flatMap((row) => + splitSummaryList(row.missing_host_requirements), + )); + const evidenceRollupStatus = evidenceReadyRows.length > 0 + ? "native_pass_evidence_present" + : "no_native_pass_evidence_accepted_while_host_blocked"; + + return [[ + "blocked_runtime_opt_in_probe_skip_evidence", + String(skipEvidenceContractRows.length), + String(skippedRows.length), + String(evidenceRequiredRows.length), + String(evidenceReadyRows.length), + listValue(uniqueSorted(skippedRows.map((row) => row.boundary_class))), + listValue(uniqueSorted(skippedRows.map((row) => row.skip_reason))), + listValue(missingHostRequirements), + listValue(uniqueSorted(skipEvidenceContractRows.map((row) => row.current_probe_status))), + listValue(uniqueSorted(skipEvidenceContractRows.map((row) => row.native_evidence_status))), + listValue(uniqueSorted(skipEvidenceContractRows.map((row) => row.skip_evidence_status))), + evidenceRollupStatus, + "0", + "0", + "opt_in_probe_skip_evidence_rollup_summary_only_no_runtime_execution_no_automatic_promotion", + ].map(tsvValue).join("\t")]; +} + +function verifyRuntimeBoundaryOptInProbeSkipEvidenceRollupRows({ + rows, + skipEvidenceContractRows, +}) { + const expectedHeaders = [ + "scope", + "probe_count", + "skip_count", + "evidence_required_now_count", + "observed_evidence_ready_count", + "skipped_families", + "skip_reasons", + "missing_host_requirements", + "current_probe_statuses", + "native_evidence_statuses", + "skip_evidence_statuses", + "evidence_rollup_status", + "execution_enabled", + "promotion_allowed", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const row = parsedRows[0]; + const expectedMissingRequirements = uniqueSorted(skipEvidenceContractRows.flatMap((contract) => + splitSummaryList(contract.missing_host_requirements), + )); + + assert.equal(parsedRows.length, 1, "opt-in probe skip evidence rollup row count drift"); + assert.equal(row.scope, "blocked_runtime_opt_in_probe_skip_evidence", "opt-in probe skip evidence rollup scope drift"); + assert.equal(row.probe_count, String(skipEvidenceContractRows.length), "opt-in probe skip evidence rollup probe count drift"); + assert.equal(row.skip_count, String(skipEvidenceContractRows.length), "opt-in probe skip evidence rollup skip count drift"); + assert.equal(row.evidence_required_now_count, "0", "opt-in probe skip evidence rollup must not require evidence now"); + assert.equal(row.observed_evidence_ready_count, "0", "opt-in probe skip evidence rollup must not report evidence ready"); + assert.equal(row.skipped_families, "L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS", "opt-in probe skip evidence rollup family drift"); + assert.equal(row.skip_reasons, "missing_host_requirements", "opt-in probe skip evidence rollup reason drift"); + assert.equal(row.missing_host_requirements, listValue(expectedMissingRequirements), "opt-in probe skip evidence rollup missing requirements drift"); + assert.equal(row.current_probe_statuses, "skipped_missing_host_runtime", "opt-in probe skip evidence rollup probe status drift"); + assert.equal(row.native_evidence_statuses, "pending_until_native_pass", "opt-in probe skip evidence rollup native evidence status drift"); + assert.equal(row.skip_evidence_statuses, "skip_valid_until_host_requirements_available", "opt-in probe skip evidence rollup skip evidence status drift"); + assert.equal(row.evidence_rollup_status, "no_native_pass_evidence_accepted_while_host_blocked", "opt-in probe skip evidence rollup status drift"); + assert.equal(row.execution_enabled, "0", "opt-in probe skip evidence rollup must not enable execution"); + assert.equal(row.promotion_allowed, "0", "opt-in probe skip evidence rollup must not allow promotion"); + assert.ok(row.notes.includes("no_automatic_promotion"), "opt-in probe skip evidence rollup note drift"); + + return parsedRows; +} + +function runtimeBoundaryNativeEvidenceAcceptanceGateRows({ + skipEvidenceContractRows, + promotionReadinessRows, + postNativePassGateRows, +}) { + const readinessByClass = new Map(promotionReadinessRows.map((row) => [row.boundary_class, row])); + const postNativeGateByClass = new Map(postNativePassGateRows.map((row) => [row.boundary_class, row])); + + return skipEvidenceContractRows.map((row) => { + const readiness = readinessByClass.get(row.boundary_class); + const postNativeGate = postNativeGateByClass.get(row.boundary_class); + const evidenceGateOpen = row.observed_evidence_ready === "1" && + readiness?.native_pass_ready === "1" && + readiness?.native_evidence_ready === "1"; + const nativeEvidenceGate = evidenceGateOpen + ? "native_pass_evidence_accepted" + : row.skip_reason === "missing_host_requirements" + ? "blocked_until_host_requirements_available" + : "blocked_until_native_pass_evidence"; + + return [ + row.boundary_class, + row.boundary_kind, + row.target, + row.blocked, + row.runtime_probe, + row.current_probe_status, + row.native_evidence_status, + row.evidence_required_now, + row.observed_evidence_ready, + row.skip_evidence_status, + readiness?.promotion_ready ?? "0", + postNativeGate?.node_gate_status ?? "-", + postNativeGate?.browser_gate_status ?? "-", + nativeEvidenceGate, + flagValue(evidenceGateOpen), + row.skip_reason === "missing_host_requirements" + ? `provide_missing_host_requirements:${row.missing_host_requirements}` + : "run_opt_in_native_runtime_probe_and_collect_evidence", + "0", + "0", + "native_evidence_acceptance_gate_summary_only_no_runtime_execution_no_automatic_promotion", + ].map(tsvValue).join("\t"); + }); +} + +function verifyRuntimeBoundaryNativeEvidenceAcceptanceGateRows({ + rows, + skipEvidenceContractRows, + promotionReadinessRows, + postNativePassGateRows, +}) { + const expectedHeaders = [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "runtime_probe", + "current_probe_status", + "native_evidence_status", + "evidence_required_now", + "observed_evidence_ready", + "skip_evidence_status", + "promotion_ready", + "node_gate_status", + "browser_gate_status", + "native_evidence_gate", + "evidence_acceptance_allowed", + "next_action", + "execution_enabled", + "promotion_allowed", + "notes", + ]; + const parsedRows = parseTsv( + `${expectedHeaders.join("\t")}\n${rows.join("\n")}\n`, + expectedHeaders, + ); + const skipByClass = new Map(skipEvidenceContractRows.map((row) => [row.boundary_class, row])); + const readinessByClass = new Map(promotionReadinessRows.map((row) => [row.boundary_class, row])); + const postNativeGateByClass = new Map(postNativePassGateRows.map((row) => [row.boundary_class, row])); + const expectedClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + + assert.equal(parsedRows.length, expectedClasses.length, "native evidence acceptance gate row count drift"); + assert.deepEqual( + parsedRows.map((row) => row.boundary_class), + expectedClasses, + "native evidence acceptance gate order drift", + ); + + for (const row of parsedRows) { + const skip = skipByClass.get(row.boundary_class); + const readiness = readinessByClass.get(row.boundary_class); + const postNativeGate = postNativeGateByClass.get(row.boundary_class); + assert.ok(skip, `${row.boundary_class}: native evidence gate lacks skip evidence contract row`); + assert.ok(readiness, `${row.boundary_class}: native evidence gate lacks promotion readiness row`); + assert.ok(postNativeGate, `${row.boundary_class}: native evidence gate lacks post-native-pass gate row`); + assert.equal(row.boundary_kind, skip.boundary_kind, `${row.boundary_class}: native evidence gate kind drift`); + assert.equal(row.target, skip.target, `${row.boundary_class}: native evidence gate target drift`); + assert.equal(row.blocked, skip.blocked, `${row.boundary_class}: native evidence gate blocked drift`); + assert.equal(row.runtime_probe, skip.runtime_probe, `${row.boundary_class}: native evidence gate runtime probe drift`); + assert.equal(row.current_probe_status, "skipped_missing_host_runtime", `${row.boundary_class}: native evidence gate probe status drift`); + assert.equal(row.current_probe_status, skip.current_probe_status, `${row.boundary_class}: native evidence gate skip status drift`); + assert.equal(row.native_evidence_status, "pending_until_native_pass", `${row.boundary_class}: native evidence gate evidence status drift`); + assert.equal(row.native_evidence_status, skip.native_evidence_status, `${row.boundary_class}: native evidence gate native evidence status drift`); + assert.equal(row.evidence_required_now, "0", `${row.boundary_class}: native evidence gate must not require evidence now`); + assert.equal(row.evidence_required_now, skip.evidence_required_now, `${row.boundary_class}: native evidence gate required-now drift`); + assert.equal(row.observed_evidence_ready, "0", `${row.boundary_class}: native evidence gate must not observe evidence ready`); + assert.equal(row.observed_evidence_ready, skip.observed_evidence_ready, `${row.boundary_class}: native evidence gate readiness drift`); + assert.equal(row.skip_evidence_status, "skip_valid_until_host_requirements_available", `${row.boundary_class}: native evidence gate skip status drift`); + assert.equal(row.promotion_ready, "0", `${row.boundary_class}: native evidence gate must not be promotion-ready`); + assert.equal(row.promotion_ready, readiness.promotion_ready, `${row.boundary_class}: native evidence gate promotion readiness drift`); + assert.equal(row.node_gate_status, "blocked_until_native_pass_evidence", `${row.boundary_class}: native evidence gate Node status drift`); + assert.equal(row.node_gate_status, postNativeGate.node_gate_status, `${row.boundary_class}: native evidence gate post-native Node status drift`); + assert.equal(row.browser_gate_status, "blocked_until_node_gate_complete", `${row.boundary_class}: native evidence gate browser status drift`); + assert.equal(row.browser_gate_status, postNativeGate.browser_gate_status, `${row.boundary_class}: native evidence gate post-native browser status drift`); + assert.equal(row.native_evidence_gate, "blocked_until_host_requirements_available", `${row.boundary_class}: native evidence gate status drift`); + assert.equal(row.evidence_acceptance_allowed, "0", `${row.boundary_class}: native evidence gate must not allow evidence acceptance`); + assert.ok(row.next_action.includes(skip.missing_host_requirements), `${row.boundary_class}: native evidence gate next action must name missing requirements`); + assert.equal(row.execution_enabled, "0", `${row.boundary_class}: native evidence gate must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.boundary_class}: native evidence gate must not allow promotion`); + assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: native evidence gate note drift`); + } + + return parsedRows; +} + +function verifyBlockedRuntimeOptInGateConsistency({ + recommendationRows, + executionPlanRows, + promotionReadinessRows, + promotionBlockerRows, + postNativePassGateRows, + hostPreflightRows, + familyHostReadinessRows, + dispatchPlanRows, + skipEvidenceContractRows, + evidenceAcceptanceGateRows, +}) { + const expectedClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + const classToKind = new Map([ + ["L4-USER-M-PROCESS", "external_user_m_process"], + ["L4-TOOL-DB", "tool_database_process"], + ["L4-PYTHON-REMAP", "python_runtime"], + ]); + const classToRecommendation = new Map( + recommendationRows.map((row) => [row.blocked, row]), + ); + const byClass = (rows) => new Map(rows.map((row) => [row.boundary_class, row])); + const executionByClass = byClass(executionPlanRows); + const readinessByClass = byClass(promotionReadinessRows); + const blockerByClass = byClass(promotionBlockerRows); + const postNativeByClass = byClass(postNativePassGateRows); + const preflightByClass = byClass(hostPreflightRows); + const familyReadinessByClass = byClass(familyHostReadinessRows); + const dispatchByClass = byClass(dispatchPlanRows); + const skipEvidenceByClass = byClass(skipEvidenceContractRows); + const evidenceGateByClass = byClass(evidenceAcceptanceGateRows); + + for (const rowSet of [ + executionPlanRows, + promotionReadinessRows, + promotionBlockerRows, + postNativePassGateRows, + hostPreflightRows, + familyHostReadinessRows, + dispatchPlanRows, + skipEvidenceContractRows, + evidenceAcceptanceGateRows, + ]) { + assert.deepEqual( + rowSet.map((row) => row.boundary_class), + expectedClasses, + "blocked runtime opt-in gate class order drift", + ); + } + + for (const boundaryClass of expectedClasses) { + const expectedKind = classToKind.get(boundaryClass); + const recommendation = classToRecommendation.get(boundaryClass); + const execution = executionByClass.get(boundaryClass); + const readiness = readinessByClass.get(boundaryClass); + const blocker = blockerByClass.get(boundaryClass); + const postNative = postNativeByClass.get(boundaryClass); + const preflight = preflightByClass.get(boundaryClass); + const familyReadiness = familyReadinessByClass.get(boundaryClass); + const dispatch = dispatchByClass.get(boundaryClass); + const skipEvidence = skipEvidenceByClass.get(boundaryClass); + const evidenceGate = evidenceGateByClass.get(boundaryClass); + + for (const [label, row] of [ + ["recommendation", recommendation], + ["execution", execution], + ["readiness", readiness], + ["blocker", blocker], + ["postNative", postNative], + ["preflight", preflight], + ["familyReadiness", familyReadiness], + ["dispatch", dispatch], + ["skipEvidence", skipEvidence], + ["evidenceGate", evidenceGate], + ]) { + assert.ok(row, `${boundaryClass}: missing ${label} row for opt-in gate consistency`); + } + + assert.equal(recommendation.boundary_kind, expectedKind, `${boundaryClass}: recommendation kind drift`); + + for (const row of [ + execution, + readiness, + blocker, + postNative, + preflight, + familyReadiness, + dispatch, + skipEvidence, + evidenceGate, + ]) { + assert.equal(row.boundary_kind, expectedKind, `${boundaryClass}: gate kind drift`); + assert.equal(row.target, recommendation.target, `${boundaryClass}: gate target drift`); + assert.equal(row.blocked ?? row.boundary_class, recommendation.blocked, `${boundaryClass}: gate blocked kind drift`); + assert.equal(row.execution_enabled, "0", `${boundaryClass}: gate must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${boundaryClass}: gate must not allow promotion`); + } + + assert.equal(execution.required_native_proof, recommendation.required_native_proof, `${boundaryClass}: native proof drift`); + assert.equal(postNative.required_node_proof, recommendation.required_node_proof, `${boundaryClass}: Node proof drift`); + assert.equal(postNative.required_browser_proof, recommendation.required_browser_proof, `${boundaryClass}: browser proof drift`); + assert.equal(preflight.required_native_proof, execution.required_native_proof, `${boundaryClass}: preflight proof drift`); + assert.equal(preflight.opt_in_env, execution.opt_in_env, `${boundaryClass}: preflight opt-in drift`); + assert.equal(dispatch.opt_in_env, execution.opt_in_env, `${boundaryClass}: dispatch opt-in drift`); + assert.equal(skipEvidence.opt_in_env, execution.opt_in_env, `${boundaryClass}: skip-evidence opt-in drift`); + assert.equal(preflight.execution_command, execution.execution_command, `${boundaryClass}: preflight command drift`); + assert.equal(dispatch.execution_command, execution.execution_command, `${boundaryClass}: dispatch command drift`); + assert.equal(skipEvidence.execution_command, execution.execution_command, `${boundaryClass}: skip-evidence command drift`); + assert.equal(preflight.current_probe_status, execution.current_probe_status, `${boundaryClass}: preflight probe status drift`); + assert.equal(readiness.current_probe_status, execution.current_probe_status, `${boundaryClass}: readiness probe status drift`); + assert.equal(postNative.current_probe_status, execution.current_probe_status, `${boundaryClass}: post-native probe status drift`); + assert.equal(evidenceGate.current_probe_status, execution.current_probe_status, `${boundaryClass}: evidence gate probe status drift`); + assert.equal(familyReadiness.current_probe_status, execution.current_probe_status, `${boundaryClass}: family readiness probe status drift`); + assert.equal(preflight.missing_requirements, execution.missing_requirements, `${boundaryClass}: preflight missing requirements drift`); + assert.equal(dispatch.missing_host_requirements, execution.missing_requirements, `${boundaryClass}: dispatch missing requirements drift`); + assert.equal(skipEvidence.missing_host_requirements, execution.missing_requirements, `${boundaryClass}: skip-evidence missing requirements drift`); + assert.equal(blocker.missing_runtime_requirements, execution.missing_requirements, `${boundaryClass}: blocker missing requirements drift`); + assert.equal(evidenceGate.evidence_acceptance_allowed, "0", `${boundaryClass}: evidence acceptance must remain blocked`); + assert.equal(dispatch.dispatch_allowed, "0", `${boundaryClass}: dispatch must remain blocked on this host`); + assert.equal(readiness.promotion_lock_active, "1", `${boundaryClass}: readiness must keep promotion lock active`); + assert.equal(postNative.promotion_lock_active, "1", `${boundaryClass}: post-native gate must keep promotion lock active`); + assert.ok(blocker.blocker_keys.includes("promotion_lock_active"), `${boundaryClass}: blocker lacks promotion lock key`); + assert.ok(blocker.blocker_keys.includes("manual_lock_update_required"), `${boundaryClass}: blocker lacks manual lock key`); + assert.ok(blocker.blocker_keys.includes("host_runtime_requirements_missing"), `${boundaryClass}: blocker lacks host runtime key`); + assert.ok(evidenceGate.native_evidence_gate.includes("blocked"), `${boundaryClass}: evidence gate must be blocked`); + } +} + +function verifyBlockedRuntimeProbeExecutionConsistency({ + executionPlanRows, + passEvidenceRows, + hostPreflightRows, + dispatchPlanRows, + skipEvidenceContractRows, +}) { + const expectedClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + const byClass = (rows) => new Map(rows.map((row) => [row.boundary_class, row])); + const passByClass = byClass(passEvidenceRows); + const preflightByClass = byClass(hostPreflightRows); + const dispatchByClass = byClass(dispatchPlanRows); + const skipByClass = byClass(skipEvidenceContractRows); + + for (const rows of [ + executionPlanRows, + passEvidenceRows, + hostPreflightRows, + dispatchPlanRows, + skipEvidenceContractRows, + ]) { + assert.deepEqual( + rows.map((row) => row.boundary_class), + expectedClasses, + "blocked runtime probe execution class order drift", + ); + } + + for (const execution of executionPlanRows) { + const pass = passByClass.get(execution.boundary_class); + const preflight = preflightByClass.get(execution.boundary_class); + const dispatch = dispatchByClass.get(execution.boundary_class); + const skip = skipByClass.get(execution.boundary_class); + + assert.ok(pass, `${execution.boundary_class}: missing pass evidence contract row`); + assert.ok(preflight, `${execution.boundary_class}: missing host preflight row`); + assert.ok(dispatch, `${execution.boundary_class}: missing dispatch plan row`); + assert.ok(skip, `${execution.boundary_class}: missing skip evidence contract row`); + + for (const row of [pass, preflight, dispatch, skip]) { + assert.equal(row.boundary_kind, execution.boundary_kind, `${execution.boundary_class}: probe execution kind drift`); + assert.equal(row.target, execution.target, `${execution.boundary_class}: probe execution target drift`); + } + assert.equal(preflight.blocked, execution.boundary_class, `${execution.boundary_class}: preflight blocked kind drift`); + assert.equal(dispatch.blocked, execution.boundary_class, `${execution.boundary_class}: dispatch blocked kind drift`); + assert.equal(skip.blocked, execution.boundary_class, `${execution.boundary_class}: skip evidence blocked kind drift`); + + assert.equal(pass.runtime_probe, execution.runtime_probe, `${execution.boundary_class}: pass evidence runtime probe drift`); + assert.equal(skip.runtime_probe, execution.runtime_probe, `${execution.boundary_class}: skip evidence runtime probe drift`); + assert.equal(pass.expected_pass_status, execution.expected_pass_status, `${execution.boundary_class}: expected pass status drift`); + assert.equal(preflight.required_native_proof, execution.required_native_proof, `${execution.boundary_class}: required native proof drift`); + assert.equal(preflight.opt_in_env, execution.opt_in_env, `${execution.boundary_class}: preflight opt-in env drift`); + assert.equal(dispatch.opt_in_env, execution.opt_in_env, `${execution.boundary_class}: dispatch opt-in env drift`); + assert.equal(skip.opt_in_env, execution.opt_in_env, `${execution.boundary_class}: skip evidence opt-in env drift`); + assert.equal(preflight.execution_command, execution.execution_command, `${execution.boundary_class}: preflight execution command drift`); + assert.equal(dispatch.execution_command, execution.execution_command, `${execution.boundary_class}: dispatch execution command drift`); + assert.equal(skip.execution_command, execution.execution_command, `${execution.boundary_class}: skip evidence execution command drift`); + assert.equal(pass.current_probe_status, execution.current_probe_status, `${execution.boundary_class}: pass evidence probe status drift`); + assert.equal(preflight.current_probe_status, execution.current_probe_status, `${execution.boundary_class}: preflight probe status drift`); + assert.equal(skip.current_probe_status, execution.current_probe_status, `${execution.boundary_class}: skip evidence probe status drift`); + assert.equal(preflight.runtime_ready, execution.runtime_ready, `${execution.boundary_class}: preflight runtime readiness drift`); + assert.equal(preflight.source_proof_ready, execution.source_proof_ready, `${execution.boundary_class}: preflight source proof readiness drift`); + assert.equal(preflight.missing_requirements, execution.missing_requirements, `${execution.boundary_class}: preflight missing requirement drift`); + assert.equal(dispatch.missing_host_requirements, execution.missing_requirements, `${execution.boundary_class}: dispatch missing requirement drift`); + assert.equal(skip.missing_host_requirements, execution.missing_requirements, `${execution.boundary_class}: skip evidence missing requirement drift`); + + assert.equal(execution.runtime_ready, "0", `${execution.boundary_class}: current host must not be runtime-ready`); + assert.equal(execution.source_proof_ready, "1", `${execution.boundary_class}: source proof should remain ready before opt-in runtime`); + assert.equal(execution.current_probe_status, "skipped_missing_host_runtime", `${execution.boundary_class}: probe should remain skipped on this host`); + assert.equal(execution.plan_status, "blocked_missing_host_runtime", `${execution.boundary_class}: execution plan must remain host-blocked`); + assert.equal(preflight.preflight_status, "blocked_missing_host_runtime", `${execution.boundary_class}: preflight must remain host-blocked`); + assert.equal(dispatch.host_readiness_status, "host_blocked_for_all_opt_in_native_probes", `${execution.boundary_class}: dispatch host readiness drift`); + assert.equal(dispatch.host_action, "skip_missing_host_requirements", `${execution.boundary_class}: dispatch action must skip`); + assert.equal(dispatch.dispatch_allowed, "0", `${execution.boundary_class}: dispatch must not be allowed`); + assert.equal(skip.host_action, "skip_missing_host_requirements", `${execution.boundary_class}: skip evidence host action drift`); + assert.equal(skip.skip_reason, "missing_host_requirements", `${execution.boundary_class}: skip reason drift`); + assert.equal(pass.evidence_required_now, "0", `${execution.boundary_class}: pass evidence must not be required yet`); + assert.equal(skip.evidence_required_now, "0", `${execution.boundary_class}: skip evidence must not be required yet`); + assert.equal(pass.observed_evidence_ready, "0", `${execution.boundary_class}: pass evidence must not be ready yet`); + assert.equal(skip.observed_evidence_ready, "0", `${execution.boundary_class}: skip evidence must not be ready yet`); + assert.equal(pass.evidence_status, "pending_until_native_pass", `${execution.boundary_class}: pass evidence status drift`); + assert.equal(skip.native_evidence_status, "pending_until_native_pass", `${execution.boundary_class}: skip evidence native status drift`); + assert.equal(skip.skip_evidence_status, "skip_valid_until_host_requirements_available", `${execution.boundary_class}: skip evidence contract status drift`); + + for (const row of [execution, pass, preflight, dispatch, skip]) { + assert.equal(row.execution_enabled, "0", `${execution.boundary_class}: probe execution row must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${execution.boundary_class}: probe execution row must not allow promotion`); + } + } + + return true; +} + +function verifyBlockedRuntimeRollupConsistency({ + hostReadinessRollupRows, + dispatchRollupRows, + skipEvidenceRollupRows, + familyHostReadinessRows, + dispatchPlanRows, + skipEvidenceContractRows, +}) { + assert.equal(hostReadinessRollupRows.length, 1, "blocked runtime rollup consistency requires one host readiness rollup row"); + assert.equal(dispatchRollupRows.length, 1, "blocked runtime rollup consistency requires one dispatch rollup row"); + assert.equal(skipEvidenceRollupRows.length, 1, "blocked runtime rollup consistency requires one skip evidence rollup row"); + + const host = hostReadinessRollupRows[0]; + const dispatch = dispatchRollupRows[0]; + const skipEvidence = skipEvidenceRollupRows[0]; + + assert.equal(host.family_count, String(familyHostReadinessRows.length), "blocked runtime rollup family count drift"); + assert.equal(dispatch.probe_count, String(dispatchPlanRows.length), "blocked runtime rollup dispatch probe count drift"); + assert.equal(skipEvidence.probe_count, String(skipEvidenceContractRows.length), "blocked runtime rollup skip evidence probe count drift"); + assert.equal(host.family_count, dispatch.probe_count, "blocked runtime rollup host/dispatch count drift"); + assert.equal(dispatch.probe_count, skipEvidence.probe_count, "blocked runtime rollup dispatch/skip count drift"); + assert.equal(host.host_ready_family_count, dispatch.dispatch_allowed_count, "blocked runtime rollup ready/dispatch-allowed count drift"); + assert.equal(host.host_blocked_family_count, dispatch.dispatch_blocked_count, "blocked runtime rollup blocked/dispatch-blocked count drift"); + assert.equal(dispatch.dispatch_blocked_count, skipEvidence.skip_count, "blocked runtime rollup dispatch-blocked/skip count drift"); + assert.equal(skipEvidence.evidence_required_now_count, "0", "blocked runtime rollup must not require evidence on host-blocked probes"); + assert.equal(skipEvidence.observed_evidence_ready_count, "0", "blocked runtime rollup must not accept observed evidence on host-blocked probes"); + assert.equal(host.blocked_families, dispatch.blocked_families, "blocked runtime rollup blocked family drift"); + assert.equal(dispatch.blocked_families, skipEvidence.skipped_families, "blocked runtime rollup skipped family drift"); + assert.equal(host.missing_host_requirements, dispatch.missing_host_requirements, "blocked runtime rollup host/dispatch missing requirement drift"); + assert.equal(dispatch.missing_host_requirements, skipEvidence.missing_host_requirements, "blocked runtime rollup dispatch/skip missing requirement drift"); + assert.equal(host.ready_opt_in_commands, dispatch.dispatch_allowed_commands, "blocked runtime rollup allowed command drift"); + assert.equal(host.blocked_opt_in_commands, dispatch.dispatch_blocked_commands, "blocked runtime rollup blocked command drift"); + assert.equal(host.host_readiness_status, "host_blocked_for_all_opt_in_native_probes", "blocked runtime rollup host status drift"); + assert.equal(dispatch.dispatch_status, "dispatch_blocked_for_all_opt_in_native_probes", "blocked runtime rollup dispatch status drift"); + assert.equal(skipEvidence.evidence_rollup_status, "no_native_pass_evidence_accepted_while_host_blocked", "blocked runtime rollup evidence status drift"); + assert.equal(skipEvidence.current_probe_statuses, "skipped_missing_host_runtime", "blocked runtime rollup probe status drift"); + assert.equal(skipEvidence.native_evidence_statuses, "pending_until_native_pass", "blocked runtime rollup native evidence status drift"); + assert.equal(skipEvidence.skip_evidence_statuses, "skip_valid_until_host_requirements_available", "blocked runtime rollup skip evidence status drift"); + + for (const [label, row] of [ + ["host readiness", host], + ["dispatch", dispatch], + ["skip evidence", skipEvidence], + ]) { + assert.equal(row.execution_enabled, "0", `blocked runtime ${label} rollup must not enable execution`); + assert.equal(row.promotion_allowed, "0", `blocked runtime ${label} rollup must not allow promotion`); + assert.ok(row.notes.includes("no_automatic_promotion"), `blocked runtime ${label} rollup note drift`); + } + + return true; +} + +function verifyBlockedRuntimeHostRequirementConsistency({ + requirementSummaryRows, + unblockPlanRows, + familyHostReadinessRows, +}) { + const requirementByName = new Map( + requirementSummaryRows.map((row) => [row.requirement, row]), + ); + const unblockByRequirement = new Map( + unblockPlanRows.map((row) => [row.requirement, row]), + ); + const familyByClass = new Map( + familyHostReadinessRows.map((row) => [row.boundary_class, row]), + ); + const blockingRequirements = requirementSummaryRows.filter((row) => + row.missing_blocks_runtime === "1", + ); + + assert.deepEqual( + blockingRequirements.map((row) => row.requirement).sort(), + unblockPlanRows.map((row) => row.requirement).sort(), + "blocked runtime host requirement/unblock requirement drift", + ); + + for (const family of familyHostReadinessRows) { + const expectedMissing = uniqueSorted( + requirementSummaryRows + .filter((row) => splitSummaryList(row.missing_for).includes(family.boundary_class)) + .map((row) => row.requirement), + ); + const expectedAvailable = uniqueSorted( + requirementSummaryRows + .filter((row) => + row.available === "1" && + splitSummaryList(row.required_by).includes(family.boundary_class), + ) + .map((row) => row.requirement), + ); + + assert.deepEqual( + uniqueSorted(splitSummaryList(family.missing_host_requirements)), + expectedMissing, + `${family.boundary_class}: family missing requirements drift from requirement summary`, + ); + assert.equal( + family.missing_requirement_count, + String(expectedMissing.length), + `${family.boundary_class}: family missing requirement count drift from requirement summary`, + ); + assert.deepEqual( + uniqueSorted(splitSummaryList(family.available_requirements)), + expectedAvailable, + `${family.boundary_class}: family available requirements drift from requirement summary`, + ); + assert.equal( + family.available_requirement_count, + String(expectedAvailable.length), + `${family.boundary_class}: family available requirement count drift from requirement summary`, + ); + assert.equal(family.execution_enabled, "0", `${family.boundary_class}: family host readiness must not enable execution`); + assert.equal(family.promotion_allowed, "0", `${family.boundary_class}: family host readiness must not allow promotion`); + } + + for (const requirement of blockingRequirements) { + const unblock = unblockByRequirement.get(requirement.requirement); + assert.ok(unblock, `${requirement.requirement}: missing unblock plan row`); + assert.equal(unblock.requirement_kind, requirement.requirement_kind, `${requirement.requirement}: unblock kind drift`); + assert.equal(unblock.available, "0", `${requirement.requirement}: unblock plan must only cover unavailable requirements`); + assert.equal(unblock.missing_for, requirement.missing_for, `${requirement.requirement}: unblock missing-for drift`); + assert.equal(unblock.opt_in_envs, requirement.opt_in_envs, `${requirement.requirement}: unblock opt-in env drift`); + + const affectedClasses = splitSummaryList(requirement.missing_for); + const expectedTargets = uniqueSorted(affectedClasses.map((boundaryClass) => + familyByClass.get(boundaryClass)?.target ?? boundaryClass, + )); + const expectedCommands = uniqueSorted(affectedClasses.map((boundaryClass) => + familyByClass.get(boundaryClass)?.execution_command ?? "-", + )); + const expectedRemaining = uniqueSorted(affectedClasses.flatMap((boundaryClass) => + splitSummaryList(familyByClass.get(boundaryClass)?.missing_host_requirements ?? "-") + .filter((missingRequirement) => missingRequirement !== requirement.requirement), + )); + + assert.equal(unblock.affected_targets, listValue(expectedTargets), `${requirement.requirement}: unblock affected target drift`); + assert.equal(unblock.affected_execution_commands, listValue(expectedCommands), `${requirement.requirement}: unblock affected command drift`); + assert.equal(unblock.remaining_missing_after_requirement, listValue(expectedRemaining), `${requirement.requirement}: unblock remaining requirement drift`); + assert.equal(unblock.unblocks_when_available, "partial_unblock_other_requirements_remain", `${requirement.requirement}: unblock readiness status drift`); + assert.equal(unblock.next_action, "provide_missing_host_requirement", `${requirement.requirement}: unblock next action drift`); + assert.equal(unblock.execution_enabled, "0", `${requirement.requirement}: unblock plan must not enable execution`); + assert.equal(unblock.promotion_allowed, "0", `${requirement.requirement}: unblock plan must not allow promotion`); + } + + for (const row of requirementSummaryRows) { + assert.equal(row.execution_enabled, "0", `${row.requirement}: host requirement summary must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${row.requirement}: host requirement summary must not allow promotion`); + } + + return true; +} + +function verifyBlockedRuntimePromotionGateConsistency({ + promotionLockRows, + promotionReadinessRows, + promotionBlockerRows, + postNativePassGateRows, + nativeEvidenceAcceptanceGateRows, + passEvidenceRows, +}) { + const expectedClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + const lockByTarget = new Map( + promotionLockRows.map((row) => [row.target, row]), + ); + const readinessByClass = new Map( + promotionReadinessRows.map((row) => [row.boundary_class, row]), + ); + const blockerByClass = new Map( + promotionBlockerRows.map((row) => [row.boundary_class, row]), + ); + const postNativeByClass = new Map( + postNativePassGateRows.map((row) => [row.boundary_class, row]), + ); + const evidenceGateByClass = new Map( + nativeEvidenceAcceptanceGateRows.map((row) => [row.boundary_class, row]), + ); + const passEvidenceByClass = new Map( + passEvidenceRows.map((row) => [row.boundary_class, row]), + ); + + for (const rows of [ + promotionReadinessRows, + promotionBlockerRows, + postNativePassGateRows, + nativeEvidenceAcceptanceGateRows, + passEvidenceRows, + ]) { + assert.deepEqual( + rows.map((row) => row.boundary_class), + expectedClasses, + "blocked runtime promotion gate class order drift", + ); + } + + for (const boundaryClass of expectedClasses) { + const readiness = readinessByClass.get(boundaryClass); + const blocker = blockerByClass.get(boundaryClass); + const postNative = postNativeByClass.get(boundaryClass); + const evidenceGate = evidenceGateByClass.get(boundaryClass); + const passEvidence = passEvidenceByClass.get(boundaryClass); + assert.ok(readiness, `${boundaryClass}: missing promotion readiness row`); + assert.ok(blocker, `${boundaryClass}: missing promotion blocker row`); + assert.ok(postNative, `${boundaryClass}: missing post-native-pass gate row`); + assert.ok(evidenceGate, `${boundaryClass}: missing native evidence acceptance row`); + assert.ok(passEvidence, `${boundaryClass}: missing pass evidence contract row`); + + const lock = lockByTarget.get(readiness.target); + assert.ok(lock, `${boundaryClass}: missing promotion lock row for ${readiness.target}`); + + for (const row of [blocker, postNative, evidenceGate, passEvidence]) { + assert.equal(row.boundary_kind, readiness.boundary_kind, `${boundaryClass}: promotion gate kind drift`); + assert.equal(row.target, readiness.target, `${boundaryClass}: promotion gate target drift`); + } + for (const row of [blocker, postNative, evidenceGate]) { + assert.equal(row.blocked, readiness.blocked, `${boundaryClass}: promotion gate blocked kind drift`); + } + + assert.equal(readiness.current_probe_status, passEvidence.current_probe_status, `${boundaryClass}: readiness/pass probe status drift`); + assert.equal(readiness.expected_pass_status, passEvidence.expected_pass_status, `${boundaryClass}: readiness/pass expected status drift`); + assert.equal(readiness.native_evidence_status, passEvidence.evidence_status, `${boundaryClass}: readiness/pass evidence status drift`); + assert.equal(readiness.native_evidence_ready, passEvidence.observed_evidence_ready, `${boundaryClass}: readiness/pass observed evidence drift`); + assert.equal(readiness.promotion_lock_active, lock.lock_active, `${boundaryClass}: readiness/lock active drift`); + assert.equal(readiness.promotion_ready, blocker.promotion_ready, `${boundaryClass}: readiness/blocker promotion flag drift`); + assert.equal(readiness.promotion_ready, evidenceGate.promotion_ready, `${boundaryClass}: readiness/evidence promotion flag drift`); + + assert.equal(postNative.current_probe_status, readiness.current_probe_status, `${boundaryClass}: post-native probe status drift`); + assert.equal(postNative.expected_pass_status, readiness.expected_pass_status, `${boundaryClass}: post-native expected status drift`); + assert.equal(postNative.native_pass_ready, readiness.native_pass_ready, `${boundaryClass}: post-native native pass drift`); + assert.equal(postNative.native_evidence_ready, readiness.native_evidence_ready, `${boundaryClass}: post-native native evidence drift`); + assert.equal(postNative.manual_lock_update_required, readiness.manual_lock_update_required, `${boundaryClass}: post-native manual lock drift`); + assert.equal(postNative.promotion_lock_active, readiness.promotion_lock_active, `${boundaryClass}: post-native promotion lock drift`); + + assert.equal(evidenceGate.current_probe_status, readiness.current_probe_status, `${boundaryClass}: evidence gate probe status drift`); + assert.equal(evidenceGate.native_evidence_status, readiness.native_evidence_status, `${boundaryClass}: evidence gate status drift`); + assert.equal(evidenceGate.evidence_required_now, passEvidence.evidence_required_now, `${boundaryClass}: evidence gate required-now drift`); + assert.equal(evidenceGate.observed_evidence_ready, passEvidence.observed_evidence_ready, `${boundaryClass}: evidence gate observed evidence drift`); + assert.equal(evidenceGate.node_gate_status, postNative.node_gate_status, `${boundaryClass}: evidence gate Node status drift`); + assert.equal(evidenceGate.browser_gate_status, postNative.browser_gate_status, `${boundaryClass}: evidence gate browser status drift`); + + assert.equal(readiness.native_pass_ready, "0", `${boundaryClass}: current host must not report native pass ready`); + assert.equal(readiness.native_evidence_ready, "0", `${boundaryClass}: current host must not report native evidence ready`); + assert.equal(readiness.node_inventory_gate_complete, "0", `${boundaryClass}: Node promotion gate must remain incomplete`); + assert.equal(readiness.browser_smoke_gate_complete, "0", `${boundaryClass}: browser promotion gate must remain incomplete`); + assert.equal(readiness.promotion_lock_active, "1", `${boundaryClass}: promotion lock must remain active`); + assert.equal(readiness.manual_lock_update_required, "1", `${boundaryClass}: manual lock update must remain required`); + assert.equal(readiness.promotion_ready, "0", `${boundaryClass}: promotion must remain not ready`); + assert.ok(blocker.blocker_keys.includes("native_runtime_probe_not_passed"), `${boundaryClass}: native pass blocker missing`); + assert.ok(blocker.blocker_keys.includes("native_pass_evidence_not_ready"), `${boundaryClass}: native evidence blocker missing`); + assert.ok(blocker.blocker_keys.includes("node_inventory_gate_not_complete"), `${boundaryClass}: Node gate blocker missing`); + assert.ok(blocker.blocker_keys.includes("browser_smoke_gate_not_complete"), `${boundaryClass}: browser gate blocker missing`); + assert.ok(blocker.blocker_keys.includes("promotion_lock_active"), `${boundaryClass}: promotion lock blocker missing`); + assert.ok(blocker.blocker_keys.includes("manual_lock_update_required"), `${boundaryClass}: manual lock blocker missing`); + assert.equal(postNative.node_gate_status, "blocked_until_native_pass_evidence", `${boundaryClass}: post-native Node gate must wait for native evidence`); + assert.equal(postNative.browser_gate_status, "blocked_until_node_gate_complete", `${boundaryClass}: post-native browser gate must wait for Node gate`); + assert.equal(postNative.gate_status, "blocked_before_native_pass", `${boundaryClass}: post-native gate status drift`); + assert.equal(evidenceGate.native_evidence_gate, "blocked_until_host_requirements_available", `${boundaryClass}: native evidence gate must remain host-blocked`); + assert.equal(evidenceGate.evidence_acceptance_allowed, "0", `${boundaryClass}: native evidence acceptance must remain blocked`); + + for (const row of [readiness, blocker, postNative, evidenceGate, passEvidence, lock]) { + assert.equal(row.execution_enabled, "0", `${boundaryClass}: promotion consistency row must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${boundaryClass}: promotion consistency row must not allow promotion`); + } + } + + return true; +} + +function verifyGeneratedArtifactDocumentationCoverage({ + artifactPaths, + documentationPaths, +}) { + const artifactNames = [...new Set(artifactPaths.map((artifactPath) => basename(artifactPath)))].sort(); + assert.equal( + artifactNames.length, + artifactPaths.length, + "generated sim-config inventory artifact list contains duplicate paths", + ); + assert.equal( + artifactNames.length, + 54, + "generated sim-config inventory artifact list count drift", + ); + assert.deepEqual( + artifactNames, + [ + "blocked-dependency-summary.tsv", + "blocked-runtime-promotion-lock.tsv", + "boundary-phase-completion-summary.tsv", + "boundary-proof-gates.tsv", + "boundary-summary.tsv", + "full-process-boundary-summary.tsv", + "ini-boundary-summary.tsv", + "native-proof-alignment-summary.tsv", + "native-runtime-probe-execution-plan.tsv", + "native-runtime-probe-pass-evidence-contract.tsv", + "native-runtime-probe-summary.tsv", + "next-boundary-recommendations.tsv", + "next-boundary-worklist.tsv", + "python-remap-boundary-summary.tsv", + "python-remap-family-summary.tsv", + "python-remap-native-runtime-alignment.tsv", + "python-remap-native-runtime-fixture-plan.tsv", + "python-remap-native-runtime-probe-gate.tsv", + "python-remap-native-runtime-readiness.tsv", + "python-remap-native-runtime-state-plan.tsv", + "python-remap-runtime-contract.tsv", + "python-remap-runtime-gates.tsv", + "runtime-boundary-contract-summary.tsv", + "runtime-boundary-family-host-readiness.tsv", + "runtime-boundary-host-preflight.tsv", + "runtime-boundary-host-readiness-rollup.tsv", + "runtime-boundary-host-requirement-summary.tsv", + "runtime-boundary-host-unblock-plan.tsv", + "runtime-boundary-native-alignment-summary.tsv", + "runtime-boundary-native-evidence-acceptance-gate.tsv", + "runtime-boundary-opt-in-probe-dispatch-plan.tsv", + "runtime-boundary-opt-in-probe-dispatch-rollup.tsv", + "runtime-boundary-opt-in-probe-skip-evidence-contract.tsv", + "runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv", + "runtime-boundary-post-native-pass-gates.tsv", + "runtime-boundary-promotion-blockers.tsv", + "runtime-boundary-promotion-readiness.tsv", + "runtime-probe-gate-alignment.tsv", + "skip-summary.tsv", + "summary.tsv", + "tool-db-process-boundary-summary.tsv", + "tool-db-process-native-protocol-alignment.tsv", + "tool-db-process-native-runtime-probe-gate.tsv", + "tool-db-process-native-runtime-readiness.tsv", + "tool-db-process-protocol-gates.tsv", + "tool-db-process-transaction-plan.tsv", + "user-m-process-boundary-summary.tsv", + "user-m-process-native-runtime-probe-gate.tsv", + "user-m-process-native-runtime-readiness.tsv", + "user-m-process-native-runtime-state-plan.tsv", + "user-m-process-native-state-alignment.tsv", + "user-m-process-native-transition-alignment.tsv", + "user-m-process-state-targets.tsv", + "user-m-process-transition-plan.tsv", + ], + "generated sim-config inventory artifact list drift", + ); + for (const artifactPath of artifactPaths) { + assert.ok(existsSync(artifactPath), `${basename(artifactPath)}: generated sim-config inventory artifact missing`); + const artifactText = readFileSync(artifactPath, "utf8"); + assert.ok( + artifactText.includes("\t") && artifactText.trim().split("\n")[0].length > 0, + `${basename(artifactPath)}: generated sim-config inventory artifact lacks TSV header`, + ); + } + + const documentationText = documentationPaths + .map((documentationPath) => readFileSync(documentationPath, "utf8")) + .join("\n"); + const missingArtifactNames = artifactNames.filter((artifactName) => + !documentationText.includes(artifactName), + ); + + assert.deepEqual( + missingArtifactNames, + [], + "generated sim-config inventory artifacts must be documented", + ); + const browserSmokeText = readFileSync(browserInterpSmokePath, "utf8"); + const missingBrowserArtifactNames = artifactNames.filter((artifactName) => + !browserSmokeText.includes(artifactName), + ); + assert.deepEqual( + missingBrowserArtifactNames, + [], + "generated sim-config inventory artifacts must be listed in browser smoke", + ); + assert.ok( + browserSmokeText.includes("wasmArtifactNames.length !== 54"), + "browser smoke must keep generated WASM artifact fixed-count guard", + ); + assert.ok( + browserSmokeText.includes("new Set(wasmArtifactNames).size !== wasmArtifactNames.length"), + "browser smoke must keep generated WASM artifact duplicate-free guard", + ); + assert.ok( + browserSmokeText.includes("browser_generated_artifact_fetchability"), + "browser smoke must keep generated WASM artifact fetchability guard", + ); + assert.ok( + browserSmokeText.includes("await fetchText(`../../build/wasm/sim-configs-inventory/${artifactName}`)"), + "browser smoke must fetch each generated WASM artifact by artifact name", + ); + assert.ok( + browserSmokeText.includes("browser_generated_artifact_documentation_coverage: missing"), + "browser smoke must keep generated WASM artifact documentation coverage guard", + ); + assert.ok( + browserSmokeText.includes( + "for (const artifactName of wasmArtifactNames) {\n if (!documentationText.includes(artifactName)) {", + ), + "browser smoke must check generated WASM artifact documentation coverage from joined documentation text", + ); + for (const documentationToken of [ + "../../docs/compatibility-validation.md", + "../../docs/sim-configs-coverage-matrix.md", + "../../docs/full-process-boundary-design.md", + ]) { + assert.ok( + browserSmokeText.includes(documentationToken), + `browser smoke must fetch ${documentationToken} for artifact documentation coverage`, + ); + } + assert.ok( + browserSmokeText.includes("const documentationText = ["), + "browser smoke must collect fetched review docs for artifact documentation coverage", + ); + assert.ok( + browserSmokeText.includes( + 'const documentationText = [\n await fetchText("../../docs/compatibility-validation.md"),\n await fetchText("../../docs/sim-configs-coverage-matrix.md"),\n await fetchText("../../docs/full-process-boundary-design.md"),\n ].join("\\n");', + ), + "browser smoke must fetch and join the exact artifact documentation review source set", + ); + assert.ok( + browserSmokeText.includes("await verifyBrowserGeneratedArtifactDocumentationCoverage();"), + "browser smoke must execute generated artifact documentation coverage helper", + ); + assert.ok( + browserSmokeText.includes('].join("\\n");'), + "browser smoke must join fetched review docs before artifact documentation coverage checks", + ); + assert.ok( + browserSmokeText.includes('"wasm_inventory_artifact_documentation_coverage",\n generatedArtifactDocumentationCoverage.wasmArtifactNames.length'), + "browser smoke must keep generated WASM artifact completion count parity guard", + ); + assert.ok( + browserSmokeText.includes("verifyBrowserBoundaryPhaseCompletionCountParity(\n boundaryPhaseCompletionRows,"), + "browser smoke must call boundary phase completion count parity with generated completion rows", + ); + assert.ok( + browserSmokeText.includes( + '[\n "wasm_inventory_artifact_documentation_coverage",\n generatedArtifactDocumentationCoverage.wasmArtifactNames.length,\n ],\n [\n "native_artifact_documentation_coverage",\n generatedArtifactDocumentationCoverage.nativeArtifactTokens.length,\n ],', + ), + "browser smoke must bind artifact documentation completion counts to returned artifact arrays", + ); + assert.ok( + browserSmokeText.includes("verifyBrowserNextBoundaryRecommendationSourceArtifactCoverage({\n recommendationRows: nextBoundaryRecommendationRows,\n wasmArtifactNames: generatedArtifactDocumentationCoverage.wasmArtifactNames,"), + "browser smoke must use generated artifact coverage names for recommendation source artifact coverage", + ); + assert.ok( + browserSmokeText.includes('row.evidence === "-" || row.evidence.length < 12'), + "browser smoke must keep completion evidence reviewability guard", + ); + assert.ok( + browserSmokeText.includes("completion evidence is not reviewable"), + "browser smoke must keep completion evidence reviewability error", + ); +} + +function generatedTsvArtifactTokens(rootPath) { + const artifacts = []; + + function walk(directory) { + for (const entry of readdirSync(directory, { withFileTypes: true })) { + const entryPath = resolve(directory, entry.name); + if (entry.isDirectory()) { + walk(entryPath); + continue; + } + if (entry.isFile() && entry.name.endsWith(".tsv")) { + artifacts.push(posix.relative(rootDir, entryPath.split("/").join(posix.sep))); + } + } + } + + walk(rootPath); + return artifacts.sort(); +} + +function verifyNativeGeneratedArtifactDocumentationCoverage(documentationPaths) { + const nativeBuildDir = resolve(rootDir, "build/native"); + assert.ok(existsSync(nativeBuildDir), "native build artifact directory is required"); + + const artifactTokens = generatedTsvArtifactTokens(nativeBuildDir); + assert.ok(artifactTokens.length > 0, "native generated TSV artifact list is empty"); + assert.equal(new Set(artifactTokens).size, artifactTokens.length, "native generated TSV artifact list contains duplicate paths"); + assert.equal(artifactTokens.length, 8, "native generated TSV artifact list count drift"); + assert.deepEqual( + artifactTokens, + [ + "build/native/native-runtime-probe-summary.tsv", + "build/native/native-source-proof-summary.tsv", + "build/native/nc-files/summary.tsv", + "build/native/sim-configs/class-summary.tsv", + "build/native/sim-configs/path-matrix.tsv", + "build/native/sim-configs/skipped.tsv", + "build/native/sim-configs/summary.tsv", + "build/native/source-probes.tsv", + ], + "native generated TSV artifact list drift", + ); + const emptyNativeArtifactTokensAllowed = new Set([ + "build/native/sim-configs/skipped.tsv", + ]); + for (const artifactToken of artifactTokens) { + const artifactPath = resolve(rootDir, artifactToken); + assert.ok(existsSync(artifactPath), `${artifactToken}: generated native TSV artifact missing`); + const artifactText = readFileSync(artifactPath, "utf8"); + if (artifactText.length === 0 && emptyNativeArtifactTokensAllowed.has(artifactToken)) { + continue; + } + assert.ok( + artifactText.includes("\t") && artifactText.trim().split("\n")[0].length > 0, + `${artifactToken}: generated native TSV artifact lacks TSV header`, + ); + } + + const documentationText = documentationPaths + .map((documentationPath) => readFileSync(documentationPath, "utf8")) + .join("\n"); + const missingArtifactTokens = artifactTokens.filter((artifactToken) => + !documentationText.includes(artifactToken), + ); + + assert.deepEqual( + missingArtifactTokens, + [], + "generated native TSV artifacts must be documented", + ); + const browserSmokeText = readFileSync(browserInterpSmokePath, "utf8"); + const missingBrowserArtifactTokens = artifactTokens.filter((artifactToken) => + !browserSmokeText.includes(artifactToken), + ); + assert.deepEqual( + missingBrowserArtifactTokens, + [], + "generated native TSV artifacts must be listed in browser smoke", + ); + assert.ok( + browserSmokeText.includes("nativeArtifactTokens.length !== 8"), + "browser smoke must keep native artifact token fixed-count guard", + ); + assert.ok( + browserSmokeText.includes("new Set(nativeArtifactTokens).size !== nativeArtifactTokens.length"), + "browser smoke must keep native artifact token duplicate-free guard", + ); + assert.ok( + browserSmokeText.includes("browser_native_artifact_fetchability"), + "browser smoke must keep native artifact fetchability guard", + ); + assert.ok( + browserSmokeText.includes("await fetchText(`../../${artifactToken}`)"), + "browser smoke must fetch each native artifact by relative artifact token", + ); + assert.ok( + browserSmokeText.includes("emptyNativeArtifactTokensAllowed.has(artifactToken)"), + "browser smoke must keep native skipped artifact empty allowance", + ); + assert.ok( + browserSmokeText.includes("browser_native_artifact_documentation_coverage: missing"), + "browser smoke must keep native artifact documentation coverage guard", + ); + assert.ok( + browserSmokeText.includes( + "for (const artifactToken of nativeArtifactTokens) {\n if (!documentationText.includes(artifactToken)) {", + ), + "browser smoke must check native artifact documentation coverage from joined documentation text", + ); + assert.ok( + browserSmokeText.includes('"native_artifact_documentation_coverage",\n generatedArtifactDocumentationCoverage.nativeArtifactTokens.length'), + "browser smoke must keep native artifact completion count parity guard", + ); +} + function nextBoundaryRecommendationRows({ worklistRows, promotionLockRows, @@ -6772,8 +8974,52 @@ function verifyNextBoundaryRecommendationRows(rows) { ["external_user_m_process", "tool_database_process", "python_runtime"], "next boundary recommendations must stay ordered by current boundary priority", ); + const expectedSourceArtifacts = new Map([ + [ + "external_user_m_process", + [ + "next-boundary-worklist.tsv", + "blocked-runtime-promotion-lock.tsv", + "user-m-process-transition-plan.tsv", + "user-m-process-native-runtime-state-plan.tsv", + "user-m-process-native-runtime-probe-gate.tsv", + ], + ], + [ + "tool_database_process", + [ + "next-boundary-worklist.tsv", + "blocked-runtime-promotion-lock.tsv", + "tool-db-process-transaction-plan.tsv", + "tool-db-process-native-runtime-readiness.tsv", + "tool-db-process-native-runtime-probe-gate.tsv", + ], + ], + [ + "python_runtime", + [ + "next-boundary-worklist.tsv", + "blocked-runtime-promotion-lock.tsv", + "python-remap-runtime-contract.tsv", + "python-remap-native-runtime-fixture-plan.tsv", + "python-remap-native-runtime-probe-gate.tsv", + ], + ], + ]); for (const row of parsedRows) { + const sourceArtifacts = row.source_artifacts === "-" ? [] : row.source_artifacts.split(","); + const expectedSources = expectedSourceArtifacts.get(row.boundary_kind); + assert.ok(expectedSources, `${row.target}: unexpected recommendation source artifact kind`); + assert.deepEqual(sourceArtifacts, expectedSources, `${row.target}: recommendation source artifact set drift`); + assert.equal(new Set(sourceArtifacts).size, sourceArtifacts.length, `${row.target}: duplicate recommendation source artifact`); + assert.ok(sourceArtifacts.includes(row.primary_artifact), `${row.target}: recommendation primary artifact lacks source trace`); + for (const artifactName of [row.primary_artifact, ...sourceArtifacts]) { + assert.ok( + existsSync(resolve(buildDir, artifactName)), + `${row.target}: recommendation references missing generated artifact ${artifactName}`, + ); + } assert.ok(/^[1-9][0-9]*$/.test(row.priority), `${row.target}: invalid recommendation priority`); assert.ok(/^[1-9][0-9]*$/.test(row.artifact_row_count), `${row.target}: invalid artifact row count`); assert.equal(row.promotion_lock_active, "1", `${row.target}: recommendation lacks active promotion lock`); @@ -6820,6 +9066,252 @@ function verifyNextBoundaryRecommendationRows(rows) { return parsedRows; } +function verifyBlockedRuntimeWorklistRecommendationConsistency({ + worklistRows, + proofGateRows, + promotionLockRows, + runtimeContractRows, + recommendationRows, +}) { + const expectedKinds = [ + "external_user_m_process", + "tool_database_process", + "python_runtime", + ]; + const worklistByTarget = new Map(worklistRows.map((row) => [row.target, row])); + const lockByTarget = new Map(promotionLockRows.map((row) => [row.target, row])); + const contractByKind = new Map(runtimeContractRows.map((row) => [row.boundary_kind, row])); + const proofRowsByTarget = new Map(); + for (const row of proofGateRows) { + const existing = proofRowsByTarget.get(row.target) ?? []; + existing.push(row); + proofRowsByTarget.set(row.target, existing); + } + + assert.deepEqual( + recommendationRows.map((row) => row.boundary_kind), + expectedKinds, + "blocked runtime recommendation kind order drift", + ); + + for (const recommendation of recommendationRows) { + const worklist = worklistByTarget.get(recommendation.target); + const lock = lockByTarget.get(recommendation.target); + const contract = contractByKind.get(recommendation.boundary_kind); + const proofRows = proofRowsByTarget.get(recommendation.target) ?? []; + const proofByLayer = new Map(proofRows.map((row) => [row.proof_layer, row])); + + assert.ok(worklist, `${recommendation.target}: recommendation lacks worklist row`); + assert.ok(lock, `${recommendation.target}: recommendation lacks promotion lock row`); + assert.ok(contract, `${recommendation.target}: recommendation lacks runtime contract summary row`); + assert.deepEqual( + [...proofByLayer.keys()].sort(), + ["browser", "native", "node"], + `${recommendation.target}: recommendation proof gate layer drift`, + ); + + assert.equal(recommendation.boundary_kind, worklist.boundary_kind, `${recommendation.target}: recommendation/worklist kind drift`); + assert.equal(recommendation.blocked, worklist.blocked, `${recommendation.target}: recommendation/worklist blocked drift`); + assert.equal(recommendation.required_node_proof, worklist.required_node_proof, `${recommendation.target}: recommendation/worklist Node proof drift`); + assert.equal(recommendation.required_browser_proof, worklist.required_browser_proof, `${recommendation.target}: recommendation/worklist browser proof drift`); + assert.equal(recommendation.promotion_lock_active, lock.lock_active, `${recommendation.target}: recommendation/lock active drift`); + assert.equal(recommendation.contract_ok, contract.contract_ok, `${recommendation.target}: recommendation/contract status drift`); + assert.equal(lock.runtime_alignment_artifact, contract.runtime_alignment_artifact, `${recommendation.target}: lock/contract alignment artifact drift`); + assert.equal(lock.runtime_alignment_row_count, contract.runtime_alignment_row_count, `${recommendation.target}: lock/contract alignment row count drift`); + assert.equal(lock.runtime_alignment_ok, contract.runtime_alignment_ok, `${recommendation.target}: lock/contract alignment status drift`); + + for (const layer of ["native", "node", "browser"]) { + const proof = proofByLayer.get(layer); + assert.ok(proof, `${recommendation.target}: missing ${layer} proof gate`); + assert.equal(proof.boundary_kind, recommendation.boundary_kind, `${recommendation.target}: ${layer} proof kind drift`); + assert.equal(proof.blocked, recommendation.blocked, `${recommendation.target}: ${layer} proof blocked drift`); + assert.equal(proof.proof_status, "pending", `${recommendation.target}: ${layer} proof gate must remain pending`); + assert.equal(proof.execution_enabled, "0", `${recommendation.target}: ${layer} proof gate must not enable execution`); + assert.equal(proof.promotion_allowed, "0", `${recommendation.target}: ${layer} proof gate must not allow promotion`); + } + assert.equal(proofByLayer.get("node").required_proof, recommendation.required_node_proof, `${recommendation.target}: Node proof/recommendation drift`); + assert.equal(proofByLayer.get("browser").required_proof, recommendation.required_browser_proof, `${recommendation.target}: browser proof/recommendation drift`); + + assert.equal(recommendation.execution_enabled, "0", `${recommendation.target}: recommendation must not enable execution`); + assert.equal(recommendation.promotion_allowed, "0", `${recommendation.target}: recommendation must not allow promotion`); + assert.equal(lock.execution_enabled, "0", `${recommendation.target}: promotion lock must not enable execution`); + assert.equal(lock.promotion_allowed, "0", `${recommendation.target}: promotion lock must not allow promotion`); + assert.equal(lock.lock_active, "1", `${recommendation.target}: promotion lock must remain active`); + assert.equal(contract.contract_ok, "1", `${recommendation.target}: runtime contract summary must remain OK`); + assert.ok(recommendation.source_artifacts.includes("next-boundary-worklist.tsv"), `${recommendation.target}: recommendation source lacks worklist`); + assert.ok(recommendation.source_artifacts.includes("blocked-runtime-promotion-lock.tsv"), `${recommendation.target}: recommendation source lacks promotion lock`); + } + + return true; +} + +function verifyRuntimeFamilyContractAlignmentConsistency({ + userMTransitionRows, + userMRuntimeStatePlanRows, + userMRuntimeReadinessRows, + userMRuntimeProbeGateRows, + toolDbTransactionRows, + toolDbRuntimeReadinessRows, + toolDbRuntimeProbeGateRows, + pythonRuntimeContractRows, + pythonRuntimeReadinessRows, + pythonRuntimeStatePlanRows, + pythonRuntimeFixturePlanRows, + pythonRuntimeProbeGateRows, + runtimeContractSummaryRows, + runtimeNativeAlignmentRows, +}) { + const contractByClass = new Map( + runtimeContractSummaryRows.map((row) => [row.boundary_class, row]), + ); + const nativeAlignmentByClass = new Map( + runtimeNativeAlignmentRows.map((row) => [row.boundary_class, row]), + ); + const requireDisabled = (row, label) => { + assert.equal(row.proof_status, "pending", `${label}: proof status must remain pending`); + assert.equal(row.execution_enabled, "0", `${label}: must not enable execution`); + assert.equal(row.promotion_allowed, "0", `${label}: must not allow promotion`); + }; + const missingRequirements = (rows) => + uniqueSorted(rows + .filter((row) => row.available === "0") + .map((row) => row.requirement)); + + const userMContract = contractByClass.get("L4-USER-M-PROCESS"); + const userMNative = nativeAlignmentByClass.get("L4-USER-M-PROCESS"); + assert.ok(userMContract, "missing user-M runtime contract summary"); + assert.ok(userMNative, "missing user-M native alignment summary"); + assert.equal(userMContract.boundary_kind, "external_user_m_process", "user-M contract boundary kind drift"); + assert.equal(userMContract.contract_artifact, "user-m-process-transition-plan.tsv", "user-M contract artifact drift"); + assert.equal(userMContract.contract_row_count, String(userMTransitionRows.length), "user-M contract row count drift"); + assert.equal(userMContract.runtime_alignment_artifact, userMNative.artifact, "user-M contract/native alignment artifact drift"); + assert.equal(userMContract.runtime_alignment_row_count, userMNative.row_count, "user-M contract/native alignment count drift"); + assert.equal(userMContract.contract_ok, "1", "user-M contract summary must remain OK"); + assert.equal(userMNative.summary_ok, "1", "user-M native alignment summary must remain OK"); + + const userMTransitionsByCode = new Map(userMTransitionRows.map((row) => [row.user_m_code, row])); + const userMGateByCode = new Map(userMRuntimeProbeGateRows.map((row) => [row.user_m_code, row])); + const userMMissingRequirements = missingRequirements(userMRuntimeReadinessRows); + assert.equal(userMRuntimeStatePlanRows.length, userMTransitionRows.length, "user-M runtime state plan count drift"); + assert.equal(userMRuntimeProbeGateRows.length, userMTransitionRows.length, "user-M runtime probe gate count drift"); + for (const plan of userMRuntimeStatePlanRows) { + const transition = userMTransitionsByCode.get(plan.user_m_code); + const gate = userMGateByCode.get(plan.user_m_code); + assert.ok(transition, `${plan.user_m_code}: missing user-M transition contract row`); + assert.ok(gate, `${plan.user_m_code}: missing user-M probe gate row`); + assert.equal(plan.path, transition.path, `${plan.user_m_code}: user-M path drift`); + assert.equal(plan.ini, transition.ini, `${plan.user_m_code}: user-M INI drift`); + assert.equal(plan.remap_code, transition.remap_code, `${plan.user_m_code}: user-M remap drift`); + assert.equal(plan.switchkins_target, transition.switchkins_target, `${plan.user_m_code}: user-M switchkins drift`); + assert.equal(plan.active_g5x, transition.active_g5x, `${plan.user_m_code}: user-M active G5x drift`); + assert.equal(plan.work_offset_pocket, transition.work_offset_pocket, `${plan.user_m_code}: user-M work offset drift`); + assert.equal(plan.expected_state_pins, transition.expected_state_pins, `${plan.user_m_code}: user-M state pin drift`); + assert.equal(plan.source_alignment_ok, "1", `${plan.user_m_code}: user-M source alignment must be OK`); + assert.ok(plan.source_alignment_artifacts.includes(userMContract.runtime_alignment_artifact), `${plan.user_m_code}: user-M plan lacks state alignment trace`); + assert.ok(plan.source_alignment_artifacts.includes("user-m-process-native-transition-alignment.tsv"), `${plan.user_m_code}: user-M plan lacks transition alignment trace`); + assert.equal(gate.required_runtime, plan.required_runtime, `${plan.user_m_code}: user-M gate runtime drift`); + assert.equal(gate.required_native_proof, plan.proof_kind, `${plan.user_m_code}: user-M gate proof drift`); + assert.equal(gate.source_proof_ready, "1", `${plan.user_m_code}: user-M source proof must be ready`); + assert.deepEqual( + uniqueSorted(splitSummaryList(gate.missing_requirements)), + userMMissingRequirements, + `${plan.user_m_code}: user-M gate missing requirements drift`, + ); + requireDisabled(transition, `${plan.user_m_code}: user-M transition`); + requireDisabled(plan, `${plan.user_m_code}: user-M runtime plan`); + requireDisabled(gate, `${plan.user_m_code}: user-M probe gate`); + } + + const toolDbContract = contractByClass.get("L4-TOOL-DB"); + const toolDbNative = nativeAlignmentByClass.get("L4-TOOL-DB"); + assert.ok(toolDbContract, "missing tool DB runtime contract summary"); + assert.ok(toolDbNative, "missing tool DB native alignment summary"); + assert.equal(toolDbContract.boundary_kind, "tool_database_process", "tool DB contract boundary kind drift"); + assert.equal(toolDbContract.contract_artifact, "tool-db-process-transaction-plan.tsv", "tool DB contract artifact drift"); + assert.equal(toolDbContract.contract_row_count, String(toolDbTransactionRows.length), "tool DB contract row count drift"); + assert.equal(toolDbContract.runtime_alignment_artifact, toolDbNative.artifact, "tool DB contract/native alignment artifact drift"); + assert.equal(toolDbContract.runtime_alignment_row_count, toolDbNative.row_count, "tool DB contract/native alignment count drift"); + assert.equal(toolDbContract.contract_ok, "1", "tool DB contract summary must remain OK"); + assert.equal(toolDbNative.summary_ok, "1", "tool DB native alignment summary must remain OK"); + assert.equal(toolDbRuntimeProbeGateRows.length, 1, "tool DB runtime probe gate count drift"); + const toolDbGate = toolDbRuntimeProbeGateRows[0]; + const toolDbMissingRequirements = missingRequirements(toolDbRuntimeReadinessRows); + const expectedToolDbTransactions = toolDbTransactionRows.map((row) => + `${row.transaction_phase}:${row.protocol_message}`, + ); + assert.deepEqual( + uniqueSorted(splitSummaryList(toolDbGate.protocol_transactions)), + uniqueSorted(expectedToolDbTransactions), + "tool DB probe gate transaction set drift", + ); + assert.deepEqual( + uniqueSorted(splitSummaryList(toolDbGate.missing_requirements)), + toolDbMissingRequirements, + "tool DB probe gate missing requirements drift", + ); + assert.equal(toolDbGate.required_native_proof, "native_db_process_protocol_probe_required", "tool DB native proof drift"); + assert.equal(toolDbGate.source_proof_ready, "1", "tool DB source proof must be ready"); + for (const row of toolDbTransactionRows) { + assert.equal(row.db_program, toolDbGate.db_program, `${row.transaction_phase}: tool DB program drift`); + requireDisabled(row, `${row.transaction_phase}: tool DB transaction`); + } + requireDisabled(toolDbGate, "tool DB probe gate"); + + const pythonContract = contractByClass.get("L4-PYTHON-REMAP"); + const pythonNative = nativeAlignmentByClass.get("L4-PYTHON-REMAP"); + assert.ok(pythonContract, "missing Python runtime contract summary"); + assert.ok(pythonNative, "missing Python native alignment summary"); + assert.equal(pythonContract.boundary_kind, "python_runtime", "Python contract boundary kind drift"); + assert.equal(pythonContract.contract_artifact, "python-remap-runtime-contract.tsv", "Python contract artifact drift"); + assert.equal(pythonContract.contract_row_count, String(pythonRuntimeContractRows.length), "Python contract row count drift"); + assert.equal(pythonContract.runtime_alignment_artifact, pythonNative.artifact, "Python contract/native alignment artifact drift"); + assert.equal(pythonContract.runtime_alignment_row_count, pythonNative.row_count, "Python contract/native alignment count drift"); + assert.equal(pythonContract.contract_ok, "1", "Python contract summary must remain OK"); + assert.equal(pythonNative.summary_ok, "1", "Python native alignment summary must remain OK"); + assert.equal(pythonRuntimeStatePlanRows.length, pythonRuntimeContractRows.length, "Python runtime state plan count drift"); + assert.equal(pythonRuntimeFixturePlanRows.length, 1, "Python fixture plan count drift"); + assert.equal(pythonRuntimeProbeGateRows.length, 1, "Python probe gate count drift"); + + const pythonContractByFamily = new Map(pythonRuntimeContractRows.map((row) => [row.family, row])); + const pythonStatePlanByFamily = new Map(pythonRuntimeStatePlanRows.map((row) => [row.family, row])); + for (const contract of pythonRuntimeContractRows) { + const statePlan = pythonStatePlanByFamily.get(contract.family); + assert.ok(statePlan, `${contract.family}: missing Python state plan row`); + assert.equal(statePlan.runtime_phases, contract.runtime_phases, `${contract.family}: Python phase drift`); + assert.equal(statePlan.python_modules, contract.python_modules, `${contract.family}: Python module drift`); + assert.equal(statePlan.python_callables, contract.python_callables, `${contract.family}: Python callable drift`); + assert.equal(statePlan.ngc_only_subpaths, contract.ngc_only_subpaths, `${contract.family}: Python NGC-only drift`); + assert.equal(statePlan.process_assumptions, contract.process_assumptions, `${contract.family}: Python process assumption drift`); + assert.equal(statePlan.source_alignment_ok, "1", `${contract.family}: Python source alignment must be OK`); + assert.ok(statePlan.source_alignment_artifacts.includes(pythonContract.runtime_alignment_artifact), `${contract.family}: Python state plan lacks native alignment trace`); + requireDisabled(contract, `${contract.family}: Python contract`); + requireDisabled(statePlan, `${contract.family}: Python state plan`); + } + + const pythonFixture = pythonRuntimeFixturePlanRows[0]; + const pythonGate = pythonRuntimeProbeGateRows[0]; + const pythonFixtureContract = pythonContractByFamily.get(pythonFixture.family); + const pythonFixtureStatePlan = pythonStatePlanByFamily.get(pythonFixture.family); + assert.ok(pythonFixtureContract, "Python fixture lacks contract row"); + assert.ok(pythonFixtureStatePlan, "Python fixture lacks state plan row"); + assert.equal(pythonGate.family, pythonFixture.family, "Python probe gate fixture family drift"); + assert.equal(pythonFixture.runtime_phases, pythonFixtureContract.runtime_phases, "Python fixture phase drift"); + assert.equal(pythonFixture.python_modules, pythonFixtureContract.python_modules, "Python fixture module drift"); + assert.equal(pythonFixture.process_assumptions, pythonFixtureContract.process_assumptions, "Python fixture process assumption drift"); + assert.equal(pythonFixture.source_alignment_ok, "1", "Python fixture source alignment must be OK"); + assert.equal(pythonGate.required_native_proof, pythonFixture.proof_kind, "Python probe gate proof drift"); + assert.equal(pythonGate.source_proof_ready, "1", "Python source proof must be ready"); + assert.deepEqual( + uniqueSorted(splitSummaryList(pythonGate.missing_requirements)), + missingRequirements(pythonRuntimeReadinessRows.filter((row) => row.family === pythonGate.family)), + "Python probe gate missing requirements drift", + ); + requireDisabled(pythonFixture, "Python fixture plan"); + requireDisabled(pythonGate, "Python probe gate"); + + return true; +} + function nativeProofAlignmentRows(worklistRows, proofGateRows) { const expectedProofs = [ { @@ -7275,6 +9767,64 @@ function verifyRuntimeProbeGateAlignmentRows(rows) { return parsedRows; } +function verifyNativeSourceProofRuntimeProbeConsistency({ + nativeProofAlignmentRows, + nativeRuntimeProbeRows, + runtimeProbeGateAlignmentRows, +}) { + const expectedClasses = [ + "L4-USER-M-PROCESS", + "L4-TOOL-DB", + "L4-PYTHON-REMAP", + ]; + const proofByClass = new Map(nativeProofAlignmentRows.map((row) => [row.boundary_class, row])); + const runtimeByClass = new Map(nativeRuntimeProbeRows.map((row) => [row.boundary_class, row])); + const gateAlignmentByClass = new Map(runtimeProbeGateAlignmentRows.map((row) => [row.boundary_class, row])); + + assert.deepEqual( + nativeProofAlignmentRows.map((row) => row.boundary_class), + expectedClasses, + "native source proof alignment class order drift", + ); + assert.deepEqual( + nativeRuntimeProbeRows.map((row) => row.boundary_class), + expectedClasses, + "native runtime probe source-proof class order drift", + ); + assert.deepEqual( + runtimeProbeGateAlignmentRows.map((row) => row.boundary_class), + expectedClasses, + "runtime probe gate source-proof class order drift", + ); + + for (const boundaryClass of expectedClasses) { + const proof = proofByClass.get(boundaryClass); + const runtime = runtimeByClass.get(boundaryClass); + const gateAlignment = gateAlignmentByClass.get(boundaryClass); + + assert.ok(proof, `${boundaryClass}: missing native source proof alignment row`); + assert.ok(runtime, `${boundaryClass}: missing native runtime probe row`); + assert.ok(gateAlignment, `${boundaryClass}: missing runtime probe gate alignment row`); + assert.equal(runtime.boundary_kind, proof.boundary_kind, `${boundaryClass}: native proof/runtime kind drift`); + if (proof.target !== "*") { + assert.equal(runtime.target, proof.target, `${boundaryClass}: native proof/runtime target drift`); + } + assert.equal(gateAlignment.boundary_kind, runtime.boundary_kind, `${boundaryClass}: gate/runtime kind drift`); + assert.equal(gateAlignment.target, runtime.target, `${boundaryClass}: gate/runtime target drift`); + assert.equal(proof.alignment_ok, "1", `${boundaryClass}: native source proof must align before runtime probe`); + assert.equal(proof.execution_enabled, "0", `${boundaryClass}: native source proof must not enable execution`); + assert.equal(proof.promotion_allowed, "0", `${boundaryClass}: native source proof must not allow promotion`); + assert.equal(runtime.source_proof_ready, proof.alignment_ok, `${boundaryClass}: runtime source proof readiness drift`); + assert.equal(gateAlignment.native_source_proof_ready, proof.alignment_ok, `${boundaryClass}: native gate source proof readiness drift`); + assert.equal(gateAlignment.gate_source_proof_ready, proof.alignment_ok, `${boundaryClass}: generated gate source proof readiness drift`); + assert.equal(runtime.execution_enabled, "0", `${boundaryClass}: native runtime probe must not enable execution`); + assert.equal(runtime.promotion_allowed, "0", `${boundaryClass}: native runtime probe must not allow promotion`); + assert.equal(gateAlignment.execution_disabled_match, "1", `${boundaryClass}: runtime gate execution-disabled alignment drift`); + assert.equal(gateAlignment.promotion_disabled_match, "1", `${boundaryClass}: runtime gate promotion-disabled alignment drift`); + assert.equal(gateAlignment.alignment_ok, "1", `${boundaryClass}: runtime probe gate alignment must pass`); + } +} + function nativeRuntimeProbeExecutionPlanRows({ nativeRuntimeProbeRows, runtimeProbeGateAlignmentRows, @@ -9508,6 +12058,11 @@ const runtimeProbeGateAlignmentRowsGenerated = runtimeProbeGateAlignmentRows({ const runtimeProbeGateAlignmentRecords = verifyRuntimeProbeGateAlignmentRows( runtimeProbeGateAlignmentRowsGenerated, ); +verifyNativeSourceProofRuntimeProbeConsistency({ + nativeProofAlignmentRows: nativeProofAlignmentRecords, + nativeRuntimeProbeRows: nativeRuntimeProbeSummaryRecords, + runtimeProbeGateAlignmentRows: runtimeProbeGateAlignmentRecords, +}); const nativeRuntimeProbeExecutionPlanRowsGenerated = nativeRuntimeProbeExecutionPlanRows({ nativeRuntimeProbeRows: nativeRuntimeProbeSummaryRecords, runtimeProbeGateAlignmentRows: runtimeProbeGateAlignmentRecords, @@ -9546,6 +12101,23 @@ const runtimeBoundaryContractSummaryRowsGenerated = runtimeBoundaryContractSumma const runtimeBoundaryContractSummaryRecords = verifyRuntimeBoundaryContractSummaryRows( runtimeBoundaryContractSummaryRowsGenerated, ); +const runtimeFamilyContractAlignmentConsistencyOk = + verifyRuntimeFamilyContractAlignmentConsistency({ + userMTransitionRows: userMProcessTransitionPlanRecords, + userMRuntimeStatePlanRows: userMProcessNativeRuntimeStatePlanRecords, + userMRuntimeReadinessRows: userMProcessNativeRuntimeReadinessRecords, + userMRuntimeProbeGateRows: userMProcessNativeRuntimeProbeGateRecords, + toolDbTransactionRows: toolDbProcessTransactionPlanRecords, + toolDbRuntimeReadinessRows: toolDbProcessNativeRuntimeReadinessRecords, + toolDbRuntimeProbeGateRows: toolDbProcessNativeRuntimeProbeGateRecords, + pythonRuntimeContractRows: pythonRemapRuntimeContractRecords, + pythonRuntimeReadinessRows: pythonRemapNativeRuntimeReadinessRecords, + pythonRuntimeStatePlanRows: pythonRemapNativeRuntimeStatePlanRecords, + pythonRuntimeFixturePlanRows: pythonRemapNativeRuntimeFixturePlanRecords, + pythonRuntimeProbeGateRows: pythonRemapNativeRuntimeProbeGateRecords, + runtimeContractSummaryRows: runtimeBoundaryContractSummaryRecords, + runtimeNativeAlignmentRows: runtimeBoundaryNativeAlignmentSummaryRecords, + }); const blockedRuntimePromotionLockRowsGenerated = blockedRuntimePromotionLockRows({ worklistRows: nextBoundaryRecords, proofGateRows: boundaryProofGateRecords, @@ -9586,6 +12158,14 @@ const nextBoundaryRecommendationRowsGenerated = nextBoundaryRecommendationRows({ }); const nextBoundaryRecommendationRecords = verifyNextBoundaryRecommendationRows(nextBoundaryRecommendationRowsGenerated); +const blockedRuntimeWorklistRecommendationConsistencyOk = + verifyBlockedRuntimeWorklistRecommendationConsistency({ + worklistRows: nextBoundaryRecords, + proofGateRows: boundaryProofGateRecords, + promotionLockRows: blockedRuntimePromotionLockRecords, + runtimeContractRows: runtimeBoundaryContractSummaryRecords, + recommendationRows: nextBoundaryRecommendationRecords, + }); const runtimeBoundaryPostNativePassGateRowsGenerated = runtimeBoundaryPostNativePassGateRows({ promotionReadinessRows: runtimeBoundaryPromotionReadinessRecords, recommendationRows: nextBoundaryRecommendationRecords, @@ -9608,7 +12188,138 @@ const runtimeBoundaryHostPreflightRecords = verifyRuntimeBoundaryHostPreflightRo pythonRuntimeProbeGateRows: pythonRemapNativeRuntimeProbeGateRecords, executionPlanRows: nativeRuntimeProbeExecutionPlanRecords, }); +const runtimeBoundaryHostRequirementSummaryRowsGenerated = + runtimeBoundaryHostRequirementSummaryRows(runtimeBoundaryHostPreflightRecords); +const runtimeBoundaryHostRequirementSummaryRecords = + verifyRuntimeBoundaryHostRequirementSummaryRows( + runtimeBoundaryHostRequirementSummaryRowsGenerated, + runtimeBoundaryHostPreflightRecords, + ); +const runtimeBoundaryHostUnblockPlanRowsGenerated = runtimeBoundaryHostUnblockPlanRows({ + requirementSummaryRows: runtimeBoundaryHostRequirementSummaryRecords, + preflightRows: runtimeBoundaryHostPreflightRecords, +}); +const runtimeBoundaryHostUnblockPlanRecords = verifyRuntimeBoundaryHostUnblockPlanRows({ + rows: runtimeBoundaryHostUnblockPlanRowsGenerated, + requirementSummaryRows: runtimeBoundaryHostRequirementSummaryRecords, + preflightRows: runtimeBoundaryHostPreflightRecords, +}); +const runtimeBoundaryFamilyHostReadinessRowsGenerated = + runtimeBoundaryFamilyHostReadinessRows(runtimeBoundaryHostPreflightRecords); +const runtimeBoundaryFamilyHostReadinessRecords = + verifyRuntimeBoundaryFamilyHostReadinessRows( + runtimeBoundaryFamilyHostReadinessRowsGenerated, + runtimeBoundaryHostPreflightRecords, + ); +const blockedRuntimeHostRequirementConsistencyOk = + verifyBlockedRuntimeHostRequirementConsistency({ + requirementSummaryRows: runtimeBoundaryHostRequirementSummaryRecords, + unblockPlanRows: runtimeBoundaryHostUnblockPlanRecords, + familyHostReadinessRows: runtimeBoundaryFamilyHostReadinessRecords, + }); +const runtimeBoundaryHostReadinessRollupRowsGenerated = runtimeBoundaryHostReadinessRollupRows({ + familyHostReadinessRows: runtimeBoundaryFamilyHostReadinessRecords, + requirementSummaryRows: runtimeBoundaryHostRequirementSummaryRecords, +}); +const runtimeBoundaryHostReadinessRollupRecords = + verifyRuntimeBoundaryHostReadinessRollupRows({ + rows: runtimeBoundaryHostReadinessRollupRowsGenerated, + familyHostReadinessRows: runtimeBoundaryFamilyHostReadinessRecords, + requirementSummaryRows: runtimeBoundaryHostRequirementSummaryRecords, + }); +const runtimeBoundaryOptInProbeDispatchPlanRowsGenerated = + runtimeBoundaryOptInProbeDispatchPlanRows({ + familyHostReadinessRows: runtimeBoundaryFamilyHostReadinessRecords, + hostReadinessRollupRows: runtimeBoundaryHostReadinessRollupRecords, + }); +const runtimeBoundaryOptInProbeDispatchPlanRecords = + verifyRuntimeBoundaryOptInProbeDispatchPlanRows({ + rows: runtimeBoundaryOptInProbeDispatchPlanRowsGenerated, + familyHostReadinessRows: runtimeBoundaryFamilyHostReadinessRecords, + hostReadinessRollupRows: runtimeBoundaryHostReadinessRollupRecords, + }); +const runtimeBoundaryOptInProbeDispatchRollupRowsGenerated = + runtimeBoundaryOptInProbeDispatchRollupRows(runtimeBoundaryOptInProbeDispatchPlanRecords); +const runtimeBoundaryOptInProbeDispatchRollupRecords = + verifyRuntimeBoundaryOptInProbeDispatchRollupRows({ + rows: runtimeBoundaryOptInProbeDispatchRollupRowsGenerated, + dispatchPlanRows: runtimeBoundaryOptInProbeDispatchPlanRecords, + }); +const runtimeBoundaryOptInProbeSkipEvidenceContractRowsGenerated = + runtimeBoundaryOptInProbeSkipEvidenceContractRows({ + dispatchPlanRows: runtimeBoundaryOptInProbeDispatchPlanRecords, + passEvidenceRows: nativeRuntimeProbePassEvidenceContractRecords, + }); +const runtimeBoundaryOptInProbeSkipEvidenceContractRecords = + verifyRuntimeBoundaryOptInProbeSkipEvidenceContractRows({ + rows: runtimeBoundaryOptInProbeSkipEvidenceContractRowsGenerated, + dispatchPlanRows: runtimeBoundaryOptInProbeDispatchPlanRecords, + passEvidenceRows: nativeRuntimeProbePassEvidenceContractRecords, + }); +const blockedRuntimeProbeExecutionConsistencyOk = + verifyBlockedRuntimeProbeExecutionConsistency({ + executionPlanRows: nativeRuntimeProbeExecutionPlanRecords, + passEvidenceRows: nativeRuntimeProbePassEvidenceContractRecords, + hostPreflightRows: runtimeBoundaryHostPreflightRecords, + dispatchPlanRows: runtimeBoundaryOptInProbeDispatchPlanRecords, + skipEvidenceContractRows: runtimeBoundaryOptInProbeSkipEvidenceContractRecords, + }); +const runtimeBoundaryOptInProbeSkipEvidenceRollupRowsGenerated = + runtimeBoundaryOptInProbeSkipEvidenceRollupRows(runtimeBoundaryOptInProbeSkipEvidenceContractRecords); +const runtimeBoundaryOptInProbeSkipEvidenceRollupRecords = + verifyRuntimeBoundaryOptInProbeSkipEvidenceRollupRows({ + rows: runtimeBoundaryOptInProbeSkipEvidenceRollupRowsGenerated, + skipEvidenceContractRows: runtimeBoundaryOptInProbeSkipEvidenceContractRecords, + }); +const runtimeBoundaryNativeEvidenceAcceptanceGateRowsGenerated = + runtimeBoundaryNativeEvidenceAcceptanceGateRows({ + skipEvidenceContractRows: runtimeBoundaryOptInProbeSkipEvidenceContractRecords, + promotionReadinessRows: runtimeBoundaryPromotionReadinessRecords, + postNativePassGateRows: runtimeBoundaryPostNativePassGateRecords, + }); +const runtimeBoundaryNativeEvidenceAcceptanceGateRecords = + verifyRuntimeBoundaryNativeEvidenceAcceptanceGateRows({ + rows: runtimeBoundaryNativeEvidenceAcceptanceGateRowsGenerated, + skipEvidenceContractRows: runtimeBoundaryOptInProbeSkipEvidenceContractRecords, + promotionReadinessRows: runtimeBoundaryPromotionReadinessRecords, + postNativePassGateRows: runtimeBoundaryPostNativePassGateRecords, + }); +const blockedRuntimePromotionGateConsistencyOk = + verifyBlockedRuntimePromotionGateConsistency({ + promotionLockRows: blockedRuntimePromotionLockRecords, + promotionReadinessRows: runtimeBoundaryPromotionReadinessRecords, + promotionBlockerRows: runtimeBoundaryPromotionBlockerRecords, + postNativePassGateRows: runtimeBoundaryPostNativePassGateRecords, + nativeEvidenceAcceptanceGateRows: runtimeBoundaryNativeEvidenceAcceptanceGateRecords, + passEvidenceRows: nativeRuntimeProbePassEvidenceContractRecords, + }); +const blockedRuntimeRollupConsistencyOk = verifyBlockedRuntimeRollupConsistency({ + hostReadinessRollupRows: runtimeBoundaryHostReadinessRollupRecords, + dispatchRollupRows: runtimeBoundaryOptInProbeDispatchRollupRecords, + skipEvidenceRollupRows: runtimeBoundaryOptInProbeSkipEvidenceRollupRecords, + familyHostReadinessRows: runtimeBoundaryFamilyHostReadinessRecords, + dispatchPlanRows: runtimeBoundaryOptInProbeDispatchPlanRecords, + skipEvidenceContractRows: runtimeBoundaryOptInProbeSkipEvidenceContractRecords, +}); +verifyBlockedRuntimeOptInGateConsistency({ + recommendationRows: nextBoundaryRecommendationRecords, + executionPlanRows: nativeRuntimeProbeExecutionPlanRecords, + promotionReadinessRows: runtimeBoundaryPromotionReadinessRecords, + promotionBlockerRows: runtimeBoundaryPromotionBlockerRecords, + postNativePassGateRows: runtimeBoundaryPostNativePassGateRecords, + hostPreflightRows: runtimeBoundaryHostPreflightRecords, + familyHostReadinessRows: runtimeBoundaryFamilyHostReadinessRecords, + dispatchPlanRows: runtimeBoundaryOptInProbeDispatchPlanRecords, + skipEvidenceContractRows: runtimeBoundaryOptInProbeSkipEvidenceContractRecords, + evidenceAcceptanceGateRows: runtimeBoundaryNativeEvidenceAcceptanceGateRecords, +}); const boundaryPhaseCompletionRows = boundaryPhaseCompletionSummaryRows({ + generatedArtifactPaths: generatedSimConfigInventoryArtifactPaths, + documentationPaths: [ + compatibilityValidationPath, + trackedMatrixPath, + fullProcessBoundaryDesignPath, + ], boundaryRows: boundarySummaryRecords, iniBoundaryRows: iniBoundarySummaryRecords, blockedRows: blockedDependencySummaryRecords, @@ -9627,16 +12338,32 @@ const boundaryPhaseCompletionRows = boundaryPhaseCompletionSummaryRows({ pythonRuntimeStatePlanRows: pythonRemapNativeRuntimeStatePlanRecords, pythonRuntimeFixturePlanRows: pythonRemapNativeRuntimeFixturePlanRecords, runtimeContractSummaryRows: runtimeBoundaryContractSummaryRecords, + runtimeFamilyContractAlignmentConsistencyOk, nativeProofAlignmentRows: nativeProofAlignmentRecords, nativeRuntimeProbeRows: nativeRuntimeProbeSummaryRecords, runtimeNativeAlignmentRows: runtimeBoundaryNativeAlignmentSummaryRecords, runtimeProbeGateAlignmentRows: runtimeProbeGateAlignmentRecords, + blockedRuntimeWorklistRecommendationConsistencyOk, nativeRuntimeProbeExecutionPlanRows: nativeRuntimeProbeExecutionPlanRecords, nativeRuntimeProbePassEvidenceRows: nativeRuntimeProbePassEvidenceContractRecords, + blockedRuntimeProbeExecutionConsistencyOk, runtimeBoundaryPromotionReadinessRows: runtimeBoundaryPromotionReadinessRecords, runtimeBoundaryPromotionBlockerRows: runtimeBoundaryPromotionBlockerRecords, runtimeBoundaryPostNativePassGateRows: runtimeBoundaryPostNativePassGateRecords, + blockedRuntimePromotionGateConsistencyOk, runtimeBoundaryHostPreflightRows: runtimeBoundaryHostPreflightRecords, + runtimeBoundaryHostRequirementSummaryRows: runtimeBoundaryHostRequirementSummaryRecords, + runtimeBoundaryHostUnblockPlanRows: runtimeBoundaryHostUnblockPlanRecords, + runtimeBoundaryFamilyHostReadinessRows: runtimeBoundaryFamilyHostReadinessRecords, + runtimeBoundaryHostReadinessRollupRows: runtimeBoundaryHostReadinessRollupRecords, + blockedRuntimeHostRequirementConsistencyOk, + runtimeBoundaryOptInProbeDispatchPlanRows: runtimeBoundaryOptInProbeDispatchPlanRecords, + runtimeBoundaryOptInProbeDispatchRollupRows: runtimeBoundaryOptInProbeDispatchRollupRecords, + runtimeBoundaryOptInProbeSkipEvidenceContractRows: runtimeBoundaryOptInProbeSkipEvidenceContractRecords, + runtimeBoundaryOptInProbeSkipEvidenceRollupRows: runtimeBoundaryOptInProbeSkipEvidenceRollupRecords, + runtimeBoundaryNativeEvidenceAcceptanceGateRows: runtimeBoundaryNativeEvidenceAcceptanceGateRecords, + nextBoundaryRecommendationRows: nextBoundaryRecommendationRecords, + blockedRuntimeRollupConsistencyOk, trackedRows: trackedMatrixRecords, inventoryRows: summaryRows, }); @@ -10730,6 +13457,223 @@ writeFileSync( ].join("\n")}\n`, ); +writeFileSync( + runtimeBoundaryHostRequirementSummaryPath, + `${[ + [ + "requirement", + "requirement_kind", + "available", + "required_by_count", + "required_by", + "missing_for", + "blocked_families", + "opt_in_envs", + "missing_blocks_runtime", + "execution_enabled", + "promotion_allowed", + "notes", + ].join("\t"), + ...runtimeBoundaryHostRequirementSummaryRowsGenerated, + ].join("\n")}\n`, +); + +writeFileSync( + runtimeBoundaryHostUnblockPlanPath, + `${[ + [ + "requirement", + "requirement_kind", + "available", + "missing_for", + "affected_targets", + "opt_in_envs", + "affected_execution_commands", + "remaining_missing_after_requirement", + "unblocks_when_available", + "next_action", + "execution_enabled", + "promotion_allowed", + "notes", + ].join("\t"), + ...runtimeBoundaryHostUnblockPlanRowsGenerated, + ].join("\n")}\n`, +); + +writeFileSync( + runtimeBoundaryFamilyHostReadinessPath, + `${[ + [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "required_runtime", + "missing_requirement_count", + "missing_host_requirements", + "available_requirement_count", + "available_requirements", + "opt_in_env", + "execution_command", + "current_probe_status", + "preflight_status", + "family_host_status", + "execution_enabled", + "promotion_allowed", + "notes", + ].join("\t"), + ...runtimeBoundaryFamilyHostReadinessRowsGenerated, + ].join("\n")}\n`, +); + +writeFileSync( + runtimeBoundaryHostReadinessRollupPath, + `${[ + [ + "scope", + "family_count", + "host_ready_family_count", + "host_blocked_family_count", + "blocked_families", + "missing_host_requirements", + "missing_host_requirement_count", + "ready_opt_in_command_count", + "ready_opt_in_commands", + "blocked_opt_in_commands", + "host_readiness_status", + "execution_enabled", + "promotion_allowed", + "notes", + ].join("\t"), + ...runtimeBoundaryHostReadinessRollupRowsGenerated, + ].join("\n")}\n`, +); + +writeFileSync( + runtimeBoundaryOptInProbeDispatchPlanPath, + `${[ + [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "opt_in_env", + "execution_command", + "family_host_status", + "missing_host_requirements", + "host_readiness_status", + "host_action", + "dispatch_allowed", + "execution_enabled", + "promotion_allowed", + "next_action", + "notes", + ].join("\t"), + ...runtimeBoundaryOptInProbeDispatchPlanRowsGenerated, + ].join("\n")}\n`, +); + +writeFileSync( + runtimeBoundaryOptInProbeDispatchRollupPath, + `${[ + [ + "scope", + "probe_count", + "dispatch_allowed_count", + "dispatch_blocked_count", + "blocked_families", + "dispatch_actions", + "dispatch_allowed_commands", + "dispatch_blocked_commands", + "missing_host_requirements", + "dispatch_status", + "execution_enabled", + "promotion_allowed", + "notes", + ].join("\t"), + ...runtimeBoundaryOptInProbeDispatchRollupRowsGenerated, + ].join("\n")}\n`, +); + +writeFileSync( + runtimeBoundaryOptInProbeSkipEvidenceContractPath, + `${[ + [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "runtime_probe", + "opt_in_env", + "execution_command", + "host_action", + "skip_reason", + "missing_host_requirements", + "current_probe_status", + "evidence_required_now", + "observed_evidence_ready", + "native_evidence_status", + "skip_evidence_status", + "execution_enabled", + "promotion_allowed", + "notes", + ].join("\t"), + ...runtimeBoundaryOptInProbeSkipEvidenceContractRowsGenerated, + ].join("\n")}\n`, +); + +writeFileSync( + runtimeBoundaryOptInProbeSkipEvidenceRollupPath, + `${[ + [ + "scope", + "probe_count", + "skip_count", + "evidence_required_now_count", + "observed_evidence_ready_count", + "skipped_families", + "skip_reasons", + "missing_host_requirements", + "current_probe_statuses", + "native_evidence_statuses", + "skip_evidence_statuses", + "evidence_rollup_status", + "execution_enabled", + "promotion_allowed", + "notes", + ].join("\t"), + ...runtimeBoundaryOptInProbeSkipEvidenceRollupRowsGenerated, + ].join("\n")}\n`, +); + +writeFileSync( + runtimeBoundaryNativeEvidenceAcceptanceGatePath, + `${[ + [ + "boundary_class", + "boundary_kind", + "target", + "blocked", + "runtime_probe", + "current_probe_status", + "native_evidence_status", + "evidence_required_now", + "observed_evidence_ready", + "skip_evidence_status", + "promotion_ready", + "node_gate_status", + "browser_gate_status", + "native_evidence_gate", + "evidence_acceptance_allowed", + "next_action", + "execution_enabled", + "promotion_allowed", + "notes", + ].join("\t"), + ...runtimeBoundaryNativeEvidenceAcceptanceGateRowsGenerated, + ].join("\n")}\n`, +); + writeFileSync( nextBoundaryRecommendationsPath, `${[ @@ -10755,6 +13699,20 @@ writeFileSync( ].join("\n")}\n`, ); +verifyGeneratedArtifactDocumentationCoverage({ + artifactPaths: generatedSimConfigInventoryArtifactPaths, + documentationPaths: [ + compatibilityValidationPath, + trackedMatrixPath, + fullProcessBoundaryDesignPath, + ], +}); +verifyNativeGeneratedArtifactDocumentationCoverage([ + compatibilityValidationPath, + trackedMatrixPath, + fullProcessBoundaryDesignPath, +]); + console.log(`sim_configs_wasm_node_inventory_executed=${executed}`); console.log(`sim_configs_wasm_node_inventory_passed=${passed}`); console.log(`sim_configs_wasm_node_inventory_skipped=${skipped}`);