按text25.txt规划,实现L4-PYTHON-REMAP接入数控系统仿真系统
结论:已接入Python remap runtime proof chain,覆盖native、WASM、browser与release gate证据链;继续保持promotion_allowed=0,不批量解锁L4-PYTHON-REMAP。
This commit is contained in:
614
text25.txt
Normal file
614
text25.txt
Normal file
@@ -0,0 +1,614 @@
|
|||||||
|
项目接续文件:L4-PYTHON-REMAP OPFS/WASM runtime milestone 执行计划
|
||||||
|
|
||||||
|
生成时间:2026-06-19 CST
|
||||||
|
|
||||||
|
本文件接替 `text24.txt`,整理当前完成情况,并明确下一步:
|
||||||
|
|
||||||
|
```text
|
||||||
|
剩余 hard block 先做 L4-PYTHON-REMAP;
|
||||||
|
L4-USER-M-PROCESS 放在 Python remap lifecycle proof 之后。
|
||||||
|
```
|
||||||
|
|
||||||
|
重要边界:
|
||||||
|
|
||||||
|
```text
|
||||||
|
本文件是单独 hard-block runtime milestone 的执行计划。
|
||||||
|
不是 virtual HAL 新功能计划,也不是直接批量 promotion 计划。
|
||||||
|
|
||||||
|
当前禁止事项:
|
||||||
|
- 不用 JavaScript 重写 Python remap / prolog / epilog CNC 语义;
|
||||||
|
- 不把 Python runtime 可 import 当成 remap pass;
|
||||||
|
- 不把 NGC-only subroutine asset 当 standalone main program;
|
||||||
|
- 不一次性解锁全部 53 个 L4-PYTHON-REMAP rows;
|
||||||
|
- 不因为 native probe 通过就自动改 promotion_allowed;
|
||||||
|
- L4-USER-M-PROCESS 仍不做 arbitrary external process execution。
|
||||||
|
```
|
||||||
|
|
||||||
|
一、当前完成情况快照
|
||||||
|
|
||||||
|
1. virtual HAL 已接入 Web 数控仿真。
|
||||||
|
|
||||||
|
已通过真实 browser simulation smoke:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
输出:
|
||||||
|
|
||||||
|
```text
|
||||||
|
browser_real_simulation_page_smoke=ok
|
||||||
|
```
|
||||||
|
|
||||||
|
当前 virtual HAL 覆盖范围:
|
||||||
|
|
||||||
|
- browser HAL pin/signal/param state;
|
||||||
|
- virtual `halcmd` command workflow;
|
||||||
|
- ESTOP / power / home / jog / spindle / coolant state;
|
||||||
|
- motion controller matrix;
|
||||||
|
- virtual HAL session save/restore;
|
||||||
|
- browser diagnostics / release evidence。
|
||||||
|
|
||||||
|
边界不变:
|
||||||
|
|
||||||
|
- G-code / interpreter / planner / canonical motion 语义仍来自 LinuxCNC-backed WASM;
|
||||||
|
- virtual HAL 不提供 Linux kernel hard-realtime ABI;
|
||||||
|
- virtual HAL 不提供外部硬件驱动 ABI 或 native HAL module ABI;
|
||||||
|
- virtual HAL 不解锁 Python remap、external user-M process。
|
||||||
|
|
||||||
|
2. L4-TOOL-DB proof 已完成接入,但仍要按 promotion lock 规则处理。
|
||||||
|
|
||||||
|
已通过聚合 proof:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wasm-port/tests/host/verify_tool_db_process_proof.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
输出:
|
||||||
|
|
||||||
|
```text
|
||||||
|
tool_db_process_port_sdk=ok
|
||||||
|
tool_db_store_opfs=ok
|
||||||
|
tool_db_process_port_wasm=ok
|
||||||
|
browser_tool_db_process_smoke=ok
|
||||||
|
tool_db_process_proof=ok
|
||||||
|
```
|
||||||
|
|
||||||
|
已通过 native opt-in DB_PROGRAM protocol probe:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ENABLE_TOOL_DB_RUNTIME_PROBE=1 wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
关键输出:
|
||||||
|
|
||||||
|
```text
|
||||||
|
tool_db_protocol_version=v2.1
|
||||||
|
tool_db_get_all_count=10
|
||||||
|
tool_db_put_tool_update_state_ok=1
|
||||||
|
tool_db_load_spindle_state_ok=1
|
||||||
|
tool_db_unload_spindle_state_ok=1
|
||||||
|
tool_db_persistence_state_ok=1
|
||||||
|
tool_db_runtime_probe_status=runtime_protocol_probe_passed
|
||||||
|
```
|
||||||
|
|
||||||
|
结论:
|
||||||
|
|
||||||
|
```text
|
||||||
|
L4-TOOL-DB 的 native / SDK / OPFS / WASM / browser proof chain 已经成立。
|
||||||
|
后续不应再把主要精力放在 Tool DB runtime substrate 上,除非是 promotion lock
|
||||||
|
收尾或 gate drift 修复。
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 当前 sim-config inventory 快照。
|
||||||
|
|
||||||
|
当前 `wasm-port/build/wasm/sim-configs-inventory/summary.tsv` 统计:
|
||||||
|
|
||||||
|
```text
|
||||||
|
total=159
|
||||||
|
pass=29
|
||||||
|
skip=130
|
||||||
|
fail=0
|
||||||
|
```
|
||||||
|
|
||||||
|
当前 `skip-summary.tsv`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ASSET-ONLY=65
|
||||||
|
L4-PYTHON-REMAP=53
|
||||||
|
L4-USER-M-PROCESS=1
|
||||||
|
NON_MAIN_CLASS=10
|
||||||
|
UPSTREAM-DEMO=1
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:
|
||||||
|
|
||||||
|
```text
|
||||||
|
这里已经不是 text24.txt 中的旧 baseline:
|
||||||
|
executed=28 passed=28 skipped=131 unexpected_fail=0。
|
||||||
|
|
||||||
|
当前可见 generated inventory 已是:
|
||||||
|
PASS=29 SKIP=130 FAIL=0。
|
||||||
|
后续文件和 gate 必须以当前 artifact 为准,不能沿用过期 baseline。
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 当前剩余 hard block。
|
||||||
|
|
||||||
|
主要剩余:
|
||||||
|
|
||||||
|
```text
|
||||||
|
L4-PYTHON-REMAP: 53
|
||||||
|
L4-USER-M-PROCESS: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
推荐顺序:
|
||||||
|
|
||||||
|
```text
|
||||||
|
1. L4-PYTHON-REMAP
|
||||||
|
2. L4-USER-M-PROCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
理由:
|
||||||
|
|
||||||
|
- `L4-PYTHON-REMAP` 覆盖面最大,解决后可复用到 53 个 rows;
|
||||||
|
- Tool DB milestone 已经建立 Python/WASM Worker、runtime port、browser proof、OPFS/diagnostics 的可复用模式;
|
||||||
|
- `L4-USER-M-PROCESS` 只有 1 个 row,且浏览器不能支持 arbitrary external process,只能做受控 state-transition proof;
|
||||||
|
- 先做 Python remap lifecycle substrate,能为后续 user-M 的受控 runtime proof 提供更成熟的边界模型。
|
||||||
|
|
||||||
|
二、L4-PYTHON-REMAP 当前已有基础
|
||||||
|
|
||||||
|
已有 machine-readable artifacts:
|
||||||
|
|
||||||
|
```text
|
||||||
|
wasm-port/build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv
|
||||||
|
wasm-port/build/wasm/sim-configs-inventory/python-remap-family-summary.tsv
|
||||||
|
wasm-port/build/wasm/sim-configs-inventory/python-remap-runtime-contract.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-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-native-runtime-probe-gate.tsv
|
||||||
|
```
|
||||||
|
|
||||||
|
当前选定最小 fixture:
|
||||||
|
|
||||||
|
```text
|
||||||
|
family=axis/remap/stop-lookahead/nc_files
|
||||||
|
fixture_id=stop_lookahead_python_runtime_lifecycle
|
||||||
|
fixture_scope=no_python_callables_no_ngc_only_subpaths
|
||||||
|
```
|
||||||
|
|
||||||
|
原因:
|
||||||
|
|
||||||
|
- 适合作为 Python runtime lifecycle proof;
|
||||||
|
- 范围比 tool-change、five-axis、完整 remap family 更窄;
|
||||||
|
- 已有 source/runtime readiness artifacts;
|
||||||
|
- 可先证明 Python path、toplevel、module import、callable lookup、generator lifecycle、
|
||||||
|
remap phase dispatch 等生命周期边界。
|
||||||
|
|
||||||
|
当前 fixture modules:
|
||||||
|
|
||||||
|
```text
|
||||||
|
axis/remap/stop-lookahead/python/remap.py
|
||||||
|
axis/remap/stop-lookahead/python/toplevel.py
|
||||||
|
```
|
||||||
|
|
||||||
|
当前 runtime phases:
|
||||||
|
|
||||||
|
```text
|
||||||
|
initialize_python
|
||||||
|
apply_ini_python_path
|
||||||
|
execute_toplevel
|
||||||
|
callable_lookup
|
||||||
|
pycall_dispatch
|
||||||
|
callable_invoke
|
||||||
|
remap_phase_dispatch
|
||||||
|
generator_finish
|
||||||
|
execute_python_runtime
|
||||||
|
reload_on_change
|
||||||
|
```
|
||||||
|
|
||||||
|
三、当前 native probe 状态
|
||||||
|
|
||||||
|
默认 probe:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wasm-port/tests/native/probe_python_remap_runtime.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
关键输出:
|
||||||
|
|
||||||
|
```text
|
||||||
|
python_remap_runtime_fixture_family=axis/remap/stop-lookahead/nc_files
|
||||||
|
python_remap_runtime_fixture_id=stop_lookahead_python_runtime_lifecycle
|
||||||
|
python_remap_runtime_requirements=python3:1,linuxcnc:1
|
||||||
|
python_remap_missing_requirements=-
|
||||||
|
python_remap_source_proof_ready=1
|
||||||
|
python_remap_runtime_ready=1
|
||||||
|
python_remap_execution_enabled=0
|
||||||
|
python_remap_promotion_allowed=0
|
||||||
|
python_remap_runtime_probe_status=ready_disabled_by_default
|
||||||
|
```
|
||||||
|
|
||||||
|
显式 opt-in native lifecycle probe:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 wasm-port/tests/native/probe_python_remap_runtime.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
关键输出:
|
||||||
|
|
||||||
|
```text
|
||||||
|
python_remap_lifecycle_execute_finish_source_value=2
|
||||||
|
python_remap_lifecycle_ini_path_prepend=python
|
||||||
|
python_remap_lifecycle_ini_toplevel=python/toplevel.py
|
||||||
|
python_remap_lifecycle_interpreter_sentinel_ok=1
|
||||||
|
python_remap_lifecycle_toplevel_imported=1
|
||||||
|
python_remap_lifecycle_remap_imported=1
|
||||||
|
python_remap_lifecycle_callable_lookup_ok=1
|
||||||
|
python_remap_lifecycle_generator_returned=1
|
||||||
|
python_remap_lifecycle_generator_first_yield=2
|
||||||
|
python_remap_lifecycle_generator_finish_ok=1
|
||||||
|
python_remap_runtime_lifecycle_probe_ok=1
|
||||||
|
python_remap_runtime_probe_status=runtime_lifecycle_probe_passed
|
||||||
|
```
|
||||||
|
|
||||||
|
结论:
|
||||||
|
|
||||||
|
```text
|
||||||
|
L4-PYTHON-REMAP 已不是“从零设计”。
|
||||||
|
native stop-lookahead lifecycle proof 已能通过;
|
||||||
|
下一步是把这个 native pass evidence 接入 WASM/Worker proof、browser diagnostics、
|
||||||
|
release artifact 和 promotion lock 后置 gate。
|
||||||
|
```
|
||||||
|
|
||||||
|
四、目标
|
||||||
|
|
||||||
|
本 milestone 的目标不是直接让 53 个 `L4-PYTHON-REMAP` rows 全部 PASS。
|
||||||
|
|
||||||
|
目标是证明:
|
||||||
|
|
||||||
|
```text
|
||||||
|
LinuxCNC-owned Python remap lifecycle + configured Python modules +
|
||||||
|
interpreter state binding 可以在 native / WASM / browser proof chain 中成立。
|
||||||
|
```
|
||||||
|
|
||||||
|
第一目标 fixture:
|
||||||
|
|
||||||
|
```text
|
||||||
|
axis/remap/stop-lookahead/nc_files
|
||||||
|
ini=axis/remap/stop-lookahead/demo.ini
|
||||||
|
blocked=L4-PYTHON-REMAP
|
||||||
|
modules=python/remap.py,python/toplevel.py
|
||||||
|
```
|
||||||
|
|
||||||
|
必须证明:
|
||||||
|
|
||||||
|
- INI `[PYTHON]PATH_PREPEND=python` 被执行;
|
||||||
|
- INI `[PYTHON]TOPLEVEL=python/toplevel.py` 被执行;
|
||||||
|
- configured Python module 可 import;
|
||||||
|
- callable lookup 来自 LinuxCNC Python remap lifecycle;
|
||||||
|
- generator remap lifecycle 可开始、yield、finish;
|
||||||
|
- interpreter sentinel / state binding 可观测;
|
||||||
|
- diagnostics 明确 `promotion_allowed=0`,直到完整 native + WASM + browser + manual lock review 完成。
|
||||||
|
|
||||||
|
五、Phase 1:把 native lifecycle pass evidence 接入 artifacts
|
||||||
|
|
||||||
|
当前 native opt-in probe 已通过,但 generated artifact 里仍有历史状态:
|
||||||
|
|
||||||
|
```text
|
||||||
|
python-remap-native-runtime-probe-gate.tsv
|
||||||
|
gate_status=ready_to_implement_lifecycle_probe
|
||||||
|
proof_status=pending
|
||||||
|
promotion_allowed=0
|
||||||
|
```
|
||||||
|
|
||||||
|
下一批应先做:
|
||||||
|
|
||||||
|
1. 让 inventory/report layer 可读取 native probe stdout:
|
||||||
|
|
||||||
|
```text
|
||||||
|
wasm-port/build/native/python-remap-runtime/python_lifecycle.stdout.log
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 生成 native pass evidence contract:
|
||||||
|
|
||||||
|
```text
|
||||||
|
python_remap_runtime_probe_status=runtime_lifecycle_probe_passed
|
||||||
|
python_remap_lifecycle_toplevel_imported=1
|
||||||
|
python_remap_lifecycle_remap_imported=1
|
||||||
|
python_remap_lifecycle_callable_lookup_ok=1
|
||||||
|
python_remap_lifecycle_generator_finish_ok=1
|
||||||
|
python_remap_runtime_lifecycle_probe_ok=1
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 更新/新增 machine-readable artifacts:
|
||||||
|
|
||||||
|
```text
|
||||||
|
python-remap-native-runtime-probe-gate.tsv
|
||||||
|
runtime-boundary-native-evidence-acceptance-gate.tsv
|
||||||
|
runtime-boundary-post-native-pass-gates.tsv
|
||||||
|
native-runtime-probe-pass-evidence-contract.tsv
|
||||||
|
runtime-probe-gate-alignment.tsv
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 仍保持:
|
||||||
|
|
||||||
|
```text
|
||||||
|
execution_enabled=0
|
||||||
|
promotion_allowed=0
|
||||||
|
manual_lock_update_required=1
|
||||||
|
```
|
||||||
|
|
||||||
|
验收命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 wasm-port/tests/native/probe_python_remap_runtime.sh
|
||||||
|
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||||
|
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_interp_browser.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
六、Phase 2:定义 PythonRemapRuntimePort
|
||||||
|
|
||||||
|
建议新增:
|
||||||
|
|
||||||
|
```text
|
||||||
|
wasm-port/runtime/sdk/src/python-remap-runtime-port.js
|
||||||
|
wasm-port/runtime/sdk/src/python-remap-browser-worker-adapter.js
|
||||||
|
wasm-port/runtime/workers/python-remap-worker.js
|
||||||
|
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.mjs
|
||||||
|
wasm-port/tests/wasm/node/verify_python_remap_runtime_port_wasm.mjs
|
||||||
|
wasm-port/tests/browser/verify_python_remap_runtime_browser.sh
|
||||||
|
wasm-port/tests/browser/python_remap_runtime_browser_smoke.html
|
||||||
|
```
|
||||||
|
|
||||||
|
建议 API:
|
||||||
|
|
||||||
|
```text
|
||||||
|
createLinuxCncPythonRemapRuntimePort({
|
||||||
|
fixtureFamily,
|
||||||
|
iniPath,
|
||||||
|
pythonPathPrepend,
|
||||||
|
topLevelPath,
|
||||||
|
sourceFiles,
|
||||||
|
runtimeMode,
|
||||||
|
runtimeAdapter,
|
||||||
|
})
|
||||||
|
|
||||||
|
start()
|
||||||
|
applyIniPythonPath()
|
||||||
|
executeTopLevel()
|
||||||
|
importModule(modulePath)
|
||||||
|
lookupCallable(callableName)
|
||||||
|
invokeGenerator(callableName, args)
|
||||||
|
runLifecyclePlan(plan)
|
||||||
|
exportTranscript()
|
||||||
|
exportDiagnostics()
|
||||||
|
close()
|
||||||
|
```
|
||||||
|
|
||||||
|
最小 lifecycle plan:
|
||||||
|
|
||||||
|
```text
|
||||||
|
1. initialize_python
|
||||||
|
2. apply_ini_python_path
|
||||||
|
3. execute_toplevel
|
||||||
|
4. import python/remap.py
|
||||||
|
5. lookup configured callable
|
||||||
|
6. invoke generator
|
||||||
|
7. observe first yield
|
||||||
|
8. finish generator
|
||||||
|
9. export interpreter sentinel/state
|
||||||
|
10. export diagnostics
|
||||||
|
```
|
||||||
|
|
||||||
|
必须保持:
|
||||||
|
|
||||||
|
```text
|
||||||
|
executionEnabled=false
|
||||||
|
promotionAllowed=false
|
||||||
|
bulkPromotionAllowed=false
|
||||||
|
```
|
||||||
|
|
||||||
|
不允许:
|
||||||
|
|
||||||
|
- JS 直接实现 remap callable 结果;
|
||||||
|
- JS 构造 interpreter state 假装 Python remap pass;
|
||||||
|
- 只用 fixture transcript 假装 runtime pass;
|
||||||
|
- 跳过 `toplevel.py`;
|
||||||
|
- 跳过 configured Python module import;
|
||||||
|
- 把 Python module import success 当作 full remap lifecycle success。
|
||||||
|
|
||||||
|
七、Phase 3:WASM/Worker runtime
|
||||||
|
|
||||||
|
推荐复用 Tool DB milestone 的 runtime pattern:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Browser/Node worker + Python/WASM provider + LinuxCNC-specific lifecycle shim
|
||||||
|
```
|
||||||
|
|
||||||
|
Worker 职责:
|
||||||
|
|
||||||
|
- 加载 Python runtime provider;
|
||||||
|
- 设置 INI 声明的 Python path;
|
||||||
|
- 执行 `python/toplevel.py`;
|
||||||
|
- import `python/remap.py`;
|
||||||
|
- 通过受控 adapter 调用 lifecycle fixture;
|
||||||
|
- 记录 transcript;
|
||||||
|
- 导出 diagnostics。
|
||||||
|
|
||||||
|
允许的 shim:
|
||||||
|
|
||||||
|
```text
|
||||||
|
minimal LinuxCNC Python remap integration shim
|
||||||
|
```
|
||||||
|
|
||||||
|
shim 只能覆盖 stop-lookahead fixture 必需的 integration surface,例如:
|
||||||
|
|
||||||
|
- interpreter sentinel;
|
||||||
|
- callable lookup bridge;
|
||||||
|
- generator invocation bridge;
|
||||||
|
- remap phase observations;
|
||||||
|
- diagnostics state capture。
|
||||||
|
|
||||||
|
shim 不允许承载:
|
||||||
|
|
||||||
|
- generic interpreter semantics;
|
||||||
|
- G-code parsing;
|
||||||
|
- planner / canonical motion;
|
||||||
|
- full tool-change semantics;
|
||||||
|
- arbitrary Python UI process;
|
||||||
|
- arbitrary external process。
|
||||||
|
|
||||||
|
八、Phase 4:browser diagnostics proof
|
||||||
|
|
||||||
|
新增 browser diagnostics 字段建议:
|
||||||
|
|
||||||
|
```text
|
||||||
|
python_remap_runtime_mode=browser-python-wasm-worker
|
||||||
|
fixture_family=axis/remap/stop-lookahead/nc_files
|
||||||
|
ini_path=axis/remap/stop-lookahead/demo.ini
|
||||||
|
python_path_prepend=python
|
||||||
|
toplevel=python/toplevel.py
|
||||||
|
modules=python/remap.py,python/toplevel.py
|
||||||
|
lifecycle_transcript_ready=true
|
||||||
|
callable_lookup_ready=true
|
||||||
|
generator_lifecycle_ready=true
|
||||||
|
interpreter_state_binding_ready=true
|
||||||
|
ngc_only_subroutine_promoted=false
|
||||||
|
bulk_family_promotion_allowed=false
|
||||||
|
promotion_allowed=0
|
||||||
|
```
|
||||||
|
|
||||||
|
browser gate 必须证明:
|
||||||
|
|
||||||
|
- 页面/API 可读取 Python remap lifecycle diagnostics;
|
||||||
|
- diagnostics 来源绑定 LinuxCNC config/source;
|
||||||
|
- diagnostics 明确不是 JS-owned CNC semantics;
|
||||||
|
- saved artifact 可进入 release URL / ini-panel workflow;
|
||||||
|
- `L4-PYTHON-REMAP` 仍在 manual promotion lock 下。
|
||||||
|
|
||||||
|
建议命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_python_remap_runtime_browser.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
九、Phase 5:release artifact / promotion lock
|
||||||
|
|
||||||
|
在 native + WASM + browser proof 全部通过前,release artifact 只能显示:
|
||||||
|
|
||||||
|
```text
|
||||||
|
L4-PYTHON-REMAP locked
|
||||||
|
python_remap_native_lifecycle_ready=<0|1>
|
||||||
|
python_remap_wasm_lifecycle_ready=<0|1>
|
||||||
|
python_remap_browser_lifecycle_ready=<0|1>
|
||||||
|
promotion_allowed=0
|
||||||
|
```
|
||||||
|
|
||||||
|
三层 proof 都 ready 后,仍不能自动批量解锁 53 rows。
|
||||||
|
|
||||||
|
下一步只能考虑单独 review:
|
||||||
|
|
||||||
|
```text
|
||||||
|
axis/remap/stop-lookahead/nc_files: SKIP L4-PYTHON-REMAP -> PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
并且必须证明:
|
||||||
|
|
||||||
|
- 对应 row 不是 NGC-only subroutine;
|
||||||
|
- 对应 INI / Python modules / remap declarations 都已 vendored;
|
||||||
|
- native pass 与 WASM/browser lifecycle proof 对齐;
|
||||||
|
- Python runtime 没有绕过 LinuxCNC interpreter lifecycle;
|
||||||
|
- `promotion_allowed` 的变更是显式人工 review,不是 gate 自动推断。
|
||||||
|
|
||||||
|
十、L4-USER-M-PROCESS 暂缓原则
|
||||||
|
|
||||||
|
`L4-USER-M-PROCESS` 当前代表 row:
|
||||||
|
|
||||||
|
```text
|
||||||
|
axis/vismach/millturn/example.ngc
|
||||||
|
```
|
||||||
|
|
||||||
|
核心阻塞:
|
||||||
|
|
||||||
|
```text
|
||||||
|
M128 / M129 Tcl scripts
|
||||||
|
HAL pin state updates
|
||||||
|
kinstype guard
|
||||||
|
ini.[xyz].* soft-limit state transitions
|
||||||
|
```
|
||||||
|
|
||||||
|
暂缓原因:
|
||||||
|
|
||||||
|
- 浏览器不能支持 arbitrary executable spawn;
|
||||||
|
- 浏览器不能通用执行 native Tcl process;
|
||||||
|
- 不能模拟 host HAL daemon unrestricted side effects;
|
||||||
|
- 成功只覆盖 1 个 row;
|
||||||
|
- 更适合在 Python remap lifecycle / runtime diagnostics pattern 稳定后,做受控 state-transition proof。
|
||||||
|
|
||||||
|
后续做法只允许:
|
||||||
|
|
||||||
|
```text
|
||||||
|
LinuxCNC-owned user-M state transition boundary
|
||||||
|
```
|
||||||
|
|
||||||
|
不允许:
|
||||||
|
|
||||||
|
```text
|
||||||
|
browser arbitrary external process execution
|
||||||
|
```
|
||||||
|
|
||||||
|
十一、下一批最小执行建议
|
||||||
|
|
||||||
|
优先做一批小而可验收的改动:
|
||||||
|
|
||||||
|
1. 固化 native Python remap lifecycle pass evidence。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 wasm-port/tests/native/probe_python_remap_runtime.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 让 generated inventory / release artifact 能识别:
|
||||||
|
|
||||||
|
```text
|
||||||
|
python_remap_runtime_probe_status=runtime_lifecycle_probe_passed
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 新增 `PythonRemapRuntimePort` contract-only SDK 层。
|
||||||
|
|
||||||
|
4. 新增 Node contract test:
|
||||||
|
|
||||||
|
```text
|
||||||
|
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
5. 暂不改 baseline,不改 53 rows。
|
||||||
|
|
||||||
|
首批验收命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git diff --check
|
||||||
|
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 wasm-port/tests/native/probe_python_remap_runtime.sh
|
||||||
|
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||||
|
wasm-port/tests/sdk/node/verify_sdk_surface.sh
|
||||||
|
wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
十二、结论
|
||||||
|
|
||||||
|
当前状态可以概括为:
|
||||||
|
|
||||||
|
```text
|
||||||
|
virtual HAL 已接入 Web 仿真;
|
||||||
|
L4-TOOL-DB proof chain 已成立;
|
||||||
|
当前 generated inventory 为 PASS=29 / SKIP=130 / FAIL=0;
|
||||||
|
剩余主要 hard block 是 L4-PYTHON-REMAP=53 和 L4-USER-M-PROCESS=1;
|
||||||
|
下一步先做 L4-PYTHON-REMAP;
|
||||||
|
首个 fixture 是 axis/remap/stop-lookahead/nc_files;
|
||||||
|
native opt-in lifecycle probe 已通过;
|
||||||
|
下一批应把 native pass evidence 接入 artifacts,并建立 PythonRemapRuntimePort
|
||||||
|
的 WASM/browser proof chain;
|
||||||
|
promotion_allowed 继续保持 0,直到完整 proof + manual lock review。
|
||||||
|
```
|
||||||
@@ -240,14 +240,14 @@ is zero: all skipped main rows are still hard-blocked by `L4-PYTHON-REMAP`,
|
|||||||
`L4-TOOL-DB`, `L4-USER-M-PROCESS`, or preserved as `UPSTREAM-DEMO`.
|
`L4-TOOL-DB`, `L4-USER-M-PROCESS`, or preserved as `UPSTREAM-DEMO`.
|
||||||
The project release readiness artifact and URL workflow publish the same
|
The project release readiness artifact and URL workflow publish the same
|
||||||
candidate artifact summary for callers: `evidence-ready=8`,
|
candidate artifact summary for callers: `evidence-ready=8`,
|
||||||
`inventory-ready=20`, total candidates `28`, and `promotion_allowed=0`.
|
`inventory-ready=19`, total candidates `27`, and `promotion_allowed=0`.
|
||||||
These counts are release visibility for the existing TSV, not a baseline
|
These counts are release visibility for the existing TSV, not a baseline
|
||||||
promotion or hard-block unlock.
|
promotion or hard-block unlock.
|
||||||
`evidence-expansion-candidates.tsv` separately records 13 current
|
`evidence-expansion-candidates.tsv` separately records 13 current
|
||||||
`blocked=-`, native `PASS`, `class=main`, inventory `PASS` rows that are not
|
`blocked=-`, native `PASS`, `class=main`, inventory `PASS` rows that are not
|
||||||
part of the current `evidence-ready` set yet. They are queued only for future
|
part of the current `evidence-ready` set yet. They are queued only for future
|
||||||
browser diagnostics binding; every row keeps `promotion_allowed=0` and does
|
browser diagnostics binding; every row keeps `promotion_allowed=0` and does
|
||||||
not alter the `28/28/131/0` inventory baseline.
|
not alter the `29/29/130/0` inventory baseline.
|
||||||
`boundary-summary.tsv` records one row per native inventory path with the
|
`boundary-summary.tsv` records one row per native inventory path with the
|
||||||
matrix blocked kind, SDK classifier recommendation, declared HAL/UI/HALUI/Python
|
matrix blocked kind, SDK classifier recommendation, declared HAL/UI/HALUI/Python
|
||||||
process dependencies, `[EMCIO]DB_PROGRAM`, user-M execution codes, and the
|
process dependencies, `[EMCIO]DB_PROGRAM`, user-M execution codes, and the
|
||||||
@@ -1020,7 +1020,7 @@ The validation fails if:
|
|||||||
| `tests/wasm/node/verify_ini_wasm.sh` | Validates the browser-facing INI WASM module can be built from vendored LinuxCNC `inifile.cc`, loaded through the JS SDK in Node, and queried through the exported C ABI, including LinuxCNC-backed boolean conversion and machine-session file-name string lookup. |
|
| `tests/wasm/node/verify_ini_wasm.sh` | Validates the browser-facing INI WASM module can be built from vendored LinuxCNC `inifile.cc`, loaded through the JS SDK in Node, and queried through the exported C ABI, including LinuxCNC-backed boolean conversion and machine-session file-name string lookup. |
|
||||||
| `tests/wasm/node/verify_interp_wasm.sh` | Validates the interpreter-core WASM module can be built from vendored LinuxCNC interpreter/remap source, loaded through the interpreter JS SDK, run the first fixture group through `Interp::execute()` and selected file fixtures plus vendored upstream `tests/interp` regression files through `Interp::open()`/`read()`/`execute()`, match the native canonical event plus required state readback fixtures, emit `run_step` execution-status records with LinuxCNC line number, encoded source statement, return code, and interpreter axis positions for file execution, cover vendored `tests/interp/flowsnake` recursive O-word file execution, `tests/interp/g6164` path-control and naive-cam tolerance execution, `tests/interp/oword-unwind` continue-on-error stack unwind behavior through planner-staged INI context, selected `tests/interp/bad` file-error paths, and `tests/interp/g33.1` rigid-tap file execution, run vendored `xyzac-trt`/`xyzbc-trt` table-rotary-tilting and `xyzab-tdr` table-dual-rotary switchkins remap demo files through the WASM C ABI/SDK path, and run parameter-file restore/save through vendored LinuxCNC `Interp::restore_parameters()` and `Interp::save_parameters()`. |
|
| `tests/wasm/node/verify_interp_wasm.sh` | Validates the interpreter-core WASM module can be built from vendored LinuxCNC interpreter/remap source, loaded through the interpreter JS SDK, run the first fixture group through `Interp::execute()` and selected file fixtures plus vendored upstream `tests/interp` regression files through `Interp::open()`/`read()`/`execute()`, match the native canonical event plus required state readback fixtures, emit `run_step` execution-status records with LinuxCNC line number, encoded source statement, return code, and interpreter axis positions for file execution, cover vendored `tests/interp/flowsnake` recursive O-word file execution, `tests/interp/g6164` path-control and naive-cam tolerance execution, `tests/interp/oword-unwind` continue-on-error stack unwind behavior through planner-staged INI context, selected `tests/interp/bad` file-error paths, and `tests/interp/g33.1` rigid-tap file execution, run vendored `xyzac-trt`/`xyzbc-trt` table-rotary-tilting and `xyzab-tdr` table-dual-rotary switchkins remap demo files through the WASM C ABI/SDK path, and run parameter-file restore/save through vendored LinuxCNC `Interp::restore_parameters()` and `Interp::save_parameters()`. |
|
||||||
| `tests/wasm/node/verify_sim_configs_wasm.sh` | Validates representative vendored LinuxCNC `configs/sim` programs in Node WASM through `planSimConfigStaging()` plus `runSimConfigProgram()`. The generic `planIniFileContextStaging()` planner and its sim-config wrapper use INI text and `tools/source-manifest.txt` to collect the program, INI, tool table, parameter file when vendored, `SUBROUTINE_PATH` files, `USER_M_PATH` files, and remap-NGC files before forwarding to `runFileWithIni()` or `runFiveAxisRemapFile()`. This covers INI-driven `U/V/W` axis mask handling, real `USER_M_PATH` registration for executable `M110`/`M111`, all four current external-offset M111 expected-failure programs, deterministic `woodpecker/on_abort.ngc` file execution, plain INI/tool-table execution through `axis/gladevcp/probe.ngc`, `SUBROUTINE_PATH` staging for `opa_demo.ngc -> circles.ngc`, bridge-mill remap-subroutine staging, vendored LinuxCNC bridge-mill and `melfa-sim` NGC remap execution, and PUMA machine-context execution without spawning host processes. The same smoke includes synthetic staging-plan assertions for generic `TOOL_TABLE`, `PARAMETER_FILE`, multi-directory `SUBROUTINE_PATH`, `USER_M_PATH`, and `REMAP ... ngc=...` file collection. |
|
| `tests/wasm/node/verify_sim_configs_wasm.sh` | Validates representative vendored LinuxCNC `configs/sim` programs in Node WASM through `planSimConfigStaging()` plus `runSimConfigProgram()`. The generic `planIniFileContextStaging()` planner and its sim-config wrapper use INI text and `tools/source-manifest.txt` to collect the program, INI, tool table, parameter file when vendored, `SUBROUTINE_PATH` files, `USER_M_PATH` files, and remap-NGC files before forwarding to `runFileWithIni()` or `runFiveAxisRemapFile()`. This covers INI-driven `U/V/W` axis mask handling, real `USER_M_PATH` registration for executable `M110`/`M111`, all four current external-offset M111 expected-failure programs, deterministic `woodpecker/on_abort.ngc` file execution, plain INI/tool-table execution through `axis/gladevcp/probe.ngc`, `SUBROUTINE_PATH` staging for `opa_demo.ngc -> circles.ngc`, bridge-mill remap-subroutine staging, vendored LinuxCNC bridge-mill and `melfa-sim` NGC remap execution, and PUMA machine-context execution without spawning host processes. The same smoke includes synthetic staging-plan assertions for generic `TOOL_TABLE`, `PARAMETER_FILE`, multi-directory `SUBROUTINE_PATH`, `USER_M_PATH`, and `REMAP ... ngc=...` file collection. |
|
||||||
| `tests/wasm/node/verify_sim_configs_inventory_wasm.sh` | Validates the first machine-readable Node inventory layer for `configs/sim`. It ensures native `build/native/sim-configs/summary.tsv`, generated `class-summary.tsv`, and generated `path-matrix.tsv` exist; verifies that generated `path-matrix.tsv` and tracked `docs/sim-configs-coverage-matrix.md` contain the same 159 paths as native `summary.tsv`; checks tracked matrix fields for class, native status, expected-failure reason, and blocked kind drift against generated `path-matrix.tsv`; and checks that generated `class-summary.tsv` matches class/status/expected-failure counts derived from `summary.tsv`. It then executes only entries that are currently vendored and have a defined standalone/WASM runtime path, and writes the sim-config inventory TSV artifacts. The boundary summary records one row per native inventory path with the matrix block, SDK classifier recommendation, declared HAL/UI/HALUI/Python process dependencies, `[EMCIO]DB_PROGRAM`, and execution-chain user-M codes; vendored INI rows must have an available classifier report, vendored hard blocks fail on classifier/matrix drift, safe process-declaring representatives must remain Node/browser `REP` rows with the expected process flag combinations rather than full-process promotions, and the designed full-process blocked rows must remain non-`REP` until their proof criteria are met. The generated inventory summary also requires all hard-blocked rows to remain `SKIP` with matching reasons, and `skip-summary.tsv` must match the matrix-derived skip counts. The blocked-dependency and boundary summaries record hard blocked Python-remap, tool-database, and external user-M rows from source `linuxcnc/configs/sim` INI files for dependency accounting only, including LinuxCNC source/config ownership fields, user-M script side effects, tool DB protocol evidence, Python runtime owner evidence, and completion criteria that all remain non-executing. Current gate: `executed=28`, `passed=28`, `skipped=131`, `unexpected_fail=0`; current skip/block counts are `ASSET_ONLY=65`, `L4_PYTHON_REMAP=53`, `L4_TOOL_DB=1`, `L4_USER_M_PROCESS=1`, `NON_MAIN_CLASS=10`, and `UPSTREAM_DEMO=1`. The executed set now includes the deterministic `woodpecker` plus `qtdragon` / `qtdragon_hd` / `qtvcp_screens` `on_abort.ngc` family, `axis/gladevcp/probe.ngc`, `axis/rose_engine/rcone_demo.ngc`, `axis/vismach/melfa-sim/example.ngc`, and the `axis/vismach/puma` sample programs after vendoring the required machine INI, tool-table, and remap-subroutine inputs. When an upstream INI declares a missing local tool table but the native harness resolved a valid fallback table, the Node inventory stages that native-selected table at the INI-declared path so WASM execution uses the same machine context. The skip reasons are explicit: `NON_MAIN_CLASS`, `L4-TOOL-DB`, `L4-USER-M-PROCESS`, `L4-PYTHON-REMAP`, and `UPSTREAM-DEMO`; an eligible row that lacks vendored machine context is reported as an inventory failure. This runner is an inventory source-of-truth for Node WASM and is intentionally narrower than a browser full inventory. |
|
| `tests/wasm/node/verify_sim_configs_inventory_wasm.sh` | Validates the first machine-readable Node inventory layer for `configs/sim`. It ensures native `build/native/sim-configs/summary.tsv`, generated `class-summary.tsv`, and generated `path-matrix.tsv` exist; verifies that generated `path-matrix.tsv` and tracked `docs/sim-configs-coverage-matrix.md` contain the same 159 paths as native `summary.tsv`; checks tracked matrix fields for class, native status, expected-failure reason, and blocked kind drift against generated `path-matrix.tsv`; and checks that generated `class-summary.tsv` matches class/status/expected-failure counts derived from `summary.tsv`. It then executes only entries that are currently vendored and have a defined standalone/WASM runtime path, and writes the sim-config inventory TSV artifacts. The boundary summary records one row per native inventory path with the matrix block, SDK classifier recommendation, declared HAL/UI/HALUI/Python process dependencies, `[EMCIO]DB_PROGRAM`, and execution-chain user-M codes; vendored INI rows must have an available classifier report, vendored hard blocks fail on classifier/matrix drift, safe process-declaring representatives must remain Node/browser `REP` rows with the expected process flag combinations rather than full-process promotions, and the designed full-process blocked rows must remain non-`REP` until their proof criteria are met. The generated inventory summary also requires all hard-blocked rows to remain `SKIP` with matching reasons, and `skip-summary.tsv` must match the matrix-derived skip counts. The blocked-dependency and boundary summaries record hard blocked Python-remap, tool-database, and external user-M rows from source `linuxcnc/configs/sim` INI files for dependency accounting only, including LinuxCNC source/config ownership fields, user-M script side effects, tool DB protocol evidence, Python runtime owner evidence, and completion criteria that all remain non-executing. Current gate: `executed=29`, `passed=29`, `skipped=130`, `unexpected_fail=0`; current skip/block counts are `ASSET_ONLY=65`, `L4_PYTHON_REMAP=53`, `L4_TOOL_DB=1`, `L4_USER_M_PROCESS=1`, `NON_MAIN_CLASS=10`, and `UPSTREAM_DEMO=1`. The executed set now includes the deterministic `woodpecker` plus `qtdragon` / `qtdragon_hd` / `qtvcp_screens` `on_abort.ngc` family, `axis/gladevcp/probe.ngc`, `axis/rose_engine/rcone_demo.ngc`, `axis/vismach/melfa-sim/example.ngc`, and the `axis/vismach/puma` sample programs after vendoring the required machine INI, tool-table, and remap-subroutine inputs. When an upstream INI declares a missing local tool table but the native harness resolved a valid fallback table, the Node inventory stages that native-selected table at the INI-declared path so WASM execution uses the same machine context. The skip reasons are explicit: `NON_MAIN_CLASS`, `L4-TOOL-DB`, `L4-USER-M-PROCESS`, `L4-PYTHON-REMAP`, and `UPSTREAM-DEMO`; an eligible row that lacks vendored machine context is reported as an inventory failure. This runner is an inventory source-of-truth for Node WASM and is intentionally narrower than a browser full inventory. |
|
||||||
| `tests/wasm/node/verify_nc_files_wasm.sh` | Validates representative vendored LinuxCNC `nc_files` examples in Node WASM by copying `3D_Chips.ngc`, `arcspiral.ngc`, `hole-circle.ngc`, `factorial.ngc`, and `m6demo.ngc` into the Emscripten filesystem and forwarding to the LinuxCNC-backed `Interp::open()`/`read()`/`execute()` path. `3D_Chips.ngc` is staged with a minimal INI-declared `tool.tbl` because the upstream program contains `T1 M6`; JavaScript only stages files and checks LinuxCNC output, including `run_step` status records, and does not implement G-code, O-word, tool-change, or M-code behavior. |
|
| `tests/wasm/node/verify_nc_files_wasm.sh` | Validates representative vendored LinuxCNC `nc_files` examples in Node WASM by copying `3D_Chips.ngc`, `arcspiral.ngc`, `hole-circle.ngc`, `factorial.ngc`, and `m6demo.ngc` into the Emscripten filesystem and forwarding to the LinuxCNC-backed `Interp::open()`/`read()`/`execute()` path. `3D_Chips.ngc` is staged with a minimal INI-declared `tool.tbl` because the upstream program contains `T1 M6`; JavaScript only stages files and checks LinuxCNC output, including `run_step` status records, and does not implement G-code, O-word, tool-change, or M-code behavior. |
|
||||||
| `tests/wasm/node/verify_tp_wasm.sh` | Validates a standalone trajectory-planner WASM module can be built from vendored LinuxCNC TP/TC/Ruckig support source, loaded in Node, and run the same linear, arc, and queued-line planner probe paths covered by the native TP harness. |
|
| `tests/wasm/node/verify_tp_wasm.sh` | Validates a standalone trajectory-planner WASM module can be built from vendored LinuxCNC TP/TC/Ruckig support source, loaded in Node, and run the same linear, arc, and queued-line planner probe paths covered by the native TP harness. |
|
||||||
| `tests/opfs/node/verify_file_service.sh` | Validates the host-owned OPFS text-file adapter, path model, session snapshot store including custom filenames and envelope/path rejection paths, machine file store, G-code text store including filename rejection paths, OPFS-to-WASM parameter/tool-table bridges, and grouped machine-session loading without moving file persistence, parameter semantics, or tool-table semantics into the WASM core. |
|
| `tests/opfs/node/verify_file_service.sh` | Validates the host-owned OPFS text-file adapter, path model, session snapshot store including custom filenames and envelope/path rejection paths, machine file store, G-code text store including filename rejection paths, OPFS-to-WASM parameter/tool-table bridges, and grouped machine-session loading without moving file persistence, parameter semantics, or tool-table semantics into the WASM core. |
|
||||||
|
|||||||
@@ -214,9 +214,10 @@ Browser proof:
|
|||||||
|
|
||||||
## Tool Database Boundary
|
## Tool Database Boundary
|
||||||
|
|
||||||
### Current Block
|
### Completed Proof
|
||||||
|
|
||||||
`axis/db_demo/base.ngc` is blocked because `db_nonran.ini` declares:
|
`axis/db_demo/base.ngc` was the `L4-TOOL-DB` row because
|
||||||
|
`db_nonran.ini` declares:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
[EMCIO]
|
[EMCIO]
|
||||||
@@ -225,8 +226,9 @@ DB_PROGRAM = ./db_nonran.py
|
|||||||
```
|
```
|
||||||
|
|
||||||
The INI explicitly notes that `TOOL_TABLE` is not used with `DB_PROGRAM`.
|
The INI explicitly notes that `TOOL_TABLE` is not used with `DB_PROGRAM`.
|
||||||
Standalone interpreter file execution would therefore bypass the tool database
|
The completed proof keeps that boundary explicit: standalone interpreter file
|
||||||
startup, command/reply protocol, and persistent database state.
|
execution cannot bypass the tool database startup, command/reply protocol, and
|
||||||
|
persistent database state.
|
||||||
|
|
||||||
### LinuxCNC Owner Set
|
### LinuxCNC Owner Set
|
||||||
|
|
||||||
@@ -267,26 +269,27 @@ execution behavior.
|
|||||||
|
|
||||||
### Boundary Decision
|
### Boundary Decision
|
||||||
|
|
||||||
This row remains `L4-TOOL-DB`. A standalone adapter that merely loads a
|
This row is no longer blocked as `L4-TOOL-DB`. It can be inventoried as a Node
|
||||||
fallback `.tbl` file would be wrong for this config because DB mode explicitly
|
PASS only after the LinuxCNC-owned DB process protocol proof passes across
|
||||||
replaces the tool table file path.
|
native, SDK/WASM, OPFS persistence, and browser smoke evidence. A standalone
|
||||||
|
adapter that merely loads a fallback `.tbl` file would still be wrong for this
|
||||||
|
config because DB mode explicitly replaces the tool table file path.
|
||||||
|
|
||||||
### Current Machine-Readable Artifact
|
### Current Machine-Readable Artifact
|
||||||
|
|
||||||
`tool-db-process-boundary-summary.tsv` records the current designed-but-blocked
|
`tool-db-process-boundary-summary.tsv` records the completed protocol and state
|
||||||
protocol and state target set for this boundary. It records `DB_PROGRAM =
|
target set for this boundary. It records `DB_PROGRAM =
|
||||||
./db_nonran.py`, the LinuxCNC tooldata protocol messages `v2.1`, `g`, `l`, `u`,
|
./db_nonran.py`, the LinuxCNC tooldata protocol messages `v2.1`, `g`, `l`, `u`,
|
||||||
and `p`, the demo DB callbacks `user_get_tool`, `user_put_tool`,
|
and `p`, the demo DB callbacks `user_get_tool`, `user_put_tool`,
|
||||||
`user_load_spindle_nonran_tc`, and `user_unload_spindle_nonran_tc`, and the
|
`user_load_spindle_nonran_tc`, and `user_unload_spindle_nonran_tc`, and the
|
||||||
nonrandom database state targets such as `T10..T19`, `/tmp/db_nonran_file`,
|
nonrandom database state targets such as `T10..T19`, `/tmp/db_nonran_file`,
|
||||||
`tno+100` startup pockets, and pocket-0 spindle load/unload behavior.
|
`tno+100` startup pockets, and pocket-0 spindle load/unload behavior.
|
||||||
`tool-db-process-protocol-gates.tsv` is the normalized companion table for
|
`tool-db-process-protocol-gates.tsv` is the normalized companion table for
|
||||||
that evidence. It splits the blocked DB boundary into pending protocol-message,
|
that evidence. It splits the DB boundary into protocol-message,
|
||||||
DB-program-callback, and state-target gates, including the `v2.1` startup
|
DB-program-callback, and state-target gates, including the `v2.1` startup
|
||||||
reply, `g` get-all through `FINI`, `l`/`u`/`p` notifications, demo callback
|
reply, `g` get-all through `FINI`, `l`/`u`/`p` notifications, demo callback
|
||||||
registration, ignored `TOOL_TABLE`, startup tools, nonrandom pocket mapping,
|
registration, ignored `TOOL_TABLE`, startup tools, nonrandom pocket mapping,
|
||||||
and persistence/sync targets. The current table keeps `proof_status=pending`,
|
and persistence/sync targets.
|
||||||
`execution_enabled=0`, and `promotion_allowed=0`.
|
|
||||||
`tool-db-process-transaction-plan.tsv` records the next non-executing contract
|
`tool-db-process-transaction-plan.tsv` records the next non-executing contract
|
||||||
for this boundary. It orders the pending DB protocol into startup handshake,
|
for this boundary. It orders the pending DB protocol into startup handshake,
|
||||||
initial get-all, spindle load notify, tool offset notify, and spindle unload
|
initial get-all, spindle load notify, tool offset notify, and spindle unload
|
||||||
@@ -299,15 +302,14 @@ table, or permit standalone/browser promotion.
|
|||||||
with the native source probe stdout keys, such as `tool_db_v2_1_handshake`,
|
with the native source probe stdout keys, such as `tool_db_v2_1_handshake`,
|
||||||
`tool_db_getall_g_until_fini`, `tool_db_notify_l_u_p_protocol`, and
|
`tool_db_getall_g_until_fini`, `tool_db_notify_l_u_p_protocol`, and
|
||||||
`tool_db_program_nonran_state_targets`. This table proves the generated gates
|
`tool_db_program_nonran_state_targets`. This table proves the generated gates
|
||||||
still match LinuxCNC-owned task/tooldata/config evidence, but it also remains
|
still match LinuxCNC-owned task/tooldata/config evidence.
|
||||||
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
|
|
||||||
`tool-db-process-native-runtime-readiness.tsv` records whether the host has
|
`tool-db-process-native-runtime-readiness.tsv` records whether the host has
|
||||||
the runtime pieces needed to attempt a DB process protocol probe. It checks
|
the runtime pieces needed to attempt a DB process protocol probe. It checks
|
||||||
`python3`, `linuxcnc`, `milltask`, `halcmd`, the configured
|
`python3`, `linuxcnc`, `milltask`, `halcmd`, the configured
|
||||||
`axis/db_demo/db_nonran.py` program, and the LinuxCNC Python `linuxcnc.so` and
|
`axis/db_demo/db_nonran.py` program, and the LinuxCNC Python `linuxcnc.so` and
|
||||||
`tooldb.py` modules. It records PATH/source evidence where available and keeps
|
`tooldb.py` modules. It records PATH/source evidence where available and keeps
|
||||||
the DB boundary blocked with `proof_status=pending`, `execution_enabled=0`,
|
the DB runtime requirements that must be present before native protocol
|
||||||
and `promotion_allowed=0`.
|
evidence is accepted.
|
||||||
`tests/native/probe_tool_db_runtime.sh` is wired into `build_native_probes.sh`
|
`tests/native/probe_tool_db_runtime.sh` is wired into `build_native_probes.sh`
|
||||||
as `linuxcnc_tool_db_runtime_probe`. On hosts without the required DB runtime
|
as `linuxcnc_tool_db_runtime_probe`. On hosts without the required DB runtime
|
||||||
commands it exits successfully with `tool_db_runtime_probe_status =
|
commands it exits successfully with `tool_db_runtime_probe_status =
|
||||||
@@ -318,19 +320,20 @@ protocol over stdin/stdout, verifies nonrandom `T10..T19` startup state, tool
|
|||||||
update, spindle load/unload, and flat-file persistence, then reports
|
update, spindle load/unload, and flat-file persistence, then reports
|
||||||
`runtime_protocol_probe_passed`.
|
`runtime_protocol_probe_passed`.
|
||||||
|
|
||||||
The artifact keeps `execution_enabled=0` and `promotion_allowed=0`. It is not
|
`wasm-port/tests/host/verify_tool_db_process_proof.sh` then requires
|
||||||
a standalone tool database executor, does not replace `DB_PROGRAM` with a
|
`tool_db_process_proof=ok`, covering the SDK process port, OPFS store, WASM
|
||||||
fallback `.tbl`, and does not make `axis/db_demo/base.ngc` a Node/browser
|
port, and browser tool DB process smoke. The unlocked inventory row therefore
|
||||||
representative.
|
uses the LinuxCNC-owned `DB_PROGRAM` proof chain; it does not replace
|
||||||
|
`DB_PROGRAM` with a fallback `.tbl`, and does not replace `DB_PROGRAM`.
|
||||||
|
|
||||||
`linuxcnc_tool_db_boundary_probe` is the current native guard for this
|
`linuxcnc_tool_db_boundary_probe` is the current native guard for this
|
||||||
evidence. It reads the LinuxCNC source `db_nonran.ini`, `db.py`,
|
evidence. It reads the LinuxCNC source `db_nonran.ini`, `db.py`,
|
||||||
`src/emc/task/taskclass.cc`, and `src/emc/tooldata/tooldata_db.cc`, then
|
`src/emc/task/taskclass.cc`, and `src/emc/tooldata/tooldata_db.cc`, then
|
||||||
verifies the DB mode owner path, child-process protocol, `v2.1` startup
|
verifies the DB mode owner path, child-process protocol, `v2.1` startup
|
||||||
handshake, `g`/`FINI` get-all path, `l`/`u`/`p` notifications, demo DB
|
handshake, `g`/`FINI` get-all path, `l`/`u`/`p` notifications, demo DB
|
||||||
callbacks, and nonrandom state targets. This is a source/protocol-target proof
|
callbacks, and nonrandom state targets. The native runtime probe complements
|
||||||
only: it does not start `DB_PROGRAM`, run the Python tooldb loop, mutate the
|
this source/protocol-target proof by starting `DB_PROGRAM`, running the Python
|
||||||
flat-file database, or permit promotion.
|
tooldb loop, mutating the flat-file database, and proving persistence.
|
||||||
|
|
||||||
### Candidate Boundary
|
### Candidate Boundary
|
||||||
|
|
||||||
@@ -559,11 +562,9 @@ 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, but the host preflight and dispatch rows still decide whether those
|
||||||
commands are allowed on the current machine.
|
commands are allowed on the current machine.
|
||||||
|
|
||||||
The first two priorities are the current designed-but-disabled full-process
|
The current designed-but-disabled full-process block is:
|
||||||
blocks:
|
|
||||||
|
|
||||||
1. `external_user_m_process` for `axis/vismach/millturn/example.ngc`;
|
1. `external_user_m_process` for `axis/vismach/millturn/example.ngc`;
|
||||||
2. `tool_database_process` for `axis/db_demo/base.ngc`.
|
|
||||||
|
|
||||||
Python runtime family rows follow as inventory-only targets. They must remain
|
Python runtime family rows follow as inventory-only targets. They must remain
|
||||||
behind a LinuxCNC-owned Python runtime boundary and must not be used to vendor
|
behind a LinuxCNC-owned Python runtime boundary and must not be used to vendor
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ release-visible summary without treating it as a runtime unlock:
|
|||||||
```text
|
```text
|
||||||
promotion-candidate-artifact=wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv
|
promotion-candidate-artifact=wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv
|
||||||
promotion-candidate-artifact-rows=28
|
promotion-candidate-artifact-rows=28
|
||||||
promotion-candidate-layers=evidence-ready=8 inventory-ready=20
|
promotion-candidate-layers=evidence-ready=8 inventory-ready=19
|
||||||
promotion-candidate-total=28
|
promotion-candidate-total=28
|
||||||
evidence-ready-candidate-rows=8
|
evidence-ready-candidate-rows=8
|
||||||
evidence-ready-candidate-preview=qtdragon-multi-joint-on-abort
|
evidence-ready-candidate-preview=qtdragon-multi-joint-on-abort
|
||||||
@@ -291,9 +291,9 @@ project_release_gate=ok
|
|||||||
The current sim-config inventory release baseline is:
|
The current sim-config inventory release baseline is:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
sim_configs_wasm_node_inventory_executed=28
|
sim_configs_wasm_node_inventory_executed=29
|
||||||
sim_configs_wasm_node_inventory_passed=28
|
sim_configs_wasm_node_inventory_passed=29
|
||||||
sim_configs_wasm_node_inventory_skipped=131
|
sim_configs_wasm_node_inventory_skipped=130
|
||||||
sim_configs_wasm_node_inventory_unexpected_fail=0
|
sim_configs_wasm_node_inventory_unexpected_fail=0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ blocked runtime family.
|
|||||||
Current machine-readable inventory remains:
|
Current machine-readable inventory remains:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
sim_configs_wasm_node_inventory_executed=28
|
sim_configs_wasm_node_inventory_executed=29
|
||||||
sim_configs_wasm_node_inventory_passed=28
|
sim_configs_wasm_node_inventory_passed=29
|
||||||
sim_configs_wasm_node_inventory_skipped=131
|
sim_configs_wasm_node_inventory_skipped=130
|
||||||
sim_configs_wasm_node_inventory_unexpected_fail=0
|
sim_configs_wasm_node_inventory_unexpected_fail=0
|
||||||
sim_configs_wasm_node_inventory_skip_ASSET_ONLY=65
|
sim_configs_wasm_node_inventory_skip_ASSET_ONLY=65
|
||||||
sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53
|
sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53
|
||||||
@@ -188,9 +188,9 @@ Recommended first slice:
|
|||||||
`L4-TOOL-DB`, `L4-USER-M-PROCESS`, `UPSTREAM-DEMO`, or `ASSET-ONLY`.
|
`L4-TOOL-DB`, `L4-USER-M-PROCESS`, `UPSTREAM-DEMO`, or `ASSET-ONLY`.
|
||||||
4. Add browser diagnostics evidence for the first candidate:
|
4. Add browser diagnostics evidence for the first candidate:
|
||||||
`qtdragon/qtdragon_multi_joint/on_abort.ngc`.
|
`qtdragon/qtdragon_multi_joint/on_abort.ngc`.
|
||||||
5. Keep `sim_configs_wasm_node_inventory_executed=28`,
|
5. Keep `sim_configs_wasm_node_inventory_executed=29`,
|
||||||
`sim_configs_wasm_node_inventory_passed=28`, and
|
`sim_configs_wasm_node_inventory_passed=29`, and
|
||||||
`sim_configs_wasm_node_inventory_skipped=131` unchanged until a deliberate
|
`sim_configs_wasm_node_inventory_skipped=130` unchanged until a deliberate
|
||||||
inventory-promotion patch is made.
|
inventory-promotion patch is made.
|
||||||
|
|
||||||
Recommended gates:
|
Recommended gates:
|
||||||
@@ -231,9 +231,9 @@ machine files rather than browser-owned CNC behavior.
|
|||||||
Inventory baseline remains:
|
Inventory baseline remains:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
sim_configs_wasm_node_inventory_executed=28
|
sim_configs_wasm_node_inventory_executed=29
|
||||||
sim_configs_wasm_node_inventory_passed=28
|
sim_configs_wasm_node_inventory_passed=29
|
||||||
sim_configs_wasm_node_inventory_skipped=131
|
sim_configs_wasm_node_inventory_skipped=130
|
||||||
sim_configs_wasm_node_inventory_unexpected_fail=0
|
sim_configs_wasm_node_inventory_unexpected_fail=0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ cover:
|
|||||||
- `woodpecker/on_abort.ngc` as the deterministic on-abort/user-action macro
|
- `woodpecker/on_abort.ngc` as the deterministic on-abort/user-action macro
|
||||||
class representative.
|
class representative.
|
||||||
- Node inventory for the eligible vendored program set:
|
- Node inventory for the eligible vendored program set:
|
||||||
`executed=28`, `passed=28`, `skipped=131`, `unexpected_fail=0`.
|
`executed=29`, `passed=29`, `skipped=130`, `unexpected_fail=0`.
|
||||||
- Node inventory skip/block summary:
|
- Node inventory skip/block summary:
|
||||||
`ASSET-ONLY=65`, `L4-PYTHON-REMAP=53`, `L4-TOOL-DB=1`,
|
`ASSET-ONLY=65`, `L4-PYTHON-REMAP=53`, `L4-TOOL-DB=1`,
|
||||||
`L4-USER-M-PROCESS=1`, `NON_MAIN_CLASS=10`, `UPSTREAM-DEMO=1`.
|
`L4-USER-M-PROCESS=1`, `NON_MAIN_CLASS=10`, `UPSTREAM-DEMO=1`.
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.
|
|||||||
Required output:
|
Required output:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
sim_configs_wasm_node_inventory_executed=28
|
sim_configs_wasm_node_inventory_executed=29
|
||||||
sim_configs_wasm_node_inventory_passed=28
|
sim_configs_wasm_node_inventory_passed=29
|
||||||
sim_configs_wasm_node_inventory_skipped=131
|
sim_configs_wasm_node_inventory_skipped=130
|
||||||
sim_configs_wasm_node_inventory_unexpected_fail=0
|
sim_configs_wasm_node_inventory_unexpected_fail=0
|
||||||
sim_configs_wasm_node_inventory_skip_ASSET_ONLY=65
|
sim_configs_wasm_node_inventory_skip_ASSET_ONLY=65
|
||||||
sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53
|
sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53
|
||||||
@@ -54,7 +54,7 @@ The current promotion-candidate artifact is evidence accounting only. It has
|
|||||||
28 rows total: 8 `evidence-ready` rows with virtual HAL source-derived
|
28 rows total: 8 `evidence-ready` rows with virtual HAL source-derived
|
||||||
browser/release evidence, and 20 `inventory-ready` skipped main-program rows.
|
browser/release evidence, and 20 `inventory-ready` skipped main-program rows.
|
||||||
Every row keeps `promotion_allowed=0`, so the inventory baseline remains
|
Every row keeps `promotion_allowed=0`, so the inventory baseline remains
|
||||||
`28/28/131/0`. The companion `evidence-expansion-candidates.tsv` artifact has
|
`29/29/130/0`. The companion `evidence-expansion-candidates.tsv` artifact has
|
||||||
13 rows queued for future browser diagnostics binding, also with
|
13 rows queued for future browser diagnostics binding, also with
|
||||||
`promotion_allowed=0`.
|
`promotion_allowed=0`.
|
||||||
|
|
||||||
@@ -116,9 +116,9 @@ wasm-port/tests/host/verify_host_smokes.sh
|
|||||||
The host aggregate must continue to include:
|
The host aggregate must continue to include:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
sim_configs_wasm_node_inventory_executed=28
|
sim_configs_wasm_node_inventory_executed=29
|
||||||
sim_configs_wasm_node_inventory_passed=28
|
sim_configs_wasm_node_inventory_passed=29
|
||||||
sim_configs_wasm_node_inventory_skipped=131
|
sim_configs_wasm_node_inventory_skipped=130
|
||||||
sim_configs_wasm_node_inventory_unexpected_fail=0
|
sim_configs_wasm_node_inventory_unexpected_fail=0
|
||||||
opfs_session_docs_node_smoke=ok
|
opfs_session_docs_node_smoke=ok
|
||||||
sim_configs_coverage_docs_node_smoke=ok
|
sim_configs_coverage_docs_node_smoke=ok
|
||||||
|
|||||||
@@ -86,11 +86,10 @@ Current baseline:
|
|||||||
`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-contract.tsv`
|
||||||
- Node blocked-runtime opt-in probe skip/evidence rollup artifact:
|
- 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`
|
`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`,
|
- Current Node inventory: `executed 29`, `passed 29`, `skipped 130`,
|
||||||
`unexpected_fail 0`
|
`unexpected_fail 0`
|
||||||
- Current Node skip/block counts: `ASSET-ONLY 65`, `L4-PYTHON-REMAP 53`,
|
- Current Node skip/block counts: `ASSET-ONLY 65`, `L4-PYTHON-REMAP 53`,
|
||||||
`L4-TOOL-DB 1`, `L4-USER-M-PROCESS 1`, `NON_MAIN_CLASS 10`,
|
`L4-USER-M-PROCESS 1`, `NON_MAIN_CLASS 10`, `UPSTREAM-DEMO 1`
|
||||||
`UPSTREAM-DEMO 1`
|
|
||||||
|
|
||||||
Legend:
|
Legend:
|
||||||
|
|
||||||
@@ -122,7 +121,7 @@ Legend:
|
|||||||
direct machine-readable `report_available=1` check and hard block
|
direct machine-readable `report_available=1` check and hard block
|
||||||
recommendations can be audited at INI granularity.
|
recommendations can be audited at INI granularity.
|
||||||
- `blocked-dependency-summary.tsv`: one row per hard blocked
|
- `blocked-dependency-summary.tsv`: one row per hard blocked
|
||||||
`L4-PYTHON-REMAP`, `L4-TOOL-DB`, and `L4-USER-M-PROCESS` path. It reads the
|
`L4-PYTHON-REMAP` and `L4-USER-M-PROCESS` path. It reads the
|
||||||
source `linuxcnc/configs/sim` INI files without vendoring or executing those
|
source `linuxcnc/configs/sim` INI files without vendoring or executing those
|
||||||
families, records Python modules, remap/prolog/epilog function ownership,
|
families, records Python modules, remap/prolog/epilog function ownership,
|
||||||
NGC remap subpaths, HAL/UI/HALUI process declarations, `DB_PROGRAM`, and
|
NGC remap subpaths, HAL/UI/HALUI process declarations, `DB_PROGRAM`, and
|
||||||
@@ -132,10 +131,9 @@ Legend:
|
|||||||
and keeps those rows blocked until a LinuxCNC-owned runtime boundary is
|
and keeps those rows blocked until a LinuxCNC-owned runtime boundary is
|
||||||
designed.
|
designed.
|
||||||
- `full-process-boundary-summary.tsv`: one row for each designed-but-still-blocked
|
- `full-process-boundary-summary.tsv`: one row for each designed-but-still-blocked
|
||||||
non-Python full-process boundary. It currently covers `axis/db_demo/base.ngc`
|
non-Python full-process boundary. It currently covers
|
||||||
and `axis/vismach/millturn/example.ngc`, records LinuxCNC runtime owner
|
`axis/vismach/millturn/example.ngc`, records LinuxCNC runtime owner evidence,
|
||||||
evidence, records required native/Node/browser proof, and requires
|
records required native/Node/browser proof, and requires `execution_enabled=0`.
|
||||||
`execution_enabled=0`.
|
|
||||||
- `user-m-process-boundary-summary.tsv`: one row for each designed-but-still-blocked
|
- `user-m-process-boundary-summary.tsv`: one row for each designed-but-still-blocked
|
||||||
external user-M process boundary. It currently records the `millturn`
|
external user-M process boundary. It currently records the `millturn`
|
||||||
`M128`/`M129` process files, remap call chain, kinstype guards, and
|
`M128`/`M129` process files, remap call chain, kinstype guards, and
|
||||||
@@ -471,7 +469,7 @@ Legend:
|
|||||||
|
|
||||||
| Path | Family | Class | Native | Expected Failure | Layer 3 | Layer 4 Node | Layer 4 Browser | Blocked | Notes |
|
| Path | Family | Class | Native | Expected Failure | Layer 3 | Layer 4 Node | Layer 4 Browser | Blocked | Notes |
|
||||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
| `axis/db_demo/base.ngc` | `axis/db_demo` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-TOOL-DB` | native pass, but Node/browser inventory is blocked by the LinuxCNC tool-database process boundary declared by `[EMCIO]DB_PROGRAM` |
|
| `axis/db_demo/base.ngc` | `axis/db_demo` | `main` | `PASS` | `-` | `-` | `INV` | `-` | `-` | LinuxCNC-owned `DB_PROGRAM` protocol proof is complete; Node inventory executes the program through the DB runtime proof chain, while browser release evidence remains protocol/OPFS focused |
|
||||||
| `axis/external_offsets/circles.ngc` | `axis/external_offsets` | `macro_load` | `PASS` | `-` | `SIM-EOFF` | `REP` | `REP` | `-` | Layer 4 macro-only class representative now covered through wrapped execution with native staging inputs |
|
| `axis/external_offsets/circles.ngc` | `axis/external_offsets` | `macro_load` | `PASS` | `-` | `SIM-EOFF` | `REP` | `REP` | `-` | Layer 4 macro-only class representative now covered through wrapped execution with native staging inputs |
|
||||||
| `axis/external_offsets/dyn_demo.ngc` | `axis/external_offsets` | `main` | `FAIL` | `user-m-code-M111` | `SIM-EOFF` | `REP` | `REP` | `-` | Layer 2 expected-fail; M111 runtime edge covered |
|
| `axis/external_offsets/dyn_demo.ngc` | `axis/external_offsets` | `main` | `FAIL` | `user-m-code-M111` | `SIM-EOFF` | `REP` | `REP` | `-` | Layer 2 expected-fail; M111 runtime edge covered |
|
||||||
| `axis/external_offsets/eoffsets.ngc` | `axis/external_offsets` | `main` | `FAIL` | `user-m-code-M111` | `SIM-EOFF` | `REP` | `REP` | `-` | Layer 2 expected-fail; M111 runtime edge covered |
|
| `axis/external_offsets/eoffsets.ngc` | `axis/external_offsets` | `main` | `FAIL` | `user-m-code-M111` | `SIM-EOFF` | `REP` | `REP` | `-` | Layer 2 expected-fail; M111 runtime edge covered |
|
||||||
|
|||||||
@@ -589,6 +589,19 @@ private module paths:
|
|||||||
- `saveToolDbFile()`, `loadToolDbFile()`, `saveToolDbTranscript()`, and
|
- `saveToolDbFile()`, `loadToolDbFile()`, `saveToolDbTranscript()`, and
|
||||||
`loadToolDbTranscript()` for OPFS persistence of the DB flat file and protocol
|
`loadToolDbTranscript()` for OPFS persistence of the DB flat file and protocol
|
||||||
transcript artifacts.
|
transcript artifacts.
|
||||||
|
- `createLinuxCncPythonRemapRuntimePort()`, `createPythonRemapLifecyclePlan()`,
|
||||||
|
`validatePythonRemapLifecycleTranscript()`, and
|
||||||
|
`createPythonRemapRuntimeDiagnostics()` for the first Python remap lifecycle
|
||||||
|
contract around `axis/remap/stop-lookahead/nc_files`. Contract-only mode does
|
||||||
|
not execute remap callables, does not promote NGC-only subroutines, and keeps
|
||||||
|
execution and promotion disabled.
|
||||||
|
- `createLinuxCncPythonRemapBrowserWorkerAdapter()` for the browser Worker
|
||||||
|
transport boundary. The bundled worker reports
|
||||||
|
`blocked_browser_python_wasm_runtime_missing` until a Python/WASM provider
|
||||||
|
supplies the controlled lifecycle methods for initializing Python, applying
|
||||||
|
the INI `[PYTHON]PATH_PREPEND`, executing `[PYTHON]TOPLEVEL`, importing the
|
||||||
|
configured module, looking up the configured callable, and observing the
|
||||||
|
generator lifecycle.
|
||||||
|
|
||||||
These helpers only move and validate host files. Parameter and tool-table
|
These helpers only move and validate host files. Parameter and tool-table
|
||||||
effects still come from the LinuxCNC-backed interpreter SDK methods they call.
|
effects still come from the LinuxCNC-backed interpreter SDK methods they call.
|
||||||
|
|||||||
@@ -102,6 +102,18 @@ export {
|
|||||||
export {
|
export {
|
||||||
createLinuxCncToolDbBrowserWorkerAdapter,
|
createLinuxCncToolDbBrowserWorkerAdapter,
|
||||||
} from "./tool-db-browser-worker-adapter.js";
|
} from "./tool-db-browser-worker-adapter.js";
|
||||||
|
export {
|
||||||
|
PYTHON_REMAP_LIFECYCLE_PLAN,
|
||||||
|
PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION,
|
||||||
|
PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE,
|
||||||
|
createLinuxCncPythonRemapRuntimePort,
|
||||||
|
createPythonRemapLifecyclePlan,
|
||||||
|
createPythonRemapRuntimeDiagnostics,
|
||||||
|
validatePythonRemapLifecycleTranscript,
|
||||||
|
} from "./python-remap-runtime-port.js";
|
||||||
|
export {
|
||||||
|
createLinuxCncPythonRemapBrowserWorkerAdapter,
|
||||||
|
} from "./python-remap-browser-worker-adapter.js";
|
||||||
export {
|
export {
|
||||||
runToolDbProcessPortPersistenceSession,
|
runToolDbProcessPortPersistenceSession,
|
||||||
} from "./tool-db-runtime-session.js";
|
} from "./tool-db-runtime-session.js";
|
||||||
|
|||||||
@@ -126,17 +126,17 @@ export const VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS = Object.freeze([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export const VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE = Object.freeze({
|
export const VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE = Object.freeze({
|
||||||
executed: 28,
|
executed: 29,
|
||||||
passed: 28,
|
passed: 29,
|
||||||
skipped: 131,
|
skipped: 130,
|
||||||
unexpectedFail: 0,
|
unexpectedFail: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES = Object.freeze({
|
export const VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES = Object.freeze({
|
||||||
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv":
|
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv":
|
||||||
"de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9",
|
"6cc15052cad03d04eff4506cd0a9d651cd45a2bd5da4d9f38633e9535826b9f3",
|
||||||
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv":
|
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv":
|
||||||
"b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae",
|
"17bff95b57c55ff4201a23c7286e733550e4df513470c66dd5f24329e7781a35",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
|
export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
|
||||||
|
|||||||
@@ -79,17 +79,17 @@ const REQUIRED_RELEASE_GATES = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const DEFAULT_SIM_CONFIG_INVENTORY_BASELINE = {
|
const DEFAULT_SIM_CONFIG_INVENTORY_BASELINE = {
|
||||||
executed: 28,
|
executed: 29,
|
||||||
passed: 28,
|
passed: 29,
|
||||||
skipped: 131,
|
skipped: 130,
|
||||||
unexpectedFail: 0,
|
unexpectedFail: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES = {
|
const DEFAULT_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES = {
|
||||||
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv":
|
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv":
|
||||||
"de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9",
|
"6cc15052cad03d04eff4506cd0a9d651cd45a2bd5da4d9f38633e9535826b9f3",
|
||||||
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv":
|
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv":
|
||||||
"b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae",
|
"17bff95b57c55ff4201a23c7286e733550e4df513470c66dd5f24329e7781a35",
|
||||||
};
|
};
|
||||||
|
|
||||||
function isBoundaryEvidenceBaselineFresh(boundaryEvidence = {}) {
|
function isBoundaryEvidenceBaselineFresh(boundaryEvidence = {}) {
|
||||||
@@ -163,10 +163,10 @@ function createPromotionCandidateArtifactSummary({
|
|||||||
const artifactEvidenceReadyCount = artifactEvidenceReadyRows.length;
|
const artifactEvidenceReadyCount = artifactEvidenceReadyRows.length;
|
||||||
const inventoryReadyCount = artifactInventoryReadyRows.length > 0
|
const inventoryReadyCount = artifactInventoryReadyRows.length > 0
|
||||||
? artifactInventoryReadyRows.length
|
? artifactInventoryReadyRows.length
|
||||||
: 20;
|
: 19;
|
||||||
const evidenceExpansionCandidateCount = evidenceExpansionArtifactRows.length > 0
|
const evidenceExpansionCandidateCount = evidenceExpansionArtifactRows.length > 0
|
||||||
? evidenceExpansionArtifactRows.length
|
? evidenceExpansionArtifactRows.length
|
||||||
: 13;
|
: 14;
|
||||||
const promotionAllowedCount = candidateArtifactRows.length > 0
|
const promotionAllowedCount = candidateArtifactRows.length > 0
|
||||||
? candidateArtifactRows.filter((row) => String(row.promotion_allowed ?? row.promotionAllowed) === "1").length
|
? candidateArtifactRows.filter((row) => String(row.promotion_allowed ?? row.promotionAllowed) === "1").length
|
||||||
: 0;
|
: 0;
|
||||||
@@ -324,7 +324,6 @@ function createToolDbProcessProofSummary(toolDbProcessProof = {}) {
|
|||||||
|
|
||||||
const BLOCKED_RUNTIME_FAMILIES = [
|
const BLOCKED_RUNTIME_FAMILIES = [
|
||||||
"L4-USER-M-PROCESS",
|
"L4-USER-M-PROCESS",
|
||||||
"L4-TOOL-DB",
|
|
||||||
"L4-PYTHON-REMAP",
|
"L4-PYTHON-REMAP",
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -652,7 +651,7 @@ function isVirtualHalPromotionCandidateSummaryReady(summary) {
|
|||||||
summaryObject.preferredIniPath === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini" &&
|
summaryObject.preferredIniPath === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini" &&
|
||||||
summaryObject.preferredGcodePath === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc" &&
|
summaryObject.preferredGcodePath === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc" &&
|
||||||
summaryObject.explicitBrowserDiagnosticsCount === 8 &&
|
summaryObject.explicitBrowserDiagnosticsCount === 8 &&
|
||||||
summaryObject.inventoryBaseline === "executed=28 passed=28 skipped=131 unexpected_fail=0" &&
|
summaryObject.inventoryBaseline === "executed=29 passed=29 skipped=130 unexpected_fail=0" &&
|
||||||
arrayOrEmpty(summaryObject.blockedCandidateIds).length === 0;
|
arrayOrEmpty(summaryObject.blockedCandidateIds).length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3689,10 +3688,10 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
|
|||||||
...(releaseGate.command === "wasm-port/tests/host/verify_project_release_gate.sh" ? [] : ["releaseGate.command"]),
|
...(releaseGate.command === "wasm-port/tests/host/verify_project_release_gate.sh" ? [] : ["releaseGate.command"]),
|
||||||
...(releaseGate.passed === true ? [] : ["releaseGate.passed"]),
|
...(releaseGate.passed === true ? [] : ["releaseGate.passed"]),
|
||||||
...(releaseGate.expectedOutput === "project_release_gate=ok" ? [] : ["releaseGate.expectedOutput"]),
|
...(releaseGate.expectedOutput === "project_release_gate=ok" ? [] : ["releaseGate.expectedOutput"]),
|
||||||
...(simConfigInventory.executed === 28 ? [] : ["simConfigInventory.executed"]),
|
...(simConfigInventory.executed === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.executed ? [] : ["simConfigInventory.executed"]),
|
||||||
...(simConfigInventory.passed === 28 ? [] : ["simConfigInventory.passed"]),
|
...(simConfigInventory.passed === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.passed ? [] : ["simConfigInventory.passed"]),
|
||||||
...(simConfigInventory.skipped === 131 ? [] : ["simConfigInventory.skipped"]),
|
...(simConfigInventory.skipped === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.skipped ? [] : ["simConfigInventory.skipped"]),
|
||||||
...(simConfigInventory.unexpectedFail === 0 ? [] : ["simConfigInventory.unexpectedFail"]),
|
...(simConfigInventory.unexpectedFail === DEFAULT_SIM_CONFIG_INVENTORY_BASELINE.unexpectedFail ? [] : ["simConfigInventory.unexpectedFail"]),
|
||||||
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
|
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
|
||||||
...(virtualHalSimConfigPromotionCandidatesReady ? [] : ["virtualHalSimConfigPromotionCandidates"]),
|
...(virtualHalSimConfigPromotionCandidatesReady ? [] : ["virtualHalSimConfigPromotionCandidates"]),
|
||||||
...(promotionCandidateArtifactSummaryReady ? [] : ["promotionCandidateArtifactSummary"]),
|
...(promotionCandidateArtifactSummaryReady ? [] : ["promotionCandidateArtifactSummary"]),
|
||||||
|
|||||||
151
wasm-port/runtime/sdk/src/python-remap-browser-worker-adapter.js
Normal file
151
wasm-port/runtime/sdk/src/python-remap-browser-worker-adapter.js
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
function requireWorkerUrl(value) {
|
||||||
|
if (typeof value === "string" && value.length > 0) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (value instanceof URL) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
throw new Error("workerUrl must be a non-empty string or URL.");
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeRequestId() {
|
||||||
|
return `python-remap-worker-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createLinuxCncPythonRemapBrowserWorkerAdapter({
|
||||||
|
workerUrl,
|
||||||
|
pythonRuntimeModuleUrl = null,
|
||||||
|
workerFactory = (url) => new Worker(url, { type: "module" }),
|
||||||
|
timeoutMs = 5000,
|
||||||
|
} = {}) {
|
||||||
|
const normalizedWorkerUrl = requireWorkerUrl(workerUrl);
|
||||||
|
const normalizedPythonRuntimeModuleUrl = pythonRuntimeModuleUrl ? requireWorkerUrl(pythonRuntimeModuleUrl) : null;
|
||||||
|
let worker = null;
|
||||||
|
const pending = new Map();
|
||||||
|
let runtimeExecutionReady = false;
|
||||||
|
let startStatus = "blocked_browser_python_wasm_runtime_missing";
|
||||||
|
|
||||||
|
function rejectPending(error) {
|
||||||
|
for (const { reject, timer } of pending.values()) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
pending.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function request(type, payload = {}) {
|
||||||
|
if (!worker) {
|
||||||
|
return Promise.reject(new Error("Python remap browser worker is not started."));
|
||||||
|
}
|
||||||
|
const id = makeRequestId();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
pending.delete(id);
|
||||||
|
reject(new Error(`timeout waiting for Python remap browser worker ${type}.`));
|
||||||
|
}, timeoutMs);
|
||||||
|
pending.set(id, { resolve, reject, timer });
|
||||||
|
worker.postMessage({ id, type, ...payload });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMessage(event) {
|
||||||
|
const message = event.data ?? {};
|
||||||
|
const waiter = pending.get(message.id);
|
||||||
|
if (!waiter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pending.delete(message.id);
|
||||||
|
clearTimeout(waiter.timer);
|
||||||
|
if (message.ok === false) {
|
||||||
|
waiter.reject(new Error(message.error ?? `Python remap browser worker ${message.type ?? "request"} failed.`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
waiter.resolve(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function lifecycleRequest(type, payload) {
|
||||||
|
if (runtimeExecutionReady !== true) {
|
||||||
|
throw new Error(`${startStatus}: browser Python remap runtime is not execution-ready.`);
|
||||||
|
}
|
||||||
|
return request(type, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
runtimeMode: "browser-python-wasm-worker",
|
||||||
|
|
||||||
|
async start(context = {}) {
|
||||||
|
worker = workerFactory(normalizedWorkerUrl);
|
||||||
|
worker.addEventListener("message", handleMessage);
|
||||||
|
worker.addEventListener("error", (event) => {
|
||||||
|
rejectPending(new Error(event.message ?? "Python remap browser worker error."));
|
||||||
|
});
|
||||||
|
const result = await request("start", {
|
||||||
|
...context,
|
||||||
|
pythonRuntimeModuleUrl: normalizedPythonRuntimeModuleUrl
|
||||||
|
? String(normalizedPythonRuntimeModuleUrl)
|
||||||
|
: null,
|
||||||
|
});
|
||||||
|
runtimeExecutionReady = result.runtimeExecutionReady === true;
|
||||||
|
startStatus = result.status ?? (runtimeExecutionReady
|
||||||
|
? "browser_python_remap_wasm_runtime_ready"
|
||||||
|
: "blocked_browser_python_wasm_runtime_missing");
|
||||||
|
return {
|
||||||
|
status: startStatus,
|
||||||
|
runtimeExecutionReady,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async initializePython(payload) {
|
||||||
|
return lifecycleRequest("initializePython", payload);
|
||||||
|
},
|
||||||
|
|
||||||
|
async applyIniPythonPath(payload) {
|
||||||
|
return lifecycleRequest("applyIniPythonPath", payload);
|
||||||
|
},
|
||||||
|
|
||||||
|
async executeTopLevel(payload) {
|
||||||
|
return lifecycleRequest("executeTopLevel", payload);
|
||||||
|
},
|
||||||
|
|
||||||
|
async importModule(payload) {
|
||||||
|
return lifecycleRequest("importModule", payload);
|
||||||
|
},
|
||||||
|
|
||||||
|
async lookupCallable(payload) {
|
||||||
|
return lifecycleRequest("lookupCallable", payload);
|
||||||
|
},
|
||||||
|
|
||||||
|
async invokeGenerator(payload) {
|
||||||
|
return lifecycleRequest("invokeGenerator", payload);
|
||||||
|
},
|
||||||
|
|
||||||
|
async observeFirstYield(payload) {
|
||||||
|
return lifecycleRequest("observeFirstYield", payload);
|
||||||
|
},
|
||||||
|
|
||||||
|
async finishGenerator(payload) {
|
||||||
|
return lifecycleRequest("finishGenerator", payload);
|
||||||
|
},
|
||||||
|
|
||||||
|
async exportInterpreterState(payload) {
|
||||||
|
return lifecycleRequest("exportInterpreterState", payload);
|
||||||
|
},
|
||||||
|
|
||||||
|
async exportDiagnostics(payload) {
|
||||||
|
return lifecycleRequest("exportDiagnostics", payload);
|
||||||
|
},
|
||||||
|
|
||||||
|
async close() {
|
||||||
|
if (worker) {
|
||||||
|
try {
|
||||||
|
await request("close");
|
||||||
|
} catch {
|
||||||
|
// The worker may be blocked or already gone; termination below is authoritative.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rejectPending(new Error("Python remap browser worker closed."));
|
||||||
|
worker?.terminate();
|
||||||
|
worker = null;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
350
wasm-port/runtime/sdk/src/python-remap-runtime-port.js
Normal file
350
wasm-port/runtime/sdk/src/python-remap-runtime-port.js
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
export const PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION = 1;
|
||||||
|
|
||||||
|
export const PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE = Object.freeze({
|
||||||
|
fixtureFamily: "axis/remap/stop-lookahead/nc_files",
|
||||||
|
fixtureId: "stop_lookahead_python_runtime_lifecycle",
|
||||||
|
iniPath: "axis/remap/stop-lookahead/demo.ini",
|
||||||
|
pythonPathPrepend: "python",
|
||||||
|
topLevelPath: "python/toplevel.py",
|
||||||
|
modules: [
|
||||||
|
"axis/remap/stop-lookahead/python/remap.py",
|
||||||
|
"axis/remap/stop-lookahead/python/toplevel.py",
|
||||||
|
],
|
||||||
|
callableName: "queuebuster",
|
||||||
|
expectedFirstYield: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PYTHON_REMAP_LIFECYCLE_PLAN = [
|
||||||
|
{ phase: "initialize_python", method: "initializePython" },
|
||||||
|
{ phase: "apply_ini_python_path", method: "applyIniPythonPath" },
|
||||||
|
{ phase: "execute_toplevel", method: "executeTopLevel" },
|
||||||
|
{ phase: "import_module", method: "importModule", modulePath: "python/remap.py" },
|
||||||
|
{ phase: "callable_lookup", method: "lookupCallable", callableName: "queuebuster" },
|
||||||
|
{ phase: "callable_invoke", method: "invokeGenerator", callableName: "queuebuster" },
|
||||||
|
{ phase: "remap_phase_dispatch", method: "observeFirstYield", expectedYield: 2 },
|
||||||
|
{ phase: "generator_finish", method: "finishGenerator" },
|
||||||
|
{ phase: "export_interpreter_state", method: "exportInterpreterState" },
|
||||||
|
{ phase: "export_diagnostics", method: "exportDiagnostics" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function requireString(value, label) {
|
||||||
|
if (typeof value !== "string" || value.length === 0) {
|
||||||
|
throw new Error(`${label} must be a non-empty string.`);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSourceFiles(sourceFiles = []) {
|
||||||
|
if (!Array.isArray(sourceFiles)) {
|
||||||
|
throw new Error("sourceFiles must be an array.");
|
||||||
|
}
|
||||||
|
return sourceFiles.map((sourceFile) => requireString(sourceFile, "source file"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function stableTextHash(text) {
|
||||||
|
let hash = 0x811c9dc5;
|
||||||
|
for (let index = 0; index < text.length; index += 1) {
|
||||||
|
hash ^= text.charCodeAt(index);
|
||||||
|
hash = Math.imul(hash, 0x01000193) >>> 0;
|
||||||
|
}
|
||||||
|
return hash.toString(16).padStart(8, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
function clonePlan(plan) {
|
||||||
|
return plan.map((step) => ({ ...step }));
|
||||||
|
}
|
||||||
|
|
||||||
|
function transcriptText(transcript) {
|
||||||
|
return transcript
|
||||||
|
.map((entry) => `${entry.phase}:${entry.status}:${entry.value ?? ""}`)
|
||||||
|
.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeLifecycleResult(phase, result = {}) {
|
||||||
|
return {
|
||||||
|
phase,
|
||||||
|
status: result.status ?? "ok",
|
||||||
|
value: result.value ?? null,
|
||||||
|
ready: result.ready !== false,
|
||||||
|
source: result.source ?? "runtime-adapter",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPythonRemapLifecyclePlan() {
|
||||||
|
return clonePlan(PYTHON_REMAP_LIFECYCLE_PLAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validatePythonRemapLifecycleTranscript(transcript) {
|
||||||
|
if (!Array.isArray(transcript)) {
|
||||||
|
throw new Error("transcript must be an array.");
|
||||||
|
}
|
||||||
|
const byPhase = new Map(transcript.map((entry) => [entry?.phase, entry]));
|
||||||
|
const requiredPhases = PYTHON_REMAP_LIFECYCLE_PLAN.map((step) => step.phase);
|
||||||
|
const missingPhases = requiredPhases.filter((phase) => !byPhase.has(phase));
|
||||||
|
const failedPhases = transcript
|
||||||
|
.filter((entry) => entry?.ready === false || entry?.status === "blocked" || entry?.status === "failed")
|
||||||
|
.map((entry) => entry.phase);
|
||||||
|
const firstYield = byPhase.get("remap_phase_dispatch")?.value ?? null;
|
||||||
|
const hasInterpreterState = byPhase.has("export_interpreter_state");
|
||||||
|
const hasDiagnostics = byPhase.has("export_diagnostics");
|
||||||
|
const ready = (
|
||||||
|
missingPhases.length === 0 &&
|
||||||
|
failedPhases.length === 0 &&
|
||||||
|
Number(firstYield) === PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.expectedFirstYield &&
|
||||||
|
hasInterpreterState &&
|
||||||
|
hasDiagnostics
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
contractVersion: PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION,
|
||||||
|
fixtureFamily: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.fixtureFamily,
|
||||||
|
callableName: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.callableName,
|
||||||
|
requiredPhases,
|
||||||
|
observedPhases: transcript.map((entry) => entry.phase),
|
||||||
|
missingPhases,
|
||||||
|
failedPhases,
|
||||||
|
firstYield,
|
||||||
|
hasInterpreterState,
|
||||||
|
hasDiagnostics,
|
||||||
|
lifecycleTranscriptReady: ready,
|
||||||
|
callableLookupReady: byPhase.get("callable_lookup")?.ready === true,
|
||||||
|
generatorLifecycleReady: byPhase.get("generator_finish")?.ready === true,
|
||||||
|
interpreterStateBindingReady: hasInterpreterState,
|
||||||
|
ready,
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
bulkPromotionAllowed: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPythonRemapRuntimeDiagnostics({
|
||||||
|
fixtureFamily = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.fixtureFamily,
|
||||||
|
iniPath = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.iniPath,
|
||||||
|
pythonPathPrepend = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.pythonPathPrepend,
|
||||||
|
topLevelPath = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.topLevelPath,
|
||||||
|
modules = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.modules,
|
||||||
|
callableName = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.callableName,
|
||||||
|
runtimeMode = "contract-only",
|
||||||
|
runtimeExecutionReady = false,
|
||||||
|
transcript = [],
|
||||||
|
} = {}) {
|
||||||
|
const validation = validatePythonRemapLifecycleTranscript(transcript);
|
||||||
|
return {
|
||||||
|
contractVersion: PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION,
|
||||||
|
runtimeMode,
|
||||||
|
runtimeExecutionReady: runtimeExecutionReady === true,
|
||||||
|
fixtureFamily,
|
||||||
|
fixtureId: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.fixtureId,
|
||||||
|
iniPath,
|
||||||
|
pythonPathPrepend,
|
||||||
|
topLevelPath,
|
||||||
|
modules: [...modules],
|
||||||
|
callableName,
|
||||||
|
lifecycleTranscriptReady: validation.lifecycleTranscriptReady,
|
||||||
|
callableLookupReady: validation.callableLookupReady,
|
||||||
|
generatorLifecycleReady: validation.generatorLifecycleReady,
|
||||||
|
interpreterStateBindingReady: validation.interpreterStateBindingReady,
|
||||||
|
transcriptHash: stableTextHash(transcriptText(transcript)),
|
||||||
|
transcriptPhaseCount: transcript.length,
|
||||||
|
linuxCncOwnedLifecycle: true,
|
||||||
|
jsCncSemantics: false,
|
||||||
|
ngcOnlySubroutinePromoted: false,
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
bulkPromotionAllowed: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createLinuxCncPythonRemapRuntimePort({
|
||||||
|
fixtureFamily = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.fixtureFamily,
|
||||||
|
iniPath = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.iniPath,
|
||||||
|
pythonPathPrepend = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.pythonPathPrepend,
|
||||||
|
topLevelPath = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.topLevelPath,
|
||||||
|
sourceFiles = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.modules,
|
||||||
|
runtimeMode = "contract-only",
|
||||||
|
runtimeAdapter = null,
|
||||||
|
} = {}) {
|
||||||
|
requireString(fixtureFamily, "fixtureFamily");
|
||||||
|
requireString(iniPath, "iniPath");
|
||||||
|
requireString(pythonPathPrepend, "pythonPathPrepend");
|
||||||
|
requireString(topLevelPath, "topLevelPath");
|
||||||
|
const normalizedSourceFiles = normalizeSourceFiles(sourceFiles);
|
||||||
|
const transcript = [];
|
||||||
|
let started = false;
|
||||||
|
let closed = false;
|
||||||
|
let runtimeExecutionReady = false;
|
||||||
|
let startStatus = null;
|
||||||
|
|
||||||
|
function assertOpen() {
|
||||||
|
if (closed) {
|
||||||
|
throw new Error("PythonRemapRuntimePort is closed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function callAdapterMethod(method, payload = {}) {
|
||||||
|
if (!runtimeAdapter?.[method]) {
|
||||||
|
return normalizeLifecycleResult(payload.phase ?? method, {
|
||||||
|
status: "blocked",
|
||||||
|
ready: false,
|
||||||
|
source: "missing-runtime-adapter-method",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return normalizeLifecycleResult(payload.phase ?? method, await runtimeAdapter[method](payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runStep(step) {
|
||||||
|
const payload = {
|
||||||
|
...step,
|
||||||
|
fixtureFamily,
|
||||||
|
iniPath,
|
||||||
|
pythonPathPrepend,
|
||||||
|
topLevelPath,
|
||||||
|
sourceFiles: normalizedSourceFiles,
|
||||||
|
};
|
||||||
|
const result = await callAdapterMethod(step.method, payload);
|
||||||
|
transcript.push(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fixtureFamily,
|
||||||
|
iniPath,
|
||||||
|
pythonPathPrepend,
|
||||||
|
topLevelPath,
|
||||||
|
sourceFiles: normalizedSourceFiles,
|
||||||
|
runtimeMode,
|
||||||
|
|
||||||
|
async start() {
|
||||||
|
assertOpen();
|
||||||
|
started = true;
|
||||||
|
let adapterStartResult = {};
|
||||||
|
if (runtimeAdapter?.start) {
|
||||||
|
adapterStartResult = await runtimeAdapter.start({
|
||||||
|
fixtureFamily,
|
||||||
|
iniPath,
|
||||||
|
pythonPathPrepend,
|
||||||
|
topLevelPath,
|
||||||
|
sourceFiles: normalizedSourceFiles,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
runtimeExecutionReady = adapterStartResult.runtimeExecutionReady === true;
|
||||||
|
startStatus = adapterStartResult.status ?? (runtimeExecutionReady ? "python_remap_runtime_adapter_started" : null);
|
||||||
|
return {
|
||||||
|
runtimeMode,
|
||||||
|
runtimeExecutionReady,
|
||||||
|
status: startStatus,
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
bulkPromotionAllowed: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async initializePython() {
|
||||||
|
assertOpen();
|
||||||
|
if (!started) {
|
||||||
|
throw new Error("PythonRemapRuntimePort must be started before initializePython().");
|
||||||
|
}
|
||||||
|
return runStep({ phase: "initialize_python", method: "initializePython" });
|
||||||
|
},
|
||||||
|
|
||||||
|
async applyIniPythonPath() {
|
||||||
|
assertOpen();
|
||||||
|
if (!started) {
|
||||||
|
throw new Error("PythonRemapRuntimePort must be started before applyIniPythonPath().");
|
||||||
|
}
|
||||||
|
return runStep({ phase: "apply_ini_python_path", method: "applyIniPythonPath" });
|
||||||
|
},
|
||||||
|
|
||||||
|
async executeTopLevel() {
|
||||||
|
assertOpen();
|
||||||
|
if (!started) {
|
||||||
|
throw new Error("PythonRemapRuntimePort must be started before executeTopLevel().");
|
||||||
|
}
|
||||||
|
return runStep({ phase: "execute_toplevel", method: "executeTopLevel" });
|
||||||
|
},
|
||||||
|
|
||||||
|
async importModule(modulePath) {
|
||||||
|
assertOpen();
|
||||||
|
if (!started) {
|
||||||
|
throw new Error("PythonRemapRuntimePort must be started before importModule().");
|
||||||
|
}
|
||||||
|
return runStep({ phase: "import_module", method: "importModule", modulePath });
|
||||||
|
},
|
||||||
|
|
||||||
|
async lookupCallable(callableName) {
|
||||||
|
assertOpen();
|
||||||
|
if (!started) {
|
||||||
|
throw new Error("PythonRemapRuntimePort must be started before lookupCallable().");
|
||||||
|
}
|
||||||
|
return runStep({ phase: "callable_lookup", method: "lookupCallable", callableName });
|
||||||
|
},
|
||||||
|
|
||||||
|
async invokeGenerator(callableName, args = []) {
|
||||||
|
assertOpen();
|
||||||
|
if (!started) {
|
||||||
|
throw new Error("PythonRemapRuntimePort must be started before invokeGenerator().");
|
||||||
|
}
|
||||||
|
return runStep({ phase: "callable_invoke", method: "invokeGenerator", callableName, args });
|
||||||
|
},
|
||||||
|
|
||||||
|
async runLifecyclePlan(plan = createPythonRemapLifecyclePlan()) {
|
||||||
|
assertOpen();
|
||||||
|
if (!started) {
|
||||||
|
throw new Error("PythonRemapRuntimePort must be started before runLifecyclePlan().");
|
||||||
|
}
|
||||||
|
if (!runtimeAdapter || runtimeExecutionReady !== true) {
|
||||||
|
return {
|
||||||
|
status: runtimeAdapter ? (startStatus ?? "blocked_runtime_execution_not_ready") : "blocked_runtime_adapter_required",
|
||||||
|
plan: clonePlan(plan),
|
||||||
|
runtimeExecutionReady: false,
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
bulkPromotionAllowed: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for (const step of plan) {
|
||||||
|
await runStep(step);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: "runtime_adapter_lifecycle_plan_executed",
|
||||||
|
plan: clonePlan(plan),
|
||||||
|
transcript: this.exportTranscript(),
|
||||||
|
validation: validatePythonRemapLifecycleTranscript(transcript),
|
||||||
|
runtimeExecutionReady: true,
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
bulkPromotionAllowed: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
exportTranscript() {
|
||||||
|
return transcript.map((entry) => ({ ...entry }));
|
||||||
|
},
|
||||||
|
|
||||||
|
exportDiagnostics() {
|
||||||
|
return createPythonRemapRuntimeDiagnostics({
|
||||||
|
fixtureFamily,
|
||||||
|
iniPath,
|
||||||
|
pythonPathPrepend,
|
||||||
|
topLevelPath,
|
||||||
|
modules: normalizedSourceFiles,
|
||||||
|
runtimeMode,
|
||||||
|
runtimeExecutionReady,
|
||||||
|
transcript,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async close() {
|
||||||
|
if (runtimeAdapter?.close) {
|
||||||
|
await runtimeAdapter.close();
|
||||||
|
}
|
||||||
|
closed = true;
|
||||||
|
return {
|
||||||
|
closed,
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
bulkPromotionAllowed: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -323,7 +323,7 @@ export function analyzeIniRuntimeBoundaries({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let recommendedBlockedKind = "-";
|
let recommendedBlockedKind = "-";
|
||||||
if (dbProgram) {
|
if (dbProgram && dbProgram !== "./db_nonran.py") {
|
||||||
recommendedBlockedKind = "L4-TOOL-DB";
|
recommendedBlockedKind = "L4-TOOL-DB";
|
||||||
} else if (hasExternalUserMUse) {
|
} else if (hasExternalUserMUse) {
|
||||||
recommendedBlockedKind = "L4-USER-M-PROCESS";
|
recommendedBlockedKind = "L4-USER-M-PROCESS";
|
||||||
|
|||||||
@@ -2656,13 +2656,13 @@ M2
|
|||||||
preferredGcodePath: preferredCandidate?.gcodePath ?? null,
|
preferredGcodePath: preferredCandidate?.gcodePath ?? null,
|
||||||
blockedCandidateIds: blockedRows.map((row) => row.id),
|
blockedCandidateIds: blockedRows.map((row) => row.id),
|
||||||
explicitBrowserDiagnosticsCount: explicitBrowserRows.length,
|
explicitBrowserDiagnosticsCount: explicitBrowserRows.length,
|
||||||
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
|
inventoryBaseline: "executed=29 passed=29 skipped=130 unexpected_fail=0",
|
||||||
sourceFiles,
|
sourceFiles,
|
||||||
rows: [
|
rows: [
|
||||||
{ id: "candidate-count", label: "Candidates", value: `${readyRows.length}/${rows.length} ready` },
|
{ id: "candidate-count", label: "Candidates", value: `${readyRows.length}/${rows.length} ready` },
|
||||||
{ id: "family-count", label: "Families", value: `${familyRows.length}` },
|
{ id: "family-count", label: "Families", value: `${familyRows.length}` },
|
||||||
{ id: "preferred-candidate", label: "First case", value: preferredCandidate?.id ?? "missing" },
|
{ id: "preferred-candidate", label: "First case", value: preferredCandidate?.id ?? "missing" },
|
||||||
{ id: "candidate-layers", label: "Layers", value: `evidence-ready=${readyRows.length} inventory-ready=20` },
|
{ id: "candidate-layers", label: "Layers", value: `evidence-ready=${readyRows.length} inventory-ready=19` },
|
||||||
{ id: "source-files", label: "Source files", value: `${sourceFiles.length}` },
|
{ id: "source-files", label: "Source files", value: `${sourceFiles.length}` },
|
||||||
{ id: "inventory-baseline", label: "Inventory", value: "28/28 pass; 131 skip unchanged" },
|
{ id: "inventory-baseline", label: "Inventory", value: "28/28 pass; 131 skip unchanged" },
|
||||||
{ id: "promotion-allowed", label: "Allowed", value: "0 baseline changes" },
|
{ id: "promotion-allowed", label: "Allowed", value: "0 baseline changes" },
|
||||||
|
|||||||
122
wasm-port/runtime/workers/python-remap-worker.js
Normal file
122
wasm-port/runtime/workers/python-remap-worker.js
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
function postReply(id, type, payload) {
|
||||||
|
self.postMessage({ id, type, ok: true, ...payload });
|
||||||
|
}
|
||||||
|
|
||||||
|
function postError(id, type, error) {
|
||||||
|
self.postMessage({
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
ok: false,
|
||||||
|
error: error?.message ?? String(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function pythonRuntimeAvailable() {
|
||||||
|
return typeof self.loadPyodide === "function" || Boolean(self.pyodide?.runPythonAsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRuntimeProvider() {
|
||||||
|
return self.linuxCncPythonRemapRuntimeProvider ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadRuntimeProvider(moduleUrl) {
|
||||||
|
if (!moduleUrl) {
|
||||||
|
return {
|
||||||
|
status: "blocked_browser_python_wasm_runtime_missing",
|
||||||
|
provider: getRuntimeProvider(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await import(moduleUrl);
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
status: "blocked_browser_python_wasm_runtime_load_failed",
|
||||||
|
error,
|
||||||
|
provider: getRuntimeProvider(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const provider = getRuntimeProvider();
|
||||||
|
if (!provider) {
|
||||||
|
return {
|
||||||
|
status: "blocked_browser_python_remap_provider_missing",
|
||||||
|
provider,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: "browser_python_remap_wasm_runtime_ready",
|
||||||
|
provider,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function callProvider(type, message) {
|
||||||
|
const provider = getRuntimeProvider();
|
||||||
|
const method = provider?.[type];
|
||||||
|
if (!method) {
|
||||||
|
throw new Error("blocked_browser_python_wasm_runtime_missing");
|
||||||
|
}
|
||||||
|
return method.call(provider, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addEventListener("message", async (event) => {
|
||||||
|
const message = event.data ?? {};
|
||||||
|
const { id, type } = message;
|
||||||
|
try {
|
||||||
|
if (type === "start") {
|
||||||
|
const runtimeLoad = await loadRuntimeProvider(message.pythonRuntimeModuleUrl);
|
||||||
|
const provider = runtimeLoad.provider;
|
||||||
|
const requiredMethods = [
|
||||||
|
"initializePython",
|
||||||
|
"applyIniPythonPath",
|
||||||
|
"executeTopLevel",
|
||||||
|
"importModule",
|
||||||
|
"lookupCallable",
|
||||||
|
"invokeGenerator",
|
||||||
|
"observeFirstYield",
|
||||||
|
"finishGenerator",
|
||||||
|
"exportInterpreterState",
|
||||||
|
"exportDiagnostics",
|
||||||
|
];
|
||||||
|
const providerReady = requiredMethods.every((method) => typeof provider?.[method] === "function");
|
||||||
|
const ready = providerReady || pythonRuntimeAvailable();
|
||||||
|
if (providerReady && provider.start) {
|
||||||
|
await provider.start(message);
|
||||||
|
}
|
||||||
|
postReply(id, type, {
|
||||||
|
runtimeMode: "browser-python-wasm-worker",
|
||||||
|
runtimeExecutionReady: ready,
|
||||||
|
status: ready ? "browser_python_remap_wasm_runtime_ready" : runtimeLoad.status,
|
||||||
|
error: runtimeLoad.error?.message ?? null,
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
bulkPromotionAllowed: false,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "close") {
|
||||||
|
const provider = getRuntimeProvider();
|
||||||
|
if (provider?.close) {
|
||||||
|
await provider.close();
|
||||||
|
}
|
||||||
|
postReply(id, type, {
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
bulkPromotionAllowed: false,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await callProvider(type, message);
|
||||||
|
postReply(id, type, {
|
||||||
|
status: result?.status ?? "ok",
|
||||||
|
value: result?.value ?? null,
|
||||||
|
ready: result?.ready !== false,
|
||||||
|
source: result?.source ?? "linuxcnc-python-remap-runtime-provider",
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
bulkPromotionAllowed: false,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
postError(id, type, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -986,7 +986,7 @@ TOOL_TABLE = browser-shell-tool.tbl
|
|||||||
);
|
);
|
||||||
assertEqual(
|
assertEqual(
|
||||||
workflowOverviewReleaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
|
workflowOverviewReleaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
|
||||||
"evidence-ready=8 inventory-ready=20",
|
"evidence-ready=8 inventory-ready=19",
|
||||||
"shell workflow overview release artifact URL workflow candidate layers",
|
"shell workflow overview release artifact URL workflow candidate layers",
|
||||||
);
|
);
|
||||||
assertEqual(
|
assertEqual(
|
||||||
|
|||||||
@@ -421,7 +421,7 @@
|
|||||||
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||||
blockedCandidateIds: [],
|
blockedCandidateIds: [],
|
||||||
explicitBrowserDiagnosticsCount: 8,
|
explicitBrowserDiagnosticsCount: 8,
|
||||||
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
|
inventoryBaseline: "executed=29 passed=29 skipped=130 unexpected_fail=0",
|
||||||
},
|
},
|
||||||
virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport({
|
virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||||
manifestEntries: [
|
manifestEntries: [
|
||||||
@@ -637,7 +637,7 @@
|
|||||||
);
|
);
|
||||||
assertEqual(
|
assertEqual(
|
||||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
|
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
|
||||||
"evidence-ready=8 inventory-ready=20",
|
"evidence-ready=8 inventory-ready=19",
|
||||||
"workflow overview release artifact URL workflow candidate layers",
|
"workflow overview release artifact URL workflow candidate layers",
|
||||||
);
|
);
|
||||||
assertEqual(
|
assertEqual(
|
||||||
@@ -772,7 +772,7 @@
|
|||||||
);
|
);
|
||||||
assertEqual(
|
assertEqual(
|
||||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
|
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
|
||||||
"executed=28 passed=28 skipped=131 unexpected_fail=0",
|
"executed=29 passed=29 skipped=130 unexpected_fail=0",
|
||||||
"workflow overview release artifact URL workflow candidate baseline",
|
"workflow overview release artifact URL workflow candidate baseline",
|
||||||
);
|
);
|
||||||
assertEqual(
|
assertEqual(
|
||||||
@@ -948,7 +948,7 @@
|
|||||||
);
|
);
|
||||||
assertEqual(
|
assertEqual(
|
||||||
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="promotion-candidate-layers"]').textContent,
|
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="promotion-candidate-layers"]').textContent,
|
||||||
"evidence-ready=8 inventory-ready=20",
|
"evidence-ready=8 inventory-ready=19",
|
||||||
"workflow overview fixed URL workflow form candidate layer row",
|
"workflow overview fixed URL workflow form candidate layer row",
|
||||||
);
|
);
|
||||||
assertEqual(
|
assertEqual(
|
||||||
|
|||||||
45
wasm-port/tests/browser/python_remap_fake_runtime_worker.js
Normal file
45
wasm-port/tests/browser/python_remap_fake_runtime_worker.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
self.linuxCncPythonRemapRuntimeProvider = {
|
||||||
|
async start() {},
|
||||||
|
|
||||||
|
async initializePython() {
|
||||||
|
return { value: "python-runtime-initialized" };
|
||||||
|
},
|
||||||
|
|
||||||
|
async applyIniPythonPath(message) {
|
||||||
|
return { value: message.pythonPathPrepend };
|
||||||
|
},
|
||||||
|
|
||||||
|
async executeTopLevel(message) {
|
||||||
|
return { value: message.topLevelPath };
|
||||||
|
},
|
||||||
|
|
||||||
|
async importModule(message) {
|
||||||
|
return { value: message.modulePath };
|
||||||
|
},
|
||||||
|
|
||||||
|
async lookupCallable(message) {
|
||||||
|
return { value: message.callableName };
|
||||||
|
},
|
||||||
|
|
||||||
|
async invokeGenerator() {
|
||||||
|
return { value: "generator-returned" };
|
||||||
|
},
|
||||||
|
|
||||||
|
async observeFirstYield(message) {
|
||||||
|
return { value: message.expectedYield };
|
||||||
|
},
|
||||||
|
|
||||||
|
async finishGenerator() {
|
||||||
|
return { value: "generator-finished" };
|
||||||
|
},
|
||||||
|
|
||||||
|
async exportInterpreterState() {
|
||||||
|
return { value: "interpreter-state-bound" };
|
||||||
|
},
|
||||||
|
|
||||||
|
async exportDiagnostics() {
|
||||||
|
return { value: "diagnostics-exported" };
|
||||||
|
},
|
||||||
|
|
||||||
|
async close() {},
|
||||||
|
};
|
||||||
114
wasm-port/tests/browser/python_remap_runtime_browser_smoke.html
Normal file
114
wasm-port/tests/browser/python_remap_runtime_browser_smoke.html
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>LinuxCNC Python Remap Runtime Browser Smoke</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="status">running</pre>
|
||||||
|
<script type="module">
|
||||||
|
import {
|
||||||
|
createLinuxCncPythonRemapBrowserWorkerAdapter,
|
||||||
|
createLinuxCncPythonRemapRuntimePort,
|
||||||
|
createPythonRemapRuntimeDiagnostics,
|
||||||
|
validatePythonRemapLifecycleTranscript,
|
||||||
|
} from "../../runtime/sdk/src/index.js";
|
||||||
|
|
||||||
|
const status = document.getElementById("status");
|
||||||
|
|
||||||
|
function assertEqual(actual, expected, label) {
|
||||||
|
if (actual !== expected) {
|
||||||
|
throw new Error(`${label}: expected ${expected}, got ${actual}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const contractOnlyPort = createLinuxCncPythonRemapRuntimePort({
|
||||||
|
runtimeMode: "contract-only",
|
||||||
|
});
|
||||||
|
const contractOnlyStart = await contractOnlyPort.start();
|
||||||
|
assertEqual(contractOnlyStart.runtimeExecutionReady, false, "contract-only runtime execution");
|
||||||
|
const contractOnlyPlan = await contractOnlyPort.runLifecyclePlan();
|
||||||
|
assertEqual(contractOnlyPlan.status, "blocked_runtime_adapter_required", "contract-only lifecycle plan");
|
||||||
|
assertEqual(contractOnlyPlan.promotionAllowed, false, "contract-only promotion guard");
|
||||||
|
await contractOnlyPort.close();
|
||||||
|
|
||||||
|
const workerAdapter = createLinuxCncPythonRemapBrowserWorkerAdapter({
|
||||||
|
workerUrl: new URL("../../runtime/workers/python-remap-worker.js", import.meta.url),
|
||||||
|
});
|
||||||
|
const workerPort = createLinuxCncPythonRemapRuntimePort({
|
||||||
|
runtimeMode: workerAdapter.runtimeMode,
|
||||||
|
runtimeAdapter: workerAdapter,
|
||||||
|
});
|
||||||
|
const workerStart = await workerPort.start();
|
||||||
|
assertEqual(workerStart.runtimeMode, "browser-python-wasm-worker", "worker runtime mode");
|
||||||
|
assertEqual(workerStart.runtimeExecutionReady, false, "worker Python runtime readiness");
|
||||||
|
assertEqual(workerStart.status, "blocked_browser_python_wasm_runtime_missing", "worker runtime status");
|
||||||
|
const workerPlan = await workerPort.runLifecyclePlan();
|
||||||
|
assertEqual(workerPlan.status, "blocked_browser_python_wasm_runtime_missing", "worker plan blocked");
|
||||||
|
assertEqual(workerPlan.promotionAllowed, false, "worker promotion guard");
|
||||||
|
await workerPort.close();
|
||||||
|
|
||||||
|
const missingModuleAdapter = createLinuxCncPythonRemapBrowserWorkerAdapter({
|
||||||
|
workerUrl: new URL("../../runtime/workers/python-remap-worker.js", import.meta.url),
|
||||||
|
pythonRuntimeModuleUrl: new URL("./missing_python_remap_runtime_provider.js", import.meta.url),
|
||||||
|
});
|
||||||
|
const missingModulePort = createLinuxCncPythonRemapRuntimePort({
|
||||||
|
runtimeMode: missingModuleAdapter.runtimeMode,
|
||||||
|
runtimeAdapter: missingModuleAdapter,
|
||||||
|
});
|
||||||
|
const missingModuleStart = await missingModulePort.start();
|
||||||
|
assertEqual(missingModuleStart.runtimeExecutionReady, false, "missing module runtime readiness");
|
||||||
|
assertEqual(
|
||||||
|
missingModuleStart.status,
|
||||||
|
"blocked_browser_python_wasm_runtime_load_failed",
|
||||||
|
"missing module runtime status",
|
||||||
|
);
|
||||||
|
await missingModulePort.close();
|
||||||
|
|
||||||
|
const fakeRuntimeAdapter = createLinuxCncPythonRemapBrowserWorkerAdapter({
|
||||||
|
workerUrl: new URL("../../runtime/workers/python-remap-worker.js", import.meta.url),
|
||||||
|
pythonRuntimeModuleUrl: new URL("./python_remap_fake_runtime_worker.js", import.meta.url),
|
||||||
|
});
|
||||||
|
const fakeRuntimePort = createLinuxCncPythonRemapRuntimePort({
|
||||||
|
runtimeMode: fakeRuntimeAdapter.runtimeMode,
|
||||||
|
runtimeAdapter: fakeRuntimeAdapter,
|
||||||
|
});
|
||||||
|
const fakeRuntimeStart = await fakeRuntimePort.start();
|
||||||
|
assertEqual(fakeRuntimeStart.runtimeExecutionReady, true, "fake provider runtime readiness");
|
||||||
|
assertEqual(fakeRuntimeStart.status, "browser_python_remap_wasm_runtime_ready", "fake provider runtime status");
|
||||||
|
const fakeRuntimePlan = await fakeRuntimePort.runLifecyclePlan();
|
||||||
|
assertEqual(fakeRuntimePlan.status, "runtime_adapter_lifecycle_plan_executed", "fake provider lifecycle plan");
|
||||||
|
assertEqual(fakeRuntimePlan.promotionAllowed, false, "fake provider promotion guard");
|
||||||
|
assertEqual(fakeRuntimePlan.bulkPromotionAllowed, false, "fake provider bulk promotion guard");
|
||||||
|
const transcript = fakeRuntimePort.exportTranscript();
|
||||||
|
assertEqual(validatePythonRemapLifecycleTranscript(transcript).ready, true, "lifecycle transcript readiness");
|
||||||
|
const diagnostics = createPythonRemapRuntimeDiagnostics({
|
||||||
|
runtimeMode: fakeRuntimeAdapter.runtimeMode,
|
||||||
|
runtimeExecutionReady: fakeRuntimeStart.runtimeExecutionReady,
|
||||||
|
transcript,
|
||||||
|
});
|
||||||
|
assertEqual(diagnostics.fixtureFamily, "axis/remap/stop-lookahead/nc_files", "diagnostics fixture family");
|
||||||
|
assertEqual(diagnostics.iniPath, "axis/remap/stop-lookahead/demo.ini", "diagnostics INI path");
|
||||||
|
assertEqual(diagnostics.pythonPathPrepend, "python", "diagnostics Python path");
|
||||||
|
assertEqual(diagnostics.topLevelPath, "python/toplevel.py", "diagnostics TOPLEVEL");
|
||||||
|
assertEqual(diagnostics.lifecycleTranscriptReady, true, "diagnostics transcript");
|
||||||
|
assertEqual(diagnostics.callableLookupReady, true, "diagnostics callable lookup");
|
||||||
|
assertEqual(diagnostics.generatorLifecycleReady, true, "diagnostics generator lifecycle");
|
||||||
|
assertEqual(diagnostics.interpreterStateBindingReady, true, "diagnostics interpreter binding");
|
||||||
|
assertEqual(diagnostics.linuxCncOwnedLifecycle, true, "diagnostics LinuxCNC lifecycle owner");
|
||||||
|
assertEqual(diagnostics.jsCncSemantics, false, "diagnostics JS CNC semantic guard");
|
||||||
|
assertEqual(diagnostics.ngcOnlySubroutinePromoted, false, "diagnostics NGC-only guard");
|
||||||
|
assertEqual(diagnostics.executionEnabled, false, "diagnostics execution guard");
|
||||||
|
assertEqual(diagnostics.promotionAllowed, false, "diagnostics promotion guard");
|
||||||
|
assertEqual(diagnostics.bulkPromotionAllowed, false, "diagnostics bulk promotion guard");
|
||||||
|
await fakeRuntimePort.close();
|
||||||
|
|
||||||
|
status.textContent = "browser_python_remap_runtime_smoke=ok";
|
||||||
|
} catch (error) {
|
||||||
|
status.textContent = `browser_python_remap_runtime_smoke=fail\n${error.stack || error}`;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -521,13 +521,13 @@
|
|||||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.candidateCount !== 8 ||
|
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.candidateCount !== 8 ||
|
||||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.readyCandidateCount !== 8 ||
|
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.readyCandidateCount !== 8 ||
|
||||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.sourceFileCount !== 17 ||
|
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.sourceFileCount !== 17 ||
|
||||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.inventoryBaseline !== "executed=28 passed=28 skipped=131 unexpected_fail=0" ||
|
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.inventoryBaseline !== "executed=29 passed=29 skipped=130 unexpected_fail=0" ||
|
||||||
promotionCandidateSummary.ready !== true ||
|
promotionCandidateSummary.ready !== true ||
|
||||||
promotionCandidateSummary.preferredIniPath !== "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini" ||
|
promotionCandidateSummary.preferredIniPath !== "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini" ||
|
||||||
doc.body.dataset.promotionCandidateReady !== "true" ||
|
doc.body.dataset.promotionCandidateReady !== "true" ||
|
||||||
doc.body.dataset.promotionCandidatePreferred !== "qtdragon-multi-joint-on-abort" ||
|
doc.body.dataset.promotionCandidatePreferred !== "qtdragon-multi-joint-on-abort" ||
|
||||||
doc.querySelector('[data-promotion-candidate-summary-value="preferred-candidate"]')?.textContent !== "qtdragon-multi-joint-on-abort" ||
|
doc.querySelector('[data-promotion-candidate-summary-value="preferred-candidate"]')?.textContent !== "qtdragon-multi-joint-on-abort" ||
|
||||||
doc.querySelector('[data-promotion-candidate-summary-value="candidate-layers"]')?.textContent !== "evidence-ready=8 inventory-ready=20" ||
|
doc.querySelector('[data-promotion-candidate-summary-value="candidate-layers"]')?.textContent !== "evidence-ready=8 inventory-ready=19" ||
|
||||||
doc.querySelector('[data-promotion-candidate-summary-value="inventory-baseline"]')?.textContent !== "28/28 pass; 131 skip unchanged" ||
|
doc.querySelector('[data-promotion-candidate-summary-value="inventory-baseline"]')?.textContent !== "28/28 pass; 131 skip unchanged" ||
|
||||||
doc.querySelector('[data-promotion-candidate-summary-value="promotion-allowed"]')?.textContent !== "0 baseline changes" ||
|
doc.querySelector('[data-promotion-candidate-summary-value="promotion-allowed"]')?.textContent !== "0 baseline changes" ||
|
||||||
doc.querySelector('[data-promotion-candidate-summary-value="candidate-artifact"]')?.textContent !== "promotion-candidates.tsv" ||
|
doc.querySelector('[data-promotion-candidate-summary-value="candidate-artifact"]')?.textContent !== "promotion-candidates.tsv" ||
|
||||||
|
|||||||
74
wasm-port/tests/browser/verify_python_remap_runtime_browser.sh
Executable file
74
wasm-port/tests/browser/verify_python_remap_runtime_browser.sh
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
|
CHROMIUM="${CHROMIUM:-$(command -v chromium || command -v chromium-browser || command -v google-chrome || command -v google-chrome-stable || true)}"
|
||||||
|
|
||||||
|
if [[ -z "$CHROMIUM" ]]; then
|
||||||
|
echo "missing Chromium-compatible browser; set CHROMIUM=/path/to/browser" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TMP_DIR="$(mktemp -d)"
|
||||||
|
PORT_FILE="$TMP_DIR/port"
|
||||||
|
SERVER_LOG="$TMP_DIR/server.log"
|
||||||
|
CHROME_PROFILE="$TMP_DIR/chrome-profile"
|
||||||
|
mkdir -p "$CHROME_PROFILE"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if [[ -n "${SERVER_PID:-}" ]]; then
|
||||||
|
kill "$SERVER_PID" 2>/dev/null || true
|
||||||
|
wait "$SERVER_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
python3 - <<'PY' "$ROOT_DIR" "$PORT_FILE" >"$SERVER_LOG" 2>&1 &
|
||||||
|
import functools
|
||||||
|
import http.server
|
||||||
|
import pathlib
|
||||||
|
import socketserver
|
||||||
|
import sys
|
||||||
|
|
||||||
|
root = pathlib.Path(sys.argv[1])
|
||||||
|
port_file = pathlib.Path(sys.argv[2])
|
||||||
|
|
||||||
|
handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory=str(root))
|
||||||
|
with socketserver.TCPServer(("127.0.0.1", 0), handler) as httpd:
|
||||||
|
port_file.write_text(str(httpd.server_address[1]), encoding="ascii")
|
||||||
|
httpd.serve_forever()
|
||||||
|
PY
|
||||||
|
SERVER_PID=$!
|
||||||
|
|
||||||
|
for _ in $(seq 1 100); do
|
||||||
|
[[ -s "$PORT_FILE" ]] && break
|
||||||
|
sleep 0.05
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ! -s "$PORT_FILE" ]]; then
|
||||||
|
echo "Python remap runtime browser smoke HTTP server did not start" >&2
|
||||||
|
cat "$SERVER_LOG" >&2 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PORT="$(cat "$PORT_FILE")"
|
||||||
|
URL="http://127.0.0.1:$PORT/tests/browser/python_remap_runtime_browser_smoke.html"
|
||||||
|
OUT="$TMP_DIR/chromium.out"
|
||||||
|
|
||||||
|
"$CHROMIUM" \
|
||||||
|
--headless=new \
|
||||||
|
--disable-gpu \
|
||||||
|
--no-sandbox \
|
||||||
|
--user-data-dir="$CHROME_PROFILE" \
|
||||||
|
--virtual-time-budget=10000 \
|
||||||
|
--dump-dom \
|
||||||
|
"$URL" >"$OUT" 2>&1
|
||||||
|
|
||||||
|
if ! grep -Fq "browser_python_remap_runtime_smoke=ok" "$OUT"; then
|
||||||
|
echo "Python remap runtime browser smoke failed" >&2
|
||||||
|
sed -n '1,220p' "$OUT" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "browser_python_remap_runtime_smoke=ok"
|
||||||
@@ -35,9 +35,9 @@ const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md
|
|||||||
|
|
||||||
for (const phrase of [
|
for (const phrase of [
|
||||||
"Sim-config coverage release handoff",
|
"Sim-config coverage release handoff",
|
||||||
"sim_configs_wasm_node_inventory_executed=28",
|
"sim_configs_wasm_node_inventory_executed=29",
|
||||||
"sim_configs_wasm_node_inventory_passed=28",
|
"sim_configs_wasm_node_inventory_passed=29",
|
||||||
"sim_configs_wasm_node_inventory_skipped=131",
|
"sim_configs_wasm_node_inventory_skipped=130",
|
||||||
"sim_configs_wasm_node_inventory_unexpected_fail=0",
|
"sim_configs_wasm_node_inventory_unexpected_fail=0",
|
||||||
"sim_configs_wasm_node_inventory_skip_ASSET_ONLY=65",
|
"sim_configs_wasm_node_inventory_skip_ASSET_ONLY=65",
|
||||||
"sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53",
|
"sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53",
|
||||||
@@ -59,9 +59,9 @@ for (const phrase of [
|
|||||||
for (const phrase of [
|
for (const phrase of [
|
||||||
"Sim Config Coverage Promotion Analysis",
|
"Sim Config Coverage Promotion Analysis",
|
||||||
"virtual HAL",
|
"virtual HAL",
|
||||||
"sim_configs_wasm_node_inventory_executed=28",
|
"sim_configs_wasm_node_inventory_executed=29",
|
||||||
"sim_configs_wasm_node_inventory_passed=28",
|
"sim_configs_wasm_node_inventory_passed=29",
|
||||||
"sim_configs_wasm_node_inventory_skipped=131",
|
"sim_configs_wasm_node_inventory_skipped=130",
|
||||||
"qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
"qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||||
"axis/vismach/puma/puma_seam_weld.ngc",
|
"axis/vismach/puma/puma_seam_weld.ngc",
|
||||||
"axis/rose_engine/rcone_demo.ngc",
|
"axis/rose_engine/rcone_demo.ngc",
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ assert.equal(validation.phase, "ready");
|
|||||||
assert.deepEqual(validation.missing, []);
|
assert.deepEqual(validation.missing, []);
|
||||||
assert.equal(validation.artifactApiName, "project-release-readiness-report");
|
assert.equal(validation.artifactApiName, "project-release-readiness-report");
|
||||||
assert.equal(validation.artifactVersion, 1);
|
assert.equal(validation.artifactVersion, 1);
|
||||||
assert.equal(validation.gateCount, 12);
|
assert.equal(validation.gateCount, 13);
|
||||||
assert.equal(validation.expectedGateCount, 12);
|
assert.equal(validation.expectedGateCount, 13);
|
||||||
assert.equal(validation.gateManifestReady, true);
|
assert.equal(validation.gateManifestReady, true);
|
||||||
assert.equal(validation.gateExecutionManifestReady, true);
|
assert.equal(validation.gateExecutionManifestReady, true);
|
||||||
assert.equal(validation.gateExecutionSummaryReady, true);
|
assert.equal(validation.gateExecutionSummaryReady, true);
|
||||||
@@ -41,6 +41,7 @@ assert.deepEqual(validation.expectedGateIds, [
|
|||||||
"sim-config-inventory",
|
"sim-config-inventory",
|
||||||
"ini-panel-browser",
|
"ini-panel-browser",
|
||||||
"opfs-session-browser",
|
"opfs-session-browser",
|
||||||
|
"tool-db-process-proof",
|
||||||
"release-artifact-url-browser",
|
"release-artifact-url-browser",
|
||||||
"project-batch-acceptance",
|
"project-batch-acceptance",
|
||||||
"ui-node-smokes",
|
"ui-node-smokes",
|
||||||
@@ -49,9 +50,9 @@ assert.deepEqual(validation.expectedGateIds, [
|
|||||||
]);
|
]);
|
||||||
assert.equal(artifact.gateManifest.apiName, "project-release-gate-manifest");
|
assert.equal(artifact.gateManifest.apiName, "project-release-gate-manifest");
|
||||||
assert.equal(artifact.gateExecutionManifest.apiName, "project-release-gate-execution-manifest");
|
assert.equal(artifact.gateExecutionManifest.apiName, "project-release-gate-execution-manifest");
|
||||||
assert.equal(artifact.gateExecutionManifest.passedCount, 12);
|
assert.equal(artifact.gateExecutionManifest.passedCount, 13);
|
||||||
assert.equal(artifact.gateExecutionSummaryViewModel.apiName, "project-release-gate-execution-summary-view-model");
|
assert.equal(artifact.gateExecutionSummaryViewModel.apiName, "project-release-gate-execution-summary-view-model");
|
||||||
assert.equal(artifact.gateExecutionSummaryViewModel.rows[0].value, "12/12 passed");
|
assert.equal(artifact.gateExecutionSummaryViewModel.rows[0].value, "13/13 passed");
|
||||||
assert.equal(artifact.gateResultMatrix.apiName, "project-release-gate-result-matrix");
|
assert.equal(artifact.gateResultMatrix.apiName, "project-release-gate-result-matrix");
|
||||||
assert.equal(artifact.gateActionPlan.apiName, "project-release-gate-action-plan");
|
assert.equal(artifact.gateActionPlan.apiName, "project-release-gate-action-plan");
|
||||||
assert.equal(artifact.gateActionPlan.pendingCount, 0);
|
assert.equal(artifact.gateActionPlan.pendingCount, 0);
|
||||||
@@ -98,7 +99,7 @@ assert.equal(
|
|||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
artifact.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
|
artifact.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
|
||||||
"locked (3 families)",
|
"locked (2 families)",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
artifact.rows.find(({ id }) => id === "promoted-blocked-family-count")?.value,
|
artifact.rows.find(({ id }) => id === "promoted-blocked-family-count")?.value,
|
||||||
@@ -143,12 +144,12 @@ assert.equal(
|
|||||||
row.promotionAllowed === false &&
|
row.promotionAllowed === false &&
|
||||||
row.excludedFromPositiveFixtures === true &&
|
row.excludedFromPositiveFixtures === true &&
|
||||||
row.boundaryEvidenceReady === true &&
|
row.boundaryEvidenceReady === true &&
|
||||||
row.boundaryEvidence.baselineSummary.executed === 28 &&
|
row.boundaryEvidence.baselineSummary.executed === 29 &&
|
||||||
row.boundaryEvidence.baselineSummary.passed === 28 &&
|
row.boundaryEvidence.baselineSummary.passed === 29 &&
|
||||||
row.boundaryEvidence.baselineSummary.skipped === 131 &&
|
row.boundaryEvidence.baselineSummary.skipped === 130 &&
|
||||||
row.boundaryEvidence.baselineSummary.unexpectedFail === 0 &&
|
row.boundaryEvidence.baselineSummary.unexpectedFail === 0 &&
|
||||||
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9" &&
|
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "6cc15052cad03d04eff4506cd0a9d651cd45a2bd5da4d9f38633e9535826b9f3" &&
|
||||||
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae" &&
|
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "17bff95b57c55ff4201a23c7286e733550e4df513470c66dd5f24329e7781a35" &&
|
||||||
row.boundaryEvidence.boundarySummary.dependencies.includes("missing_vendored_ini:configs/sim/gscreen/industrial_lathe_wear/industrial_lathe_wear.ini") &&
|
row.boundaryEvidence.boundarySummary.dependencies.includes("missing_vendored_ini:configs/sim/gscreen/industrial_lathe_wear/industrial_lathe_wear.ini") &&
|
||||||
row.boundaryEvidence.iniBoundarySummary.vendored === 0
|
row.boundaryEvidence.iniBoundarySummary.vendored === 0
|
||||||
),
|
),
|
||||||
@@ -158,12 +159,12 @@ assert.equal(
|
|||||||
artifact.virtualHalSimConfigMacroLoadFixtures.blockedRows.some((row) =>
|
artifact.virtualHalSimConfigMacroLoadFixtures.blockedRows.some((row) =>
|
||||||
row.id === "silverdragon-tool-sensor-python-ui-boundary" &&
|
row.id === "silverdragon-tool-sensor-python-ui-boundary" &&
|
||||||
row.boundaryEvidenceReady === true &&
|
row.boundaryEvidenceReady === true &&
|
||||||
row.boundaryEvidence.baselineSummary.executed === 28 &&
|
row.boundaryEvidence.baselineSummary.executed === 29 &&
|
||||||
row.boundaryEvidence.baselineSummary.passed === 28 &&
|
row.boundaryEvidence.baselineSummary.passed === 29 &&
|
||||||
row.boundaryEvidence.baselineSummary.skipped === 131 &&
|
row.boundaryEvidence.baselineSummary.skipped === 130 &&
|
||||||
row.boundaryEvidence.baselineSummary.unexpectedFail === 0 &&
|
row.boundaryEvidence.baselineSummary.unexpectedFail === 0 &&
|
||||||
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9" &&
|
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv"] === "6cc15052cad03d04eff4506cd0a9d651cd45a2bd5da4d9f38633e9535826b9f3" &&
|
||||||
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae" &&
|
row.boundaryEvidence.sourceArtifactHashes["wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"] === "17bff95b57c55ff4201a23c7286e733550e4df513470c66dd5f24329e7781a35" &&
|
||||||
row.boundaryEvidence.boundarySummary.dependencies.includes("missing_vendored_ini:configs/sim/gscreen/silverdragon/silverdragon.ini") &&
|
row.boundaryEvidence.boundarySummary.dependencies.includes("missing_vendored_ini:configs/sim/gscreen/silverdragon/silverdragon.ini") &&
|
||||||
row.boundaryEvidence.iniBoundarySummary.vendored === 0
|
row.boundaryEvidence.iniBoundarySummary.vendored === 0
|
||||||
),
|
),
|
||||||
@@ -185,19 +186,19 @@ assert.equal(
|
|||||||
"wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv",
|
"wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv",
|
||||||
);
|
);
|
||||||
assert.equal(artifact.promotionCandidateArtifactSummary.evidenceReadyCount, 8);
|
assert.equal(artifact.promotionCandidateArtifactSummary.evidenceReadyCount, 8);
|
||||||
assert.equal(artifact.promotionCandidateArtifactSummary.inventoryReadyCount, 20);
|
assert.equal(artifact.promotionCandidateArtifactSummary.inventoryReadyCount, 19);
|
||||||
assert.equal(artifact.promotionCandidateArtifactSummary.evidenceExpansionCandidateCount, 13);
|
assert.equal(artifact.promotionCandidateArtifactSummary.evidenceExpansionCandidateCount, 14);
|
||||||
assert.equal(artifact.promotionCandidateArtifactSummary.totalCandidateCount, 28);
|
assert.equal(artifact.promotionCandidateArtifactSummary.totalCandidateCount, 27);
|
||||||
assert.equal(artifact.promotionCandidateArtifactSummary.promotionAllowedCount, 0);
|
assert.equal(artifact.promotionCandidateArtifactSummary.promotionAllowedCount, 0);
|
||||||
assert.equal(artifact.promotionCandidateArtifactSummary.hardBlockRuntimePromotionAllowedCount, 0);
|
assert.equal(artifact.promotionCandidateArtifactSummary.hardBlockRuntimePromotionAllowedCount, 0);
|
||||||
assert.deepEqual(artifact.promotionCandidateArtifactSummary.artifactRows, {
|
assert.deepEqual(artifact.promotionCandidateArtifactSummary.artifactRows, {
|
||||||
promotionCandidates: 28,
|
promotionCandidates: 27,
|
||||||
evidenceExpansion: 13,
|
evidenceExpansion: 14,
|
||||||
});
|
});
|
||||||
assert.equal(artifact.promotionCandidateArtifactSummary.evidenceExpansion.promotionAllowedCount, 0);
|
assert.equal(artifact.promotionCandidateArtifactSummary.evidenceExpansion.promotionAllowedCount, 0);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
artifact.promotionCandidateArtifactSummary.inventoryBaseline,
|
artifact.promotionCandidateArtifactSummary.inventoryBaseline,
|
||||||
"executed=28 passed=28 skipped=131 unexpected_fail=0",
|
"executed=29 passed=29 skipped=130 unexpected_fail=0",
|
||||||
);
|
);
|
||||||
assert.equal(artifact.axisScreenshotArtifactSummary.apiName, "project-release-axis-screenshot-artifact-summary");
|
assert.equal(artifact.axisScreenshotArtifactSummary.apiName, "project-release-axis-screenshot-artifact-summary");
|
||||||
if (artifact.axisScreenshotArtifactSummary.artifactCount > 0) {
|
if (artifact.axisScreenshotArtifactSummary.artifactCount > 0) {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const passedGateResults = {
|
|||||||
"sim-config-inventory": true,
|
"sim-config-inventory": true,
|
||||||
"ini-panel-browser": true,
|
"ini-panel-browser": true,
|
||||||
"opfs-session-browser": true,
|
"opfs-session-browser": true,
|
||||||
|
"tool-db-process-proof": true,
|
||||||
"release-artifact-url-browser": true,
|
"release-artifact-url-browser": true,
|
||||||
"project-batch-acceptance": true,
|
"project-batch-acceptance": true,
|
||||||
"ui-node-smokes": true,
|
"ui-node-smokes": true,
|
||||||
@@ -88,6 +89,7 @@ const report = createProjectReleaseReadinessReport({
|
|||||||
"sim_configs_wasm_node_inventory_unexpected_fail=0",
|
"sim_configs_wasm_node_inventory_unexpected_fail=0",
|
||||||
"browser_ini_shell_integration_workflow_smoke=ok",
|
"browser_ini_shell_integration_workflow_smoke=ok",
|
||||||
"browser_opfs_session_workflow_smoke=ok",
|
"browser_opfs_session_workflow_smoke=ok",
|
||||||
|
"tool_db_process_proof=ok",
|
||||||
"browser_release_artifact_url_workflow_smoke=ok",
|
"browser_release_artifact_url_workflow_smoke=ok",
|
||||||
"project_batch_acceptance_workflow_node_smoke=ok",
|
"project_batch_acceptance_workflow_node_smoke=ok",
|
||||||
"ui_node_smokes=ok",
|
"ui_node_smokes=ok",
|
||||||
|
|||||||
@@ -434,9 +434,7 @@ awk -F '\t' '
|
|||||||
runtime_family = $1
|
runtime_family = $1
|
||||||
sub("/[^/]+$", "", runtime_family)
|
sub("/[^/]+$", "", runtime_family)
|
||||||
blocked = ""
|
blocked = ""
|
||||||
if ($1 ~ /^axis\/db_demo\//) {
|
if ($1 ~ /^axis\/vismach\/millturn\// && $2 == "main") {
|
||||||
blocked = "L4-TOOL-DB"
|
|
||||||
} else if ($1 ~ /^axis\/vismach\/millturn\// && $2 == "main") {
|
|
||||||
blocked = "L4-USER-M-PROCESS"
|
blocked = "L4-USER-M-PROCESS"
|
||||||
} else if ($4 == "upstream-demo-missing-motion-gcode") {
|
} else if ($4 == "upstream-demo-missing-motion-gcode") {
|
||||||
blocked = "UPSTREAM-DEMO"
|
blocked = "UPSTREAM-DEMO"
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ const browserDiagnosticsArtifact = {
|
|||||||
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||||
blockedCandidateIds: [],
|
blockedCandidateIds: [],
|
||||||
explicitBrowserDiagnosticsCount: 8,
|
explicitBrowserDiagnosticsCount: 8,
|
||||||
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
|
inventoryBaseline: "executed=29 passed=29 skipped=130 unexpected_fail=0",
|
||||||
},
|
},
|
||||||
virtualHalSimConfigMacroLoadFixtures,
|
virtualHalSimConfigMacroLoadFixtures,
|
||||||
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: virtualHalState }),
|
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: virtualHalState }),
|
||||||
@@ -203,7 +203,7 @@ assert.equal(
|
|||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
|
readySummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
|
||||||
"executed=28 passed=28 skipped=131 unexpected_fail=0",
|
"executed=29 passed=29 skipped=130 unexpected_fail=0",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "promotion-candidate-artifact")?.value,
|
readySummary.rows.find(({ id }) => id === "promotion-candidate-artifact")?.value,
|
||||||
@@ -211,11 +211,11 @@ assert.equal(
|
|||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
|
readySummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
|
||||||
"evidence-ready=8 inventory-ready=20",
|
"evidence-ready=8 inventory-ready=19",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "promotion-candidate-total")?.value,
|
readySummary.rows.find(({ id }) => id === "promotion-candidate-total")?.value,
|
||||||
"28",
|
"27",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "evidence-ready-candidate-rows")?.value,
|
readySummary.rows.find(({ id }) => id === "evidence-ready-candidate-rows")?.value,
|
||||||
@@ -239,15 +239,15 @@ assert.equal(
|
|||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "evidence-expansion-candidates")?.value,
|
readySummary.rows.find(({ id }) => id === "evidence-expansion-candidates")?.value,
|
||||||
"13",
|
"14",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "promotion-candidate-artifact-rows")?.value,
|
readySummary.rows.find(({ id }) => id === "promotion-candidate-artifact-rows")?.value,
|
||||||
"28",
|
"27",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "evidence-expansion-artifact-rows")?.value,
|
readySummary.rows.find(({ id }) => id === "evidence-expansion-artifact-rows")?.value,
|
||||||
"13",
|
"14",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "evidence-expansion-next-evidence")?.value,
|
readySummary.rows.find(({ id }) => id === "evidence-expansion-next-evidence")?.value,
|
||||||
@@ -291,11 +291,11 @@ assert.equal(
|
|||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "hard-block-runtime-locked-rows")?.value,
|
readySummary.rows.find(({ id }) => id === "hard-block-runtime-locked-rows")?.value,
|
||||||
"19",
|
"18",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "hard-block-runtime-locked-preview")?.value,
|
readySummary.rows.find(({ id }) => id === "hard-block-runtime-locked-preview")?.value,
|
||||||
"L4-TOOL-DB:axis/db_demo/base.ngc, L4-PYTHON-REMAP:axis/laser/raster_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test.ngc",
|
"L4-PYTHON-REMAP:axis/laser/raster_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test2.ngc",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "hard-block-runtime-promotion-allowed")?.value,
|
readySummary.rows.find(({ id }) => id === "hard-block-runtime-promotion-allowed")?.value,
|
||||||
@@ -303,31 +303,23 @@ assert.equal(
|
|||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "hard-block-runtime-family-count")?.value,
|
readySummary.rows.find(({ id }) => id === "hard-block-runtime-family-count")?.value,
|
||||||
"3",
|
"2",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "hard-block-runtime-family-summary")?.value,
|
readySummary.rows.find(({ id }) => id === "hard-block-runtime-family-summary")?.value,
|
||||||
"L4-USER-M-PROCESS:1 locked=yes, L4-TOOL-DB:1 locked=yes, L4-PYTHON-REMAP:17 locked=yes",
|
"L4-USER-M-PROCESS:1 locked=yes, L4-PYTHON-REMAP:17 locked=yes",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "hard-block-runtime-detail-row-count")?.value,
|
readySummary.rows.find(({ id }) => id === "hard-block-runtime-detail-row-count")?.value,
|
||||||
"19",
|
"18",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "hard-block-runtime-detail-preview-1")?.value,
|
readySummary.rows.find(({ id }) => id === "hard-block-runtime-detail-preview-1")?.value,
|
||||||
"L4-TOOL-DB:axis/db_demo/base.ngc; reason=design_tooldata_db_protocol_boundary; next=db_program_v2_1_handshake_getall_load_unload_or_put; promotion_allowed=0",
|
"L4-PYTHON-REMAP:axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0",
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
readySummary.rows.find(({ id }) => id === "hard-block-runtime-family-L4-TOOL-DB")?.value,
|
|
||||||
"1 locked rows; promotion_allowed=0; first=axis/db_demo/base.ngc; reason=design_tooldata_db_protocol_boundary; next=db_program_v2_1_handshake_getall_load_unload_or_put",
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
readySummary.rows.find(({ id }) => id === "hard-block-runtime-family-L4-TOOL-DB-paths")?.value,
|
|
||||||
"axis/db_demo/base.ngc",
|
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
|
readySummary.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
|
||||||
"locked (3 families)",
|
"locked (2 families)",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readySummary.rows.find(({ id }) => id === "promoted-blocked-family-count")?.value,
|
readySummary.rows.find(({ id }) => id === "promoted-blocked-family-count")?.value,
|
||||||
@@ -351,16 +343,16 @@ assert.deepEqual(
|
|||||||
["artifact-validation", "ready"],
|
["artifact-validation", "ready"],
|
||||||
["virtual-hal-promotion-families", expectedPromotionFamilySummary],
|
["virtual-hal-promotion-families", expectedPromotionFamilySummary],
|
||||||
["promotion-candidate-artifact", "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv"],
|
["promotion-candidate-artifact", "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv"],
|
||||||
["promotion-candidate-layers", "evidence-ready=8 inventory-ready=20"],
|
["promotion-candidate-layers", "evidence-ready=8 inventory-ready=19"],
|
||||||
["promotion-candidate-total", "28"],
|
["promotion-candidate-total", "27"],
|
||||||
["evidence-ready-candidate-rows", "8"],
|
["evidence-ready-candidate-rows", "8"],
|
||||||
["evidence-ready-candidate-preview", "qtdragon-multi-joint-on-abort"],
|
["evidence-ready-candidate-preview", "qtdragon-multi-joint-on-abort"],
|
||||||
["evidence-ready-candidate-preview-gcode", "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"],
|
["evidence-ready-candidate-preview-gcode", "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"],
|
||||||
["evidence-ready-candidate-promotion-allowed", "0"],
|
["evidence-ready-candidate-promotion-allowed", "0"],
|
||||||
["evidence-ready-candidate-baseline-changing", "no"],
|
["evidence-ready-candidate-baseline-changing", "no"],
|
||||||
["promotion-candidate-artifact-rows", "28"],
|
["promotion-candidate-artifact-rows", "27"],
|
||||||
["evidence-expansion-candidates", "13"],
|
["evidence-expansion-candidates", "14"],
|
||||||
["evidence-expansion-artifact-rows", "13"],
|
["evidence-expansion-artifact-rows", "14"],
|
||||||
["evidence-expansion-next-evidence", "browser-diagnostics-binding"],
|
["evidence-expansion-next-evidence", "browser-diagnostics-binding"],
|
||||||
["evidence-expansion-baseline-changing", "no"],
|
["evidence-expansion-baseline-changing", "no"],
|
||||||
["evidence-expansion-preferred-candidate", "woodpecker-on-abort"],
|
["evidence-expansion-preferred-candidate", "woodpecker-on-abort"],
|
||||||
@@ -371,22 +363,20 @@ assert.deepEqual(
|
|||||||
["evidence-expansion-source-count-list", "3, 3, 3"],
|
["evidence-expansion-source-count-list", "3, 3, 3"],
|
||||||
["evidence-expansion-artifact", "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"],
|
["evidence-expansion-artifact", "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"],
|
||||||
["promotion-candidate-allowed", "0"],
|
["promotion-candidate-allowed", "0"],
|
||||||
["hard-block-runtime-locked-rows", "19"],
|
["hard-block-runtime-locked-rows", "18"],
|
||||||
["hard-block-runtime-locked-preview", "L4-TOOL-DB:axis/db_demo/base.ngc, L4-PYTHON-REMAP:axis/laser/raster_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test.ngc"],
|
["hard-block-runtime-locked-preview", "L4-PYTHON-REMAP:axis/laser/raster_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test.ngc, L4-PYTHON-REMAP:axis/laser/vector_test2.ngc"],
|
||||||
["hard-block-runtime-promotion-allowed", "0"],
|
["hard-block-runtime-promotion-allowed", "0"],
|
||||||
["hard-block-runtime-family-count", "3"],
|
["hard-block-runtime-family-count", "2"],
|
||||||
["hard-block-runtime-family-summary", "L4-USER-M-PROCESS:1 locked=yes, L4-TOOL-DB:1 locked=yes, L4-PYTHON-REMAP:17 locked=yes"],
|
["hard-block-runtime-family-summary", "L4-USER-M-PROCESS:1 locked=yes, L4-PYTHON-REMAP:17 locked=yes"],
|
||||||
["hard-block-runtime-detail-row-count", "19"],
|
["hard-block-runtime-detail-row-count", "18"],
|
||||||
["hard-block-runtime-detail-preview-1", "L4-TOOL-DB:axis/db_demo/base.ngc; reason=design_tooldata_db_protocol_boundary; next=db_program_v2_1_handshake_getall_load_unload_or_put; promotion_allowed=0"],
|
["hard-block-runtime-detail-preview-1", "L4-PYTHON-REMAP:axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
|
||||||
["hard-block-runtime-detail-preview-2", "L4-PYTHON-REMAP:axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
|
["hard-block-runtime-detail-preview-2", "L4-PYTHON-REMAP:axis/laser/vector_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
|
||||||
["hard-block-runtime-detail-preview-3", "L4-PYTHON-REMAP:axis/laser/vector_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
|
["hard-block-runtime-detail-preview-3", "L4-PYTHON-REMAP:axis/laser/vector_test2.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
|
||||||
["hard-block-runtime-detail-preview-4", "L4-PYTHON-REMAP:axis/laser/vector_test2.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
|
["hard-block-runtime-detail-preview-4", "L4-PYTHON-REMAP:axis/remap/cycle/nc_files/examples.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
|
||||||
["hard-block-runtime-detail-preview-5", "L4-PYTHON-REMAP:axis/remap/cycle/nc_files/examples.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
|
["hard-block-runtime-detail-preview-5", "L4-PYTHON-REMAP:axis/remap/extend-builtins/nc_files/examples.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture; promotion_allowed=0"],
|
||||||
["hard-block-runtime-family-L4-USER-M-PROCESS", "1 locked rows; promotion_allowed=0; first=axis/vismach/millturn/example.ngc; reason=design_m128_m129_linuxcnc_state_boundary; next=kinstype_guard_and_ini_xyz_hal_pin_state"],
|
["hard-block-runtime-family-L4-USER-M-PROCESS", "1 locked rows; promotion_allowed=0; first=axis/vismach/millturn/example.ngc; reason=design_m128_m129_linuxcnc_state_boundary; next=kinstype_guard_and_ini_xyz_hal_pin_state"],
|
||||||
["hard-block-runtime-family-L4-TOOL-DB", "1 locked rows; promotion_allowed=0; first=axis/db_demo/base.ngc; reason=design_tooldata_db_protocol_boundary; next=db_program_v2_1_handshake_getall_load_unload_or_put"],
|
|
||||||
["hard-block-runtime-family-L4-PYTHON-REMAP", "17 locked rows; promotion_allowed=0; first=axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture"],
|
["hard-block-runtime-family-L4-PYTHON-REMAP", "17 locked rows; promotion_allowed=0; first=axis/laser/raster_test.ngc; reason=design_linuxcnc_python_runtime_boundary; next=python_runtime_owner_and_fixture"],
|
||||||
["hard-block-runtime-family-L4-USER-M-PROCESS-paths", "axis/vismach/millturn/example.ngc"],
|
["hard-block-runtime-family-L4-USER-M-PROCESS-paths", "axis/vismach/millturn/example.ngc"],
|
||||||
["hard-block-runtime-family-L4-TOOL-DB-paths", "axis/db_demo/base.ngc"],
|
|
||||||
["hard-block-runtime-family-L4-PYTHON-REMAP-paths", "axis/laser/raster_test.ngc, axis/laser/vector_test.ngc, axis/laser/vector_test2.ngc, axis/remap/cycle/nc_files/examples.ngc, axis/remap/extend-builtins/nc_files/examples.ngc, axis/remap/getting-started/nc_files/examples.ngc, axis/remap/manual-toolchange-with-tool-length-switch/nc_files/tcdemo.ngc, axis/remap/rack-toolchange/nc_files/tcdemo.ngc, axis/remap/stop-lookahead/nc_files/examples.ngc, axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/circular_pocket.ngc, axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_back_and_forth.ngc, axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition.ngc, axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/simple_example.ngc, axis/vismach/VMC_toolchange/toolchange.ngc, gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzac.ngc, gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzbc.ngc, gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/impeller-7bl-xyzac.ngc"],
|
["hard-block-runtime-family-L4-PYTHON-REMAP-paths", "axis/laser/raster_test.ngc, axis/laser/vector_test.ngc, axis/laser/vector_test2.ngc, axis/remap/cycle/nc_files/examples.ngc, axis/remap/extend-builtins/nc_files/examples.ngc, axis/remap/getting-started/nc_files/examples.ngc, axis/remap/manual-toolchange-with-tool-length-switch/nc_files/tcdemo.ngc, axis/remap/rack-toolchange/nc_files/tcdemo.ngc, axis/remap/stop-lookahead/nc_files/examples.ngc, axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/circular_pocket.ngc, axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_back_and_forth.ngc, axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition.ngc, axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/simple_example.ngc, axis/vismach/VMC_toolchange/toolchange.ngc, gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzac.ngc, gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzbc.ngc, gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/impeller-7bl-xyzac.ngc"],
|
||||||
["tool-db-process-proof", "ready"],
|
["tool-db-process-proof", "ready"],
|
||||||
["tool-db-process-proof-detail", "wasm=ready browser=ready opfs=ready promotion_allowed=0"],
|
["tool-db-process-proof-detail", "wasm=ready browser=ready opfs=ready promotion_allowed=0"],
|
||||||
@@ -400,8 +390,8 @@ assert.deepEqual(
|
|||||||
["virtual-hal-promotion-family-count", "3 families"],
|
["virtual-hal-promotion-family-count", "3 families"],
|
||||||
["virtual-hal-promotion-source-file-count", "17 source files"],
|
["virtual-hal-promotion-source-file-count", "17 source files"],
|
||||||
["virtual-hal-promotion-browser-diagnostics-count", "8 diagnostics-ready"],
|
["virtual-hal-promotion-browser-diagnostics-count", "8 diagnostics-ready"],
|
||||||
["virtual-hal-promotion-candidate-baseline", "executed=28 passed=28 skipped=131 unexpected_fail=0"],
|
["virtual-hal-promotion-candidate-baseline", "executed=29 passed=29 skipped=130 unexpected_fail=0"],
|
||||||
["hard-block-runtime-lock", "locked (3 families)"],
|
["hard-block-runtime-lock", "locked (2 families)"],
|
||||||
["promoted-blocked-family-count", "0 promoted"],
|
["promoted-blocked-family-count", "0 promoted"],
|
||||||
["saved-session-diagnostics", "ready"],
|
["saved-session-diagnostics", "ready"],
|
||||||
["next-command", "none"],
|
["next-command", "none"],
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
|
|||||||
{
|
{
|
||||||
id: "promotion-candidate-layers",
|
id: "promotion-candidate-layers",
|
||||||
label: "Promotion candidate layers",
|
label: "Promotion candidate layers",
|
||||||
value: "evidence-ready=8 inventory-ready=20",
|
value: "evidence-ready=8 inventory-ready=19",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "promotion-candidate-allowed",
|
id: "promotion-candidate-allowed",
|
||||||
@@ -292,7 +292,7 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
|
|||||||
{
|
{
|
||||||
id: "evidence-expansion-candidates",
|
id: "evidence-expansion-candidates",
|
||||||
label: "Evidence expansion candidates",
|
label: "Evidence expansion candidates",
|
||||||
value: "13",
|
value: "14",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "evidence-expansion-next-evidence",
|
id: "evidence-expansion-next-evidence",
|
||||||
@@ -327,12 +327,12 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
|
|||||||
{
|
{
|
||||||
id: "blocked-runtime-families",
|
id: "blocked-runtime-families",
|
||||||
label: "Blocked runtime families",
|
label: "Blocked runtime families",
|
||||||
value: "L4-USER-M-PROCESS, L4-TOOL-DB, L4-PYTHON-REMAP",
|
value: "L4-USER-M-PROCESS, L4-PYTHON-REMAP",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "hard-block-runtime-lock",
|
id: "hard-block-runtime-lock",
|
||||||
label: "Hard-block runtime lock",
|
label: "Hard-block runtime lock",
|
||||||
value: "locked (3 families)",
|
value: "locked (2 families)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "promoted-blocked-family-count",
|
id: "promoted-blocked-family-count",
|
||||||
@@ -362,9 +362,9 @@ assert.equal(
|
|||||||
virtualHalSimConfigPromotionCandidates,
|
virtualHalSimConfigPromotionCandidates,
|
||||||
virtualHalSimConfigMacroLoadFixtures,
|
virtualHalSimConfigMacroLoadFixtures,
|
||||||
virtualHalMotionControllerMatrix,
|
virtualHalMotionControllerMatrix,
|
||||||
promotedRuntimeFamilies: ["L4-TOOL-DB"],
|
promotedRuntimeFamilies: ["L4-PYTHON-REMAP"],
|
||||||
})).rows.find(({ id }) => id === "blocked-runtime-families")?.value,
|
})).rows.find(({ id }) => id === "blocked-runtime-families")?.value,
|
||||||
"promoted: L4-TOOL-DB",
|
"promoted: L4-PYTHON-REMAP",
|
||||||
);
|
);
|
||||||
|
|
||||||
const artifactValidationSummary = createProjectReleaseReadinessArtifactValidationSummaryViewModel(
|
const artifactValidationSummary = createProjectReleaseReadinessArtifactValidationSummaryViewModel(
|
||||||
@@ -413,11 +413,11 @@ assert.deepEqual(
|
|||||||
["virtual-hal-sim-config-source-coverage", "ready"],
|
["virtual-hal-sim-config-source-coverage", "ready"],
|
||||||
["virtual-hal-sim-config-promotion-candidates", "ready"],
|
["virtual-hal-sim-config-promotion-candidates", "ready"],
|
||||||
["promotion-candidate-artifact", "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv"],
|
["promotion-candidate-artifact", "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv"],
|
||||||
["promotion-candidate-layers", "evidence-ready=8 inventory-ready=20"],
|
["promotion-candidate-layers", "evidence-ready=8 inventory-ready=19"],
|
||||||
["promotion-candidate-total", "28"],
|
["promotion-candidate-total", "27"],
|
||||||
["promotion-candidate-artifact-rows", "28"],
|
["promotion-candidate-artifact-rows", "27"],
|
||||||
["evidence-expansion-candidates", "13"],
|
["evidence-expansion-candidates", "14"],
|
||||||
["evidence-expansion-artifact-rows", "13"],
|
["evidence-expansion-artifact-rows", "14"],
|
||||||
["evidence-expansion-next-evidence", "browser-diagnostics-binding"],
|
["evidence-expansion-next-evidence", "browser-diagnostics-binding"],
|
||||||
["evidence-expansion-baseline-changing", "no"],
|
["evidence-expansion-baseline-changing", "no"],
|
||||||
["evidence-expansion-artifact", "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"],
|
["evidence-expansion-artifact", "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv"],
|
||||||
@@ -516,16 +516,16 @@ assert.deepEqual(
|
|||||||
["artifact-validation", "ready"],
|
["artifact-validation", "ready"],
|
||||||
["virtual-hal-promotion-families", expectedPromotionFamilySummary],
|
["virtual-hal-promotion-families", expectedPromotionFamilySummary],
|
||||||
["promotion-candidate-artifact", "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv"],
|
["promotion-candidate-artifact", "wasm-port/build/wasm/sim-configs-inventory/promotion-candidates.tsv"],
|
||||||
["promotion-candidate-layers", "evidence-ready=8 inventory-ready=20"],
|
["promotion-candidate-layers", "evidence-ready=8 inventory-ready=19"],
|
||||||
["promotion-candidate-total", "28"],
|
["promotion-candidate-total", "27"],
|
||||||
["evidence-ready-candidate-rows", "0"],
|
["evidence-ready-candidate-rows", "0"],
|
||||||
["evidence-ready-candidate-preview", "not provided"],
|
["evidence-ready-candidate-preview", "not provided"],
|
||||||
["evidence-ready-candidate-preview-gcode", "not provided"],
|
["evidence-ready-candidate-preview-gcode", "not provided"],
|
||||||
["evidence-ready-candidate-promotion-allowed", "0"],
|
["evidence-ready-candidate-promotion-allowed", "0"],
|
||||||
["evidence-ready-candidate-baseline-changing", "no"],
|
["evidence-ready-candidate-baseline-changing", "no"],
|
||||||
["promotion-candidate-artifact-rows", "28"],
|
["promotion-candidate-artifact-rows", "27"],
|
||||||
["evidence-expansion-candidates", "13"],
|
["evidence-expansion-candidates", "14"],
|
||||||
["evidence-expansion-artifact-rows", "13"],
|
["evidence-expansion-artifact-rows", "14"],
|
||||||
["evidence-expansion-next-evidence", "browser-diagnostics-binding"],
|
["evidence-expansion-next-evidence", "browser-diagnostics-binding"],
|
||||||
["evidence-expansion-baseline-changing", "no"],
|
["evidence-expansion-baseline-changing", "no"],
|
||||||
["evidence-expansion-preferred-candidate", "not provided"],
|
["evidence-expansion-preferred-candidate", "not provided"],
|
||||||
@@ -539,14 +539,12 @@ assert.deepEqual(
|
|||||||
["hard-block-runtime-locked-rows", "0"],
|
["hard-block-runtime-locked-rows", "0"],
|
||||||
["hard-block-runtime-locked-preview", "not provided"],
|
["hard-block-runtime-locked-preview", "not provided"],
|
||||||
["hard-block-runtime-promotion-allowed", "0"],
|
["hard-block-runtime-promotion-allowed", "0"],
|
||||||
["hard-block-runtime-family-count", "3"],
|
["hard-block-runtime-family-count", "2"],
|
||||||
["hard-block-runtime-family-summary", "L4-USER-M-PROCESS:0 locked=yes, L4-TOOL-DB:0 locked=yes, L4-PYTHON-REMAP:0 locked=yes"],
|
["hard-block-runtime-family-summary", "L4-USER-M-PROCESS:0 locked=yes, L4-PYTHON-REMAP:0 locked=yes"],
|
||||||
["hard-block-runtime-detail-row-count", "0"],
|
["hard-block-runtime-detail-row-count", "0"],
|
||||||
["hard-block-runtime-family-L4-USER-M-PROCESS", "0 locked rows; promotion_allowed=0; first=not provided; reason=not provided; next=not provided"],
|
["hard-block-runtime-family-L4-USER-M-PROCESS", "0 locked rows; promotion_allowed=0; first=not provided; reason=not provided; next=not provided"],
|
||||||
["hard-block-runtime-family-L4-TOOL-DB", "0 locked rows; promotion_allowed=0; first=not provided; reason=not provided; next=not provided"],
|
|
||||||
["hard-block-runtime-family-L4-PYTHON-REMAP", "0 locked rows; promotion_allowed=0; first=not provided; reason=not provided; next=not provided"],
|
["hard-block-runtime-family-L4-PYTHON-REMAP", "0 locked rows; promotion_allowed=0; first=not provided; reason=not provided; next=not provided"],
|
||||||
["hard-block-runtime-family-L4-USER-M-PROCESS-paths", "not provided"],
|
["hard-block-runtime-family-L4-USER-M-PROCESS-paths", "not provided"],
|
||||||
["hard-block-runtime-family-L4-TOOL-DB-paths", "not provided"],
|
|
||||||
["hard-block-runtime-family-L4-PYTHON-REMAP-paths", "not provided"],
|
["hard-block-runtime-family-L4-PYTHON-REMAP-paths", "not provided"],
|
||||||
["tool-db-process-proof", "ready"],
|
["tool-db-process-proof", "ready"],
|
||||||
["tool-db-process-proof-detail", "wasm=ready browser=ready opfs=ready promotion_allowed=0"],
|
["tool-db-process-proof-detail", "wasm=ready browser=ready opfs=ready promotion_allowed=0"],
|
||||||
@@ -561,7 +559,7 @@ assert.deepEqual(
|
|||||||
["virtual-hal-promotion-source-file-count", "not provided"],
|
["virtual-hal-promotion-source-file-count", "not provided"],
|
||||||
["virtual-hal-promotion-browser-diagnostics-count", "not provided"],
|
["virtual-hal-promotion-browser-diagnostics-count", "not provided"],
|
||||||
["virtual-hal-promotion-candidate-baseline", "not provided"],
|
["virtual-hal-promotion-candidate-baseline", "not provided"],
|
||||||
["hard-block-runtime-lock", "locked (3 families)"],
|
["hard-block-runtime-lock", "locked (2 families)"],
|
||||||
["promoted-blocked-family-count", "0 promoted"],
|
["promoted-blocked-family-count", "0 promoted"],
|
||||||
["saved-session-diagnostics", "not requested"],
|
["saved-session-diagnostics", "not requested"],
|
||||||
["next-command", "none"],
|
["next-command", "none"],
|
||||||
|
|||||||
211
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.mjs
Normal file
211
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.mjs
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
|
||||||
|
import {
|
||||||
|
createLinuxCncPythonRemapBrowserWorkerAdapter,
|
||||||
|
} from "../../../runtime/sdk/src/python-remap-browser-worker-adapter.js";
|
||||||
|
import {
|
||||||
|
PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION,
|
||||||
|
PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE,
|
||||||
|
createLinuxCncPythonRemapRuntimePort,
|
||||||
|
createPythonRemapLifecyclePlan,
|
||||||
|
createPythonRemapRuntimeDiagnostics,
|
||||||
|
validatePythonRemapLifecycleTranscript,
|
||||||
|
} from "../../../runtime/sdk/src/python-remap-runtime-port.js";
|
||||||
|
|
||||||
|
assert.equal(PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION, 1);
|
||||||
|
assert.equal(PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.fixtureFamily, "axis/remap/stop-lookahead/nc_files");
|
||||||
|
assert.equal(PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.topLevelPath, "python/toplevel.py");
|
||||||
|
assert.equal(PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.callableName, "queuebuster");
|
||||||
|
assert.deepEqual(
|
||||||
|
createPythonRemapLifecyclePlan().map((step) => step.phase),
|
||||||
|
[
|
||||||
|
"initialize_python",
|
||||||
|
"apply_ini_python_path",
|
||||||
|
"execute_toplevel",
|
||||||
|
"import_module",
|
||||||
|
"callable_lookup",
|
||||||
|
"callable_invoke",
|
||||||
|
"remap_phase_dispatch",
|
||||||
|
"generator_finish",
|
||||||
|
"export_interpreter_state",
|
||||||
|
"export_diagnostics",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const contractOnlyPort = createLinuxCncPythonRemapRuntimePort({
|
||||||
|
runtimeMode: "contract-only",
|
||||||
|
});
|
||||||
|
const contractOnlyStart = await contractOnlyPort.start();
|
||||||
|
assert.deepEqual(contractOnlyStart, {
|
||||||
|
runtimeMode: "contract-only",
|
||||||
|
runtimeExecutionReady: false,
|
||||||
|
status: null,
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
bulkPromotionAllowed: false,
|
||||||
|
});
|
||||||
|
const contractOnlyPlan = await contractOnlyPort.runLifecyclePlan();
|
||||||
|
assert.equal(contractOnlyPlan.status, "blocked_runtime_adapter_required");
|
||||||
|
assert.equal(contractOnlyPlan.runtimeExecutionReady, false);
|
||||||
|
assert.equal(contractOnlyPlan.executionEnabled, false);
|
||||||
|
assert.equal(contractOnlyPlan.promotionAllowed, false);
|
||||||
|
assert.equal(contractOnlyPlan.bulkPromotionAllowed, false);
|
||||||
|
assert.equal(contractOnlyPort.exportDiagnostics().lifecycleTranscriptReady, false);
|
||||||
|
await contractOnlyPort.close();
|
||||||
|
|
||||||
|
class FakeBlockedWorker {
|
||||||
|
constructor() {
|
||||||
|
this.listeners = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener(type, listener) {
|
||||||
|
this.listeners.set(type, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
postMessage(message) {
|
||||||
|
queueMicrotask(() => {
|
||||||
|
this.listeners.get("message")?.({
|
||||||
|
data: {
|
||||||
|
id: message.id,
|
||||||
|
type: message.type,
|
||||||
|
ok: true,
|
||||||
|
runtimeExecutionReady: false,
|
||||||
|
status: "blocked_browser_python_wasm_runtime_missing",
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
bulkPromotionAllowed: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
terminate() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeReadyWorker {
|
||||||
|
constructor() {
|
||||||
|
this.listeners = new Map();
|
||||||
|
this.startMessages = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener(type, listener) {
|
||||||
|
this.listeners.set(type, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
postMessage(message) {
|
||||||
|
queueMicrotask(() => {
|
||||||
|
if (message.type === "start") {
|
||||||
|
this.startMessages.push(message);
|
||||||
|
this.reply(message, {
|
||||||
|
runtimeExecutionReady: true,
|
||||||
|
status: "browser_python_remap_wasm_runtime_ready",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const values = {
|
||||||
|
initializePython: "python-runtime-initialized",
|
||||||
|
applyIniPythonPath: message.pythonPathPrepend,
|
||||||
|
executeTopLevel: message.topLevelPath,
|
||||||
|
importModule: message.modulePath,
|
||||||
|
lookupCallable: message.callableName,
|
||||||
|
invokeGenerator: "generator-returned",
|
||||||
|
observeFirstYield: message.expectedYield,
|
||||||
|
finishGenerator: "generator-finished",
|
||||||
|
exportInterpreterState: "interpreter-state-bound",
|
||||||
|
exportDiagnostics: "diagnostics-exported",
|
||||||
|
};
|
||||||
|
this.reply(message, {
|
||||||
|
status: "ok",
|
||||||
|
ready: true,
|
||||||
|
value: values[message.type] ?? null,
|
||||||
|
source: "fake-linuxcnc-python-remap-provider",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reply(message, payload) {
|
||||||
|
this.listeners.get("message")?.({
|
||||||
|
data: {
|
||||||
|
id: message.id,
|
||||||
|
type: message.type,
|
||||||
|
ok: true,
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
bulkPromotionAllowed: false,
|
||||||
|
...payload,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
terminate() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockedWorkerAdapter = createLinuxCncPythonRemapBrowserWorkerAdapter({
|
||||||
|
workerUrl: "./python-remap-worker.js",
|
||||||
|
workerFactory: () => new FakeBlockedWorker(),
|
||||||
|
});
|
||||||
|
const blockedWorkerPort = createLinuxCncPythonRemapRuntimePort({
|
||||||
|
runtimeMode: blockedWorkerAdapter.runtimeMode,
|
||||||
|
runtimeAdapter: blockedWorkerAdapter,
|
||||||
|
});
|
||||||
|
const blockedWorkerStart = await blockedWorkerPort.start();
|
||||||
|
assert.equal(blockedWorkerStart.runtimeMode, "browser-python-wasm-worker");
|
||||||
|
assert.equal(blockedWorkerStart.runtimeExecutionReady, false);
|
||||||
|
assert.equal(blockedWorkerStart.status, "blocked_browser_python_wasm_runtime_missing");
|
||||||
|
const blockedWorkerPlan = await blockedWorkerPort.runLifecyclePlan();
|
||||||
|
assert.equal(blockedWorkerPlan.status, "blocked_browser_python_wasm_runtime_missing");
|
||||||
|
assert.equal(blockedWorkerPlan.promotionAllowed, false);
|
||||||
|
await blockedWorkerPort.close();
|
||||||
|
|
||||||
|
const fakeReadyWorker = new FakeReadyWorker();
|
||||||
|
const readyWorkerAdapter = createLinuxCncPythonRemapBrowserWorkerAdapter({
|
||||||
|
workerUrl: "./python-remap-worker.js",
|
||||||
|
pythonRuntimeModuleUrl: "./python-remap-runtime-provider.js",
|
||||||
|
workerFactory: () => fakeReadyWorker,
|
||||||
|
});
|
||||||
|
const readyWorkerPort = createLinuxCncPythonRemapRuntimePort({
|
||||||
|
runtimeMode: readyWorkerAdapter.runtimeMode,
|
||||||
|
runtimeAdapter: readyWorkerAdapter,
|
||||||
|
});
|
||||||
|
const readyWorkerStart = await readyWorkerPort.start();
|
||||||
|
assert.equal(readyWorkerStart.runtimeExecutionReady, true);
|
||||||
|
assert.equal(readyWorkerStart.status, "browser_python_remap_wasm_runtime_ready");
|
||||||
|
assert.equal(fakeReadyWorker.startMessages[0].pythonRuntimeModuleUrl, "./python-remap-runtime-provider.js");
|
||||||
|
const initializeResult = await readyWorkerPort.initializePython();
|
||||||
|
assert.equal(initializeResult.phase, "initialize_python");
|
||||||
|
assert.equal(initializeResult.value, "python-runtime-initialized");
|
||||||
|
const readyWorkerPlan = await readyWorkerPort.runLifecyclePlan();
|
||||||
|
assert.equal(readyWorkerPlan.status, "runtime_adapter_lifecycle_plan_executed");
|
||||||
|
assert.equal(readyWorkerPlan.runtimeExecutionReady, true);
|
||||||
|
assert.equal(readyWorkerPlan.promotionAllowed, false);
|
||||||
|
assert.equal(readyWorkerPlan.bulkPromotionAllowed, false);
|
||||||
|
const transcript = readyWorkerPort.exportTranscript();
|
||||||
|
assert.equal(transcript.filter((entry) => entry.phase === "initialize_python").length, 2);
|
||||||
|
const validation = validatePythonRemapLifecycleTranscript(transcript);
|
||||||
|
assert.equal(validation.ready, true);
|
||||||
|
assert.equal(validation.lifecycleTranscriptReady, true);
|
||||||
|
assert.equal(validation.firstYield, 2);
|
||||||
|
assert.equal(validation.callableLookupReady, true);
|
||||||
|
assert.equal(validation.generatorLifecycleReady, true);
|
||||||
|
assert.equal(validation.interpreterStateBindingReady, true);
|
||||||
|
const diagnostics = readyWorkerPort.exportDiagnostics();
|
||||||
|
assert.equal(diagnostics.runtimeMode, "browser-python-wasm-worker");
|
||||||
|
assert.equal(diagnostics.runtimeExecutionReady, true);
|
||||||
|
assert.equal(diagnostics.lifecycleTranscriptReady, true);
|
||||||
|
assert.equal(diagnostics.callableLookupReady, true);
|
||||||
|
assert.equal(diagnostics.generatorLifecycleReady, true);
|
||||||
|
assert.equal(diagnostics.interpreterStateBindingReady, true);
|
||||||
|
assert.equal(diagnostics.linuxCncOwnedLifecycle, true);
|
||||||
|
assert.equal(diagnostics.jsCncSemantics, false);
|
||||||
|
assert.equal(diagnostics.ngcOnlySubroutinePromoted, false);
|
||||||
|
assert.equal(diagnostics.executionEnabled, false);
|
||||||
|
assert.equal(diagnostics.promotionAllowed, false);
|
||||||
|
assert.equal(diagnostics.bulkPromotionAllowed, false);
|
||||||
|
await readyWorkerPort.close();
|
||||||
|
|
||||||
|
const explicitDiagnostics = createPythonRemapRuntimeDiagnostics({
|
||||||
|
runtimeMode: "contract-only",
|
||||||
|
transcript,
|
||||||
|
});
|
||||||
|
assert.equal(explicitDiagnostics.transcriptHash.length, 8);
|
||||||
|
assert.equal(explicitDiagnostics.lifecycleTranscriptReady, true);
|
||||||
6
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.sh
Executable file
6
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||||
|
|
||||||
|
node "$ROOT_DIR/tests/sdk/node/verify_python_remap_runtime_port.mjs"
|
||||||
@@ -92,9 +92,14 @@ import {
|
|||||||
createProjectReleaseReadinessSummaryViewModel,
|
createProjectReleaseReadinessSummaryViewModel,
|
||||||
createLinuxCncToolDbBrowserWorkerAdapter,
|
createLinuxCncToolDbBrowserWorkerAdapter,
|
||||||
createLinuxCncToolDbProcessPort,
|
createLinuxCncToolDbProcessPort,
|
||||||
|
createLinuxCncPythonRemapBrowserWorkerAdapter,
|
||||||
|
createLinuxCncPythonRemapRuntimePort,
|
||||||
|
createPythonRemapLifecyclePlan,
|
||||||
|
createPythonRemapRuntimeDiagnostics,
|
||||||
createToolDbProcessDiagnostics,
|
createToolDbProcessDiagnostics,
|
||||||
createToolDbTransactionPlan,
|
createToolDbTransactionPlan,
|
||||||
runToolDbProcessPortPersistenceSession,
|
runToolDbProcessPortPersistenceSession,
|
||||||
|
validatePythonRemapLifecycleTranscript,
|
||||||
createVirtualHalBridgeActionPlan,
|
createVirtualHalBridgeActionPlan,
|
||||||
createVirtualHalBridgeReadiness,
|
createVirtualHalBridgeReadiness,
|
||||||
createVirtualHalCommandScriptFixtureReport,
|
createVirtualHalCommandScriptFixtureReport,
|
||||||
@@ -230,16 +235,16 @@ const expectedPromotionCandidateArtifactSummary = {
|
|||||||
evidenceExpansionArtifactPath: "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv",
|
evidenceExpansionArtifactPath: "wasm-port/build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv",
|
||||||
layerCount: 2,
|
layerCount: 2,
|
||||||
evidenceReadyCount: 8,
|
evidenceReadyCount: 8,
|
||||||
inventoryReadyCount: 20,
|
inventoryReadyCount: 19,
|
||||||
evidenceExpansionCandidateCount: 13,
|
evidenceExpansionCandidateCount: 14,
|
||||||
totalCandidateCount: 28,
|
totalCandidateCount: 27,
|
||||||
promotionAllowedCount: 0,
|
promotionAllowedCount: 0,
|
||||||
hardBlockRuntimeLock: "locked (3 families)",
|
hardBlockRuntimeLock: "locked (2 families)",
|
||||||
hardBlockRuntimePromotionAllowedCount: 0,
|
hardBlockRuntimePromotionAllowedCount: 0,
|
||||||
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
|
inventoryBaseline: "executed=29 passed=29 skipped=130 unexpected_fail=0",
|
||||||
artifactRows: {
|
artifactRows: {
|
||||||
promotionCandidates: 28,
|
promotionCandidates: 27,
|
||||||
evidenceExpansion: 13,
|
evidenceExpansion: 14,
|
||||||
},
|
},
|
||||||
hardBlockRuntimeFamilyRows: [],
|
hardBlockRuntimeFamilyRows: [],
|
||||||
hardBlockRuntimeFamilyDetailRows: [],
|
hardBlockRuntimeFamilyDetailRows: [],
|
||||||
@@ -254,16 +259,6 @@ const expectedPromotionCandidateArtifactSummary = {
|
|||||||
firstBlockReason: null,
|
firstBlockReason: null,
|
||||||
nextProof: null,
|
nextProof: null,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
family: "L4-TOOL-DB",
|
|
||||||
candidateCount: 0,
|
|
||||||
promotionAllowedCount: 0,
|
|
||||||
locked: true,
|
|
||||||
lockedPaths: [],
|
|
||||||
firstPath: null,
|
|
||||||
firstBlockReason: null,
|
|
||||||
nextProof: null,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
family: "L4-PYTHON-REMAP",
|
family: "L4-PYTHON-REMAP",
|
||||||
candidateCount: 0,
|
candidateCount: 0,
|
||||||
@@ -285,7 +280,7 @@ const expectedPromotionCandidateArtifactSummary = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "inventory-ready",
|
id: "inventory-ready",
|
||||||
candidateCount: 20,
|
candidateCount: 19,
|
||||||
promotionAllowedCount: 0,
|
promotionAllowedCount: 0,
|
||||||
virtualHalEvidenceReady: false,
|
virtualHalEvidenceReady: false,
|
||||||
baselineChanging: false,
|
baselineChanging: false,
|
||||||
@@ -293,8 +288,8 @@ const expectedPromotionCandidateArtifactSummary = {
|
|||||||
],
|
],
|
||||||
evidenceExpansion: {
|
evidenceExpansion: {
|
||||||
id: "browser-diagnostics-expansion",
|
id: "browser-diagnostics-expansion",
|
||||||
candidateCount: 13,
|
candidateCount: 14,
|
||||||
artifactRowCount: 13,
|
artifactRowCount: 14,
|
||||||
promotionAllowedCount: 0,
|
promotionAllowedCount: 0,
|
||||||
baselineChanging: false,
|
baselineChanging: false,
|
||||||
nextEvidence: "browser-diagnostics-binding",
|
nextEvidence: "browser-diagnostics-binding",
|
||||||
@@ -525,9 +520,14 @@ const requiredExports = [
|
|||||||
["gcodeFilenameFromProgramPath", gcodeFilenameFromProgramPath],
|
["gcodeFilenameFromProgramPath", gcodeFilenameFromProgramPath],
|
||||||
["createLinuxCncToolDbBrowserWorkerAdapter", createLinuxCncToolDbBrowserWorkerAdapter],
|
["createLinuxCncToolDbBrowserWorkerAdapter", createLinuxCncToolDbBrowserWorkerAdapter],
|
||||||
["createLinuxCncToolDbProcessPort", createLinuxCncToolDbProcessPort],
|
["createLinuxCncToolDbProcessPort", createLinuxCncToolDbProcessPort],
|
||||||
|
["createLinuxCncPythonRemapBrowserWorkerAdapter", createLinuxCncPythonRemapBrowserWorkerAdapter],
|
||||||
|
["createLinuxCncPythonRemapRuntimePort", createLinuxCncPythonRemapRuntimePort],
|
||||||
|
["createPythonRemapLifecyclePlan", createPythonRemapLifecyclePlan],
|
||||||
|
["createPythonRemapRuntimeDiagnostics", createPythonRemapRuntimeDiagnostics],
|
||||||
["createToolDbProcessDiagnostics", createToolDbProcessDiagnostics],
|
["createToolDbProcessDiagnostics", createToolDbProcessDiagnostics],
|
||||||
["createToolDbTransactionPlan", createToolDbTransactionPlan],
|
["createToolDbTransactionPlan", createToolDbTransactionPlan],
|
||||||
["runToolDbProcessPortPersistenceSession", runToolDbProcessPortPersistenceSession],
|
["runToolDbProcessPortPersistenceSession", runToolDbProcessPortPersistenceSession],
|
||||||
|
["validatePythonRemapLifecycleTranscript", validatePythonRemapLifecycleTranscript],
|
||||||
["validateToolDbTranscript", validateToolDbTranscript],
|
["validateToolDbTranscript", validateToolDbTranscript],
|
||||||
["saveToolDbFile", saveToolDbFile],
|
["saveToolDbFile", saveToolDbFile],
|
||||||
["loadToolDbFile", loadToolDbFile],
|
["loadToolDbFile", loadToolDbFile],
|
||||||
@@ -1053,16 +1053,16 @@ assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_FIXTURES.length >= 2, true);
|
|||||||
assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES.length >= 2, true);
|
assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_BLOCKED_FIXTURES.length >= 2, true);
|
||||||
assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES.length >= 2, true);
|
assert.equal(VIRTUAL_HAL_SIM_CONFIG_MACRO_LOAD_AUDIT_CANDIDATES.length >= 2, true);
|
||||||
assert.deepEqual(VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE, {
|
assert.deepEqual(VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE, {
|
||||||
executed: 28,
|
executed: 29,
|
||||||
passed: 28,
|
passed: 29,
|
||||||
skipped: 131,
|
skipped: 130,
|
||||||
unexpectedFail: 0,
|
unexpectedFail: 0,
|
||||||
});
|
});
|
||||||
assert.deepEqual(VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES, {
|
assert.deepEqual(VIRTUAL_HAL_SIM_CONFIG_INVENTORY_ARTIFACT_HASHES, {
|
||||||
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv":
|
"wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv":
|
||||||
"de6cf57b7c07182e3bcb32e22dbdf202b618cabc14620d6dff1ef815587950b9",
|
"6cc15052cad03d04eff4506cd0a9d651cd45a2bd5da4d9f38633e9535826b9f3",
|
||||||
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv":
|
"wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv":
|
||||||
"b0afe27224e97a82fbecbbd75a7c86233c98fe957d9c1ba20f0656f8745a5eae",
|
"17bff95b57c55ff4201a23c7286e733550e4df513470c66dd5f24329e7781a35",
|
||||||
});
|
});
|
||||||
const simConfigMacroLoadFixtureReport = createVirtualHalSimConfigMacroLoadFixtureReport({
|
const simConfigMacroLoadFixtureReport = createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||||
manifestText: sourceManifestText,
|
manifestText: sourceManifestText,
|
||||||
@@ -1250,7 +1250,7 @@ assert.equal(createVirtualHalSimConfigMacroLoadFixtureReport({
|
|||||||
).boundaryEvidence,
|
).boundaryEvidence,
|
||||||
baselineSummary: {
|
baselineSummary: {
|
||||||
...VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
|
...VIRTUAL_HAL_SIM_CONFIG_INVENTORY_BASELINE,
|
||||||
skipped: 130,
|
skipped: 131,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1395,7 +1395,7 @@ const browserDiagnosticsArtifact = {
|
|||||||
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||||
blockedCandidateIds: [],
|
blockedCandidateIds: [],
|
||||||
explicitBrowserDiagnosticsCount: 8,
|
explicitBrowserDiagnosticsCount: 8,
|
||||||
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
|
inventoryBaseline: "executed=29 passed=29 skipped=130 unexpected_fail=0",
|
||||||
},
|
},
|
||||||
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
|
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
|
||||||
virtualHalCommandScriptFixtures: commandScriptFixtureReport,
|
virtualHalCommandScriptFixtures: commandScriptFixtureReport,
|
||||||
@@ -1418,7 +1418,7 @@ browserDiagnosticsArtifact.virtualHalSessionDiagnostics = {
|
|||||||
};
|
};
|
||||||
const browserDiagnosticsArtifactValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsArtifact);
|
const browserDiagnosticsArtifactValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsArtifact);
|
||||||
assert.equal(browserDiagnosticsArtifactValidation.apiName, "project-release-browser-diagnostics-artifact-validation");
|
assert.equal(browserDiagnosticsArtifactValidation.apiName, "project-release-browser-diagnostics-artifact-validation");
|
||||||
assert.equal(browserDiagnosticsArtifactValidation.ready, true);
|
assert.equal(browserDiagnosticsArtifactValidation.ready, true, JSON.stringify(browserDiagnosticsArtifactValidation));
|
||||||
assert.equal(browserDiagnosticsArtifactValidation.sourceComplianceReady, true);
|
assert.equal(browserDiagnosticsArtifactValidation.sourceComplianceReady, true);
|
||||||
assert.equal(browserDiagnosticsArtifactValidation.simConfigSourceCoverageReady, true);
|
assert.equal(browserDiagnosticsArtifactValidation.simConfigSourceCoverageReady, true);
|
||||||
assert.equal(browserDiagnosticsArtifactValidation.promotionCandidatesReady, true);
|
assert.equal(browserDiagnosticsArtifactValidation.promotionCandidatesReady, true);
|
||||||
@@ -1629,11 +1629,11 @@ assert.equal(releaseReadinessWaiting.gateActionPlan.pendingCount, createProjectR
|
|||||||
assert.equal(releaseReadinessWaiting.gateActionPlan.nextGateId, "diff-check");
|
assert.equal(releaseReadinessWaiting.gateActionPlan.nextGateId, "diff-check");
|
||||||
assert.equal(
|
assert.equal(
|
||||||
releaseReadinessWaiting.rows.find(({ id }) => id === "blocked-runtime-families")?.value,
|
releaseReadinessWaiting.rows.find(({ id }) => id === "blocked-runtime-families")?.value,
|
||||||
"L4-USER-M-PROCESS, L4-TOOL-DB, L4-PYTHON-REMAP",
|
"L4-USER-M-PROCESS, L4-PYTHON-REMAP",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
releaseReadinessWaiting.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
|
releaseReadinessWaiting.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
|
||||||
"locked (3 families)",
|
"locked (2 families)",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
releaseReadinessWaiting.rows.find(({ id }) => id === "promoted-blocked-family-count")?.value,
|
releaseReadinessWaiting.rows.find(({ id }) => id === "promoted-blocked-family-count")?.value,
|
||||||
@@ -1657,11 +1657,11 @@ assert.deepEqual(
|
|||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
releaseReadinessReady.rows.find(({ id }) => id === "promotion-candidate-artifact-rows")?.value,
|
releaseReadinessReady.rows.find(({ id }) => id === "promotion-candidate-artifact-rows")?.value,
|
||||||
"28",
|
"27",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
releaseReadinessReady.rows.find(({ id }) => id === "evidence-expansion-artifact-rows")?.value,
|
releaseReadinessReady.rows.find(({ id }) => id === "evidence-expansion-artifact-rows")?.value,
|
||||||
"13",
|
"14",
|
||||||
);
|
);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
releaseReadinessReady.virtualHalPromotionFamilyRows.map(({ id, value }) => [
|
releaseReadinessReady.virtualHalPromotionFamilyRows.map(({ id, value }) => [
|
||||||
@@ -1713,7 +1713,7 @@ assert.equal(
|
|||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
createProjectReleaseReadinessSummaryViewModel(releaseReadinessReady).rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
|
createProjectReleaseReadinessSummaryViewModel(releaseReadinessReady).rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
|
||||||
"evidence-ready=8 inventory-ready=20",
|
"evidence-ready=8 inventory-ready=19",
|
||||||
);
|
);
|
||||||
const releaseReadinessWithCandidateArtifacts = createProjectReleaseReadinessReport({
|
const releaseReadinessWithCandidateArtifacts = createProjectReleaseReadinessReport({
|
||||||
observedOutputs: ["project_release_gate=ok"],
|
observedOutputs: ["project_release_gate=ok"],
|
||||||
@@ -1724,11 +1724,11 @@ const releaseReadinessWithCandidateArtifacts = createProjectReleaseReadinessRepo
|
|||||||
promotionCandidateRows: promotionCandidateArtifactRows,
|
promotionCandidateRows: promotionCandidateArtifactRows,
|
||||||
evidenceExpansionRows: evidenceExpansionArtifactRows,
|
evidenceExpansionRows: evidenceExpansionArtifactRows,
|
||||||
});
|
});
|
||||||
assert.equal(releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.artifactRows.promotionCandidates, 28);
|
assert.equal(releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.artifactRows.promotionCandidates, 27);
|
||||||
assert.equal(releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.artifactRows.evidenceExpansion, 13);
|
assert.equal(releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.artifactRows.evidenceExpansion, 14);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimeFamilyRows.length,
|
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimeFamilyRows.length,
|
||||||
19,
|
18,
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimePromotionAllowedCount,
|
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimePromotionAllowedCount,
|
||||||
@@ -1736,8 +1736,8 @@ assert.equal(
|
|||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimeFamilyRows
|
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimeFamilyRows
|
||||||
.some((row) => row.skipKind === "L4-TOOL-DB" && row.promotionAllowed === false),
|
.some((row) => row.skipKind === "L4-TOOL-DB"),
|
||||||
true,
|
false,
|
||||||
);
|
);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimeFamilySummaryRows
|
releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.hardBlockRuntimeFamilySummaryRows
|
||||||
@@ -1749,7 +1749,6 @@ assert.deepEqual(
|
|||||||
]),
|
]),
|
||||||
[
|
[
|
||||||
["L4-USER-M-PROCESS", 1, 0, true],
|
["L4-USER-M-PROCESS", 1, 0, true],
|
||||||
["L4-TOOL-DB", 1, 0, true],
|
|
||||||
["L4-PYTHON-REMAP", 17, 0, true],
|
["L4-PYTHON-REMAP", 17, 0, true],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -1764,13 +1763,6 @@ assert.deepEqual(
|
|||||||
promotionAllowed,
|
promotionAllowed,
|
||||||
]),
|
]),
|
||||||
[
|
[
|
||||||
[
|
|
||||||
"L4-TOOL-DB",
|
|
||||||
"axis/db_demo/base.ngc",
|
|
||||||
"design_tooldata_db_protocol_boundary",
|
|
||||||
"db_program_v2_1_handshake_getall_load_unload_or_put",
|
|
||||||
false,
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
"L4-PYTHON-REMAP",
|
"L4-PYTHON-REMAP",
|
||||||
"axis/laser/raster_test.ngc",
|
"axis/laser/raster_test.ngc",
|
||||||
@@ -1785,6 +1777,13 @@ assert.deepEqual(
|
|||||||
"python_runtime_owner_and_fixture",
|
"python_runtime_owner_and_fixture",
|
||||||
false,
|
false,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"L4-PYTHON-REMAP",
|
||||||
|
"axis/laser/vector_test2.ngc",
|
||||||
|
"design_linuxcnc_python_runtime_boundary",
|
||||||
|
"python_runtime_owner_and_fixture",
|
||||||
|
false,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
const releaseReadinessWithScreenshots = createProjectReleaseReadinessReport({
|
const releaseReadinessWithScreenshots = createProjectReleaseReadinessReport({
|
||||||
@@ -1863,7 +1862,6 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
|
|||||||
axisScreenshotArtifactCount: 0,
|
axisScreenshotArtifactCount: 0,
|
||||||
blockedRuntimeFamilies: [
|
blockedRuntimeFamilies: [
|
||||||
"L4-USER-M-PROCESS",
|
"L4-USER-M-PROCESS",
|
||||||
"L4-TOOL-DB",
|
|
||||||
"L4-PYTHON-REMAP",
|
"L4-PYTHON-REMAP",
|
||||||
],
|
],
|
||||||
rows: [
|
rows: [
|
||||||
@@ -1920,27 +1918,27 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
|
|||||||
{
|
{
|
||||||
id: "promotion-candidate-layers",
|
id: "promotion-candidate-layers",
|
||||||
label: "Promotion candidate layers",
|
label: "Promotion candidate layers",
|
||||||
value: "evidence-ready=8 inventory-ready=20",
|
value: "evidence-ready=8 inventory-ready=19",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "promotion-candidate-total",
|
id: "promotion-candidate-total",
|
||||||
label: "Promotion candidate total",
|
label: "Promotion candidate total",
|
||||||
value: "28",
|
value: "27",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "promotion-candidate-artifact-rows",
|
id: "promotion-candidate-artifact-rows",
|
||||||
label: "Promotion candidate artifact rows",
|
label: "Promotion candidate artifact rows",
|
||||||
value: "28",
|
value: "27",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "evidence-expansion-candidates",
|
id: "evidence-expansion-candidates",
|
||||||
label: "Evidence expansion candidates",
|
label: "Evidence expansion candidates",
|
||||||
value: "13",
|
value: "14",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "evidence-expansion-artifact-rows",
|
id: "evidence-expansion-artifact-rows",
|
||||||
label: "Evidence expansion artifact rows",
|
label: "Evidence expansion artifact rows",
|
||||||
value: "13",
|
value: "14",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "evidence-expansion-next-evidence",
|
id: "evidence-expansion-next-evidence",
|
||||||
@@ -1998,12 +1996,12 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
|
|||||||
{
|
{
|
||||||
id: "blocked-runtime-families",
|
id: "blocked-runtime-families",
|
||||||
label: "Blocked runtime families",
|
label: "Blocked runtime families",
|
||||||
value: "L4-USER-M-PROCESS, L4-TOOL-DB, L4-PYTHON-REMAP",
|
value: "L4-USER-M-PROCESS, L4-PYTHON-REMAP",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "hard-block-runtime-lock",
|
id: "hard-block-runtime-lock",
|
||||||
label: "Hard-block runtime lock",
|
label: "Hard-block runtime lock",
|
||||||
value: "locked (3 families)",
|
value: "locked (2 families)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "promoted-blocked-family-count",
|
id: "promoted-blocked-family-count",
|
||||||
@@ -2109,7 +2107,7 @@ assert.equal(
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
).rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
|
).rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
|
||||||
"evidence-ready=8 inventory-ready=20",
|
"evidence-ready=8 inventory-ready=19",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||||
@@ -2138,7 +2136,7 @@ assert.equal(
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
).rows.find(({ id }) => id === "evidence-expansion-candidates")?.value,
|
).rows.find(({ id }) => id === "evidence-expansion-candidates")?.value,
|
||||||
"13",
|
"14",
|
||||||
);
|
);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||||
@@ -2290,14 +2288,14 @@ assert.equal(createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadi
|
|||||||
assert.equal(
|
assert.equal(
|
||||||
createProjectReleaseReadinessReport({
|
createProjectReleaseReadinessReport({
|
||||||
observedOutputs: ["project_release_gate=ok"],
|
observedOutputs: ["project_release_gate=ok"],
|
||||||
promotedRuntimeFamilies: ["L4-TOOL-DB"],
|
promotedRuntimeFamilies: ["L4-PYTHON-REMAP"],
|
||||||
}).missing.includes("blocked-runtime-families"),
|
}).missing.includes("blocked-runtime-families"),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport({
|
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport({
|
||||||
observedOutputs: ["project_release_gate=ok"],
|
observedOutputs: ["project_release_gate=ok"],
|
||||||
promotedRuntimeFamilies: ["L4-TOOL-DB"],
|
promotedRuntimeFamilies: ["L4-PYTHON-REMAP"],
|
||||||
})).rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
|
})).rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
|
||||||
"violated (1 promoted)",
|
"violated (1 promoted)",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1507,7 +1507,7 @@ const browserDiagnosticsArtifact = {
|
|||||||
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||||
blockedCandidateIds: [],
|
blockedCandidateIds: [],
|
||||||
explicitBrowserDiagnosticsCount: 8,
|
explicitBrowserDiagnosticsCount: 8,
|
||||||
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
|
inventoryBaseline: "executed=29 passed=29 skipped=130 unexpected_fail=0",
|
||||||
},
|
},
|
||||||
virtualHalSimConfigMacroLoadFixtures: releaseReadinessMacroLoadFixtures,
|
virtualHalSimConfigMacroLoadFixtures: releaseReadinessMacroLoadFixtures,
|
||||||
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: browserDiagnosticsVirtualHalState }),
|
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: browserDiagnosticsVirtualHalState }),
|
||||||
@@ -2073,7 +2073,7 @@ assert.equal(
|
|||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
|
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
|
||||||
"executed=28 passed=28 skipped=131 unexpected_fail=0",
|
"executed=29 passed=29 skipped=130 unexpected_fail=0",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
|
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import { resolve } from "node:path";
|
||||||
|
|
||||||
|
import {
|
||||||
|
PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE,
|
||||||
|
createLinuxCncPythonRemapRuntimePort,
|
||||||
|
createPythonRemapLifecyclePlan,
|
||||||
|
createPythonRemapRuntimeDiagnostics,
|
||||||
|
validatePythonRemapLifecycleTranscript,
|
||||||
|
} from "../../../runtime/sdk/src/python-remap-runtime-port.js";
|
||||||
|
|
||||||
|
const iniText = readFileSync(
|
||||||
|
resolve("linuxcnc/configs/sim/axis/remap/stop-lookahead/demo.ini"),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
assert.match(iniText, /\[PYTHON\]/);
|
||||||
|
assert.match(iniText, /PATH_PREPEND\s*=\s*python/);
|
||||||
|
assert.match(iniText, /TOPLEVEL\s*=\s*python\/toplevel\.py/);
|
||||||
|
|
||||||
|
const plan = createPythonRemapLifecyclePlan();
|
||||||
|
assert.equal(plan.length, 10);
|
||||||
|
assert.equal(plan[0].phase, "initialize_python");
|
||||||
|
assert.equal(plan.some((step) => step.phase === "callable_lookup" && step.callableName === "queuebuster"), true);
|
||||||
|
assert.equal(plan.some((step) => step.phase === "remap_phase_dispatch" && step.expectedYield === 2), true);
|
||||||
|
|
||||||
|
const port = createLinuxCncPythonRemapRuntimePort({
|
||||||
|
fixtureFamily: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.fixtureFamily,
|
||||||
|
iniPath: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.iniPath,
|
||||||
|
pythonPathPrepend: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.pythonPathPrepend,
|
||||||
|
topLevelPath: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.topLevelPath,
|
||||||
|
sourceFiles: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.modules,
|
||||||
|
runtimeMode: "contract-only",
|
||||||
|
});
|
||||||
|
assert.equal((await port.start()).runtimeExecutionReady, false);
|
||||||
|
assert.equal((await port.runLifecyclePlan()).status, "blocked_runtime_adapter_required");
|
||||||
|
assert.equal(port.exportDiagnostics().promotionAllowed, false);
|
||||||
|
|
||||||
|
const transcript = [
|
||||||
|
{ phase: "initialize_python", status: "ok", value: "python-runtime-initialized", ready: true },
|
||||||
|
{ phase: "apply_ini_python_path", status: "ok", value: "python", ready: true },
|
||||||
|
{ phase: "execute_toplevel", status: "ok", value: "python/toplevel.py", ready: true },
|
||||||
|
{ phase: "import_module", status: "ok", value: "python/remap.py", ready: true },
|
||||||
|
{ phase: "callable_lookup", status: "ok", value: "queuebuster", ready: true },
|
||||||
|
{ phase: "callable_invoke", status: "ok", value: "generator-returned", ready: true },
|
||||||
|
{ phase: "remap_phase_dispatch", status: "ok", value: 2, ready: true },
|
||||||
|
{ phase: "generator_finish", status: "ok", value: "generator-finished", ready: true },
|
||||||
|
{ phase: "export_interpreter_state", status: "ok", value: "interpreter-state-bound", ready: true },
|
||||||
|
{ phase: "export_diagnostics", status: "ok", value: "diagnostics-exported", ready: true },
|
||||||
|
];
|
||||||
|
assert.equal(validatePythonRemapLifecycleTranscript(transcript).ready, true);
|
||||||
|
const diagnostics = createPythonRemapRuntimeDiagnostics({
|
||||||
|
runtimeMode: "contract-only",
|
||||||
|
transcript,
|
||||||
|
});
|
||||||
|
assert.equal(diagnostics.fixtureFamily, "axis/remap/stop-lookahead/nc_files");
|
||||||
|
assert.equal(diagnostics.lifecycleTranscriptReady, true);
|
||||||
|
assert.equal(diagnostics.callableLookupReady, true);
|
||||||
|
assert.equal(diagnostics.generatorLifecycleReady, true);
|
||||||
|
assert.equal(diagnostics.interpreterStateBindingReady, true);
|
||||||
|
assert.equal(diagnostics.jsCncSemantics, false);
|
||||||
|
assert.equal(diagnostics.ngcOnlySubroutinePromoted, false);
|
||||||
|
assert.equal(diagnostics.executionEnabled, false);
|
||||||
|
assert.equal(diagnostics.promotionAllowed, false);
|
||||||
|
assert.equal(diagnostics.bulkPromotionAllowed, false);
|
||||||
|
|
||||||
|
console.log("python_remap_runtime_port_wasm=ok");
|
||||||
7
wasm-port/tests/wasm/node/verify_python_remap_runtime_port_wasm.sh
Executable file
7
wasm-port/tests/wasm/node/verify_python_remap_runtime_port_wasm.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||||
|
|
||||||
|
cd "$ROOT_DIR/.."
|
||||||
|
node "$ROOT_DIR/tests/wasm/node/verify_python_remap_runtime_port_wasm.mjs"
|
||||||
@@ -556,12 +556,12 @@ function verifyTrackedBaselineText(markdownText, nativeRecords, inventory, skipR
|
|||||||
"tracked matrix Node executed drift",
|
"tracked matrix Node executed drift",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
numberFromTrackedBaseline(markdownText, /`passed ([0-9]+)`/, "Node passed"),
|
numberFromTrackedBaseline(markdownText, /Current Node inventory: `executed [0-9]+`, `passed ([0-9]+)`/, "Node passed"),
|
||||||
inventory.passed,
|
inventory.passed,
|
||||||
"tracked matrix Node passed drift",
|
"tracked matrix Node passed drift",
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
numberFromTrackedBaseline(markdownText, /`skipped ([0-9]+)`/, "Node skipped"),
|
numberFromTrackedBaseline(markdownText, /Current Node inventory: `executed [0-9]+`, `passed [0-9]+`, `skipped ([0-9]+)`/, "Node skipped"),
|
||||||
inventory.skipped,
|
inventory.skipped,
|
||||||
"tracked matrix Node skipped drift",
|
"tracked matrix Node skipped drift",
|
||||||
);
|
);
|
||||||
@@ -605,7 +605,6 @@ function verifyRequiredClassRepresentatives(records) {
|
|||||||
"gmoccapy/macros/on_abort.ngc",
|
"gmoccapy/macros/on_abort.ngc",
|
||||||
];
|
];
|
||||||
const requiredFullProcessBlocked = new Map([
|
const requiredFullProcessBlocked = new Map([
|
||||||
["axis/db_demo/base.ngc", "L4-TOOL-DB"],
|
|
||||||
["axis/vismach/millturn/example.ngc", "L4-USER-M-PROCESS"],
|
["axis/vismach/millturn/example.ngc", "L4-USER-M-PROCESS"],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -2937,13 +2936,13 @@ function linuxCncOwnerForBlockedKind(blocked) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function blockedDependencySummaryRow(record, pathMatrixByPath) {
|
function blockedDependencySummaryRow(record, pathMatrixByPath) {
|
||||||
const blocked = pathMatrixByPath.get(record.path)?.blocked ?? "-";
|
const matrixBlocked = pathMatrixByPath.get(record.path)?.blocked ?? "-";
|
||||||
const sourceAvailable = sourceConfigPathExists(record.ini);
|
const sourceAvailable = sourceConfigPathExists(record.ini);
|
||||||
if (!sourceAvailable) {
|
if (!sourceAvailable) {
|
||||||
return [
|
return [
|
||||||
record.path,
|
record.path,
|
||||||
record.ini,
|
record.ini,
|
||||||
blocked,
|
matrixBlocked,
|
||||||
posix.dirname(record.path),
|
posix.dirname(record.path),
|
||||||
"0",
|
"0",
|
||||||
"-",
|
"-",
|
||||||
@@ -2993,6 +2992,7 @@ function blockedDependencySummaryRow(record, pathMatrixByPath) {
|
|||||||
const uiProcess = displayValues.some((value) => !/^axis$/i.test(value));
|
const uiProcess = displayValues.some((value) => !/^axis$/i.test(value));
|
||||||
const haluiMdiProcess = iniValues(entries, "HALUI", "MDI_COMMAND").length > 0;
|
const haluiMdiProcess = iniValues(entries, "HALUI", "MDI_COMMAND").length > 0;
|
||||||
const dbProgram = iniValues(entries, "EMCIO", "DB_PROGRAM")[0] ?? "";
|
const dbProgram = iniValues(entries, "EMCIO", "DB_PROGRAM")[0] ?? "";
|
||||||
|
const blocked = matrixBlocked === "-" && dbProgram ? "L4-TOOL-DB" : matrixBlocked;
|
||||||
const toolDbEvidence = toolDbProtocolEvidence({ iniText, dbProgram });
|
const toolDbEvidence = toolDbProtocolEvidence({ iniText, dbProgram });
|
||||||
const userMCodes = uniqueSorted(sourceExecutionTextsForBlocked(record).flatMap(userMCodesInExecutionText));
|
const userMCodes = uniqueSorted(sourceExecutionTextsForBlocked(record).flatMap(userMCodesInExecutionText));
|
||||||
const userMProcessFiles = userMProcessFilesForCodes(record.ini, entries, userMCodes);
|
const userMProcessFiles = userMProcessFilesForCodes(record.ini, entries, userMCodes);
|
||||||
@@ -3195,31 +3195,6 @@ function verifyFullProcessBoundaryDesign(markdownText, rows) {
|
|||||||
"`USER_M_COMMAND` event alone is not sufficient",
|
"`USER_M_COMMAND` event alone is not sufficient",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "axis/db_demo/base.ngc",
|
|
||||||
blocked: "L4-TOOL-DB",
|
|
||||||
tokens: [
|
|
||||||
"Tool Database Boundary",
|
|
||||||
"configs/sim/axis/db_demo/db_nonran.ini",
|
|
||||||
"configs/sim/axis/db_demo/db.py",
|
|
||||||
"src/emc/task/taskclass.cc",
|
|
||||||
"src/emc/tooldata/tooldata_db.cc",
|
|
||||||
"src/emc/tooldata/tooldata_common.cc",
|
|
||||||
"tool-db-process-boundary-summary.tsv",
|
|
||||||
"v2.1",
|
|
||||||
"`g`, `l`, `u`, and `p`",
|
|
||||||
"user_get_tool",
|
|
||||||
"user_put_tool",
|
|
||||||
"user_load_spindle_nonran_tc",
|
|
||||||
"user_unload_spindle_nonran_tc",
|
|
||||||
"T10..T19",
|
|
||||||
"/tmp/db_nonran_file",
|
|
||||||
"tno+100",
|
|
||||||
"promotion_allowed=0",
|
|
||||||
"does not replace `DB_PROGRAM`",
|
|
||||||
"fallback `.tbl`",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const required of requiredRows) {
|
for (const required of requiredRows) {
|
||||||
@@ -3256,7 +3231,6 @@ function verifyFullProcessBoundaryDesign(markdownText, rows) {
|
|||||||
"native-proof-alignment-summary.tsv",
|
"native-proof-alignment-summary.tsv",
|
||||||
"consumed by generated worklist and native proof-gate",
|
"consumed by generated worklist and native proof-gate",
|
||||||
"`external_user_m_process` for `axis/vismach/millturn/example.ngc`",
|
"`external_user_m_process` for `axis/vismach/millturn/example.ngc`",
|
||||||
"`tool_database_process` for `axis/db_demo/base.ngc`",
|
|
||||||
"Python runtime family rows follow as inventory-only targets",
|
"Python runtime family rows follow as inventory-only targets",
|
||||||
]) {
|
]) {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
@@ -3268,6 +3242,19 @@ function verifyFullProcessBoundaryDesign(markdownText, rows) {
|
|||||||
markdownText.includes("Do not implement HAL, task, HALUI, Tcl, Python, tool-database, or external"),
|
markdownText.includes("Do not implement HAL, task, HALUI, Tcl, Python, tool-database, or external"),
|
||||||
"full-process boundary design must preserve non-goal guard",
|
"full-process boundary design must preserve non-goal guard",
|
||||||
);
|
);
|
||||||
|
for (const token of [
|
||||||
|
"`axis/db_demo/base.ngc`",
|
||||||
|
"Tool Database Boundary",
|
||||||
|
"runtime_protocol_probe_passed",
|
||||||
|
"tool_db_process_proof=ok",
|
||||||
|
"does not replace `DB_PROGRAM`",
|
||||||
|
"fallback `.tbl`",
|
||||||
|
]) {
|
||||||
|
assert.ok(
|
||||||
|
markdownText.includes(token),
|
||||||
|
`full-process boundary design missing completed tool DB proof token ${token}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fullProcessBoundarySummaryRow(row) {
|
function fullProcessBoundarySummaryRow(row) {
|
||||||
@@ -5240,6 +5227,14 @@ function boundaryPhaseCompletionSummaryRows({
|
|||||||
const blockedFamiliesUnpromotedOk = hardBlockedRows.every((row) => {
|
const blockedFamiliesUnpromotedOk = hardBlockedRows.every((row) => {
|
||||||
const tracked = trackedByPath.get(row.path);
|
const tracked = trackedByPath.get(row.path);
|
||||||
const inventory = inventoryByPath.get(row.path);
|
const inventory = inventoryByPath.get(row.path);
|
||||||
|
if (
|
||||||
|
row.blocked === "L4-TOOL-DB" &&
|
||||||
|
row.path === "axis/db_demo/base.ngc" &&
|
||||||
|
tracked?.layer4Node === "INV" &&
|
||||||
|
inventory?.inventoryStatus === "PASS"
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
tracked &&
|
tracked &&
|
||||||
inventory &&
|
inventory &&
|
||||||
@@ -5554,15 +5549,15 @@ function boundaryPhaseCompletionSummaryRows({
|
|||||||
const runtimeBoundaryPostNativePassGatesOk = (
|
const runtimeBoundaryPostNativePassGatesOk = (
|
||||||
runtimeBoundaryPostNativePassGateRows.length === 3 &&
|
runtimeBoundaryPostNativePassGateRows.length === 3 &&
|
||||||
runtimeBoundaryPostNativePassGateRows.every((row) =>
|
runtimeBoundaryPostNativePassGateRows.every((row) =>
|
||||||
row.native_pass_ready === "0" &&
|
["0", "1"].includes(row.native_pass_ready) &&
|
||||||
row.native_evidence_ready === "0" &&
|
["0", "1"].includes(row.native_evidence_ready) &&
|
||||||
row.node_gate_status === "blocked_until_native_pass_evidence" &&
|
["blocked_until_native_pass_evidence", "pending_node_inventory_promotion_gate"].includes(row.node_gate_status) &&
|
||||||
row.browser_gate_status === "blocked_until_node_gate_complete" &&
|
row.browser_gate_status === "blocked_until_node_gate_complete" &&
|
||||||
row.manual_lock_update_required === "1" &&
|
row.manual_lock_update_required === "1" &&
|
||||||
row.promotion_lock_active === "1" &&
|
row.promotion_lock_active === "1" &&
|
||||||
row.execution_enabled === "0" &&
|
row.execution_enabled === "0" &&
|
||||||
row.promotion_allowed === "0" &&
|
row.promotion_allowed === "0" &&
|
||||||
row.gate_status === "blocked_before_native_pass",
|
["blocked_before_native_pass", "waiting_for_node_browser_manual_promotion"].includes(row.gate_status),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const runtimeBoundaryHostPreflightOk = (
|
const runtimeBoundaryHostPreflightOk = (
|
||||||
@@ -5707,15 +5702,18 @@ function boundaryPhaseCompletionSummaryRows({
|
|||||||
(row.host_action === "ready_for_manual_opt_in_native_probe" &&
|
(row.host_action === "ready_for_manual_opt_in_native_probe" &&
|
||||||
row.skip_reason === "-" &&
|
row.skip_reason === "-" &&
|
||||||
row.missing_host_requirements === "-" &&
|
row.missing_host_requirements === "-" &&
|
||||||
row.skip_evidence_status === "skip_contract_not_applicable_dispatch_allowed") ||
|
[
|
||||||
|
"skip_contract_not_applicable_dispatch_allowed",
|
||||||
|
"native_pass_evidence_observed_skip_contract_closed",
|
||||||
|
].includes(row.skip_evidence_status)) ||
|
||||||
(row.host_action === "skip_missing_host_requirements" &&
|
(row.host_action === "skip_missing_host_requirements" &&
|
||||||
row.skip_reason === "missing_host_requirements" &&
|
row.skip_reason === "missing_host_requirements" &&
|
||||||
row.missing_host_requirements !== "-" &&
|
row.missing_host_requirements !== "-" &&
|
||||||
row.current_probe_status === "skipped_missing_host_runtime" &&
|
row.current_probe_status === "skipped_missing_host_runtime" &&
|
||||||
row.skip_evidence_status === "skip_valid_until_host_requirements_available")
|
row.skip_evidence_status === "skip_valid_until_host_requirements_available")
|
||||||
) &&
|
) &&
|
||||||
row.evidence_required_now === "0" &&
|
/^[0-9]+$/.test(row.evidence_required_now) &&
|
||||||
row.observed_evidence_ready === "0" &&
|
/^[0-9]+$/.test(row.observed_evidence_ready) &&
|
||||||
row.execution_enabled === "0" &&
|
row.execution_enabled === "0" &&
|
||||||
row.promotion_allowed === "0",
|
row.promotion_allowed === "0",
|
||||||
)
|
)
|
||||||
@@ -5726,10 +5724,10 @@ function boundaryPhaseCompletionSummaryRows({
|
|||||||
row.scope === "blocked_runtime_opt_in_probe_skip_evidence" &&
|
row.scope === "blocked_runtime_opt_in_probe_skip_evidence" &&
|
||||||
row.probe_count === "3" &&
|
row.probe_count === "3" &&
|
||||||
/^[0-9]+$/.test(row.skip_count) &&
|
/^[0-9]+$/.test(row.skip_count) &&
|
||||||
row.evidence_required_now_count === "0" &&
|
/^[0-9]+$/.test(row.evidence_required_now_count) &&
|
||||||
row.observed_evidence_ready_count === "0" &&
|
/^[0-9]+$/.test(row.observed_evidence_ready_count) &&
|
||||||
row.skip_evidence_statuses !== "-" &&
|
row.skip_evidence_statuses !== "-" &&
|
||||||
row.evidence_rollup_status === "no_native_pass_evidence_accepted_while_host_blocked" &&
|
["no_native_pass_evidence_accepted_while_host_blocked", "native_pass_evidence_present"].includes(row.evidence_rollup_status) &&
|
||||||
row.execution_enabled === "0" &&
|
row.execution_enabled === "0" &&
|
||||||
row.promotion_allowed === "0",
|
row.promotion_allowed === "0",
|
||||||
)
|
)
|
||||||
@@ -5737,11 +5735,15 @@ function boundaryPhaseCompletionSummaryRows({
|
|||||||
const runtimeBoundaryNativeEvidenceAcceptanceGateOk = (
|
const runtimeBoundaryNativeEvidenceAcceptanceGateOk = (
|
||||||
runtimeBoundaryNativeEvidenceAcceptanceGateRows.length === 3 &&
|
runtimeBoundaryNativeEvidenceAcceptanceGateRows.length === 3 &&
|
||||||
runtimeBoundaryNativeEvidenceAcceptanceGateRows.every((row) =>
|
runtimeBoundaryNativeEvidenceAcceptanceGateRows.every((row) =>
|
||||||
row.native_evidence_status === "pending_until_native_pass" &&
|
["pending_until_native_pass", "native_pass_evidence_observed"].includes(row.native_evidence_status) &&
|
||||||
row.evidence_required_now === "0" &&
|
/^[0-1]$/.test(row.evidence_required_now) &&
|
||||||
row.observed_evidence_ready === "0" &&
|
/^[0-1]$/.test(row.observed_evidence_ready) &&
|
||||||
["blocked_until_host_requirements_available", "blocked_until_native_pass_evidence"].includes(row.native_evidence_gate) &&
|
[
|
||||||
row.evidence_acceptance_allowed === "0" &&
|
"blocked_until_host_requirements_available",
|
||||||
|
"blocked_until_native_pass_evidence",
|
||||||
|
"native_pass_evidence_accepted",
|
||||||
|
].includes(row.native_evidence_gate) &&
|
||||||
|
["0", "1"].includes(row.evidence_acceptance_allowed) &&
|
||||||
row.promotion_ready === "0" &&
|
row.promotion_ready === "0" &&
|
||||||
row.execution_enabled === "0" &&
|
row.execution_enabled === "0" &&
|
||||||
row.promotion_allowed === "0",
|
row.promotion_allowed === "0",
|
||||||
@@ -5771,7 +5773,7 @@ function boundaryPhaseCompletionSummaryRows({
|
|||||||
row.promotion_allowed === "0",
|
row.promotion_allowed === "0",
|
||||||
) &&
|
) &&
|
||||||
runtimeBoundaryNativeEvidenceAcceptanceGateRows.every((row) =>
|
runtimeBoundaryNativeEvidenceAcceptanceGateRows.every((row) =>
|
||||||
row.evidence_acceptance_allowed === "0" &&
|
["0", "1"].includes(row.evidence_acceptance_allowed) &&
|
||||||
row.execution_enabled === "0" &&
|
row.execution_enabled === "0" &&
|
||||||
row.promotion_allowed === "0",
|
row.promotion_allowed === "0",
|
||||||
) &&
|
) &&
|
||||||
@@ -7865,10 +7867,13 @@ function runtimeBoundaryOptInProbeSkipEvidenceContractRows({
|
|||||||
|
|
||||||
return dispatchPlanRows.map((row) => {
|
return dispatchPlanRows.map((row) => {
|
||||||
const evidence = evidenceByClass.get(row.boundary_class);
|
const evidence = evidenceByClass.get(row.boundary_class);
|
||||||
const skipReason = row.dispatch_allowed === "1" ? "-" : "missing_host_requirements";
|
const nativeEvidenceObserved = evidence?.evidence_status === "native_pass_evidence_observed";
|
||||||
const skipEvidenceStatus = row.dispatch_allowed === "1"
|
const skipReason = nativeEvidenceObserved || row.dispatch_allowed === "1" ? "-" : "missing_host_requirements";
|
||||||
? "skip_contract_not_applicable_dispatch_allowed"
|
const skipEvidenceStatus = nativeEvidenceObserved
|
||||||
: "skip_valid_until_host_requirements_available";
|
? "native_pass_evidence_observed_skip_contract_closed"
|
||||||
|
: row.dispatch_allowed === "1"
|
||||||
|
? "skip_contract_not_applicable_dispatch_allowed"
|
||||||
|
: "skip_valid_until_host_requirements_available";
|
||||||
|
|
||||||
return [
|
return [
|
||||||
row.boundary_class,
|
row.boundary_class,
|
||||||
@@ -7951,18 +7956,29 @@ function verifyRuntimeBoundaryOptInProbeSkipEvidenceContractRows({
|
|||||||
assert.equal(row.host_action, dispatch.host_action, `${row.boundary_class}: skip evidence host action drift`);
|
assert.equal(row.host_action, dispatch.host_action, `${row.boundary_class}: skip evidence host action drift`);
|
||||||
assert.equal(row.missing_host_requirements, dispatch.missing_host_requirements, `${row.boundary_class}: skip evidence missing requirements drift`);
|
assert.equal(row.missing_host_requirements, dispatch.missing_host_requirements, `${row.boundary_class}: skip evidence missing requirements 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.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.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.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.native_evidence_status, evidence.evidence_status, `${row.boundary_class}: skip evidence native status drift`);
|
||||||
if (dispatch.dispatch_allowed === "1") {
|
if (evidence.evidence_status === "native_pass_evidence_observed") {
|
||||||
|
assert.equal(row.evidence_required_now, "1", `${row.boundary_class}: native pass evidence should be required now`);
|
||||||
|
assert.equal(row.observed_evidence_ready, "1", `${row.boundary_class}: native pass evidence must be observed`);
|
||||||
|
assert.equal(row.native_evidence_status, "native_pass_evidence_observed", `${row.boundary_class}: native pass evidence status drift`);
|
||||||
|
assert.equal(row.host_action, "ready_for_manual_opt_in_native_probe", `${row.boundary_class}: native pass skip evidence action drift`);
|
||||||
|
assert.equal(row.skip_reason, "-", `${row.boundary_class}: native pass skip evidence reason drift`);
|
||||||
|
assert.equal(row.missing_host_requirements, "-", `${row.boundary_class}: native pass skip evidence missing requirements drift`);
|
||||||
|
assert.equal(row.skip_evidence_status, "native_pass_evidence_observed_skip_contract_closed", `${row.boundary_class}: native pass skip evidence status drift`);
|
||||||
|
} else if (dispatch.dispatch_allowed === "1") {
|
||||||
|
assert.equal(row.evidence_required_now, "0", `${row.boundary_class}: pending probe must not require evidence now`);
|
||||||
|
assert.equal(row.observed_evidence_ready, "0", `${row.boundary_class}: pending probe must not report evidence ready`);
|
||||||
|
assert.equal(row.native_evidence_status, "pending_until_native_pass", `${row.boundary_class}: pending evidence status drift`);
|
||||||
assert.equal(row.host_action, "ready_for_manual_opt_in_native_probe", `${row.boundary_class}: ready skip evidence action drift`);
|
assert.equal(row.host_action, "ready_for_manual_opt_in_native_probe", `${row.boundary_class}: ready skip evidence action drift`);
|
||||||
assert.equal(row.skip_reason, "-", `${row.boundary_class}: ready dispatch must not have skip reason`);
|
assert.equal(row.skip_reason, "-", `${row.boundary_class}: ready dispatch must not have skip reason`);
|
||||||
assert.equal(row.missing_host_requirements, "-", `${row.boundary_class}: ready dispatch must not list missing host requirements`);
|
assert.equal(row.missing_host_requirements, "-", `${row.boundary_class}: ready dispatch must not list missing host requirements`);
|
||||||
assert.equal(row.skip_evidence_status, "skip_contract_not_applicable_dispatch_allowed", `${row.boundary_class}: ready skip evidence status drift`);
|
assert.equal(row.skip_evidence_status, "skip_contract_not_applicable_dispatch_allowed", `${row.boundary_class}: ready skip evidence status drift`);
|
||||||
} else {
|
} else {
|
||||||
|
assert.equal(row.evidence_required_now, "0", `${row.boundary_class}: skipped probe must not require evidence now`);
|
||||||
|
assert.equal(row.observed_evidence_ready, "0", `${row.boundary_class}: skipped probe must not report evidence ready`);
|
||||||
|
assert.equal(row.native_evidence_status, "pending_until_native_pass", `${row.boundary_class}: skip evidence status drift`);
|
||||||
assert.equal(row.host_action, "skip_missing_host_requirements", `${row.boundary_class}: blocked skip evidence action drift`);
|
assert.equal(row.host_action, "skip_missing_host_requirements", `${row.boundary_class}: blocked skip evidence action drift`);
|
||||||
assert.equal(row.skip_reason, "missing_host_requirements", `${row.boundary_class}: blocked skip evidence reason drift`);
|
assert.equal(row.skip_reason, "missing_host_requirements", `${row.boundary_class}: blocked skip evidence reason drift`);
|
||||||
assert.notEqual(row.missing_host_requirements, "-", `${row.boundary_class}: blocked skip evidence must list missing host requirements`);
|
assert.notEqual(row.missing_host_requirements, "-", `${row.boundary_class}: blocked skip evidence must list missing host requirements`);
|
||||||
@@ -8042,15 +8058,21 @@ function verifyRuntimeBoundaryOptInProbeSkipEvidenceRollupRows({
|
|||||||
assert.equal(row.scope, "blocked_runtime_opt_in_probe_skip_evidence", "opt-in probe skip evidence rollup scope 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.probe_count, String(skipEvidenceContractRows.length), "opt-in probe skip evidence rollup probe count drift");
|
||||||
assert.equal(row.skip_count, String(skippedRows.length), "opt-in probe skip evidence rollup skip count drift");
|
assert.equal(row.skip_count, String(skippedRows.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.evidence_required_now_count, String(skipEvidenceContractRows.filter((contract) => contract.evidence_required_now === "1").length), "opt-in probe skip evidence rollup required-now count drift");
|
||||||
assert.equal(row.observed_evidence_ready_count, "0", "opt-in probe skip evidence rollup must not report evidence ready");
|
assert.equal(row.observed_evidence_ready_count, String(skipEvidenceContractRows.filter((contract) => contract.observed_evidence_ready === "1").length), "opt-in probe skip evidence rollup observed evidence count drift");
|
||||||
assert.equal(row.skipped_families, listValue(uniqueSorted(skippedRows.map((contract) => contract.boundary_class))), "opt-in probe skip evidence rollup family drift");
|
assert.equal(row.skipped_families, listValue(uniqueSorted(skippedRows.map((contract) => contract.boundary_class))), "opt-in probe skip evidence rollup family drift");
|
||||||
assert.equal(row.skip_reasons, listValue(uniqueSorted(skippedRows.map((contract) => contract.skip_reason))), "opt-in probe skip evidence rollup reason drift");
|
assert.equal(row.skip_reasons, listValue(uniqueSorted(skippedRows.map((contract) => contract.skip_reason))), "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.missing_host_requirements, listValue(expectedMissingRequirements), "opt-in probe skip evidence rollup missing requirements drift");
|
||||||
assert.equal(row.current_probe_statuses, listValue(uniqueSorted(skipEvidenceContractRows.map((contract) => contract.current_probe_status))), "opt-in probe skip evidence rollup probe status drift");
|
assert.equal(row.current_probe_statuses, listValue(uniqueSorted(skipEvidenceContractRows.map((contract) => contract.current_probe_status))), "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.native_evidence_statuses, listValue(uniqueSorted(skipEvidenceContractRows.map((contract) => contract.native_evidence_status))), "opt-in probe skip evidence rollup native evidence status drift");
|
||||||
assert.equal(row.skip_evidence_statuses, listValue(uniqueSorted(skipEvidenceContractRows.map((contract) => contract.skip_evidence_status))), "opt-in probe skip evidence rollup skip evidence status drift");
|
assert.equal(row.skip_evidence_statuses, listValue(uniqueSorted(skipEvidenceContractRows.map((contract) => contract.skip_evidence_status))), "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.evidence_rollup_status,
|
||||||
|
skipEvidenceContractRows.some((contract) => contract.observed_evidence_ready === "1")
|
||||||
|
? "native_pass_evidence_present"
|
||||||
|
: "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.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.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");
|
assert.ok(row.notes.includes("no_automatic_promotion"), "opt-in probe skip evidence rollup note drift");
|
||||||
@@ -8163,27 +8185,39 @@ function verifyRuntimeBoundaryNativeEvidenceAcceptanceGateRows({
|
|||||||
assert.equal(row.blocked, skip.blocked, `${row.boundary_class}: native evidence gate blocked 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.runtime_probe, skip.runtime_probe, `${row.boundary_class}: native evidence gate runtime probe drift`);
|
||||||
assert.equal(row.current_probe_status, skip.current_probe_status, `${row.boundary_class}: native evidence gate skip 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.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.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.observed_evidence_ready, skip.observed_evidence_ready, `${row.boundary_class}: native evidence gate readiness drift`);
|
||||||
assert.equal(row.skip_evidence_status, skip.skip_evidence_status, `${row.boundary_class}: native evidence gate skip status drift`);
|
assert.equal(row.skip_evidence_status, skip.skip_evidence_status, `${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, "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.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.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, "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.browser_gate_status, postNativeGate.browser_gate_status, `${row.boundary_class}: native evidence gate post-native browser status drift`);
|
||||||
if (skip.skip_reason === "missing_host_requirements") {
|
if (skip.native_evidence_status === "native_pass_evidence_observed") {
|
||||||
|
assert.equal(row.evidence_required_now, "1", `${row.boundary_class}: native evidence gate should require observed evidence`);
|
||||||
|
assert.equal(row.observed_evidence_ready, "1", `${row.boundary_class}: native evidence gate should observe evidence ready`);
|
||||||
|
assert.equal(row.node_gate_status, "pending_node_inventory_promotion_gate", `${row.boundary_class}: native evidence gate Node status drift`);
|
||||||
|
assert.equal(row.native_evidence_gate, "native_pass_evidence_accepted", `${row.boundary_class}: native evidence gate status drift`);
|
||||||
|
assert.equal(row.evidence_acceptance_allowed, "1", `${row.boundary_class}: native evidence gate acceptance drift`);
|
||||||
|
assert.equal(row.next_action, "run_opt_in_native_runtime_probe_and_collect_evidence", `${row.boundary_class}: native evidence gate next action drift`);
|
||||||
|
} else if (skip.skip_reason === "missing_host_requirements") {
|
||||||
|
assert.equal(row.native_evidence_status, "pending_until_native_pass", `${row.boundary_class}: native evidence gate evidence status drift`);
|
||||||
|
assert.equal(row.evidence_required_now, "0", `${row.boundary_class}: native evidence gate must not require evidence now`);
|
||||||
|
assert.equal(row.observed_evidence_ready, "0", `${row.boundary_class}: native evidence gate must not observe evidence ready`);
|
||||||
|
assert.equal(row.node_gate_status, "blocked_until_native_pass_evidence", `${row.boundary_class}: native evidence gate Node status drift`);
|
||||||
assert.equal(row.native_evidence_gate, "blocked_until_host_requirements_available", `${row.boundary_class}: native evidence gate status drift`);
|
assert.equal(row.native_evidence_gate, "blocked_until_host_requirements_available", `${row.boundary_class}: native evidence gate status drift`);
|
||||||
assert.ok(row.next_action.includes(skip.missing_host_requirements), `${row.boundary_class}: native evidence gate next action must name missing requirements`);
|
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.evidence_acceptance_allowed, "0", `${row.boundary_class}: native evidence gate must not allow evidence acceptance`);
|
||||||
} else {
|
} else {
|
||||||
|
assert.equal(row.native_evidence_status, "pending_until_native_pass", `${row.boundary_class}: native evidence gate evidence status drift`);
|
||||||
|
assert.equal(row.evidence_required_now, "0", `${row.boundary_class}: native evidence gate must not require evidence now`);
|
||||||
|
assert.equal(row.observed_evidence_ready, "0", `${row.boundary_class}: native evidence gate must not observe evidence ready`);
|
||||||
|
assert.equal(row.node_gate_status, "blocked_until_native_pass_evidence", `${row.boundary_class}: native evidence gate Node status drift`);
|
||||||
assert.equal(row.native_evidence_gate, "blocked_until_native_pass_evidence", `${row.boundary_class}: native evidence gate status drift`);
|
assert.equal(row.native_evidence_gate, "blocked_until_native_pass_evidence", `${row.boundary_class}: native evidence gate status drift`);
|
||||||
assert.equal(row.next_action, "run_opt_in_native_runtime_probe_and_collect_evidence", `${row.boundary_class}: native evidence gate next action drift`);
|
assert.equal(row.next_action, "run_opt_in_native_runtime_probe_and_collect_evidence", `${row.boundary_class}: native evidence gate next action drift`);
|
||||||
|
assert.equal(row.evidence_acceptance_allowed, "0", `${row.boundary_class}: native evidence gate must not allow evidence acceptance`);
|
||||||
}
|
}
|
||||||
assert.equal(row.evidence_acceptance_allowed, "0", `${row.boundary_class}: native evidence gate must not allow evidence acceptance`);
|
|
||||||
assert.equal(row.execution_enabled, "0", `${row.boundary_class}: native evidence gate must not enable execution`);
|
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.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`);
|
assert.ok(row.notes.includes("no_automatic_promotion"), `${row.boundary_class}: native evidence gate note drift`);
|
||||||
@@ -8313,7 +8347,11 @@ function verifyBlockedRuntimeOptInGateConsistency({
|
|||||||
assert.equal(dispatch.missing_host_requirements, execution.missing_requirements, `${boundaryClass}: dispatch 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(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(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(
|
||||||
|
evidenceGate.evidence_acceptance_allowed,
|
||||||
|
evidenceGate.native_evidence_gate === "native_pass_evidence_accepted" ? "1" : "0",
|
||||||
|
`${boundaryClass}: evidence acceptance flag drift`,
|
||||||
|
);
|
||||||
assert.equal(dispatch.dispatch_allowed, execution.runtime_ready, `${boundaryClass}: dispatch readiness drift`);
|
assert.equal(dispatch.dispatch_allowed, execution.runtime_ready, `${boundaryClass}: dispatch readiness drift`);
|
||||||
assert.equal(readiness.promotion_lock_active, "1", `${boundaryClass}: readiness must keep promotion lock active`);
|
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.equal(postNative.promotion_lock_active, "1", `${boundaryClass}: post-native gate must keep promotion lock active`);
|
||||||
@@ -8324,7 +8362,11 @@ function verifyBlockedRuntimeOptInGateConsistency({
|
|||||||
} else {
|
} else {
|
||||||
assert.equal(blocker.blocker_keys.includes("host_runtime_requirements_missing"), false, `${boundaryClass}: ready runtime must not keep host runtime blocker`);
|
assert.equal(blocker.blocker_keys.includes("host_runtime_requirements_missing"), false, `${boundaryClass}: ready runtime must not keep host runtime blocker`);
|
||||||
}
|
}
|
||||||
assert.ok(evidenceGate.native_evidence_gate.includes("blocked"), `${boundaryClass}: evidence gate must be blocked`);
|
assert.ok(
|
||||||
|
evidenceGate.native_evidence_gate.includes("blocked") ||
|
||||||
|
evidenceGate.native_evidence_gate === "native_pass_evidence_accepted",
|
||||||
|
`${boundaryClass}: evidence gate status drift`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8401,8 +8443,17 @@ function verifyBlockedRuntimeProbeExecutionConsistency({
|
|||||||
assert.equal(execution.source_proof_ready, "1", `${execution.boundary_class}: source proof should remain ready before opt-in runtime`);
|
assert.equal(execution.source_proof_ready, "1", `${execution.boundary_class}: source proof should remain ready before opt-in runtime`);
|
||||||
assert.ok(["0", "1"].includes(execution.runtime_ready), `${execution.boundary_class}: invalid runtime readiness`);
|
assert.ok(["0", "1"].includes(execution.runtime_ready), `${execution.boundary_class}: invalid runtime readiness`);
|
||||||
if (execution.runtime_ready === "1") {
|
if (execution.runtime_ready === "1") {
|
||||||
assert.equal(execution.current_probe_status, "ready_disabled_by_default", `${execution.boundary_class}: ready probe must remain opt-in`);
|
assert.ok(
|
||||||
assert.equal(execution.plan_status, "ready_to_run_opt_in_probe", `${execution.boundary_class}: execution plan must remain opt-in`);
|
[execution.expected_pass_status, "ready_disabled_by_default"].includes(execution.current_probe_status),
|
||||||
|
`${execution.boundary_class}: ready probe status drift`,
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
execution.plan_status,
|
||||||
|
execution.current_probe_status === execution.expected_pass_status
|
||||||
|
? "native_probe_passed_waiting_for_node_browser_promotion_proof"
|
||||||
|
: "ready_to_run_opt_in_probe",
|
||||||
|
`${execution.boundary_class}: execution plan opt-in/pass status drift`,
|
||||||
|
);
|
||||||
assert.equal(execution.missing_requirements, "-", `${execution.boundary_class}: ready execution plan must not list missing requirements`);
|
assert.equal(execution.missing_requirements, "-", `${execution.boundary_class}: ready execution plan must not list missing requirements`);
|
||||||
} else {
|
} else {
|
||||||
assert.equal(execution.current_probe_status, "skipped_missing_host_runtime", `${execution.boundary_class}: probe should remain skipped on missing host`);
|
assert.equal(execution.current_probe_status, "skipped_missing_host_runtime", `${execution.boundary_class}: probe should remain skipped on missing host`);
|
||||||
@@ -8422,14 +8473,27 @@ function verifyBlockedRuntimeProbeExecutionConsistency({
|
|||||||
assert.equal(skip.host_action, "skip_missing_host_requirements", `${execution.boundary_class}: skip evidence host action drift`);
|
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(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`);
|
if (execution.current_probe_status === execution.expected_pass_status) {
|
||||||
assert.equal(skip.evidence_required_now, "0", `${execution.boundary_class}: skip evidence must not be required yet`);
|
assert.equal(pass.evidence_required_now, "1", `${execution.boundary_class}: pass evidence should be required after native pass`);
|
||||||
assert.equal(pass.observed_evidence_ready, "0", `${execution.boundary_class}: pass evidence must not be ready yet`);
|
assert.equal(skip.evidence_required_now, "1", `${execution.boundary_class}: skip evidence should reflect native pass evidence`);
|
||||||
assert.equal(skip.observed_evidence_ready, "0", `${execution.boundary_class}: skip evidence must not be ready yet`);
|
assert.equal(pass.observed_evidence_ready, "1", `${execution.boundary_class}: pass evidence must be ready after native pass`);
|
||||||
assert.equal(pass.evidence_status, "pending_until_native_pass", `${execution.boundary_class}: pass evidence status drift`);
|
assert.equal(skip.observed_evidence_ready, "1", `${execution.boundary_class}: skip evidence must reflect ready native evidence`);
|
||||||
assert.equal(skip.native_evidence_status, "pending_until_native_pass", `${execution.boundary_class}: skip evidence native status drift`);
|
assert.equal(pass.evidence_status, "native_pass_evidence_observed", `${execution.boundary_class}: pass evidence status drift`);
|
||||||
|
assert.equal(skip.native_evidence_status, "native_pass_evidence_observed", `${execution.boundary_class}: skip evidence native status drift`);
|
||||||
|
} else {
|
||||||
|
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.ok(
|
assert.ok(
|
||||||
["skip_valid_until_host_requirements_available", "skip_contract_not_applicable_dispatch_allowed"].includes(skip.skip_evidence_status),
|
[
|
||||||
|
"skip_valid_until_host_requirements_available",
|
||||||
|
"skip_contract_not_applicable_dispatch_allowed",
|
||||||
|
"native_pass_evidence_observed_skip_contract_closed",
|
||||||
|
].includes(skip.skip_evidence_status),
|
||||||
`${execution.boundary_class}: skip evidence contract status drift`,
|
`${execution.boundary_class}: skip evidence contract status drift`,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -8466,8 +8530,8 @@ function verifyBlockedRuntimeRollupConsistency({
|
|||||||
assert.equal(host.host_ready_family_count, dispatch.dispatch_allowed_count, "blocked runtime rollup ready/dispatch-allowed 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(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(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.ok(/^[0-9]+$/.test(skipEvidence.evidence_required_now_count), "blocked runtime rollup evidence required count drift");
|
||||||
assert.equal(skipEvidence.observed_evidence_ready_count, "0", "blocked runtime rollup must not accept observed evidence on host-blocked probes");
|
assert.ok(/^[0-9]+$/.test(skipEvidence.observed_evidence_ready_count), "blocked runtime rollup evidence ready count drift");
|
||||||
assert.equal(host.blocked_families, dispatch.blocked_families, "blocked runtime rollup blocked family drift");
|
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(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(host.missing_host_requirements, dispatch.missing_host_requirements, "blocked runtime rollup host/dispatch missing requirement drift");
|
||||||
@@ -8490,9 +8554,12 @@ function verifyBlockedRuntimeRollupConsistency({
|
|||||||
].includes(dispatch.dispatch_status),
|
].includes(dispatch.dispatch_status),
|
||||||
"blocked runtime rollup dispatch status drift",
|
"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.ok(
|
||||||
|
["no_native_pass_evidence_accepted_while_host_blocked", "native_pass_evidence_present"].includes(skipEvidence.evidence_rollup_status),
|
||||||
|
"blocked runtime rollup evidence status drift",
|
||||||
|
);
|
||||||
assert.notEqual(skipEvidence.current_probe_statuses, "-", "blocked runtime rollup probe status drift");
|
assert.notEqual(skipEvidence.current_probe_statuses, "-", "blocked runtime rollup probe status drift");
|
||||||
assert.equal(skipEvidence.native_evidence_statuses, "pending_until_native_pass", "blocked runtime rollup native evidence status drift");
|
assert.notEqual(skipEvidence.native_evidence_statuses, "-", "blocked runtime rollup native evidence status drift");
|
||||||
assert.notEqual(skipEvidence.skip_evidence_statuses, "-", "blocked runtime rollup skip evidence status drift");
|
assert.notEqual(skipEvidence.skip_evidence_statuses, "-", "blocked runtime rollup skip evidence status drift");
|
||||||
|
|
||||||
for (const [label, row] of [
|
for (const [label, row] of [
|
||||||
@@ -8699,27 +8766,53 @@ function verifyBlockedRuntimePromotionGateConsistency({
|
|||||||
assert.equal(evidenceGate.node_gate_status, postNative.node_gate_status, `${boundaryClass}: evidence gate Node status 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(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`);
|
if (readiness.current_probe_status === readiness.expected_pass_status) {
|
||||||
assert.equal(readiness.native_evidence_ready, "0", `${boundaryClass}: current host must not report native evidence ready`);
|
assert.equal(readiness.native_pass_ready, "1", `${boundaryClass}: native pass flag drift`);
|
||||||
|
assert.equal(readiness.native_evidence_ready, "1", `${boundaryClass}: native evidence flag drift`);
|
||||||
|
} else {
|
||||||
|
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.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.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.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.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.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`);
|
if (readiness.native_pass_ready !== "1") {
|
||||||
assert.ok(blocker.blocker_keys.includes("native_pass_evidence_not_ready"), `${boundaryClass}: native evidence blocker missing`);
|
assert.ok(blocker.blocker_keys.includes("native_runtime_probe_not_passed"), `${boundaryClass}: native pass blocker missing`);
|
||||||
|
}
|
||||||
|
if (readiness.native_evidence_ready !== "1") {
|
||||||
|
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("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("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("promotion_lock_active"), `${boundaryClass}: promotion lock blocker missing`);
|
||||||
assert.ok(blocker.blocker_keys.includes("manual_lock_update_required"), `${boundaryClass}: manual 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(
|
||||||
assert.equal(postNative.browser_gate_status, "blocked_until_node_gate_complete", `${boundaryClass}: post-native browser gate must wait for Node gate`);
|
postNative.node_gate_status,
|
||||||
assert.equal(postNative.gate_status, "blocked_before_native_pass", `${boundaryClass}: post-native gate status drift`);
|
readiness.native_evidence_ready === "1"
|
||||||
assert.ok(
|
? "pending_node_inventory_promotion_gate"
|
||||||
["blocked_until_host_requirements_available", "blocked_until_native_pass_evidence"].includes(evidenceGate.native_evidence_gate),
|
: "blocked_until_native_pass_evidence",
|
||||||
`${boundaryClass}: native evidence gate must remain blocked`,
|
`${boundaryClass}: post-native Node gate status drift`,
|
||||||
);
|
);
|
||||||
assert.equal(evidenceGate.evidence_acceptance_allowed, "0", `${boundaryClass}: native evidence acceptance must remain blocked`);
|
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,
|
||||||
|
readiness.native_evidence_ready === "1"
|
||||||
|
? "waiting_for_node_browser_manual_promotion"
|
||||||
|
: "blocked_before_native_pass",
|
||||||
|
`${boundaryClass}: post-native gate status drift`,
|
||||||
|
);
|
||||||
|
if (readiness.native_evidence_ready === "1") {
|
||||||
|
assert.equal(evidenceGate.native_evidence_gate, "native_pass_evidence_accepted", `${boundaryClass}: native evidence gate acceptance drift`);
|
||||||
|
assert.equal(evidenceGate.evidence_acceptance_allowed, "1", `${boundaryClass}: native evidence acceptance flag drift`);
|
||||||
|
} else {
|
||||||
|
assert.ok(
|
||||||
|
["blocked_until_host_requirements_available", "blocked_until_native_pass_evidence"].includes(evidenceGate.native_evidence_gate),
|
||||||
|
`${boundaryClass}: native evidence gate must remain 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]) {
|
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.execution_enabled, "0", `${boundaryClass}: promotion consistency row must not enable execution`);
|
||||||
@@ -11583,6 +11676,21 @@ function executionModeFor(record) {
|
|||||||
|
|
||||||
function verifyInventoryCase(record, output, executionMode) {
|
function verifyInventoryCase(record, output, executionMode) {
|
||||||
if (record.status === "PASS") {
|
if (record.status === "PASS") {
|
||||||
|
if (record.path === "axis/db_demo/base.ngc") {
|
||||||
|
verifyExpectedOutput(
|
||||||
|
`inventory_${record.path}`,
|
||||||
|
output,
|
||||||
|
[
|
||||||
|
"file_open=0",
|
||||||
|
"file_saw_error=0",
|
||||||
|
"canon_event=MESSAGE: fini",
|
||||||
|
"canon_event=FINISH",
|
||||||
|
"absent=file_error_text=",
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (executionMode === "fiveAxisRemap") {
|
if (executionMode === "fiveAxisRemap") {
|
||||||
verifyExpectedOutput(
|
verifyExpectedOutput(
|
||||||
`inventory_${record.path}`,
|
`inventory_${record.path}`,
|
||||||
@@ -11773,7 +11881,7 @@ const iniBoundarySummaryRecords = verifyIniBoundarySummaryRows(
|
|||||||
const blockedDependencyRecords = nativeRecords.filter((record) =>
|
const blockedDependencyRecords = nativeRecords.filter((record) =>
|
||||||
["L4-PYTHON-REMAP", "L4-TOOL-DB", "L4-USER-M-PROCESS"].includes(
|
["L4-PYTHON-REMAP", "L4-TOOL-DB", "L4-USER-M-PROCESS"].includes(
|
||||||
pathMatrixByPath.get(record.path)?.blocked ?? "-",
|
pathMatrixByPath.get(record.path)?.blocked ?? "-",
|
||||||
),
|
) || record.path === "axis/db_demo/base.ngc",
|
||||||
);
|
);
|
||||||
const blockedDependencySummaryRows = blockedDependencyRecords.map((record) =>
|
const blockedDependencySummaryRows = blockedDependencyRecords.map((record) =>
|
||||||
blockedDependencySummaryRow(record, pathMatrixByPath),
|
blockedDependencySummaryRow(record, pathMatrixByPath),
|
||||||
@@ -12287,7 +12395,7 @@ function verifyEvidenceExpansionCandidateRows(rows, summaryRows, boundaryRows, t
|
|||||||
expectedPaths,
|
expectedPaths,
|
||||||
"evidence expansion candidates must cover every non-hard-block PASS main candidate outside evidence-ready",
|
"evidence expansion candidates must cover every non-hard-block PASS main candidate outside evidence-ready",
|
||||||
);
|
);
|
||||||
assert.equal(parsedRows.length, 13, "current evidence expansion candidate count drift");
|
assert.equal(parsedRows.length, 14, "current evidence expansion candidate count drift");
|
||||||
for (const row of parsedRows) {
|
for (const row of parsedRows) {
|
||||||
const boundary = boundaryByPath.get(row.path);
|
const boundary = boundaryByPath.get(row.path);
|
||||||
assert.ok(boundary, `${row.path}: evidence expansion candidate lacks boundary evidence`);
|
assert.ok(boundary, `${row.path}: evidence expansion candidate lacks boundary evidence`);
|
||||||
@@ -12365,7 +12473,7 @@ function verifyPromotionCandidateRows(rows, summaryRows, boundaryRows) {
|
|||||||
0,
|
0,
|
||||||
"current baseline has no direct skipped-main inventory promotion candidate",
|
"current baseline has no direct skipped-main inventory promotion candidate",
|
||||||
);
|
);
|
||||||
for (const lockedKind of ["L4-PYTHON-REMAP", "L4-TOOL-DB", "L4-USER-M-PROCESS", "UPSTREAM-DEMO"]) {
|
for (const lockedKind of ["L4-PYTHON-REMAP", "L4-USER-M-PROCESS", "UPSTREAM-DEMO"]) {
|
||||||
const lockedRows = parsedInventoryReady.filter((row) => row.skip_kind === lockedKind);
|
const lockedRows = parsedInventoryReady.filter((row) => row.skip_kind === lockedKind);
|
||||||
assert.ok(lockedRows.length > 0, `${lockedKind}: promotion-candidates missing locked inventory rows`);
|
assert.ok(lockedRows.length > 0, `${lockedKind}: promotion-candidates missing locked inventory rows`);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
@@ -12394,7 +12502,7 @@ function verifyPromotionCandidateRows(rows, summaryRows, boundaryRows) {
|
|||||||
assert.equal(row.blocked_kind, row.skip_kind, `${row.path}: skipped inventory blocked kind drift`);
|
assert.equal(row.blocked_kind, row.skip_kind, `${row.path}: skipped inventory blocked kind drift`);
|
||||||
assert.equal(row.virtual_hal_evidence_ready, "0", `${row.path}: skipped inventory row must not claim virtual HAL evidence ready`);
|
assert.equal(row.virtual_hal_evidence_ready, "0", `${row.path}: skipped inventory row must not claim virtual HAL evidence ready`);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
["L4-PYTHON-REMAP", "L4-TOOL-DB", "L4-USER-M-PROCESS", "UPSTREAM-DEMO"].includes(row.skip_kind),
|
["L4-PYTHON-REMAP", "L4-USER-M-PROCESS", "UPSTREAM-DEMO"].includes(row.skip_kind),
|
||||||
`${row.path}: direct promotion candidate must remain hard-blocked or upstream-demo`,
|
`${row.path}: direct promotion candidate must remain hard-blocked or upstream-demo`,
|
||||||
);
|
);
|
||||||
const boundary = boundaryByPath.get(row.path);
|
const boundary = boundaryByPath.get(row.path);
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ NATIVE_SUMMARY="$ROOT_DIR/build/native/sim-configs/summary.tsv"
|
|||||||
NATIVE_CLASS_SUMMARY="$ROOT_DIR/build/native/sim-configs/class-summary.tsv"
|
NATIVE_CLASS_SUMMARY="$ROOT_DIR/build/native/sim-configs/class-summary.tsv"
|
||||||
NATIVE_PATH_MATRIX="$ROOT_DIR/build/native/sim-configs/path-matrix.tsv"
|
NATIVE_PATH_MATRIX="$ROOT_DIR/build/native/sim-configs/path-matrix.tsv"
|
||||||
NATIVE_SOURCE_PROOF_SUMMARY="$ROOT_DIR/build/native/native-source-proof-summary.tsv"
|
NATIVE_SOURCE_PROOF_SUMMARY="$ROOT_DIR/build/native/native-source-proof-summary.tsv"
|
||||||
|
NATIVE_RUNTIME_PROBE_SUMMARY="$ROOT_DIR/build/native/native-runtime-probe-summary.tsv"
|
||||||
|
PYTHON_REMAP_LIFECYCLE_STDOUT="$ROOT_DIR/build/native/python-remap-runtime/python_lifecycle.stdout.log"
|
||||||
NATIVE_BUILD_DIR="$ROOT_DIR/build/native/sim-configs"
|
NATIVE_BUILD_DIR="$ROOT_DIR/build/native/sim-configs"
|
||||||
NATIVE_SOURCE_PROOF_INPUTS=(
|
NATIVE_SOURCE_PROOF_INPUTS=(
|
||||||
"$ROOT_DIR/tools/build_native_probes.sh"
|
"$ROOT_DIR/tools/build_native_probes.sh"
|
||||||
@@ -27,11 +29,38 @@ native_source_proof_refresh_required() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
python_remap_lifecycle_pass_observed() {
|
||||||
|
[[ -f "$PYTHON_REMAP_LIFECYCLE_STDOUT" ]] || return 1
|
||||||
|
grep -Fq "python_remap_lifecycle_interpreter_sentinel_ok=1" "$PYTHON_REMAP_LIFECYCLE_STDOUT" &&
|
||||||
|
grep -Fq "python_remap_lifecycle_toplevel_imported=1" "$PYTHON_REMAP_LIFECYCLE_STDOUT" &&
|
||||||
|
grep -Fq "python_remap_lifecycle_remap_imported=1" "$PYTHON_REMAP_LIFECYCLE_STDOUT" &&
|
||||||
|
grep -Fq "python_remap_lifecycle_callable_lookup_ok=1" "$PYTHON_REMAP_LIFECYCLE_STDOUT" &&
|
||||||
|
grep -Fq "python_remap_lifecycle_generator_first_yield=2" "$PYTHON_REMAP_LIFECYCLE_STDOUT" &&
|
||||||
|
grep -Fq "python_remap_runtime_lifecycle_probe_ok=1" "$PYTHON_REMAP_LIFECYCLE_STDOUT"
|
||||||
|
}
|
||||||
|
|
||||||
|
native_runtime_probe_refresh_required() {
|
||||||
|
python_remap_lifecycle_pass_observed || return 1
|
||||||
|
if [[ ! -f "$NATIVE_RUNTIME_PROBE_SUMMARY" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ "$PYTHON_REMAP_LIFECYCLE_STDOUT" -nt "$NATIVE_RUNTIME_PROBE_SUMMARY" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
! grep -Fq $'L4-PYTHON-REMAP\tpython_runtime\taxis/remap/stop-lookahead/nc_files\tlinuxcnc_python_remap_runtime_probe\tlinuxcnc_python_runtime_lifecycle_probe_required\tENABLE_PYTHON_REMAP_RUNTIME_PROBE=1\t1\t1\truntime_lifecycle_probe_passed' "$NATIVE_RUNTIME_PROBE_SUMMARY"
|
||||||
|
}
|
||||||
|
|
||||||
if [[ ! -f "$NATIVE_SUMMARY" || ! -f "$NATIVE_CLASS_SUMMARY" || ! -f "$NATIVE_PATH_MATRIX" ]]; then
|
if [[ ! -f "$NATIVE_SUMMARY" || ! -f "$NATIVE_CLASS_SUMMARY" || ! -f "$NATIVE_PATH_MATRIX" ]]; then
|
||||||
"$ROOT_DIR/tests/native/verify_sim_configs.sh"
|
"$ROOT_DIR/tests/native/verify_sim_configs.sh"
|
||||||
fi
|
fi
|
||||||
if native_source_proof_refresh_required; then
|
if native_source_proof_refresh_required; then
|
||||||
"$ROOT_DIR/tools/build_native_probes.sh"
|
if python_remap_lifecycle_pass_observed; then
|
||||||
|
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 "$ROOT_DIR/tools/build_native_probes.sh"
|
||||||
|
else
|
||||||
|
"$ROOT_DIR/tools/build_native_probes.sh"
|
||||||
|
fi
|
||||||
|
elif native_runtime_probe_refresh_required; then
|
||||||
|
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 "$ROOT_DIR/tools/build_native_probes.sh"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "$NATIVE_BUILD_DIR"
|
mkdir -p "$NATIVE_BUILD_DIR"
|
||||||
|
|||||||
@@ -171,6 +171,9 @@ native_input_fingerprint() {
|
|||||||
"$CXX" --version 2>/dev/null | sed -n '1p' || true
|
"$CXX" --version 2>/dev/null | sed -n '1p' || true
|
||||||
printf 'CC=%s\n' "$CC"
|
printf 'CC=%s\n' "$CC"
|
||||||
"$CC" --version 2>/dev/null | sed -n '1p' || true
|
"$CC" --version 2>/dev/null | sed -n '1p' || true
|
||||||
|
printf 'ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=%s\n' "${ENABLE_MILLTURN_USER_M_RUNTIME_PROBE:-0}"
|
||||||
|
printf 'ENABLE_TOOL_DB_RUNTIME_PROBE=%s\n' "${ENABLE_TOOL_DB_RUNTIME_PROBE:-0}"
|
||||||
|
printf 'ENABLE_PYTHON_REMAP_RUNTIME_PROBE=%s\n' "${ENABLE_PYTHON_REMAP_RUNTIME_PROBE:-0}"
|
||||||
sha256sum \
|
sha256sum \
|
||||||
"$ROOT_DIR/tools/build_native_probes.sh" \
|
"$ROOT_DIR/tools/build_native_probes.sh" \
|
||||||
"$ROOT_DIR/tools/source-manifest.txt"
|
"$ROOT_DIR/tools/source-manifest.txt"
|
||||||
@@ -1341,6 +1344,8 @@ if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_millturn_user_m_boundary_probe
|
|||||||
"$BUILD_DIR/linuxcnc_millturn_user_m_boundary_probe"
|
"$BUILD_DIR/linuxcnc_millturn_user_m_boundary_probe"
|
||||||
run_logged_command \
|
run_logged_command \
|
||||||
linuxcnc_millturn_user_m_runtime_probe \
|
linuxcnc_millturn_user_m_runtime_probe \
|
||||||
|
env \
|
||||||
|
ENABLE_MILLTURN_USER_M_RUNTIME_PROBE="${ENABLE_MILLTURN_USER_M_RUNTIME_PROBE:-0}" \
|
||||||
bash \
|
bash \
|
||||||
"$MILLTURN_USER_M_RUNTIME_PROBE"
|
"$MILLTURN_USER_M_RUNTIME_PROBE"
|
||||||
else
|
else
|
||||||
@@ -1362,6 +1367,8 @@ if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_tool_db_boundary_probe.exitcod
|
|||||||
"$BUILD_DIR/linuxcnc_tool_db_boundary_probe"
|
"$BUILD_DIR/linuxcnc_tool_db_boundary_probe"
|
||||||
run_logged_command \
|
run_logged_command \
|
||||||
linuxcnc_tool_db_runtime_probe \
|
linuxcnc_tool_db_runtime_probe \
|
||||||
|
env \
|
||||||
|
ENABLE_TOOL_DB_RUNTIME_PROBE="${ENABLE_TOOL_DB_RUNTIME_PROBE:-0}" \
|
||||||
bash \
|
bash \
|
||||||
"$TOOL_DB_RUNTIME_PROBE"
|
"$TOOL_DB_RUNTIME_PROBE"
|
||||||
else
|
else
|
||||||
@@ -1382,6 +1389,8 @@ if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_python_remap_boundary_probe.ex
|
|||||||
"$BUILD_DIR/linuxcnc_python_remap_boundary_probe"
|
"$BUILD_DIR/linuxcnc_python_remap_boundary_probe"
|
||||||
run_logged_command \
|
run_logged_command \
|
||||||
linuxcnc_python_remap_runtime_probe \
|
linuxcnc_python_remap_runtime_probe \
|
||||||
|
env \
|
||||||
|
ENABLE_PYTHON_REMAP_RUNTIME_PROBE="${ENABLE_PYTHON_REMAP_RUNTIME_PROBE:-0}" \
|
||||||
bash \
|
bash \
|
||||||
"$PYTHON_REMAP_RUNTIME_PROBE"
|
"$PYTHON_REMAP_RUNTIME_PROBE"
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user