先提交到云仓库

结论:已提交当前 Python-remap proof 链路、runtime/browser/UI/docs 相关变更;保持 L4-PYTHON-REMAP inventory baseline 不直接批量 PASS。
This commit is contained in:
2026-06-20 00:06:10 +08:00
parent fbf9dade9c
commit ac52b48d62
36 changed files with 4388 additions and 240 deletions

View File

@@ -612,3 +612,80 @@ native opt-in lifecycle probe 已通过;
的 WASM/browser proof chain
promotion_allowed 继续保持 0直到完整 proof + manual lock review。
```
十三、2026-06-19 接续执行记录L4-PYTHON-REMAP 仿真系统接入收尾
本轮结论:
```text
L4-PYTHON-REMAP 的 native / SDK / WASM / browser proof chain 已接入;
真实数控仿真页 runtime/ui/simulation 现在也暴露 Python remap runtime 状态;
release artifact / URL workflow 能识别 python-remap-runtime-proof=ready
53 个 L4-PYTHON-REMAP inventory rows 仍保持 SKIP/manual promotion lock
不做批量 PASS不启用 execution不允许 promotion_allowed。
```
新增/补齐的仿真接入点:
```text
wasm-port/runtime/ui/simulation/simulation-app.js
- createPythonRemapSimulationRuntimeStatus()
- renderPythonRemapRuntimeStatus()
- runRealBrowserSimulation() state.pythonRemapRuntime
wasm-port/runtime/ui/simulation/index.html
- Machine State 面板显示 L4-PYTHON-REMAP fixture/runtime/lifecycle/promotion 状态
- window.linuxCncRealSimulationApi.getPythonRemapRuntimeStatus()
- exportDiagnosticsArtifact().pythonRemapRuntime
wasm-port/tests/browser/real_simulation_page_smoke.html
- 验证 DOM / API / diagnostics artifact 的 Python remap runtime 状态
```
本轮验证:
```bash
wasm-port/tests/native/probe_python_remap_runtime.sh
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 wasm-port/tests/native/probe_python_remap_runtime.sh
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_python_remap_runtime_port_wasm.sh
wasm-port/tests/browser/verify_python_remap_runtime_browser.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh
wasm-port/tests/sdk/node/verify_sdk_surface.sh
wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh
wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
git diff --check
```
关键验证结果:
```text
python_remap_runtime_probe_status=ready_disabled_by_default
python_remap_runtime_probe_status=runtime_lifecycle_probe_passed
python_remap_runtime_lifecycle_probe_ok=1
python_remap_lifecycle_generator_first_yield=2
python_remap_execution_enabled=0
python_remap_promotion_allowed=0
browser_python_remap_runtime_smoke=ok
browser_real_simulation_page_smoke=ok
browser_release_artifact_url_workflow_smoke=ok
sim_configs_wasm_node_inventory_executed=29
sim_configs_wasm_node_inventory_passed=29
sim_configs_wasm_node_inventory_skipped=130
sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53
sim_configs_wasm_node_inventory_unexpected_fail=0
standalone CNC semantics guard complete
```
仍保持的边界:
```text
executionEnabled=false
promotionAllowed=false
bulkPromotionAllowed=false
ngcOnlySubroutinePromoted=false
jsCncSemantics=false
```

442
text26.txt Normal file
View File

@@ -0,0 +1,442 @@
项目接续文件L4-PYTHON-REMAP 53 rows 批量 PASS 方法与执行计划
生成时间2026-06-19 CST
本文件接替 `text25.txt`,专门回答:
```text
如何把 53 个 Python-remap inventory rows 从 SKIP L4-PYTHON-REMAP 批量提升为 PASS
以及下一批应按什么步骤推进。
```
重要边界:
```text
这里的“批量提升为 PASS”不能等价于删除 L4-PYTHON-REMAP skip reason。
必须是 proof-driven bulk promotion
1. LinuxCNC-owned Python remap runtime 真实执行;
2. 每个 row 的 INI / Python modules / remap declarations / prolog / epilog / NGC remap assets
都已 vendored 并 staged
3. native -> WASM/Node -> browser proof 全部通过;
4. promotion_allowed 由明确的 promotion artifact 置 1
5. inventory baseline 从 PASS=29 / SKIP=130 变为 PASS=82 / SKIP=77
6. 不新增 JS-owned G-code、planner、canonical motion、tool、parameter、kinematics、remap 语义。
```
当前禁止事项:
```text
- 不直接把 `recommendedBlockedKind === "L4-PYTHON-REMAP"` 改成 `-`
- 不把现有 stop-lookahead lifecycle fixture 当作 53 rows 全量 pass
- 不用 fake worker / contract-only runtime 作为 PASS 证据;
- 不把 Python import success 当作 remap execution pass
- 不把 NGC-only subroutine asset 当 standalone main program
- 不绕过 HAL/UI/HALUI/tool-change/TWP 等边界;
- 不手工改 summary.tsv 的 PASS/SKIP 数字。
```
一、当前状态
当前已经完成:
```text
L4-PYTHON-REMAP native / SDK / WASM / browser proof chain 已接入;
runtime/ui/simulation 已暴露 Python remap runtime 状态;
release artifact / URL workflow 能识别 python-remap-runtime-proof=ready
project release gate 通过;
```
当前仍未完成:
```text
53 个 L4-PYTHON-REMAP rows 仍保持 SKIP
promotion_allowed=0
execution_enabled=0
manual promotion lock 仍开启;
```
当前 inventory baseline
```text
total=159
pass=29
skip=130
fail=0
L4-PYTHON-REMAP=53
L4-USER-M-PROCESS=1
```
目标 bulk promotion baseline
```text
total=159
pass=82
skip=77
fail=0
L4-PYTHON-REMAP=0
L4-USER-M-PROCESS=1
```
二、53 rows 审计结果
来源:
```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
```
总量:
```text
rows=53
families=12
```
横向依赖统计:
```text
hal_process=53
ui_process=49
halui_mdi_process=5
prolog_rows=43
epilog_rows=43
python_remap_rows=9
ngc_only_subpath_rows=7
mixed_ngc_python_rows=6
```
family 分组:
```text
axis/laser rows=3 hal=1 ui=1 halui=0 prolog=0 epilog=0 pyremap=1 ngc_only=0
axis/remap/cycle/nc_files rows=1 hal=1 ui=0 halui=0 prolog=1 epilog=1 pyremap=1 ngc_only=0
axis/remap/extend-builtins/nc_files rows=1 hal=1 ui=1 halui=0 prolog=1 epilog=1 pyremap=0 ngc_only=1 mixed=1
axis/remap/getting-started/nc_files rows=1 hal=1 ui=0 halui=0 prolog=0 epilog=0 pyremap=0 ngc_only=1
axis/remap/manual-toolchange-with-tool-length-switch/nc_files rows=1 hal=1 ui=1 halui=0 prolog=1 epilog=1 pyremap=0 ngc_only=0
axis/remap/rack-toolchange/nc_files rows=1 hal=1 ui=1 halui=0 prolog=1 epilog=1 pyremap=0 ngc_only=0
axis/remap/stop-lookahead/nc_files rows=1 hal=1 ui=0 halui=0 prolog=0 epilog=0 pyremap=0 ngc_only=0
axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos rows=5 hal=1 ui=1 halui=1 prolog=0 epilog=0 pyremap=1 ngc_only=1 mixed=1
axis/vismach/VMC_toolchange rows=1 hal=1 ui=0 halui=0 prolog=1 epilog=1 pyremap=0 ngc_only=0
gmoccapy/lathe_configs rows=8 hal=1 ui=1 halui=0 prolog=1 epilog=1 pyremap=0 ngc_only=0
gmoccapy/macros rows=25 hal=1 ui=1 halui=0 prolog=1 epilog=1 pyremap=0 ngc_only=0
gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples rows=5 hal=1 ui=1 halui=0 prolog=1 epilog=1 pyremap=0 ngc_only=0
```
三、批量 PASS 的正确方法
方法不是“解锁一个 family id”而是新增一个 promotion pipeline
```text
python-remap-bulk-promotion-plan.tsv
-> python-remap-row-runtime-proof.tsv
-> python-remap-row-browser-proof.tsv
-> python-remap-bulk-promotion-gate.tsv
-> inventory rows promotion_allowed=1
-> summary baseline PASS=82 / SKIP=77
```
每个 row 至少要有这些字段:
```text
path
ini
family
main_or_macro_load_class
python_modules_staged
toplevel_executed
path_prepend_applied
python_remap_functions_bound
prolog_functions_bound
epilog_functions_bound
ngc_remap_assets_staged
ngc_only_subpaths_not_standalone
interpreter_state_binding_ready
canonical_events_ready
hal_state_boundary_ready
ui_process_boundary_ready
halui_mdi_boundary_ready
native_pass_ready
wasm_node_pass_ready
browser_pass_ready
execution_enabled
promotion_allowed
```
四、实现阶段
Phase 0冻结现状与审计
目标:
```text
确认当前 53 rows 分类、当前 proof 状态、当前 baseline。
```
验收:
```bash
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
wasm-port/tests/browser/verify_python_remap_runtime_browser.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
git diff --check
```
状态:
```text
本文件创建时 Phase 0 已执行;
当前仍是 PASS=29 / SKIP=130 / L4-PYTHON-REMAP=53
不允许从 Phase 0 直接跳到 PASS=82。
```
Phase 1生成 row-level bulk promotion plan
新增 artifact
```text
wasm-port/build/wasm/sim-configs-inventory/python-remap-bulk-promotion-plan.tsv
```
生成逻辑:
```text
读取 python-remap-boundary-summary.tsv
按 row 输出 proof requirements
默认 promotion_allowed=0
把 blockers 拆成:
python_runtime
prolog_epilog
python_remap_callable
ngc_remap_asset
hal_process_boundary
ui_process_boundary
halui_mdi_boundary
tool_change_boundary
twp_boundary
```
验收:
```text
plan row count = 53
每个 L4-PYTHON-REMAP row 有且只有一条 plan row
所有 plan row promotion_allowed=0
```
Phase 2扩展 PythonRemapRuntimePort 为 row runtime
当前 port 只证明 stop-lookahead lifecycle。批量 PASS 需要变成 row-aware runtime
```text
createLinuxCncPythonRemapRowRuntimePort({
path,
iniPath,
machineRoot,
pythonPathPrepend,
topLevelPath,
sourceFiles,
remapDeclarations,
prologFunctions,
epilogFunctions,
ngcRemapFiles,
runtimeAdapter,
})
```
必须支持:
```text
INI [PYTHON] PATH_PREPEND
INI [PYTHON] TOPLEVEL
configured Python module import
LinuxCNC stdglue prolog/epilog
Python remap callable lookup
Python generator lifecycle
LinuxCNC interpreter state / sentinel binding
LinuxCNC emccanon / canonical event bridge
reload-on-change behavior
```
禁止:
```text
JS 解释 G-code
JS 计算 remap result
JS 伪造 prolog/epilog semantic state
```
Phase 3处理 HAL/UI/HALUI 边界
因为 53/53 rows 有 HAL 声明49/53 rows 有 UI 进程声明5/53 rows 有 HALUI MDI
```text
批量 PASS 必须复用 virtual HAL proof
UI process 不能按 native GUI 执行,只能证明 browser/standalone UI boundary sufficient
HALUI MDI 必须有 LinuxCNC-owned command/state boundary
```
新增 artifact
```text
python-remap-hal-ui-boundary-proof.tsv
```
每个 row 必须明确:
```text
hal_process_boundary_ready=<0|1>
ui_process_boundary_ready=<0|1>
halui_mdi_boundary_ready=<0|1>
arbitrary_native_gui_launched=0
arbitrary_native_hal_daemon_required=0
```
Phase 4按 family 做执行 proof
推荐顺序:
```text
1. axis/remap/stop-lookahead/nc_files rows=1
2. axis/remap/cycle/nc_files rows=1
3. axis/remap/getting-started/nc_files rows=1
4. axis/remap/extend-builtins/nc_files rows=1 mixed NGC/Python guard
5. axis/remap/manual-toolchange-with-tool-length-switch rows=1 tool-change prolog/epilog
6. axis/remap/rack-toolchange/nc_files rows=1 tool-change prolog/epilog
7. axis/vismach/VMC_toolchange rows=1 tool-change body
8. axis/laser rows=3 Python remap callable family
9. axis/vismach/5axis/table-rotary.../demos rows=5 TWP + HALUI + mixed NGC/Python
10. gmoccapy/lathe_configs rows=8 stdglue prolog/epilog
11. gmoccapy/non_trivial_kinematics/... rows=5 stdglue + kinematics context
12. gmoccapy/macros rows=25 macro bulk family
```
每个 family 完成定义:
```text
native row pass evidence exists
WASM/Node row pass evidence exists
browser row diagnostics exists
promotion_allowed remains 0 until family review
```
Phase 5bulk promotion gate
新增 artifact
```text
python-remap-bulk-promotion-gate.tsv
```
gate 规则:
```text
all 53 row proof rows ready
all family proof rows ready
no ngc_only_subpath standalone violations
no JS CNC semantics violations
no missing vendored source files
no native/browser result drift
manual_lock_review=approved
```
只有 gate 全部满足时:
```text
promotion_allowed=1
execution_enabled=1
```
Phase 6inventory promotion implementation
允许改动的位置:
```text
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs
wasm-port/runtime/sdk/src/sim-config-staging.js
wasm-port/runtime/sdk/src/project-release-readiness.js
相关 docs/tests expected baseline
```
禁止实现方式:
```text
if blocked === "L4-PYTHON-REMAP" return PASS
```
允许实现方式:
```text
if blocked === "L4-PYTHON-REMAP" &&
row proof exists &&
bulk promotion gate approved &&
promotion_allowed === "1"
then execute through LinuxCNC-backed row runtime and count PASS
```
Phase 7baseline 更新
成功后必须更新:
```text
summary.tsv expected:
pass=82
skip=77
fail=0
skip-summary.tsv expected:
ASSET-ONLY=65
L4-USER-M-PROCESS=1
NON_MAIN_CLASS=10
UPSTREAM-DEMO=1
```
必须删除/更新:
```text
L4-PYTHON-REMAP=53 的 hard-block summary assertions
host-runtime docs 中 Python-remap locked wording
release artifact hard-block rows
workflow overview locked detail rows
```
五、下一步立即执行
不要直接做 Phase 6/7。
下一批从 Phase 1 开始:
```text
1. 在 verify_sim_configs_inventory_wasm.mjs 里生成 python-remap-bulk-promotion-plan.tsv
2. 增加验证函数,确认 53 rows 全部进入 plan
3. 增加 docs 说明bulk PASS 需要 row proof不接受 direct unlock
4. 保持 PASS=29 / SKIP=130 不变;
5. 跑 inventory/browser/release gate。
```
建议验收命令:
```bash
git diff --check
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
wasm-port/tests/browser/verify_python_remap_runtime_browser.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
wasm-port/tests/host/verify_project_release_gate.sh
```
六、本文件的执行状态
```text
已完成:
- 当前 53 rows 审计;
- bulk PASS 正确方法定义;
- Phase 1 目标和验收条件定义;
未完成:
- python-remap-bulk-promotion-plan.tsv 生成;
- row-aware PythonRemapRuntimePort
- per-family execution proof
- promotion_allowed=1
- PASS=82 / SKIP=77 baseline update。
```

287
text27.txt Normal file
View File

@@ -0,0 +1,287 @@
项目接续文件L4-PYTHON-REMAP 53 rows 批量 PASS Phase 1 执行记录
生成时间2026-06-19 CST
本文件接替 `text26.txt`。
目标:
```text
把“53 个 Python-remap inventory rows 批量提升为 PASS”的方法落成第一层
machine-readable promotion plan。
本轮只做 Phase 1
- 生成 `python-remap-bulk-promotion-plan.tsv`
- 校验它 1:1 覆盖 53 个 `L4-PYTHON-REMAP` rows
- 明确每行还缺哪些 proof
- 保持 inventory baseline 不变PASS=29 / SKIP=130 / L4-PYTHON-REMAP=53。
```
禁止事项:
```text
- 不把 53 行直接改 PASS
- 不删除 `L4-PYTHON-REMAP` skip reason
- 不把 stop-lookahead lifecycle proof 扩大解释为 53 行全量 pass
- 不让 fake/browser worker 充当 PASS 证据;
- 不把 Python import success 当作 remap execution proof
- 不让 NGC-only remap subpath 被当成 standalone main program
- 不手工改 `summary.tsv` / `skip-summary.tsv` 数字。
```
一、Phase 1 应生成的 artifact
新增:
```text
wasm-port/build/wasm/sim-configs-inventory/python-remap-bulk-promotion-plan.tsv
```
来源:
```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-native-runtime-fixture-plan.tsv
wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-probe-gate.tsv
```
输出字段必须表达:
```text
path
ini
family
main_or_macro_load_class
python_modules_staged
toplevel_executed
path_prepend_applied
python_remap_functions_bound
prolog_functions_bound
epilog_functions_bound
ngc_remap_assets_staged
ngc_only_subpaths_not_standalone
interpreter_state_binding_ready
canonical_events_ready
hal_state_boundary_ready
ui_process_boundary_ready
halui_mdi_boundary_ready
native_pass_ready
wasm_node_pass_ready
browser_pass_ready
execution_enabled
promotion_allowed
blockers
notes
```
二、Phase 1 验收条件
```text
plan row count = 53
plan path set == python-remap-boundary-summary path set
所有 row blocked = L4-PYTHON-REMAP
所有 row execution_enabled = 0
所有 row promotion_allowed = 0
所有 row native_pass_ready = 0
所有 row wasm_node_pass_ready = 0
所有 row browser_pass_ready = 0
所有 row blockers 非空
HAL/UI/HALUI flags 与 boundary summary 保持一致
存在 NGC-only subpath 的 row 必须显式记录 ngc_remap_asset / ngc_only_subpaths_not_standalone blocker
```
三、后续 Phase 2/3 的输入
Phase 2 扩展 row-aware runtime 时消费:
```text
path
ini
family
python_modules_staged
toplevel_executed
path_prepend_applied
python_remap_functions_bound
prolog_functions_bound
epilog_functions_bound
ngc_remap_assets_staged
ngc_only_subpaths_not_standalone
interpreter_state_binding_ready
canonical_events_ready
```
Phase 3 HAL/UI/HALUI proof 时消费:
```text
hal_state_boundary_ready
ui_process_boundary_ready
halui_mdi_boundary_ready
```
四、本轮应改文件
```text
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs
wasm-port/tests/browser/interp_smoke.html
wasm-port/docs/compatibility-validation.md
wasm-port/docs/full-process-boundary-design.md
wasm-port/docs/sim-configs-coverage-matrix.md
text27.txt
```
五、本轮验收命令
```bash
git diff --check
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
wasm-port/tests/browser/verify_python_remap_runtime_browser.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
wasm-port/tests/host/verify_project_release_gate.sh
```
六、完成后允许进入的下一步
```text
Phase 2实现 row-aware Python remap runtime port。
下一轮不要先改 baseline。
先让 `python-remap-row-runtime-proof.tsv` 能逐 row 证明:
- INI [PYTHON] PATH_PREPEND
- INI [PYTHON] TOPLEVEL
- configured module import
- Python remap callable / stdglue prolog / epilog lookup
- generator lifecycle
- interpreter state / canonical event bridge
- NGC remap assets staged
- NGC-only subpaths 不作为 standalone main。
```
七、2026-06-19 Phase 2 执行结果
本轮已完成 Phase 2 的 proof substrate不做 baseline promotion。
新增 / 更新:
```text
wasm-port/runtime/sdk/src/python-remap-runtime-port.js
新增 createLinuxCncPythonRemapRowRuntimePort()
新增 createPythonRemapRowRuntimePlan()
新增 validatePythonRemapRowRuntimeTranscript()
新增 PYTHON_REMAP_ROW_RUNTIME_PROOF_PHASES
wasm-port/runtime/sdk/src/index.js
导出 row-aware Python remap runtime API
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.mjs
覆盖 row runtime contract-only blocked path
覆盖 fake row runtime adapter ready path
验证 module/callable/NGC/canonical proof transcript
wasm-port/tests/sdk/node/verify_sdk_surface.mjs
wasm-port/runtime/sdk/README.md
同步 SDK public surface
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs
新增 python-remap-row-runtime-proof.tsv 生成与校验
wasm-port/tests/browser/interp_smoke.html
wasm-port/docs/compatibility-validation.md
wasm-port/docs/full-process-boundary-design.md
wasm-port/docs/sim-configs-coverage-matrix.md
generated artifact coverage 从 56 个 WASM TSV 更新到 57 个
```
新增 artifact
```text
wasm-port/build/wasm/sim-configs-inventory/python-remap-row-runtime-proof.tsv
```
artifact 当前状态:
```text
rows=53
row_runtime_port_api_ready=1 53
row_runtime_plan_ready=1 53
row_runtime_transcript_ready=0 53
native_pass_ready=0 53
wasm_node_pass_ready=0 53
browser_pass_ready=0 53
execution_enabled=0 53
proof_status=pending_row_runtime_adapter_execution 53
```
inventory baseline 保持不变:
```text
sim_configs_wasm_node_inventory_executed=29
sim_configs_wasm_node_inventory_passed=29
sim_configs_wasm_node_inventory_skipped=130
sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53
sim_configs_wasm_node_inventory_unexpected_fail=0
```
本轮明确没有完成:
```text
- 未执行 53 rows 的真实 Python remap callbacks
- 未把 row_runtime_transcript_ready 置 1
- 未把 native_pass_ready / wasm_node_pass_ready / browser_pass_ready 置 1
- 未解除 manual promotion lock
- 未把 PASS=29 / SKIP=130 改为 PASS=82 / SKIP=77。
```
八、本轮已通过验证
```bash
git diff --check
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.sh
node wasm-port/tests/sdk/node/verify_sdk_surface.mjs
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
wasm-port/tests/wasm/node/verify_python_remap_runtime_port_wasm.sh
wasm-port/tests/browser/verify_python_remap_runtime_browser.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
wasm-port/tests/host/verify_project_release_gate.sh
```
九、下一步工作建议
建议下一轮进入 Phase 2.5,而不是直接 Phase 3 或 baseline promotion。
Phase 2.5 目标:
```text
让 python-remap-row-runtime-proof.tsv 对第一批小 family 产生真实 row runtime transcript
1. axis/remap/stop-lookahead/nc_files
2. axis/remap/cycle/nc_files
3. axis/remap/getting-started/nc_files
```
推荐顺序:
```text
1. 为 createLinuxCncPythonRemapRowRuntimePort() 增加 Node/WASM row adapter
2. adapter 只负责调用 LinuxCNC-owned Python runtime boundary不解释 G-code
3. 先让 stop-lookahead row 的:
python_modules_staged=1
toplevel_executed=1
path_prepend_applied=1
interpreter_state_binding_ready=1
canonical_events_ready=1
row_runtime_transcript_ready=1
4. cycle / getting-started 再加入 callable、prolog/epilog、NGC remap asset targets
5. 继续保持 native_pass_ready=0 / wasm_node_pass_ready=0 / browser_pass_ready=0
直到 row runtime transcript 可以和 native proof 对齐。
```
下一轮仍禁止:
```text
- 不改 PASS/SKIP baseline
- 不把 fake row adapter proof 当真实 PASS
- 不把 module import 成功当 row execution pass
- 不把 NGC-only subpath 当 standalone main program
- 不解除 promotion_allowed。
```

617
text28.txt Normal file
View File

@@ -0,0 +1,617 @@
项目接续文件L4-PYTHON-REMAP 53 rows 批量 PASS Phase 2.5 执行规范
生成时间2026-06-19 CST
本文件接替 `text27.txt`。
目标:
```text
继续推进“53 个 Python-remap inventory rows 批量提升为 PASS”的 proof 链路。
本轮不是 baseline promotion。
本轮只做 Phase 2.5
- 固化 53 rows 最终批量 PASS 的方法和步骤;
- 让 `python-remap-row-runtime-proof.tsv` 至少对第一批小 family 产生真实 row runtime transcript
- 优先让 stop-lookahead 首行从 pending 进入 transcript-ready
- 继续保持 inventory baseline 不变PASS=29 / SKIP=130 / L4-PYTHON-REMAP=53。
```
一、53 rows 批量 PASS 的完整方法
```text
Phase 1machine-readable bulk promotion plan
- 生成 `python-remap-bulk-promotion-plan.tsv`
- 1:1 覆盖 53 个 `L4-PYTHON-REMAP` rows
- 明确每行缺少的 runtime/native/node/browser proof
- 保持 execution_enabled=0、promotion_allowed=0。
Phase 2row-aware runtime proof substrate
- SDK 暴露 `createLinuxCncPythonRemapRowRuntimePort()`
- 生成 `python-remap-row-runtime-proof.tsv`
- 每行表达 module staging、TOPLEVEL、PATH_PREPEND、callable/prolog/epilog、NGC asset、interpreter state、canonical event 的 proof target
- 仍不把 fake adapter 或 Python import-only 当 PASS。
Phase 2.5:第一批真实 row runtime transcript
- 对小 family 先落真实 transcript而不是全量一次性 promotion
- 第一批顺序:
1. `axis/remap/stop-lookahead/nc_files`
2. `axis/remap/cycle/nc_files`
3. `axis/remap/getting-started/nc_files`
- transcript 必须来自 LinuxCNC-owned Python runtime boundary 或 native lifecycle stdout
- transcript ready 只解除 `row_runtime_adapter_execution` blocker
- 不解除 native/node/browser/manual promotion blockers。
Phase 3row native pass proof
- 逐 row 证明 native runtime pass
- 对齐 `python-remap-row-runtime-proof.tsv` 的 proof targets
- native proof 不能只证明 stop-lookahead lifecycle 后推断 53 rows 全 PASS。
Phase 4WASM Node pass proof
- 逐 row 证明 WASM Node runtime bridge 可执行同等 row contract
- 保留 LinuxCNC-owned semantics禁止 JS 重写 CNC 语义。
Phase 5Browser pass proof
- 逐 row 证明 browser worker / UI workflow 与 Node proof 对齐;
- browser proof 必须在 Node proof 后接受。
Phase 6manual promotion lock review
- 只有 row_runtime、native、wasm_node、browser proof 全部 ready 后,才允许解除 manual lock
- 最终才可把 53 rows 从 `L4-PYTHON-REMAP` 批量提升为 PASS
- 目标 baseline 才能从 PASS=29 / SKIP=130 变为 PASS=82 / SKIP=77。
```
二、本轮 Phase 2.5 执行步骤
```text
1. 先确认 native Python lifecycle stdout 是否存在:
`wasm-port/build/native/python-remap-runtime/python_lifecycle.stdout.log`
2. 确认 native runtime probe summary 是否存在并包含:
`wasm-port/build/native/native-runtime-probe-summary.tsv`
`L4-PYTHON-REMAP python_runtime axis/remap/stop-lookahead/nc_files`
`runtime_lifecycle_probe_passed`
3. 运行 inventory verifier
`SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh`
4. 验收 `python-remap-row-runtime-proof.tsv`
- rows=53
- `axis/remap/stop-lookahead/nc_files/examples.ngc`
- `python_modules_staged=1`
- `toplevel_executed=1`
- `path_prepend_applied=1`
- `interpreter_state_binding_ready=1`
- `canonical_events_ready=1`
- `row_runtime_transcript_ready=1`
- `proof_status=native_lifecycle_stdout_row_runtime_transcript_ready`
- 其他 rows 可继续 pending
- 所有 rows 仍保持:
- `native_pass_ready=0`
- `wasm_node_pass_ready=0`
- `browser_pass_ready=0`
- `execution_enabled=0`
5. 如果 stop-lookahead 仍 pending
- 优先修正 verifier 对 native stdout / native summary 的读取和验收;
- 不伪造 row transcript
- 不把 build artifact 手工改成 ready。
6. stop-lookahead 通过后,下一轮再扩展 cycle / getting-started
- 增加 row adapter 对 callable/prolog/epilog lookup 的真实 proof
- 增加 NGC remap asset staged proof
- 对 NGC-only subpaths 明确 reject standalone proof。
```
三、本轮禁止事项
```text
- 不改 PASS/SKIP baseline
- 不直接把 53 rows 改 PASS
- 不删除 `L4-PYTHON-REMAP` skip reason
- 不把 stop-lookahead lifecycle proof 解释成 53 rows 全量 pass
- 不让 fake/browser worker 充当 PASS 证据;
- 不把 Python import success 当 row execution proof
- 不把 NGC-only subpath 当 standalone main program
- 不解除 `manual_promotion_lock`
- 不手工改 `summary.tsv` / `skip-summary.tsv` 数字。
```
四、本轮应关注文件
```text
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
wasm-port/runtime/sdk/src/python-remap-runtime-port.js
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.mjs
wasm-port/build/wasm/sim-configs-inventory/python-remap-row-runtime-proof.tsv
text28.txt
```
五、本轮验收命令
```bash
git diff --check
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
```
六、完成后写入本文件的结果
```text
记录:
- stop-lookahead row runtime transcript 是否 ready
- 53 rows row_runtime_proof 状态分布;
- inventory baseline 是否保持不变;
- 已通过和未通过的验证命令;
- 下一轮 cycle / getting-started 的具体入口。
```
七、2026-06-19 Phase 2.5 执行结果
本轮已完成 stop-lookahead 首行的真实 row runtime transcript 接入,不做 baseline promotion。
代码修正:
```text
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs
- 修正 `python-remap-row-runtime-proof.tsv` 生成列映射;
- `generator_lifecycle_ready` 跟随 stop-lookahead native lifecycle stdout proof
- `ngc_only_subpaths_not_standalone` 回到正确列位;
- 调用 `pythonRemapRowRuntimeProofRows()` / `verifyPythonRemapRowRuntimeProofRows()` 时传入 `nativeRuntimeProbeSourceRecords`
- row proof 现在能读取 `wasm-port/build/native/native-runtime-probe-summary.tsv` 和对应 stdout log。
```
新增接续文件:
```text
text28.txt
```
artifact 当前状态:
```text
wasm-port/build/wasm/sim-configs-inventory/python-remap-row-runtime-proof.tsv
rows=53
proof_status=native_lifecycle_stdout_row_runtime_transcript_ready 1
proof_status=pending_row_runtime_adapter_execution 52
```
stop-lookahead 首行状态:
```text
path=axis/remap/stop-lookahead/nc_files/examples.ngc
python_modules_staged=1
toplevel_executed=1
path_prepend_applied=1
generator_lifecycle_ready=1
interpreter_state_binding_ready=1
canonical_events_ready=1
row_runtime_transcript_ready=1
native_pass_ready=0
wasm_node_pass_ready=0
browser_pass_ready=0
execution_enabled=0
proof_status=native_lifecycle_stdout_row_runtime_transcript_ready
```
仍未解除的 blockers
```text
browser_row_pass
hal_process_boundary
manual_promotion_lock
native_row_pass
python_runtime
row_runtime_proof
wasm_node_row_pass
```
inventory baseline 保持不变:
```text
sim_configs_wasm_node_inventory_executed=29
sim_configs_wasm_node_inventory_passed=29
sim_configs_wasm_node_inventory_skipped=130
sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53
sim_configs_wasm_node_inventory_unexpected_fail=0
```
本轮已通过验证:
```bash
git diff --check
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
```
验证中的已知非失败 stderr
```text
/work/sim-inventory/axis/db_demo/base.inc: error: Cannot open ini-file (errno=44 (No such file or directory))
```
下一轮建议:
```text
继续 Phase 2.5,不进入 baseline promotion。
下一轮优先扩展:
1. `axis/remap/cycle/nc_files`
2. `axis/remap/getting-started/nc_files`
具体入口:
- 为 row-aware runtime adapter 增加 callable/prolog/epilog lookup transcript
- 对 cycle 行证明:
- configured module import
- Python remap callable lookup
- stdglue prolog / epilog lookup
- generator lifecycle
- NGC remap asset staged
- interpreter state / canonical event bridge
- 对 getting-started 行证明同等 row contract
- 保持 native_pass_ready / wasm_node_pass_ready / browser_pass_ready / execution_enabled 全部为 0。
```
八、2026-06-19 Phase 2.5 cycle / getting-started 执行结果
本轮继续 Phase 2.5,完成第一批小 family 的 row runtime transcript 扩展,不做 baseline promotion。
代码修正:
```text
wasm-port/tests/native/probe_python_remap_runtime.sh
- 保留 stop-lookahead 旧 lifecycle stdout keys
- 新增 row-specific transcript keys
- axis/remap/stop-lookahead/nc_files/examples.ngc
- axis/remap/cycle/nc_files/examples.ngc
- axis/remap/getting-started/nc_files/examples.ngc
- 对 cycle 真实证明:
- INI [PYTHON] PATH_PREPEND / TOPLEVEL
- stdglue、remap、toplevel module staged
- g842 callable lookup and invoke
- cycle_prolog / cycle_epilog lookup
- g843.ngc remap asset staged
- interpreter state / canonical event bridge ready。
- 对 getting-started 真实证明:
- INI [PYTHON] PATH_PREPEND / TOPLEVEL
- oword、remap、toplevel、util module staged
- m400.ngc / m410.ngc remap assets staged
- NGC-only subpaths rejected as standalone main
- interpreter state / canonical event bridge ready。
wasm-port/tools/build_native_probes.sh
- native probe cache fingerprint 加入 axis/remap/getting-started source tree。
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs
- row proof 改为读取 native Python runtime stdout 的 row-specific keys
- 允许 cycle/getting-started 从 native lifecycle stdout 进入 row runtime transcript ready
- 仍保持 native_pass_ready / wasm_node_pass_ready / browser_pass_ready / execution_enabled 全部为 0
- 仍不解除 manual promotion lock。
```
artifact 当前状态:
```text
wasm-port/build/wasm/sim-configs-inventory/python-remap-row-runtime-proof.tsv
rows=53
proof_status=native_lifecycle_stdout_row_runtime_transcript_ready 3
proof_status=pending_row_runtime_adapter_execution 50
```
本轮新增 ready rows
```text
axis/remap/cycle/nc_files/examples.ngc
python_modules_staged=1
python_remap_functions_bound=1
prolog_functions_bound=1
epilog_functions_bound=1
generator_lifecycle_ready=1
ngc_remap_assets_staged=1
ngc_only_subpaths_not_standalone=1
row_runtime_transcript_ready=1
proof_status=native_lifecycle_stdout_row_runtime_transcript_ready
axis/remap/getting-started/nc_files/examples.ngc
python_modules_staged=1
python_remap_functions_bound=0
prolog_functions_bound=0
epilog_functions_bound=0
generator_lifecycle_ready=0
ngc_remap_assets_staged=1
ngc_only_subpaths_not_standalone=1
row_runtime_transcript_ready=1
proof_status=native_lifecycle_stdout_row_runtime_transcript_ready
```
inventory baseline 保持不变:
```text
sim_configs_wasm_node_inventory_executed=29
sim_configs_wasm_node_inventory_passed=29
sim_configs_wasm_node_inventory_skipped=130
sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53
sim_configs_wasm_node_inventory_unexpected_fail=0
```
本轮已通过验证:
```bash
git diff --check
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
wasm-port/tests/native/verify_native_probes.sh
```
验证中的已知非失败 stderr
```text
/work/sim-inventory/axis/db_demo/base.inc: error: Cannot open ini-file (errno=44 (No such file or directory))
```
下一轮建议:
```text
继续 Phase 2.5,不进入 baseline promotion。
下一轮可以选择:
1. 扩展 axis/laser family 的 row-specific transcript
2. 或扩展 vismach/5axis table-rotary_spindle-rotary-nutating family
3. 或开始设计 Phase 3 row native pass proof contract。
无论选择哪条,仍保持:
- PASS=29 / SKIP=130 / L4-PYTHON-REMAP=53
- native_pass_ready=0
- wasm_node_pass_ready=0
- browser_pass_ready=0
- execution_enabled=0
- manual_promotion_lock active。
```
九、2026-06-19 批量 PASS 请求 gate 复核与 axis/laser 推进结果
本轮按“text28.txt 大建议”复核 53 个 Python-remap inventory rows 是否可以批量提升为 PASS。
结论:
```text
不能直接批量 PASS。
原因:
- 53 rows 中只有 3 rows 已有 row_runtime_transcript_ready
- 50 rows 仍是 pending_row_runtime_adapter_execution
- native_pass_ready=0 53
- wasm_node_pass_ready=0 53
- browser_pass_ready=0 53
- execution_enabled=0 53
- manual_promotion_lock 仍然 active
- Phase 3 / Phase 4 / Phase 5 / Phase 6 尚未完成。
```
因此本轮没有把 `L4-PYTHON-REMAP` rows 改 PASS没有改 baseline 数字。
本轮可安全推进的内容:
```text
继续 Phase 2.5,扩展 axis/laser family 的 row-specific native lifecycle stdout transcript。
```
代码修正:
```text
wasm-port/tests/native/probe_python_remap_runtime.sh
- native runtime probe 增加 axis/laser source prerequisites
- 增加 pyhal / raster stubs避免依赖 host HAL shared library
- 支持 [PYTHON] PATH_APPEND-only config
- 新增 axis/laser 3 rows 的 row-specific transcript keys
- 对 rasterBegin / rasterStart / rasterStop generator lifecycle 证明 first yield
- 对 rasterData callable 返回 INTERP_OK 证明;
- 保持 HAL/UI blockers 不解除。
```
artifact 当前状态:
```text
wasm-port/build/wasm/sim-configs-inventory/python-remap-row-runtime-proof.tsv
rows=53
proof_status=native_lifecycle_stdout_row_runtime_transcript_ready 6
proof_status=pending_row_runtime_adapter_execution 47
native_pass_ready=0 53
wasm_node_pass_ready=0 53
browser_pass_ready=0 53
execution_enabled=0 53
```
本轮新增 ready rows
```text
axis/laser/raster_test.ngc
axis/laser/vector_test.ngc
axis/laser/vector_test2.ngc
```
新增 ready rows 状态:
```text
python_modules_staged=1
python_remap_functions_bound=1
generator_lifecycle_ready=1
row_runtime_transcript_ready=1
proof_status=native_lifecycle_stdout_row_runtime_transcript_ready
```
inventory baseline 保持不变:
```text
sim_configs_wasm_node_inventory_executed=29
sim_configs_wasm_node_inventory_passed=29
sim_configs_wasm_node_inventory_skipped=130
sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53
sim_configs_wasm_node_inventory_unexpected_fail=0
```
本轮已通过验证:
```bash
git diff --check
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
wasm-port/tests/native/verify_native_probes.sh
```
下一轮建议:
```text
继续 Phase 2.5,不进入 baseline promotion。
优先扩展剩余 row runtime transcript
1. axis/remap/extend-builtins/nc_files
2. axis/remap/manual-toolchange-with-tool-length-switch/nc_files
3. axis/remap/rack-toolchange/nc_files
4. axis/vismach/VMC_toolchange
5. gmoccapy/lathe_configs
6. gmoccapy/macros
7. gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples
8. axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos
只有 53/53 rows 同时满足:
- row_runtime_transcript_ready=1
- native_pass_ready=1
- wasm_node_pass_ready=1
- browser_pass_ready=1
- execution_enabled=1
- manual_promotion_lock reviewed/unlocked
才允许进入真正 PASS=82 / SKIP=77 的 baseline promotion。
```
十、2026-06-19 批量 PASS 请求 gate 复核与 axis/remap toolchange 推进结果
本轮再次按“text28.txt”的 promotion gate 复核 53 个 Python-remap inventory rows 是否可以批量提升为 PASS。
结论:
```text
仍不能直接批量 PASS。
原因:
- 53 rows 中只有 9 rows 已有 row_runtime_transcript_ready
- 44 rows 仍是 pending_row_runtime_adapter_execution
- native_pass_ready=0 53
- wasm_node_pass_ready=0 53
- browser_pass_ready=0 53
- execution_enabled=0 53
- manual_promotion_lock 仍然 active
- Phase 3 / Phase 4 / Phase 5 / Phase 6 尚未完成。
```
因此本轮没有把 `L4-PYTHON-REMAP` rows 改 PASS没有改 baseline 数字。
本轮可安全推进的内容:
```text
继续 Phase 2.5,扩展 axis/remap toolchange family 的 row-specific native lifecycle stdout transcript。
```
代码修正:
```text
wasm-port/tests/native/probe_python_remap_runtime.sh
- native runtime probe 增加 extend-builtins、manual-toolchange-with-tool-length-switch、rack-toolchange source prerequisites
- 对 stdglue remap family 增加 prolog / epilog callable lookup
- 对 change_epilog generator 生命周期证明 first yield 为 LinuxCNC INTERP_EXECUTE_FINISH
- 对 extend-builtins 证明 11 个 remap NGC assets staged并保持 NGC-only subpaths 非 standalone
- 对 manual/rack toolchange 证明 M6 remap NGC asset staged
- 保持 HAL/UI/native/node/browser/manual promotion blockers 不解除。
wasm-port/tools/build_native_probes.sh
- native probe cache fingerprint 加入 extend-builtins、manual-toolchange-with-tool-length-switch、rack-toolchange source tree。
```
artifact 当前状态:
```text
wasm-port/build/wasm/sim-configs-inventory/python-remap-row-runtime-proof.tsv
rows=53
proof_status=native_lifecycle_stdout_row_runtime_transcript_ready 9
proof_status=pending_row_runtime_adapter_execution 44
native_pass_ready=0 53
wasm_node_pass_ready=0 53
browser_pass_ready=0 53
execution_enabled=0 53
```
本轮新增 ready rows
```text
axis/remap/extend-builtins/nc_files/examples.ngc
axis/remap/manual-toolchange-with-tool-length-switch/nc_files/tcdemo.ngc
axis/remap/rack-toolchange/nc_files/tcdemo.ngc
```
新增 ready rows 状态:
```text
python_modules_staged=1
prolog_functions_bound=1
epilog_functions_bound=1
generator_lifecycle_ready=1
ngc_remap_assets_staged=1
row_runtime_transcript_ready=1
proof_status=native_lifecycle_stdout_row_runtime_transcript_ready
```
inventory baseline 保持不变:
```text
sim_configs_wasm_node_inventory_executed=29
sim_configs_wasm_node_inventory_passed=29
sim_configs_wasm_node_inventory_skipped=130
sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53
sim_configs_wasm_node_inventory_unexpected_fail=0
```
本轮已通过验证:
```bash
git diff --check
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh
wasm-port/tests/sdk/node/verify_python_remap_runtime_port.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
```
验证中的已知非失败 stderr
```text
/work/sim-inventory/axis/db_demo/base.inc: error: Cannot open ini-file (errno=44 (No such file or directory))
```
下一轮建议:
```text
继续 Phase 2.5,不进入 baseline promotion。
优先扩展剩余 row runtime transcript
1. axis/vismach/VMC_toolchange
2. gmoccapy/lathe_configs
3. gmoccapy/macros
4. gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples
5. axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos
只有 53/53 rows 同时满足:
- row_runtime_transcript_ready=1
- native_pass_ready=1
- wasm_node_pass_ready=1
- browser_pass_ready=1
- execution_enabled=1
- manual_promotion_lock reviewed/unlocked
才允许进入真正 PASS=82 / SKIP=77 的 baseline promotion。
```

View File

@@ -200,6 +200,8 @@ wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-probe-gat
wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-state-plan.tsv
wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-fixture-plan.tsv
wasm-port/build/wasm/sim-configs-inventory/python-remap-family-summary.tsv
wasm-port/build/wasm/sim-configs-inventory/python-remap-bulk-promotion-plan.tsv
wasm-port/build/wasm/sim-configs-inventory/python-remap-row-runtime-proof.tsv
wasm-port/build/wasm/sim-configs-inventory/boundary-phase-completion-summary.tsv
wasm-port/build/wasm/sim-configs-inventory/native-proof-alignment-summary.tsv
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-native-alignment-summary.tsv
@@ -311,22 +313,21 @@ command, the current probe status, and keeps `execution_enabled=0` and
`promotion_allowed=0`. It does not run probes or relax promotion locks.
`runtime-boundary-host-requirement-summary.tsv` normalizes those preflight
requirements into one row per host command or source/module prerequisite, so
missing `halcmd`, `halrun`, `linuxcnc`, and `milltask` can be audited by the
blocked families they affect. `runtime-boundary-host-unblock-plan.tsv`
filters that table to unavailable requirements and records the affected
opt-in commands and remaining missing requirements; it is a planning artifact
only.
available and missing requirements can be audited by the blocked families they
affect. `runtime-boundary-host-unblock-plan.tsv` filters that table to
unavailable requirements and records the affected opt-in commands and
remaining missing requirements; it is a planning artifact only.
`runtime-boundary-family-host-readiness.tsv` reduces the same host evidence to
one row per blocked runtime family, and
`runtime-boundary-host-readiness-rollup.tsv` gives the aggregate decision. On
this host the rollup remains `host_blocked_for_all_opt_in_native_probes` with
zero ready opt-in commands. These rows are non-executing and non-promoting.
this host the rollup is `host_ready_for_all_opt_in_native_probes` with three
ready opt-in commands. These rows are non-executing and non-promoting.
`runtime-boundary-opt-in-probe-dispatch-plan.tsv` turns each family readiness
row into a per-probe dispatch action. `runtime-boundary-opt-in-probe-dispatch-rollup.tsv`
then gives the aggregate dispatch switch; currently every blocked runtime
probe is skipped for missing host requirements. Dispatch permission only means
the guarded native probe may be manually run on a ready host, not that the row
is promoted.
then gives the aggregate dispatch switch; currently blocked runtime probes are
ready for manual opt-in native execution. Dispatch permission only means the
guarded native probe may be manually run on a ready host, not that the row is
promoted.
`runtime-boundary-opt-in-probe-skip-evidence-contract.tsv` records why each
non-dispatched probe is skipped and whether native pass evidence is required
yet. `runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv` gives the
@@ -403,10 +404,10 @@ coverage: every TSV emitted under
`full-process-boundary-design.md`. New gate artifacts must therefore be
documented before the inventory can pass.
The Node coverage gate also requires the generated WASM inventory artifact list
to remain the exact duplicate-free 55-entry baseline and the native generated
to remain the exact duplicate-free 57-entry baseline and the native generated
TSV token list to remain the exact duplicate-free 8-token baseline. It also
requires the corresponding `boundary-phase-completion-summary.tsv`
documentation-coverage counts to remain `55` and `8`, respectively. It also
documentation-coverage counts to remain `57` and `8`, respectively. It also
checks that the browser smoke source lists the same generated WASM and native
artifact tokens and preserves the same fixed-count, duplicate-free,
fetchability/header, documentation-missing, and completion count-parity guards
@@ -530,6 +531,18 @@ Python runtime lifecycle fixture, `axis/remap/stop-lookahead/nc_files`, because
it exercises Python runtime phases and configured modules without Python
callable or NGC-only subpath complexity. It is still a fixture plan only:
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
`python-remap-bulk-promotion-plan.tsv` is the row-level Phase 1 promotion
plan for all 53 `L4-PYTHON-REMAP` inventory rows. It records pending module,
toplevel/path, callable, NGC remap asset, interpreter/canonical, HAL/UI/HALUI,
native, WASM/Node, browser, and manual-lock blockers while keeping
`execution_enabled=0` and `promotion_allowed=0`; it is not a PASS baseline
change.
`python-remap-row-runtime-proof.tsv` is the Phase 2 row-runtime proof table.
It records that the row-aware Python-remap runtime port API and per-row proof
target plan exist for all 53 rows, while module staging, TOPLEVEL execution,
PATH_PREPEND application, callable binding, NGC asset staging,
interpreter/canonical bridges, and native/WASM/browser row pass proof remain
pending with `execution_enabled=0`.
When explicitly enabled with `ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1`,
`probe_python_remap_runtime.sh` follows the fixture `demo.ini` Python path and
toplevel declarations, imports the vendored modules, verifies `queuebuster`

View File

@@ -424,6 +424,17 @@ modules but avoids Python callable and NGC-only subpath complexity. The
fixture plan is still non-executing: it records the proof target only and
keeps `proof_status=pending`, `execution_enabled=0`, and
`promotion_allowed=0`.
`python-remap-bulk-promotion-plan.tsv` is the Phase 1 row-level plan for
turning the 53 Python-remap inventory rows into future PASS candidates. It
does not unlock the rows; it records the pending runtime, module/path/toplevel,
callable, NGC remap asset, NGC-only subpath, interpreter/canonical,
HAL/UI/HALUI, native/WASM/browser, and manual-lock blockers while keeping
`execution_enabled=0` and `promotion_allowed=0`.
`python-remap-row-runtime-proof.tsv` is the Phase 2 row-runtime proof table.
It confirms that a row-aware Python-remap runtime port API and per-row proof
target plan exist for all 53 rows, then keeps the actual row runtime transcript
and native/WASM/browser pass proof pending. It is not an inventory promotion
gate and keeps `execution_enabled=0`.
`tests/native/probe_python_remap_runtime.sh` is wired into
`build_native_probes.sh` as `linuxcnc_python_remap_runtime_probe`. It records
the stop-lookahead fixture identity, configured Python modules, required
@@ -510,13 +521,13 @@ cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-native-evidence-
cat wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-promotion-blockers.tsv
```
The current host is not ready for any blocked runtime opt-in probe. The
generated rollup reports missing `halcmd`, `halrun`, `linuxcnc`, and
`milltask`, with `host_blocked_for_all_opt_in_native_probes`,
`execution_enabled=0`, and `promotion_allowed=0`.
The current host is ready for manual blocked-runtime opt-in probes. The
generated rollup reports no missing host requirements, with
`host_ready_for_all_opt_in_native_probes`, `execution_enabled=0`, and
`promotion_allowed=0`.
The opt-in commands remain forbidden while their dispatch rows have
`dispatch_allowed=0`:
The opt-in commands remain manual proof gates; dispatch rows may have
`dispatch_allowed=1`, but that does not enable execution or promotion:
```bash
ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh

View File

@@ -12,14 +12,19 @@ Detailed references:
- `docs/sim-configs-coverage-handoff.md`
- `tests/wasm/node/verify_sim_configs_inventory_wasm.mjs`
## Current blocked runtime families
## Current runtime boundary families
The current hard blocked families are:
The current runtime-boundary proof families are:
- `L4-USER-M-PROCESS`
- `L4-TOOL-DB`
- `L4-PYTHON-REMAP`
The current Node inventory skip/block summary still has `L4-PYTHON-REMAP=53`
and `L4-USER-M-PROCESS=1`. `L4-TOOL-DB` is no longer a skipped inventory row,
but it remains represented in the runtime-boundary proof ledger with
`promotion_allowed=0`.
Current host readiness is summarized by:
- `build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv`
@@ -32,13 +37,13 @@ Current host readiness is summarized by:
Current rollup state:
```text
host_readiness_status=host_blocked_for_all_opt_in_native_probes
host_readiness_status=host_ready_for_all_opt_in_native_probes
family_count=3
host_ready_family_count=0
host_blocked_family_count=3
blocked_families=L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS
missing_host_requirements=halcmd,halrun,linuxcnc,milltask
ready_opt_in_command_count=0
host_ready_family_count=3
host_blocked_family_count=0
blocked_families=-
missing_host_requirements=-
ready_opt_in_command_count=3
execution_enabled=0
promotion_allowed=0
```
@@ -48,10 +53,9 @@ promotion_allowed=0
Each blocked family currently has `promotion_ready=0`, `execution_enabled=0`,
and `promotion_allowed=0`.
The current blocker keys are:
For `L4-USER-M-PROCESS` and `L4-TOOL-DB`, the current blocker keys are:
```text
host_runtime_requirements_missing
native_runtime_probe_not_passed
native_pass_evidence_not_ready
node_inventory_gate_not_complete
@@ -60,25 +64,46 @@ promotion_lock_active
manual_lock_update_required
```
The next unblock action is to provide the missing host runtime requirements for
the relevant family. Providing host commands is not enough to promote a row; it
only allows the next opt-in probe gate to be considered.
For the first Python remap fixture, `axis/remap/stop-lookahead/nc_files`, the
native lifecycle evidence has been accepted and the Node/browser lifecycle
gates are complete:
```text
current_probe_status=runtime_lifecycle_probe_passed
native_evidence_status=native_pass_evidence_observed
node_inventory_gate_complete=1
browser_smoke_gate_complete=1
node_gate_status=node_inventory_promotion_gate_complete
browser_gate_status=browser_smoke_promotion_gate_complete
blocking_reason=promotion_lock_active_manual_review_required
next_unblock_action=manual_lock_review_required
```
The Python row still has `promotion_ready=0`, `execution_enabled=0`, and
`promotion_allowed=0`; the remaining blockers are `promotion_lock_active` and
`manual_lock_update_required`. This proof-chain state does not promote any of
the 53 `L4-PYTHON-REMAP` rows.
For rows that still lack native evidence, the next unblock action is to provide
the missing host runtime requirements for the relevant family. Providing host
commands is not enough to promote a row; it only allows the next opt-in probe
gate to be considered.
## Opt-in probe dispatch
The current dispatch rollup is:
```text
dispatch_status=dispatch_blocked_for_all_opt_in_native_probes
dispatch_status=dispatch_allowed_for_all_opt_in_native_probes
probe_count=3
dispatch_allowed_count=0
dispatch_blocked_count=3
dispatch_actions=skip_missing_host_requirements
dispatch_allowed_count=3
dispatch_blocked_count=0
dispatch_actions=ready_for_manual_opt_in_native_probe
execution_enabled=0
promotion_allowed=0
```
These commands remain blocked on this host:
These commands are opt-in only and must never run implicitly:
```bash
ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh
@@ -86,23 +111,37 @@ ENABLE_TOOL_DB_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_tool_db_runtime
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh
```
Do not run them unless the generated preflight, family readiness, dispatch
plan, and dispatch rollup all permit the specific family.
Only run them deliberately when updating native proof evidence. A passing probe
does not enable execution or promotion by itself.
## Native evidence acceptance
For rows still blocked before native evidence,
`runtime-boundary-native-evidence-acceptance-gate.tsv` currently records:
- `current_probe_status=skipped_missing_host_runtime`
- `current_probe_status=ready_disabled_by_default`
- `native_evidence_status=pending_until_native_pass`
- `skip_evidence_status=skip_valid_until_host_requirements_available`
- `skip_evidence_status=skip_contract_not_applicable_dispatch_allowed`
- `node_gate_status=blocked_until_native_pass_evidence`
- `browser_gate_status=blocked_until_node_gate_complete`
- `native_evidence_gate=blocked_until_host_requirements_available`
- `native_evidence_gate=blocked_until_native_pass_evidence`
- `evidence_acceptance_allowed=0`
A skipped opt-in probe is not a native pass. It only proves that this host is
correctly blocked until the required LinuxCNC runtime commands are available.
A disabled opt-in probe is not a native pass. It only proves that this host is
not executing the family-specific runtime probe by default.
For `L4-PYTHON-REMAP`, the same artifact records accepted native lifecycle
evidence for the stop-lookahead fixture:
- `current_probe_status=runtime_lifecycle_probe_passed`
- `native_evidence_status=native_pass_evidence_observed`
- `skip_evidence_status=native_pass_evidence_observed_skip_contract_closed`
- `node_gate_status=node_inventory_promotion_gate_complete`
- `browser_gate_status=browser_smoke_promotion_gate_complete`
- `native_evidence_gate=native_pass_evidence_accepted`
- `evidence_acceptance_allowed=1`
This is evidence acceptance only. It does not change `promotion_allowed=0`.
## Release gate
@@ -143,4 +182,5 @@ No blocked family may be promoted until all of these are true:
deliberately.
Until then, `L4-USER-M-PROCESS`, `L4-TOOL-DB`, and `L4-PYTHON-REMAP` remain
blocked, inventory-only runtime families.
locked in the runtime-boundary proof ledger with `promotion_allowed=0`; only
the inventory rows explicitly eligible for Node/WASM execution may run.

View File

@@ -16,7 +16,6 @@ sim_configs_wasm_node_inventory_skipped=130
sim_configs_wasm_node_inventory_unexpected_fail=0
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_TOOL_DB=1
sim_configs_wasm_node_inventory_skip_L4_USER_M_PROCESS=1
sim_configs_wasm_node_inventory_skip_NON_MAIN_CLASS=10
sim_configs_wasm_node_inventory_skip_UPSTREAM_DEMO=1
@@ -24,7 +23,7 @@ sim_configs_wasm_node_inventory_skip_UPSTREAM_DEMO=1
The current generated inventory has no skipped main-program row whose hard
block is already empty. That matters: virtual HAL maturity should not be used
as a blanket reason to reduce `skipped=131`. The next useful promotions are
as a blanket reason to reduce the skip baseline. The next useful promotions are
case promotions from Node inventory or representative coverage into browser,
diagnostics, and release evidence.
@@ -399,9 +398,9 @@ carries an inventory `baselineSummary`. The macro/load report requires that
summary to match the current generated inventory baseline:
```text
executed=28
passed=28
skipped=131
executed=29
passed=29
skipped=130
unexpectedFail=0
```

View File

@@ -29,7 +29,6 @@ sim_configs_wasm_node_inventory_skipped=130
sim_configs_wasm_node_inventory_unexpected_fail=0
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_TOOL_DB=1
sim_configs_wasm_node_inventory_skip_L4_USER_M_PROCESS=1
sim_configs_wasm_node_inventory_skip_NON_MAIN_CLASS=10
sim_configs_wasm_node_inventory_skip_UPSTREAM_DEMO=1
@@ -51,11 +50,11 @@ The generated artifact baseline is:
- `build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-rollup.tsv`
The current promotion-candidate artifact is evidence accounting only. It has
28 rows total: 8 `evidence-ready` rows with virtual HAL source-derived
browser/release evidence, and 20 `inventory-ready` skipped main-program rows.
27 rows total: 8 `evidence-ready` rows with virtual HAL source-derived
browser/release evidence, and 19 `inventory-ready` skipped main-program rows.
Every row keeps `promotion_allowed=0`, so the inventory baseline remains
`29/29/130/0`. The companion `evidence-expansion-candidates.tsv` artifact has
13 rows queued for future browser diagnostics binding, also with
14 rows queued for future browser diagnostics binding, also with
`promotion_allowed=0`.
SDK and workflow-overview release URL summaries expose the current
@@ -76,7 +75,6 @@ underscore-safe metric names. Treat these as the same baseline:
| --- | --- | ---: | --- |
| `ASSET-ONLY` | `ASSET_ONLY` | 65 | Subroutine/support asset; not a standalone browser main-program target. |
| `L4-PYTHON-REMAP` | `L4_PYTHON_REMAP` | 53 | Keep inventory-only until a LinuxCNC-owned Python-remap runtime proof exists. |
| `L4-TOOL-DB` | `L4_TOOL_DB` | 1 | Keep blocked until a LinuxCNC-owned tool database process/protocol proof exists. |
| `L4-USER-M-PROCESS` | `L4_USER_M_PROCESS` | 1 | Keep blocked until a LinuxCNC-owned external user-M process state proof exists. |
| `NON_MAIN_CLASS` | `NON_MAIN_CLASS` | 10 | Covered by class representative or macro/load policy, not direct standalone promotion. |
| `UPSTREAM-DEMO` | `UPSTREAM_DEMO` | 1 | Preserved upstream demo edge; do not force pass. |
@@ -99,6 +97,9 @@ Do not promote these runtime families from JavaScript glue alone:
- `L4-TOOL-DB`
- `L4-USER-M-PROCESS`
`L4-TOOL-DB` is no longer counted in the current skip summary, but it remains
in the runtime-boundary proof ledger with `promotion_allowed=0`.
## Release gate
Use this gate when changing sim-config coverage, promotion state, or inventory

View File

@@ -52,6 +52,10 @@ Current baseline:
`wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-state-plan.tsv`
- Node Python-remap native runtime fixture-plan artifact:
`wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-fixture-plan.tsv`
- Node Python-remap bulk promotion plan artifact:
`wasm-port/build/wasm/sim-configs-inventory/python-remap-bulk-promotion-plan.tsv`
- Node Python-remap row runtime proof artifact:
`wasm-port/build/wasm/sim-configs-inventory/python-remap-row-runtime-proof.tsv`
- Node runtime-boundary contract summary artifact:
`wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-contract-summary.tsv`
- Node boundary-phase completion artifact:
@@ -235,6 +239,21 @@ Legend:
`ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1` proves the `demo.ini`
path/toplevel import, `queuebuster` callable lookup, generator return, and
first `INTERP_EXECUTE_FINISH` yield.
- `python-remap-bulk-promotion-plan.tsv`: one row per current
`L4-PYTHON-REMAP` inventory row. It records the Phase 1 blockers required
before any bulk promotion, including row runtime proof, Python module and
callable binding, NGC remap asset staging, NGC-only standalone guards,
interpreter/canonical bridges, HAL/UI/HALUI boundaries, native/WASM/browser
row pass proof, and the manual lock. It keeps every row
`execution_enabled=0` and `promotion_allowed=0`, so the current
PASS/SKIP baseline is unchanged.
- `python-remap-row-runtime-proof.tsv`: one row per current
`L4-PYTHON-REMAP` inventory row. It records the Phase 2 row-aware runtime
proof substrate: the SDK row runtime port API and row proof plan are ready,
while module staging, TOPLEVEL execution, PATH_PREPEND application, callable
binding, generator lifecycle, NGC remap staging, NGC-only standalone guards,
interpreter/canonical bridges, and native/WASM/browser row pass proof remain
pending. Every row keeps `execution_enabled=0`.
- `runtime-boundary-contract-summary.tsv`: one row per blocked runtime boundary
class. It unifies the user-M transition contract, tool DB transaction
contract, and Python runtime contract with their native-alignment artifact,
@@ -349,7 +368,7 @@ Legend:
`build/wasm/sim-configs-inventory/` must be referenced by the current
compatibility, matrix, or full-process boundary docs, so generated gate
artifacts cannot silently appear without review text. The generated WASM
inventory artifact list must remain the exact duplicate-free 55-entry
inventory artifact list must remain the exact duplicate-free 57-entry
baseline, and every token plus the fixed-count, duplicate-free, and
fetchability guards must also be listed by browser smoke.
- The same inventory check covers native generated TSV artifacts under
@@ -362,7 +381,7 @@ Legend:
compatibility, matrix, and full-process boundary docs and checking the
generated WASM inventory artifact names plus native TSV tokens against the
same completion-summary counts. The Node coverage gate also locks the two
documentation-coverage completion counts to the current `55` WASM inventory
documentation-coverage completion counts to the current `57` WASM inventory
artifacts and `8` native TSV artifacts. It verifies that browser smoke
executes the artifact documentation coverage helper, calls the completion
count-parity helper with the generated completion rows, fetches and joins the
@@ -409,23 +428,25 @@ Legend:
affected opt-in environments, and exact probe command traceability while
remaining non-executing and non-promoting.
- `runtime-boundary-host-readiness-rollup.tsv`: the aggregate host-readiness
decision for all blocked runtime families. On this host it records zero
ready families, missing `halcmd`, `halrun`, `linuxcnc`, and `milltask`, and
`host_blocked_for_all_opt_in_native_probes`.
decision for all runtime-boundary proof families. On this host it records
three ready opt-in native probes, no missing host requirements, and
`host_ready_for_all_opt_in_native_probes`.
- `runtime-boundary-opt-in-probe-dispatch-plan.tsv`: one dispatch row per
guarded native runtime probe. It turns host readiness into a dispatch action
and remains non-promoting; a dispatchable row only means the manual opt-in
native probe may be attempted on a ready host.
- `runtime-boundary-opt-in-probe-dispatch-rollup.tsv`: the aggregate dispatch
switch for blocked runtime probes. Current rows are blocked for missing host
requirements, so no opt-in native probe is dispatchable on this host.
switch for runtime-boundary probes. Current rows are dispatchable for manual
opt-in native probes, while `execution_enabled=0` and `promotion_allowed=0`
remain unchanged.
- `runtime-boundary-opt-in-probe-skip-evidence-contract.tsv`: the per-probe
skip/evidence contract. It records the skip reason, missing host
requirements, current probe status, whether native pass evidence is required
now, and keeps every skipped probe non-executing and non-promoting.
- `runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv`: the aggregate
evidence decision for skipped opt-in probes. While all probes are skipped for
missing host requirements, no native pass evidence is accepted.
evidence decision for opt-in probes. Dispatchable probes do not require skip
evidence; accepted Python remap lifecycle evidence still does not allow
promotion.
## Summary

View File

@@ -127,6 +127,10 @@ import {
createProjectReleaseReadinessReport,
createProjectReleaseReadinessSummaryViewModel,
createLinuxCncToolDbProcessPort,
createLinuxCncPythonRemapRowRuntimePort,
createLinuxCncPythonRemapRuntimePort,
createPythonRemapRowRuntimePlan,
validatePythonRemapRowRuntimeTranscript,
createToolDbProcessDiagnostics,
createToolDbTransactionPlan,
createSessionSnapshot,
@@ -595,6 +599,13 @@ private module paths:
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.
- `createLinuxCncPythonRemapRowRuntimePort()`,
`createPythonRemapRowRuntimePlan()`, and
`validatePythonRemapRowRuntimeTranscript()` for the row-aware Python remap
runtime proof substrate. The row port accepts an inventory path, INI path,
Python modules, remap/prolog/epilog callables, NGC remap assets, and
NGC-only subpaths, then records the runtime-adapter proof transcript while
keeping 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

View File

@@ -104,12 +104,16 @@ export {
} from "./tool-db-browser-worker-adapter.js";
export {
PYTHON_REMAP_LIFECYCLE_PLAN,
PYTHON_REMAP_ROW_RUNTIME_PROOF_PHASES,
PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION,
PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE,
createLinuxCncPythonRemapRowRuntimePort,
createLinuxCncPythonRemapRuntimePort,
createPythonRemapLifecyclePlan,
createPythonRemapRowRuntimePlan,
createPythonRemapRuntimeDiagnostics,
validatePythonRemapLifecycleTranscript,
validatePythonRemapRowRuntimeTranscript,
} from "./python-remap-runtime-port.js";
export {
createLinuxCncPythonRemapBrowserWorkerAdapter,

View File

@@ -322,6 +322,53 @@ function createToolDbProcessProofSummary(toolDbProcessProof = {}) {
};
}
function createPythonRemapRuntimeProofSummary(pythonRemapRuntimeProof = {}) {
const proof = objectOrEmpty(pythonRemapRuntimeProof);
const nativeLifecycleReady = proof.nativeLifecycleReady === true;
const wasmLifecycleReady = proof.wasmLifecycleReady !== false;
const browserLifecycleReady = proof.browserLifecycleReady !== false;
const promotionAllowed = proof.promotionAllowed === true;
const executionEnabled = proof.executionEnabled === true;
const ready = nativeLifecycleReady &&
wasmLifecycleReady &&
browserLifecycleReady &&
promotionAllowed === false &&
executionEnabled === false;
return {
apiName: "python-remap-runtime-proof-summary",
summaryVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
boundaryClass: "L4-PYTHON-REMAP",
fixtureFamily: proof.fixtureFamily ?? "axis/remap/stop-lookahead/nc_files",
fixtureId: proof.fixtureId ?? "stop_lookahead_python_runtime_lifecycle",
iniPath: proof.iniPath ?? "axis/remap/stop-lookahead/demo.ini",
pythonPathPrepend: proof.pythonPathPrepend ?? "python",
topLevelPath: proof.topLevelPath ?? "python/toplevel.py",
callableName: proof.callableName ?? "queuebuster",
runtimeMode: proof.runtimeMode ?? "browser-python-wasm-worker",
nativeProbeStatus: proof.nativeProbeStatus ?? (nativeLifecycleReady ? "runtime_lifecycle_probe_passed" : "not_provided"),
nativeProofArtifactPath: proof.nativeProofArtifactPath ?? "wasm-port/build/native/native-runtime-probe-summary.tsv",
nativeProofStdoutLog: proof.nativeProofStdoutLog ?? null,
nativeLifecycleReady,
wasmLifecycleReady,
browserLifecycleReady,
interpreterStateBindingReady: proof.interpreterStateBindingReady !== false,
generatorLifecycleReady: proof.generatorLifecycleReady !== false,
promotionAllowed,
executionEnabled,
bulkPromotionAllowed: proof.bulkPromotionAllowed === true,
manualLockUpdateRequired: proof.manualLockUpdateRequired !== false,
requiredCommands: [
"ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 wasm-port/tests/native/probe_python_remap_runtime.sh",
"wasm-port/tests/sdk/node/verify_python_remap_runtime_port.sh",
"SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_python_remap_runtime_port_wasm.sh",
"SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_python_remap_runtime_browser.sh",
],
};
}
const BLOCKED_RUNTIME_FAMILIES = [
"L4-USER-M-PROCESS",
"L4-PYTHON-REMAP",
@@ -2069,6 +2116,7 @@ export function createProjectReleaseReadinessReport({
virtualHalSimConfigMacroLoadFixtures = null,
virtualHalMotionControllerMatrix = null,
toolDbProcessProof = {},
pythonRemapRuntimeProof = {},
blockedRuntimeFamilies = BLOCKED_RUNTIME_FAMILIES,
promotedRuntimeFamilies = [],
axisScreenshotArtifacts = [],
@@ -2130,6 +2178,8 @@ export function createProjectReleaseReadinessReport({
const axisScreenshotArtifactsReady = axisScreenshotArtifactSummary.ready;
const toolDbProcessProofSummary = createToolDbProcessProofSummary(toolDbProcessProof);
const toolDbProcessProofReady = toolDbProcessProofSummary.ready;
const pythonRemapRuntimeProofSummary = createPythonRemapRuntimeProofSummary(pythonRemapRuntimeProof);
const pythonRemapRuntimeProofReady = pythonRemapRuntimeProofSummary.ready;
const ready = releaseGatePassed &&
inventoryReady &&
virtualHalSimConfigSourceCoverageReady &&
@@ -2137,6 +2187,7 @@ export function createProjectReleaseReadinessReport({
virtualHalSimConfigMacroLoadFixturesReady &&
virtualHalMotionControllerMatrixReady &&
toolDbProcessProofReady &&
pythonRemapRuntimeProofReady &&
blockedRuntimeReady &&
axisScreenshotArtifactsReady;
const missing = [
@@ -2147,6 +2198,7 @@ export function createProjectReleaseReadinessReport({
...(virtualHalSimConfigMacroLoadFixturesReady ? [] : ["virtual-hal-sim-config-macro-load-fixtures"]),
...(virtualHalMotionControllerMatrixReady ? [] : ["virtual-hal-motion-controller-matrix"]),
...(toolDbProcessProofReady ? [] : ["tool-db-process-proof"]),
...(pythonRemapRuntimeProofReady ? [] : ["python-remap-runtime-proof"]),
...(blockedRuntimeReady ? [] : ["blocked-runtime-families"]),
...(axisScreenshotArtifactsReady ? [] : ["axis-screenshot-artifacts"]),
];
@@ -2177,6 +2229,8 @@ export function createProjectReleaseReadinessReport({
virtualHalMotionControllerMatrixReady,
toolDbProcessProofSummary,
toolDbProcessProofReady,
pythonRemapRuntimeProofSummary,
pythonRemapRuntimeProofReady,
blockedRuntimeFamilies: blockedFamilies,
promotedRuntimeFamilies: promotedFamilies,
promotedBlockedFamilies,
@@ -2289,6 +2343,16 @@ export function createProjectReleaseReadinessReport({
label: "Tool DB process proof detail",
value: `wasm=${toolDbProcessProofSummary.wasmProtocolReady ? "ready" : "missing"} browser=${toolDbProcessProofSummary.browserProtocolReady ? "ready" : "missing"} opfs=${toolDbProcessProofSummary.opfsPersistenceReady ? "ready" : "missing"} promotion_allowed=${toolDbProcessProofSummary.promotionAllowed ? "1" : "0"}`,
},
{
id: "python-remap-runtime-proof",
label: "Python remap runtime proof",
value: pythonRemapRuntimeProofReady ? "ready" : "locked",
},
{
id: "python-remap-runtime-proof-detail",
label: "Python remap runtime proof detail",
value: `native=${pythonRemapRuntimeProofSummary.nativeLifecycleReady ? "ready" : "missing"} wasm=${pythonRemapRuntimeProofSummary.wasmLifecycleReady ? "ready" : "missing"} browser=${pythonRemapRuntimeProofSummary.browserLifecycleReady ? "ready" : "missing"} promotion_allowed=${pythonRemapRuntimeProofSummary.promotionAllowed ? "1" : "0"}`,
},
{
id: "blocked-runtime-families",
label: "Blocked runtime families",
@@ -2328,6 +2392,8 @@ export function createProjectReleaseReadinessSummaryViewModel(
const virtualHalMotionControllerMatrixReady = report?.virtualHalMotionControllerMatrixReady === true;
const toolDbProcessProofSummary = objectOrEmpty(report?.toolDbProcessProofSummary);
const toolDbProcessProofReady = report?.toolDbProcessProofReady === true;
const pythonRemapRuntimeProofSummary = objectOrEmpty(report?.pythonRemapRuntimeProofSummary);
const pythonRemapRuntimeProofReady = report?.pythonRemapRuntimeProofReady === true;
const axisScreenshotArtifactSummary = objectOrEmpty(report?.axisScreenshotArtifactSummary);
const blockedRuntimeFamilies = arrayOrEmpty(report?.blockedRuntimeFamilies);
const promotedBlockedFamilies = arrayOrEmpty(report?.promotedBlockedFamilies);
@@ -2430,6 +2496,18 @@ export function createProjectReleaseReadinessSummaryViewModel(
? `wasm=${toolDbProcessProofSummary.wasmProtocolReady ? "ready" : "missing"} browser=${toolDbProcessProofSummary.browserProtocolReady ? "ready" : "missing"} opfs=${toolDbProcessProofSummary.opfsPersistenceReady ? "ready" : "missing"} promotion_allowed=${toolDbProcessProofSummary.promotionAllowed ? "1" : "0"}`
: "missing",
},
{
id: "python-remap-runtime-proof",
label: "Python remap runtime proof",
value: pythonRemapRuntimeProofReady ? "ready" : "locked",
},
{
id: "python-remap-runtime-proof-detail",
label: "Python remap runtime proof detail",
value: pythonRemapRuntimeProofSummary.apiName
? `native=${pythonRemapRuntimeProofSummary.nativeLifecycleReady ? "ready" : "missing"} wasm=${pythonRemapRuntimeProofSummary.wasmLifecycleReady ? "ready" : "missing"} browser=${pythonRemapRuntimeProofSummary.browserLifecycleReady ? "ready" : "missing"} promotion_allowed=${pythonRemapRuntimeProofSummary.promotionAllowed ? "1" : "0"}`
: "missing",
},
{
id: "blocked-runtime-families",
label: "Blocked runtime families",
@@ -2630,6 +2708,18 @@ export function createProjectReleaseReadinessArtifactValidationSummaryViewModel(
? `wasm=${validation.toolDbProcessProofSummary.wasmProtocolReady ? "ready" : "missing"} browser=${validation.toolDbProcessProofSummary.browserProtocolReady ? "ready" : "missing"} opfs=${validation.toolDbProcessProofSummary.opfsPersistenceReady ? "ready" : "missing"} promotion_allowed=${validation.toolDbProcessProofSummary.promotionAllowed ? "1" : "0"}`
: "missing",
},
{
id: "python-remap-runtime-proof",
label: "Python remap runtime proof",
value: validation?.pythonRemapRuntimeProofSummary?.ready === true ? "ready" : "locked",
},
{
id: "python-remap-runtime-proof-detail",
label: "Python remap runtime proof detail",
value: validation?.pythonRemapRuntimeProofSummary?.apiName
? `native=${validation.pythonRemapRuntimeProofSummary.nativeLifecycleReady ? "ready" : "missing"} wasm=${validation.pythonRemapRuntimeProofSummary.wasmLifecycleReady ? "ready" : "missing"} browser=${validation.pythonRemapRuntimeProofSummary.browserLifecycleReady ? "ready" : "missing"} promotion_allowed=${validation.pythonRemapRuntimeProofSummary.promotionAllowed ? "1" : "0"}`
: "missing",
},
{
id: "missing",
label: "Missing artifact evidence",
@@ -3043,6 +3133,8 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel
workflow?.validation?.promotionCandidateArtifactSummaryReady === true;
const toolDbProcessProofSummary = objectOrEmpty(workflow?.validation?.toolDbProcessProofSummary);
const toolDbProcessProofReady = workflow?.validation?.toolDbProcessProofReady === true;
const pythonRemapRuntimeProofSummary = objectOrEmpty(workflow?.validation?.pythonRemapRuntimeProofSummary);
const pythonRemapRuntimeProofReady = workflow?.validation?.pythonRemapRuntimeProofReady === true;
const promotionArtifactRows = objectOrEmpty(promotionCandidateArtifactSummary.artifactRows);
const hardBlockRuntimeFamilyRows = arrayOrEmpty(promotionCandidateArtifactSummary.hardBlockRuntimeFamilyRows);
const hardBlockRuntimeFamilySummaryRows = arrayOrEmpty(
@@ -3321,6 +3413,18 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel
? `wasm=${toolDbProcessProofSummary.wasmProtocolReady ? "ready" : "missing"} browser=${toolDbProcessProofSummary.browserProtocolReady ? "ready" : "missing"} opfs=${toolDbProcessProofSummary.opfsPersistenceReady ? "ready" : "missing"} promotion_allowed=${toolDbProcessProofSummary.promotionAllowed ? "1" : "0"}`
: "not provided",
},
{
id: "python-remap-runtime-proof",
label: "Python remap runtime proof",
value: pythonRemapRuntimeProofSummary.ready === true ? "ready" : "locked",
},
{
id: "python-remap-runtime-proof-detail",
label: "Python remap runtime proof detail",
value: pythonRemapRuntimeProofSummary.apiName
? `native=${pythonRemapRuntimeProofSummary.nativeLifecycleReady ? "ready" : "missing"} wasm=${pythonRemapRuntimeProofSummary.wasmLifecycleReady ? "ready" : "missing"} browser=${pythonRemapRuntimeProofSummary.browserLifecycleReady ? "ready" : "missing"} promotion_allowed=${pythonRemapRuntimeProofSummary.promotionAllowed ? "1" : "0"}`
: "not provided",
},
...arrayOrEmpty(workflow?.validation?.virtualHalPromotionFamilyRows).map((row) => ({
id: `virtual-hal-promotion-family-${row.id}`,
label: `Promotion family ${row.label}`,
@@ -3523,6 +3627,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
const virtualHalSimConfigMacroLoadFixtures = objectOrEmpty(artifactObject.virtualHalSimConfigMacroLoadFixtures);
const virtualHalMotionControllerMatrix = objectOrEmpty(artifactObject.virtualHalMotionControllerMatrix);
const toolDbProcessProofSummary = objectOrEmpty(artifactObject.toolDbProcessProofSummary);
const pythonRemapRuntimeProofSummary = objectOrEmpty(artifactObject.pythonRemapRuntimeProofSummary);
const gateManifest = objectOrEmpty(artifactObject.gateManifest);
const gateExecutionManifest = objectOrEmpty(artifactObject.gateExecutionManifest);
const gateExecutionSummaryViewModel = objectOrEmpty(artifactObject.gateExecutionSummaryViewModel);
@@ -3649,6 +3754,31 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
arrayOrEmpty(toolDbProcessProofSummary.requiredCommands).join("\n") ===
expectedToolDbProcessProofSummary.requiredCommands.join("\n") &&
artifactObject.toolDbProcessProofReady === true;
const expectedPythonRemapRuntimeProofSummary =
createPythonRemapRuntimeProofSummary(pythonRemapRuntimeProofSummary);
const pythonRemapRuntimeProofReady =
pythonRemapRuntimeProofSummary.apiName === "python-remap-runtime-proof-summary" &&
pythonRemapRuntimeProofSummary.summaryVersion === 1 &&
pythonRemapRuntimeProofSummary.boundaryClass === "L4-PYTHON-REMAP" &&
pythonRemapRuntimeProofSummary.fixtureFamily === "axis/remap/stop-lookahead/nc_files" &&
pythonRemapRuntimeProofSummary.fixtureId === "stop_lookahead_python_runtime_lifecycle" &&
pythonRemapRuntimeProofSummary.iniPath === "axis/remap/stop-lookahead/demo.ini" &&
pythonRemapRuntimeProofSummary.pythonPathPrepend === "python" &&
pythonRemapRuntimeProofSummary.topLevelPath === "python/toplevel.py" &&
pythonRemapRuntimeProofSummary.callableName === "queuebuster" &&
pythonRemapRuntimeProofSummary.nativeProbeStatus === (
pythonRemapRuntimeProofSummary.nativeLifecycleReady
? "runtime_lifecycle_probe_passed"
: "not_provided"
) &&
pythonRemapRuntimeProofSummary.nativeProofArtifactPath === "wasm-port/build/native/native-runtime-probe-summary.tsv" &&
pythonRemapRuntimeProofSummary.promotionAllowed === false &&
pythonRemapRuntimeProofSummary.executionEnabled === false &&
pythonRemapRuntimeProofSummary.bulkPromotionAllowed === false &&
pythonRemapRuntimeProofSummary.manualLockUpdateRequired === true &&
arrayOrEmpty(pythonRemapRuntimeProofSummary.requiredCommands).join("\n") ===
expectedPythonRemapRuntimeProofSummary.requiredCommands.join("\n") &&
artifactObject.pythonRemapRuntimeProofReady === pythonRemapRuntimeProofSummary.ready;
const axisScreenshotArtifacts = arrayOrEmpty(axisScreenshotArtifactSummary.artifacts);
const blockedRuntimeFamilies = arrayOrEmpty(artifactObject.blockedRuntimeFamilies);
const promotedBlockedFamilies = arrayOrEmpty(artifactObject.promotedBlockedFamilies);
@@ -3741,6 +3871,8 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
virtualHalMotionControllerMatrixReady,
toolDbProcessProofSummary,
toolDbProcessProofReady,
pythonRemapRuntimeProofSummary,
pythonRemapRuntimeProofReady,
axisScreenshotArtifactSummaryReady: axisScreenshotSummaryReady,
axisScreenshotArtifactCount: axisScreenshotArtifacts.length,
blockedRuntimeFamilies,
@@ -3891,6 +4023,18 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
? `wasm=${toolDbProcessProofSummary.wasmProtocolReady ? "ready" : "missing"} browser=${toolDbProcessProofSummary.browserProtocolReady ? "ready" : "missing"} opfs=${toolDbProcessProofSummary.opfsPersistenceReady ? "ready" : "missing"} promotion_allowed=${toolDbProcessProofSummary.promotionAllowed ? "1" : "0"}`
: "missing",
},
{
id: "python-remap-runtime-proof",
label: "Python remap runtime proof",
value: pythonRemapRuntimeProofSummary.ready === true ? "ready" : "locked",
},
{
id: "python-remap-runtime-proof-detail",
label: "Python remap runtime proof detail",
value: pythonRemapRuntimeProofSummary.apiName
? `native=${pythonRemapRuntimeProofSummary.nativeLifecycleReady ? "ready" : "missing"} wasm=${pythonRemapRuntimeProofSummary.wasmLifecycleReady ? "ready" : "missing"} browser=${pythonRemapRuntimeProofSummary.browserLifecycleReady ? "ready" : "missing"} promotion_allowed=${pythonRemapRuntimeProofSummary.promotionAllowed ? "1" : "0"}`
: "missing",
},
{
id: "blocked-runtime-families",
label: "Blocked runtime families",

View File

@@ -27,6 +27,22 @@ export const PYTHON_REMAP_LIFECYCLE_PLAN = [
{ phase: "export_diagnostics", method: "exportDiagnostics" },
];
export const PYTHON_REMAP_ROW_RUNTIME_PROOF_PHASES = Object.freeze([
"initialize_python",
"apply_ini_python_path",
"execute_toplevel",
"import_module",
"callable_lookup",
"callable_invoke",
"remap_phase_dispatch",
"generator_finish",
"stage_ngc_remap_asset",
"reject_ngc_only_standalone",
"export_interpreter_state",
"export_canonical_events",
"export_diagnostics",
]);
function requireString(value, label) {
if (typeof value !== "string" || value.length === 0) {
throw new Error(`${label} must be a non-empty string.`);
@@ -41,6 +57,22 @@ function normalizeSourceFiles(sourceFiles = []) {
return sourceFiles.map((sourceFile) => requireString(sourceFile, "source file"));
}
function normalizeStringList(values = [], label = "value") {
if (values === "-" || values === null || values === undefined) {
return [];
}
if (!Array.isArray(values)) {
throw new Error(`${label} must be an array.`);
}
return values
.filter((value) => value !== "-")
.map((value) => requireString(value, label));
}
function callableNameFromSpec(callableSpec) {
return requireString(callableSpec, "callable").split("@")[0];
}
function stableTextHash(text) {
let hash = 0x811c9dc5;
for (let index = 0; index < text.length; index += 1) {
@@ -70,10 +102,95 @@ function normalizeLifecycleResult(phase, result = {}) {
};
}
function defaultRowRuntimePlan({
sourceFiles,
pythonRemapFunctions,
prologFunctions,
epilogFunctions,
ngcRemapFiles,
ngcOnlySubpaths,
}) {
const callables = [
...pythonRemapFunctions,
...prologFunctions,
...epilogFunctions,
];
return [
{ phase: "initialize_python", method: "initializePython" },
{ phase: "apply_ini_python_path", method: "applyIniPythonPath" },
{ phase: "execute_toplevel", method: "executeTopLevel" },
...sourceFiles.map((modulePath) => ({
phase: "import_module",
method: "importModule",
modulePath,
})),
...callables.flatMap((callableSpec) => {
const callableName = callableNameFromSpec(callableSpec);
return [
{
phase: "callable_lookup",
method: "lookupCallable",
callableName,
callableSpec,
},
{
phase: "callable_invoke",
method: "invokeGenerator",
callableName,
callableSpec,
},
{
phase: "remap_phase_dispatch",
method: "observeFirstYield",
callableName,
callableSpec,
},
{
phase: "generator_finish",
method: "finishGenerator",
callableName,
callableSpec,
},
];
}),
...ngcRemapFiles.map((ngcPath) => ({
phase: "stage_ngc_remap_asset",
method: "stageNgcRemapAsset",
ngcPath,
})),
...ngcOnlySubpaths.map((ngcPath) => ({
phase: "reject_ngc_only_standalone",
method: "rejectNgcOnlyStandalone",
ngcPath,
})),
{ phase: "export_interpreter_state", method: "exportInterpreterState" },
{ phase: "export_canonical_events", method: "exportCanonicalEvents" },
{ phase: "export_diagnostics", method: "exportDiagnostics" },
];
}
export function createPythonRemapLifecyclePlan() {
return clonePlan(PYTHON_REMAP_LIFECYCLE_PLAN);
}
export function createPythonRemapRowRuntimePlan({
sourceFiles = [],
pythonRemapFunctions = [],
prologFunctions = [],
epilogFunctions = [],
ngcRemapFiles = [],
ngcOnlySubpaths = [],
} = {}) {
return clonePlan(defaultRowRuntimePlan({
sourceFiles: normalizeStringList(sourceFiles, "source file"),
pythonRemapFunctions: normalizeStringList(pythonRemapFunctions, "Python remap function"),
prologFunctions: normalizeStringList(prologFunctions, "prolog function"),
epilogFunctions: normalizeStringList(epilogFunctions, "epilog function"),
ngcRemapFiles: normalizeStringList(ngcRemapFiles, "NGC remap file"),
ngcOnlySubpaths: normalizeStringList(ngcOnlySubpaths, "NGC-only subpath"),
}));
}
export function validatePythonRemapLifecycleTranscript(transcript) {
if (!Array.isArray(transcript)) {
throw new Error("transcript must be an array.");
@@ -85,11 +202,21 @@ export function validatePythonRemapLifecycleTranscript(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 expectedValues = {
apply_ini_python_path: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.pythonPathPrepend,
execute_toplevel: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.topLevelPath,
import_module: "python/remap.py",
callable_lookup: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.callableName,
};
const mismatchedValues = Object.entries(expectedValues)
.filter(([phase, expectedValue]) => byPhase.get(phase)?.value !== expectedValue)
.map(([phase]) => phase);
const hasInterpreterState = byPhase.has("export_interpreter_state");
const hasDiagnostics = byPhase.has("export_diagnostics");
const ready = (
missingPhases.length === 0 &&
failedPhases.length === 0 &&
mismatchedValues.length === 0 &&
Number(firstYield) === PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.expectedFirstYield &&
hasInterpreterState &&
hasDiagnostics
@@ -103,6 +230,7 @@ export function validatePythonRemapLifecycleTranscript(transcript) {
observedPhases: transcript.map((entry) => entry.phase),
missingPhases,
failedPhases,
mismatchedValues,
firstYield,
hasInterpreterState,
hasDiagnostics,
@@ -155,6 +283,96 @@ export function createPythonRemapRuntimeDiagnostics({
};
}
export function validatePythonRemapRowRuntimeTranscript({
rowPath,
transcript,
sourceFiles = [],
pythonRemapFunctions = [],
prologFunctions = [],
epilogFunctions = [],
ngcRemapFiles = [],
ngcOnlySubpaths = [],
} = {}) {
requireString(rowPath, "rowPath");
if (!Array.isArray(transcript)) {
throw new Error("transcript must be an array.");
}
const expectedPlan = createPythonRemapRowRuntimePlan({
sourceFiles,
pythonRemapFunctions,
prologFunctions,
epilogFunctions,
ngcRemapFiles,
ngcOnlySubpaths,
});
const expectedPhases = expectedPlan.map((step) => step.phase);
const observedPhases = transcript.map((entry) => entry.phase);
const phaseCounts = new Map();
for (const phase of observedPhases) {
phaseCounts.set(phase, (phaseCounts.get(phase) ?? 0) + 1);
}
const failedPhases = transcript
.filter((entry) => entry?.ready === false || entry?.status === "blocked" || entry?.status === "failed")
.map((entry) => entry.phase);
const missingPhases = [...new Set(expectedPhases)].filter((phase) => !phaseCounts.has(phase));
const expectedModuleCount = normalizeStringList(sourceFiles, "source file").length;
const expectedCallableCount = [
...normalizeStringList(pythonRemapFunctions, "Python remap function"),
...normalizeStringList(prologFunctions, "prolog function"),
...normalizeStringList(epilogFunctions, "epilog function"),
].length;
const expectedNgcRemapCount = normalizeStringList(ngcRemapFiles, "NGC remap file").length;
const expectedNgcOnlyCount = normalizeStringList(ngcOnlySubpaths, "NGC-only subpath").length;
const countDrift = [
["import_module", expectedModuleCount],
["callable_lookup", expectedCallableCount],
["callable_invoke", expectedCallableCount],
["remap_phase_dispatch", expectedCallableCount],
["generator_finish", expectedCallableCount],
["stage_ngc_remap_asset", expectedNgcRemapCount],
["reject_ngc_only_standalone", expectedNgcOnlyCount],
].filter(([phase, expectedCount]) => (phaseCounts.get(phase) ?? 0) !== expectedCount)
.map(([phase]) => phase);
const hasInterpreterState = phaseCounts.has("export_interpreter_state");
const hasCanonicalEvents = phaseCounts.has("export_canonical_events");
const hasDiagnostics = phaseCounts.has("export_diagnostics");
const ready = (
missingPhases.length === 0 &&
failedPhases.length === 0 &&
countDrift.length === 0 &&
hasInterpreterState &&
hasCanonicalEvents &&
hasDiagnostics
);
return {
contractVersion: PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION,
rowPath,
requiredPhases: [...new Set(expectedPhases)],
observedPhases,
missingPhases,
failedPhases,
countDrift,
moduleImportReady: (phaseCounts.get("import_module") ?? 0) === expectedModuleCount,
callableLookupReady: (phaseCounts.get("callable_lookup") ?? 0) === expectedCallableCount,
generatorLifecycleReady: (
(phaseCounts.get("callable_invoke") ?? 0) === expectedCallableCount &&
(phaseCounts.get("remap_phase_dispatch") ?? 0) === expectedCallableCount &&
(phaseCounts.get("generator_finish") ?? 0) === expectedCallableCount
),
ngcRemapAssetsReady: (phaseCounts.get("stage_ngc_remap_asset") ?? 0) === expectedNgcRemapCount,
ngcOnlySubpathsGuarded: (phaseCounts.get("reject_ngc_only_standalone") ?? 0) === expectedNgcOnlyCount,
interpreterStateBindingReady: hasInterpreterState,
canonicalEventsReady: hasCanonicalEvents,
diagnosticsReady: hasDiagnostics,
rowRuntimeTranscriptReady: ready,
ready,
executionEnabled: false,
promotionAllowed: false,
bulkPromotionAllowed: false,
};
}
export function createLinuxCncPythonRemapRuntimePort({
fixtureFamily = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.fixtureFamily,
iniPath = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.iniPath,
@@ -348,3 +566,241 @@ export function createLinuxCncPythonRemapRuntimePort({
},
};
}
export function createLinuxCncPythonRemapRowRuntimePort({
path,
iniPath,
family = null,
machineRoot = "",
pythonPathPrepend = "-",
topLevelPath = "-",
sourceFiles = [],
remapDeclarations = [],
pythonRemapFunctions = [],
prologFunctions = [],
epilogFunctions = [],
ngcRemapFiles = [],
ngcOnlySubpaths = [],
runtimeMode = "row-runtime-contract-only",
runtimeAdapter = null,
} = {}) {
requireString(path, "path");
requireString(iniPath, "iniPath");
const normalizedFamily = family ?? path.split("/").slice(0, -1).join("/");
requireString(normalizedFamily, "family");
const normalizedMachineRoot = machineRoot === "-" ? "" : String(machineRoot ?? "");
const normalizedPythonPathPrepend = pythonPathPrepend === "-" ? "" : String(pythonPathPrepend ?? "");
const normalizedTopLevelPath = topLevelPath === "-" ? "" : String(topLevelPath ?? "");
const normalizedSourceFiles = normalizeStringList(sourceFiles, "source file");
const normalizedRemapDeclarations = normalizeStringList(remapDeclarations, "remap declaration");
const normalizedPythonRemapFunctions = normalizeStringList(pythonRemapFunctions, "Python remap function");
const normalizedPrologFunctions = normalizeStringList(prologFunctions, "prolog function");
const normalizedEpilogFunctions = normalizeStringList(epilogFunctions, "epilog function");
const normalizedNgcRemapFiles = normalizeStringList(ngcRemapFiles, "NGC remap file");
const normalizedNgcOnlySubpaths = normalizeStringList(ngcOnlySubpaths, "NGC-only subpath");
const transcript = [];
let started = false;
let closed = false;
let runtimeExecutionReady = false;
let startStatus = null;
function assertOpen() {
if (closed) {
throw new Error("PythonRemapRowRuntimePort 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,
path,
iniPath,
family: normalizedFamily,
machineRoot: normalizedMachineRoot,
pythonPathPrepend: normalizedPythonPathPrepend,
topLevelPath: normalizedTopLevelPath,
sourceFiles: normalizedSourceFiles,
remapDeclarations: normalizedRemapDeclarations,
pythonRemapFunctions: normalizedPythonRemapFunctions,
prologFunctions: normalizedPrologFunctions,
epilogFunctions: normalizedEpilogFunctions,
ngcRemapFiles: normalizedNgcRemapFiles,
ngcOnlySubpaths: normalizedNgcOnlySubpaths,
};
const result = await callAdapterMethod(step.method, payload);
transcript.push(result);
return result;
}
function rowPlan() {
return createPythonRemapRowRuntimePlan({
sourceFiles: normalizedSourceFiles,
pythonRemapFunctions: normalizedPythonRemapFunctions,
prologFunctions: normalizedPrologFunctions,
epilogFunctions: normalizedEpilogFunctions,
ngcRemapFiles: normalizedNgcRemapFiles,
ngcOnlySubpaths: normalizedNgcOnlySubpaths,
});
}
function validation() {
return validatePythonRemapRowRuntimeTranscript({
rowPath: path,
transcript,
sourceFiles: normalizedSourceFiles,
pythonRemapFunctions: normalizedPythonRemapFunctions,
prologFunctions: normalizedPrologFunctions,
epilogFunctions: normalizedEpilogFunctions,
ngcRemapFiles: normalizedNgcRemapFiles,
ngcOnlySubpaths: normalizedNgcOnlySubpaths,
});
}
return {
path,
iniPath,
family: normalizedFamily,
machineRoot: normalizedMachineRoot,
pythonPathPrepend: normalizedPythonPathPrepend,
topLevelPath: normalizedTopLevelPath,
sourceFiles: normalizedSourceFiles,
remapDeclarations: normalizedRemapDeclarations,
pythonRemapFunctions: normalizedPythonRemapFunctions,
prologFunctions: normalizedPrologFunctions,
epilogFunctions: normalizedEpilogFunctions,
ngcRemapFiles: normalizedNgcRemapFiles,
ngcOnlySubpaths: normalizedNgcOnlySubpaths,
runtimeMode,
createRowRuntimePlan: rowPlan,
async start() {
assertOpen();
started = true;
let adapterStartResult = {};
if (runtimeAdapter?.start) {
adapterStartResult = await runtimeAdapter.start({
path,
iniPath,
family: normalizedFamily,
machineRoot: normalizedMachineRoot,
pythonPathPrepend: normalizedPythonPathPrepend,
topLevelPath: normalizedTopLevelPath,
sourceFiles: normalizedSourceFiles,
remapDeclarations: normalizedRemapDeclarations,
pythonRemapFunctions: normalizedPythonRemapFunctions,
prologFunctions: normalizedPrologFunctions,
epilogFunctions: normalizedEpilogFunctions,
ngcRemapFiles: normalizedNgcRemapFiles,
ngcOnlySubpaths: normalizedNgcOnlySubpaths,
});
}
runtimeExecutionReady = adapterStartResult.runtimeExecutionReady === true;
startStatus = adapterStartResult.status ?? (runtimeExecutionReady ? "python_remap_row_runtime_adapter_started" : null);
return {
runtimeMode,
runtimeExecutionReady,
status: startStatus,
executionEnabled: false,
promotionAllowed: false,
bulkPromotionAllowed: false,
};
},
async runRowRuntimePlan(plan = rowPlan()) {
assertOpen();
if (!started) {
throw new Error("PythonRemapRowRuntimePort must be started before runRowRuntimePlan().");
}
if (!runtimeAdapter || runtimeExecutionReady !== true) {
return {
status: runtimeAdapter ? (startStatus ?? "blocked_row_runtime_execution_not_ready") : "blocked_row_runtime_adapter_required",
plan: clonePlan(plan),
runtimeExecutionReady: false,
executionEnabled: false,
promotionAllowed: false,
bulkPromotionAllowed: false,
};
}
for (const step of plan) {
await runStep(step);
}
return {
status: "row_runtime_adapter_plan_executed",
plan: clonePlan(plan),
transcript: this.exportTranscript(),
validation: validation(),
runtimeExecutionReady: true,
executionEnabled: false,
promotionAllowed: false,
bulkPromotionAllowed: false,
};
},
exportTranscript() {
return transcript.map((entry) => ({ ...entry }));
},
exportDiagnostics() {
const rowValidation = validation();
return {
contractVersion: PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION,
runtimeMode,
runtimeExecutionReady,
path,
iniPath,
family: normalizedFamily,
machineRoot: normalizedMachineRoot,
pythonPathPrepend: normalizedPythonPathPrepend,
topLevelPath: normalizedTopLevelPath,
sourceFiles: [...normalizedSourceFiles],
remapDeclarations: [...normalizedRemapDeclarations],
pythonRemapFunctions: [...normalizedPythonRemapFunctions],
prologFunctions: [...normalizedPrologFunctions],
epilogFunctions: [...normalizedEpilogFunctions],
ngcRemapFiles: [...normalizedNgcRemapFiles],
ngcOnlySubpaths: [...normalizedNgcOnlySubpaths],
rowRuntimeTranscriptReady: rowValidation.rowRuntimeTranscriptReady,
moduleImportReady: rowValidation.moduleImportReady,
callableLookupReady: rowValidation.callableLookupReady,
generatorLifecycleReady: rowValidation.generatorLifecycleReady,
ngcRemapAssetsReady: rowValidation.ngcRemapAssetsReady,
ngcOnlySubpathsGuarded: rowValidation.ngcOnlySubpathsGuarded,
interpreterStateBindingReady: rowValidation.interpreterStateBindingReady,
canonicalEventsReady: rowValidation.canonicalEventsReady,
transcriptHash: stableTextHash(transcriptText(transcript)),
transcriptPhaseCount: transcript.length,
linuxCncOwnedLifecycle: true,
jsCncSemantics: false,
ngcOnlySubroutinePromoted: false,
executionEnabled: false,
promotionAllowed: false,
bulkPromotionAllowed: false,
};
},
async close() {
if (runtimeAdapter?.close) {
await runtimeAdapter.close();
}
closed = true;
return {
closed,
executionEnabled: false,
promotionAllowed: false,
bulkPromotionAllowed: false,
};
},
};
}

View File

@@ -1726,6 +1726,12 @@ M2
<dd>T<span data-machine-status="tool-current">0</span> selected <span data-machine-status="tool-selected">0</span> pocket <span data-machine-status="tool-pocket">0</span> TLO <span data-machine-status="tool-length-offset">z=0.000</span></dd>
<dt>Tool Table</dt>
<dd><span data-tool-table-summary="state">not-loaded</span> | <span data-tool-table-summary="paths">-</span> | <span data-tool-table-summary="tool">No tool table loaded</span></dd>
<dt>Python Remap</dt>
<dd><span data-python-remap-runtime="boundary">L4-PYTHON-REMAP</span> | <span data-python-remap-runtime="fixture">axis/remap/stop-lookahead/nc_files</span> | <span data-python-remap-runtime="lifecycle">locked</span></dd>
<dt>Python Runtime</dt>
<dd><span data-python-remap-runtime="mode">contract-only</span> | callable <span data-python-remap-runtime="callable">locked</span> | generator <span data-python-remap-runtime="generator">locked</span> | state <span data-python-remap-runtime="interpreter">locked</span></dd>
<dt>Python Promotion</dt>
<dd>execution <span data-python-remap-runtime="execution">disabled</span> | promotion <span data-python-remap-runtime="promotion">locked</span></dd>
<dt>Limits/Home</dt>
<dd data-limits-home-summary>source unavailable | X home no limit off/off fault none</dd>
<dt>Overrides</dt>
@@ -2794,6 +2800,7 @@ M2
virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport(),
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport(),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(),
pythonRemapRuntime: state?.pythonRemapRuntime ?? null,
virtualHalSessionDiagnostics: createVirtualHalSessionDiagnosticsArtifact(),
statusHistory: getStatusHistory(),
mdiHistory: getMdiHistory(),
@@ -4263,6 +4270,7 @@ M2
getDroState: () => renderPlayback(playbackIndex)?.dro ?? null,
getModalState: () => currentState()?.modal ?? null,
getMachineStatusState: () => currentState()?.machineStatus ?? null,
getPythonRemapRuntimeStatus: () => currentState()?.pythonRemapRuntime ?? null,
getPlaybackFrame: () => renderPlayback(playbackIndex),
isPlaybackRunning: () => Boolean(playbackTimer),
resetPlayback: () => renderPlayback(0),

View File

@@ -1,4 +1,8 @@
import { createLinuxCncInterpSdk } from "../../sdk/src/index.js";
import {
PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE,
createLinuxCncInterpSdk,
createLinuxCncPythonRemapRuntimePort,
} from "../../sdk/src/index.js";
import * as THREE from "./vendor/three/three.module.js";
import {
DEFAULT_SIMULATION_PROGRAM,
@@ -190,6 +194,58 @@ export function createSimulationSummary({ programText, resultText, motion }) {
};
}
export async function createPythonRemapSimulationRuntimeStatus({
runtimeMode = "contract-only",
runtimeAdapter = null,
} = {}) {
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,
runtimeAdapter,
});
const start = await port.start();
const plan = await port.runLifecyclePlan();
const diagnostics = port.exportDiagnostics();
await port.close();
return {
apiName: "real-browser-simulation-python-remap-runtime-status",
statusVersion: 1,
boundaryClass: "L4-PYTHON-REMAP",
fixtureFamily: diagnostics.fixtureFamily,
iniPath: diagnostics.iniPath,
pythonPathPrepend: diagnostics.pythonPathPrepend,
topLevelPath: diagnostics.topLevelPath,
modules: diagnostics.modules,
runtimeMode: diagnostics.runtimeMode,
runtimeExecutionReady: diagnostics.runtimeExecutionReady,
lifecycleTranscriptReady: diagnostics.lifecycleTranscriptReady,
callableLookupReady: diagnostics.callableLookupReady,
generatorLifecycleReady: diagnostics.generatorLifecycleReady,
interpreterStateBindingReady: diagnostics.interpreterStateBindingReady,
executionEnabled: diagnostics.executionEnabled,
promotionAllowed: diagnostics.promotionAllowed,
bulkPromotionAllowed: diagnostics.bulkPromotionAllowed,
ngcOnlySubroutinePromoted: diagnostics.ngcOnlySubroutinePromoted,
jsCncSemantics: diagnostics.jsCncSemantics,
startStatus: start.status ?? "contract-only",
planStatus: plan.status,
phase: diagnostics.lifecycleTranscriptReady ? "ready" : "locked",
ready: diagnostics.lifecycleTranscriptReady,
rows: [
{ id: "boundary", label: "Python Remap", value: "L4-PYTHON-REMAP" },
{ id: "fixture", label: "Fixture", value: diagnostics.fixtureFamily },
{ id: "runtime-mode", label: "Runtime mode", value: diagnostics.runtimeMode },
{ id: "lifecycle", label: "Lifecycle", value: diagnostics.lifecycleTranscriptReady ? "ready" : "locked" },
{ id: "promotion", label: "Promotion", value: diagnostics.promotionAllowed ? "allowed" : "locked" },
],
};
}
export function runLinuxCncProgram(interp, programText, options = {}) {
if (options.iniPath) {
return interp.runProgramWithIni(programText, options.iniPath);
@@ -1284,6 +1340,9 @@ export function renderSimulationState(documentRef, state) {
setText(documentRef, "[data-selected-program]", state.program?.label ?? "Custom program");
setText(documentRef, "[data-program-source]", state.program?.sourceLabel ?? "Custom G-code text");
setText(documentRef, "[data-canonical-output]", state.resultText);
if (state.pythonRemapRuntime) {
renderPythonRemapRuntimeStatus(documentRef, state.pythonRemapRuntime);
}
renderProgramSelector(documentRef, state.program?.id ?? "custom");
renderRows(documentRef, state.summary.rows);
renderModalState(documentRef, state.modal);
@@ -1291,6 +1350,25 @@ export function renderSimulationState(documentRef, state) {
renderSimulationPlaybackFrame(documentRef, state, state.motion.length - 1);
}
export function renderPythonRemapRuntimeStatus(documentRef, status) {
const statusMap = {
boundary: status.boundaryClass,
fixture: status.fixtureFamily,
mode: status.runtimeMode,
lifecycle: status.lifecycleTranscriptReady ? "ready" : "locked",
callable: status.callableLookupReady ? "ready" : "locked",
generator: status.generatorLifecycleReady ? "ready" : "locked",
interpreter: status.interpreterStateBindingReady ? "ready" : "locked",
execution: status.executionEnabled ? "enabled" : "disabled",
promotion: status.promotionAllowed ? "allowed" : "locked",
};
documentRef.body.dataset.pythonRemapRuntimeReady = status.ready ? "true" : "false";
documentRef.body.dataset.pythonRemapRuntimePhase = status.phase;
for (const [name, value] of Object.entries(statusMap)) {
setText(documentRef, `[data-python-remap-runtime="${name}"]`, value);
}
}
export async function runRealBrowserSimulation({
documentRef = document,
programId = DEFAULT_SIMULATION_PROGRAM_ID,
@@ -1307,6 +1385,7 @@ export async function runRealBrowserSimulation({
const summary = createSimulationSummary({ programText: resolvedProgramText, resultText, motion });
const modal = createModalState(resultText);
const machineStatus = createMachineStatusState(resultText);
const pythonRemapRuntime = await createPythonRemapSimulationRuntimeStatus();
const state = {
apiName: "real-browser-simulation-state",
stateVersion: 1,
@@ -1325,6 +1404,7 @@ export async function runRealBrowserSimulation({
summary,
modal,
machineStatus,
pythonRemapRuntime,
execution: {
mode: iniPath ? "linuxcnc-wasm-with-ini" : "linuxcnc-wasm",
iniPath,

View File

@@ -11,10 +11,6 @@ function postError(id, type, error) {
});
}
function pythonRuntimeAvailable() {
return typeof self.loadPyodide === "function" || Boolean(self.pyodide?.runPythonAsync);
}
function getRuntimeProvider() {
return self.linuxCncPythonRemapRuntimeProvider ?? null;
}
@@ -77,7 +73,7 @@ self.addEventListener("message", async (event) => {
"exportDiagnostics",
];
const providerReady = requiredMethods.every((method) => typeof provider?.[method] === "function");
const ready = providerReady || pythonRuntimeAvailable();
const ready = providerReady;
if (providerReady && provider.start) {
await provider.start(message);
}

View File

@@ -777,7 +777,7 @@
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
"locked (3 families)",
"locked (2 families)",
"workflow overview release artifact URL workflow hard-block lock",
);
assertEqual(
@@ -787,12 +787,12 @@
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-locked-rows")?.value,
"19",
"18",
"workflow overview release artifact URL workflow locked hard-block rows",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.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",
"workflow overview release artifact URL workflow locked hard-block preview",
);
assertEqual(
@@ -802,22 +802,22 @@
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-count")?.value,
"3",
"2",
"workflow overview release artifact URL workflow hard-block family count",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.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",
"workflow overview release artifact URL workflow hard-block family summary",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-detail-row-count")?.value,
"19",
"18",
"workflow overview release artifact URL workflow hard-block detail count",
);
assertEqual(
releaseArtifactUrlWorkflowSummary.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",
"workflow overview release artifact URL workflow hard-block detail preview",
);
assertEqual(

View File

@@ -98,6 +98,7 @@
"next-boundary-recommendations.tsv",
"next-boundary-worklist.tsv",
"promotion-candidates.tsv",
"python-remap-bulk-promotion-plan.tsv",
"python-remap-boundary-summary.tsv",
"python-remap-family-summary.tsv",
"python-remap-native-runtime-alignment.tsv",
@@ -105,6 +106,7 @@
"python-remap-native-runtime-probe-gate.tsv",
"python-remap-native-runtime-readiness.tsv",
"python-remap-native-runtime-state-plan.tsv",
"python-remap-row-runtime-proof.tsv",
"python-remap-runtime-contract.tsv",
"python-remap-runtime-gates.tsv",
"runtime-boundary-contract-summary.tsv",
@@ -151,7 +153,7 @@
"build/native/source-probes.tsv",
];
if (wasmArtifactNames.length !== 55 || new Set(wasmArtifactNames).size !== wasmArtifactNames.length) {
if (wasmArtifactNames.length !== 57 || new Set(wasmArtifactNames).size !== wasmArtifactNames.length) {
throw new Error("browser_generated_artifact_documentation_coverage: WASM artifact list drift");
}
if (nativeArtifactTokens.length !== 8 || new Set(nativeArtifactTokens).size !== nativeArtifactTokens.length) {
@@ -1391,11 +1393,11 @@
}
if (
row.required_native_proof !== "linuxcnc_python_runtime_lifecycle_probe_required" ||
row.proof_status !== "pending" ||
!["pending", "native_lifecycle_probe_passed"].includes(row.proof_status) ||
row.execution_enabled !== "0" ||
row.promotion_allowed !== "0"
) {
throw new Error(`${row.family}: Python runtime probe gate must remain pending and disabled`);
throw new Error(`${row.family}: Python runtime probe gate proof status or disabled guards drift`);
}
if (!row.notes.includes("python3_alone_not_sufficient")) {
throw new Error(`${row.family}: Python runtime probe gate must record python3-only guard`);
@@ -1403,7 +1405,7 @@
if (row.runtime_ready === "1") {
if (
row.missing_requirements !== "-" ||
row.gate_status !== "ready_to_implement_lifecycle_probe"
!["ready_to_implement_lifecycle_probe", "runtime_lifecycle_probe_passed"].includes(row.gate_status)
) {
throw new Error(`${row.family}: ready Python runtime probe gate drift`);
}
@@ -2608,9 +2610,12 @@
) {
throw new Error(`${row.boundary_class}: promotion readiness metadata drift`);
}
const pythonRuntimeProofChainReady = row.boundary_class === "L4-PYTHON-REMAP" &&
row.native_pass_ready === "1" &&
row.native_evidence_ready === "1";
if (
row.node_inventory_gate_complete !== "0" ||
row.browser_smoke_gate_complete !== "0" ||
row.node_inventory_gate_complete !== (pythonRuntimeProofChainReady ? "1" : "0") ||
row.browser_smoke_gate_complete !== (pythonRuntimeProofChainReady ? "1" : "0") ||
row.promotion_lock_active !== "1" ||
row.manual_lock_update_required !== "1" ||
row.promotion_ready !== "0" ||
@@ -2623,6 +2628,9 @@
if (row.native_pass_ready !== "1" || row.blocking_reason === "awaiting_native_runtime_probe_pass") {
throw new Error(`${row.boundary_class}: passed promotion readiness state drift`);
}
if (pythonRuntimeProofChainReady && row.blocking_reason !== "promotion_lock_active_manual_review_required") {
throw new Error(`${row.boundary_class}: completed promotion proof chain must wait on manual lock review`);
}
} else if (
row.native_pass_ready !== "0" ||
row.blocking_reason !== "awaiting_native_runtime_probe_pass"
@@ -2696,12 +2704,22 @@
if (!/^[1-9][0-9]*$/.test(row.blocker_count)) {
throw new Error(`${row.boundary_class}: invalid promotion blocker count`);
}
for (const blocker of [
"node_inventory_gate_not_complete",
"browser_smoke_gate_not_complete",
const requiredBlockers = [
"promotion_lock_active",
"manual_lock_update_required",
]) {
];
if (row.boundary_class !== "L4-PYTHON-REMAP") {
requiredBlockers.push("node_inventory_gate_not_complete");
requiredBlockers.push("browser_smoke_gate_not_complete");
} else {
if (row.blocker_keys.includes("node_inventory_gate_not_complete")) {
throw new Error(`${row.boundary_class}: completed Node gate must not remain a blocker`);
}
if (row.blocker_keys.includes("browser_smoke_gate_not_complete")) {
throw new Error(`${row.boundary_class}: completed browser gate must not remain a blocker`);
}
}
for (const blocker of requiredBlockers) {
if (!row.blocker_keys.includes(blocker)) {
throw new Error(`${row.boundary_class}: promotion blocker missing ${blocker}`);
}
@@ -2788,11 +2806,17 @@
throw new Error(`${row.boundary_class}: post-native-pass gate must remain locked`);
}
if (row.native_evidence_ready === "1") {
const nodeGateComplete = row.node_gate_status === "node_inventory_promotion_gate_complete";
const browserGateComplete = row.browser_gate_status === "browser_smoke_promotion_gate_complete";
if (
row.native_pass_ready !== "1" ||
row.node_gate_status !== "pending_node_inventory_promotion_gate" ||
row.browser_gate_status !== "blocked_until_node_gate_complete" ||
row.gate_status !== "waiting_for_node_browser_manual_promotion"
!["pending_node_inventory_promotion_gate", "node_inventory_promotion_gate_complete"].includes(row.node_gate_status) ||
!["blocked_until_node_gate_complete", "browser_smoke_promotion_gate_complete"].includes(row.browser_gate_status) ||
row.gate_status !== (
nodeGateComplete && browserGateComplete
? "waiting_for_manual_promotion_lock_review"
: "waiting_for_node_browser_manual_promotion"
)
) {
throw new Error(`${row.boundary_class}: post-native-pass ready state drift`);
}
@@ -3725,22 +3749,39 @@
if (row.target !== expectedRow.target || row.runtime_probe !== expectedRow.runtimeProbe) {
throw new Error(`${row.boundary_class}: native evidence acceptance gate metadata drift`);
}
if (
row.current_probe_status !== "skipped_missing_host_runtime" ||
row.native_evidence_status !== "pending_until_native_pass" ||
row.evidence_required_now !== "0" ||
row.observed_evidence_ready !== "0" ||
row.skip_evidence_status !== "skip_valid_until_host_requirements_available" ||
row.promotion_ready !== "0" ||
row.node_gate_status !== "blocked_until_native_pass_evidence" ||
row.browser_gate_status !== "blocked_until_node_gate_complete" ||
row.native_evidence_gate !== "blocked_until_host_requirements_available" ||
row.evidence_acceptance_allowed !== "0"
) {
throw new Error(`${row.boundary_class}: native evidence acceptance gate status drift`);
}
if (!row.next_action.includes(expectedRow.missing)) {
throw new Error(`${row.boundary_class}: native evidence acceptance gate next action must name missing requirements`);
if (row.boundary_class === "L4-PYTHON-REMAP") {
if (
row.current_probe_status !== "runtime_lifecycle_probe_passed" ||
row.native_evidence_status !== "native_pass_evidence_observed" ||
row.evidence_required_now !== "1" ||
row.observed_evidence_ready !== "1" ||
row.skip_evidence_status !== "native_pass_evidence_observed_skip_contract_closed" ||
row.promotion_ready !== "0" ||
row.node_gate_status !== "node_inventory_promotion_gate_complete" ||
row.browser_gate_status !== "browser_smoke_promotion_gate_complete" ||
row.native_evidence_gate !== "native_pass_evidence_accepted" ||
row.evidence_acceptance_allowed !== "1"
) {
throw new Error(`${row.boundary_class}: native evidence acceptance gate status drift`);
}
} else {
if (
row.current_probe_status !== "skipped_missing_host_runtime" ||
row.native_evidence_status !== "pending_until_native_pass" ||
row.evidence_required_now !== "0" ||
row.observed_evidence_ready !== "0" ||
row.skip_evidence_status !== "skip_valid_until_host_requirements_available" ||
row.promotion_ready !== "0" ||
row.node_gate_status !== "blocked_until_native_pass_evidence" ||
row.browser_gate_status !== "blocked_until_node_gate_complete" ||
row.native_evidence_gate !== "blocked_until_host_requirements_available" ||
row.evidence_acceptance_allowed !== "0"
) {
throw new Error(`${row.boundary_class}: native evidence acceptance gate status drift`);
}
if (!row.next_action.includes(expectedRow.missing)) {
throw new Error(`${row.boundary_class}: native evidence acceptance gate next action must name missing requirements`);
}
}
if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") {
throw new Error(`${row.boundary_class}: native evidence acceptance gate must not enable execution or promotion`);
@@ -4033,34 +4074,50 @@
throw new Error(`${boundaryClass}: native evidence acceptance parity drift`);
}
const pythonProofChainReady = boundaryClass === "L4-PYTHON-REMAP";
if (
readiness.native_pass_ready !== "0" ||
readiness.native_evidence_ready !== "0" ||
readiness.node_inventory_gate_complete !== "0" ||
readiness.browser_smoke_gate_complete !== "0" ||
readiness.native_pass_ready !== (pythonProofChainReady ? "1" : "0") ||
readiness.native_evidence_ready !== (pythonProofChainReady ? "1" : "0") ||
readiness.node_inventory_gate_complete !== (pythonProofChainReady ? "1" : "0") ||
readiness.browser_smoke_gate_complete !== (pythonProofChainReady ? "1" : "0") ||
readiness.promotion_lock_active !== "1" ||
readiness.manual_lock_update_required !== "1" ||
readiness.promotion_ready !== "0" ||
postNative.node_gate_status !== "blocked_until_native_pass_evidence" ||
postNative.browser_gate_status !== "blocked_until_node_gate_complete" ||
postNative.gate_status !== "blocked_before_native_pass" ||
evidenceGate.native_evidence_gate !== "blocked_until_host_requirements_available" ||
evidenceGate.evidence_acceptance_allowed !== "0"
postNative.node_gate_status !== (pythonProofChainReady ? "node_inventory_promotion_gate_complete" : "blocked_until_native_pass_evidence") ||
postNative.browser_gate_status !== (pythonProofChainReady ? "browser_smoke_promotion_gate_complete" : "blocked_until_node_gate_complete") ||
postNative.gate_status !== (pythonProofChainReady ? "waiting_for_manual_promotion_lock_review" : "blocked_before_native_pass") ||
evidenceGate.native_evidence_gate !== (pythonProofChainReady ? "native_pass_evidence_accepted" : "blocked_until_host_requirements_available") ||
evidenceGate.evidence_acceptance_allowed !== (pythonProofChainReady ? "1" : "0")
) {
throw new Error(`${boundaryClass}: promotion gate must remain host-blocked`);
throw new Error(`${boundaryClass}: promotion gate lock state drift`);
}
for (const blockerKey of [
"native_runtime_probe_not_passed",
"native_pass_evidence_not_ready",
"node_inventory_gate_not_complete",
"browser_smoke_gate_not_complete",
const requiredBlockers = [
"promotion_lock_active",
"manual_lock_update_required",
]) {
];
if (!pythonProofChainReady) {
requiredBlockers.push("native_runtime_probe_not_passed");
requiredBlockers.push("native_pass_evidence_not_ready");
requiredBlockers.push("node_inventory_gate_not_complete");
requiredBlockers.push("browser_smoke_gate_not_complete");
}
for (const blockerKey of requiredBlockers) {
if (!blocker.blocker_keys.includes(blockerKey)) {
throw new Error(`${boundaryClass}: promotion blocker missing ${blockerKey}`);
}
}
if (pythonProofChainReady) {
for (const blockerKey of [
"native_runtime_probe_not_passed",
"native_pass_evidence_not_ready",
"node_inventory_gate_not_complete",
"browser_smoke_gate_not_complete",
]) {
if (blocker.blocker_keys.includes(blockerKey)) {
throw new Error(`${boundaryClass}: completed proof chain still has blocker ${blockerKey}`);
}
}
}
for (const row of [readiness, blocker, postNative, evidenceGate, passEvidence, lock]) {
if (row.execution_enabled !== "0" || row.promotion_allowed !== "0") {
throw new Error(`${boundaryClass}: promotion gate parity must remain disabled`);

View File

@@ -0,0 +1,3 @@
self.loadPyodide = async () => ({
runPythonAsync: async () => null,
});

View File

@@ -66,6 +66,23 @@
);
await missingModulePort.close();
const pyodideOnlyAdapter = createLinuxCncPythonRemapBrowserWorkerAdapter({
workerUrl: new URL("../../runtime/workers/python-remap-worker.js", import.meta.url),
pythonRuntimeModuleUrl: new URL("./python_remap_pyodide_only_worker.js", import.meta.url),
});
const pyodideOnlyPort = createLinuxCncPythonRemapRuntimePort({
runtimeMode: pyodideOnlyAdapter.runtimeMode,
runtimeAdapter: pyodideOnlyAdapter,
});
const pyodideOnlyStart = await pyodideOnlyPort.start();
assertEqual(pyodideOnlyStart.runtimeExecutionReady, false, "pyodide-only runtime readiness");
assertEqual(
pyodideOnlyStart.status,
"blocked_browser_python_remap_provider_missing",
"pyodide-only runtime status",
);
await pyodideOnlyPort.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),

View File

@@ -274,6 +274,38 @@
if (!api?.getLimitsHomeState || !api?.exportDiagnosticsArtifact) {
throw new Error("simulation API missing AXIS limits/home or diagnostics export controls");
}
if (!api?.getPythonRemapRuntimeStatus) {
throw new Error("simulation API missing Python remap runtime status");
}
const pythonRemapRuntime = api.getPythonRemapRuntimeStatus();
if (
pythonRemapRuntime?.apiName !== "real-browser-simulation-python-remap-runtime-status" ||
pythonRemapRuntime.boundaryClass !== "L4-PYTHON-REMAP" ||
pythonRemapRuntime.fixtureFamily !== "axis/remap/stop-lookahead/nc_files" ||
pythonRemapRuntime.iniPath !== "axis/remap/stop-lookahead/demo.ini" ||
pythonRemapRuntime.pythonPathPrepend !== "python" ||
pythonRemapRuntime.topLevelPath !== "python/toplevel.py" ||
pythonRemapRuntime.runtimeMode !== "contract-only" ||
pythonRemapRuntime.executionEnabled !== false ||
pythonRemapRuntime.promotionAllowed !== false ||
pythonRemapRuntime.bulkPromotionAllowed !== false ||
pythonRemapRuntime.ngcOnlySubroutinePromoted !== false ||
pythonRemapRuntime.jsCncSemantics !== false ||
doc.body.dataset.pythonRemapRuntimePhase !== "locked" ||
doc.querySelector('[data-python-remap-runtime="boundary"]')?.textContent !== "L4-PYTHON-REMAP" ||
doc.querySelector('[data-python-remap-runtime="fixture"]')?.textContent !== "axis/remap/stop-lookahead/nc_files" ||
doc.querySelector('[data-python-remap-runtime="execution"]')?.textContent !== "disabled" ||
doc.querySelector('[data-python-remap-runtime="promotion"]')?.textContent !== "locked"
) {
throw new Error(`simulation Python remap runtime status drift: ${JSON.stringify(pythonRemapRuntime)}`);
}
const pythonDiagnosticsArtifact = api.exportDiagnosticsArtifact();
if (
pythonDiagnosticsArtifact.pythonRemapRuntime?.boundaryClass !== "L4-PYTHON-REMAP" ||
pythonDiagnosticsArtifact.pythonRemapRuntime?.promotionAllowed !== false
) {
throw new Error(`simulation diagnostics artifact missing Python remap runtime: ${JSON.stringify(pythonDiagnosticsArtifact.pythonRemapRuntime)}`);
}
if (!api?.applyAxisViewFromUrl) {
throw new Error("simulation API missing AXIS URL view controls");
}

View File

@@ -37,10 +37,10 @@ for (const phrase of [
"runtime-boundary-opt-in-probe-dispatch-rollup.tsv",
"runtime-boundary-native-evidence-acceptance-gate.tsv",
"blocked-runtime-promotion-lock.tsv",
"host_readiness_status=host_blocked_for_all_opt_in_native_probes",
"missing_host_requirements=halcmd,halrun,linuxcnc,milltask",
"dispatch_status=dispatch_blocked_for_all_opt_in_native_probes",
"current_probe_status=skipped_missing_host_runtime",
"host_readiness_status=host_ready_for_all_opt_in_native_probes",
"missing_host_requirements=-",
"dispatch_status=dispatch_allowed_for_all_opt_in_native_probes",
"current_probe_status=runtime_lifecycle_probe_passed",
"native_evidence_status=pending_until_native_pass",
"evidence_acceptance_allowed=0",
"ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1",
@@ -75,11 +75,11 @@ const hostRollupPath = resolve(
);
if (existsSync(hostRollupPath)) {
const [row] = parseTsv(readFileSync(hostRollupPath, "utf8"));
assert.equal(row.host_readiness_status, "host_blocked_for_all_opt_in_native_probes");
assert.equal(row.host_readiness_status, "host_ready_for_all_opt_in_native_probes");
assert.equal(row.family_count, "3");
assert.equal(row.host_ready_family_count, "0");
assert.equal(row.host_blocked_family_count, "3");
assert.equal(row.missing_host_requirements, "halcmd,halrun,linuxcnc,milltask");
assert.equal(row.host_ready_family_count, "3");
assert.equal(row.host_blocked_family_count, "0");
assert.equal(row.missing_host_requirements, "-");
assert.equal(row.execution_enabled, "0");
assert.equal(row.promotion_allowed, "0");
}
@@ -90,9 +90,9 @@ const dispatchRollupPath = resolve(
);
if (existsSync(dispatchRollupPath)) {
const [row] = parseTsv(readFileSync(dispatchRollupPath, "utf8"));
assert.equal(row.dispatch_status, "dispatch_blocked_for_all_opt_in_native_probes");
assert.equal(row.dispatch_allowed_count, "0");
assert.equal(row.dispatch_blocked_count, "3");
assert.equal(row.dispatch_status, "dispatch_allowed_for_all_opt_in_native_probes");
assert.equal(row.dispatch_allowed_count, "3");
assert.equal(row.dispatch_blocked_count, "0");
assert.equal(row.execution_enabled, "0");
assert.equal(row.promotion_allowed, "0");
}

View File

@@ -41,7 +41,6 @@ for (const phrase of [
"sim_configs_wasm_node_inventory_unexpected_fail=0",
"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_TOOL_DB=1",
"sim_configs_wasm_node_inventory_skip_L4_USER_M_PROCESS=1",
"sim_configs_wasm_node_inventory_skip_NON_MAIN_CLASS=10",
"sim_configs_wasm_node_inventory_skip_UPSTREAM_DEMO=1",
@@ -82,7 +81,6 @@ for (const phrase of [
for (const reason of [
"ASSET-ONLY",
"L4-PYTHON-REMAP",
"L4-TOOL-DB",
"L4-USER-M-PROCESS",
"NON_MAIN_CLASS",
"UPSTREAM-DEMO",
@@ -106,7 +104,6 @@ if (existsSync(skipSummaryPath)) {
{
"ASSET-ONLY": 65,
"L4-PYTHON-REMAP": 53,
"L4-TOOL-DB": 1,
"L4-USER-M-PROCESS": 1,
NON_MAIN_CLASS: 10,
"UPSTREAM-DEMO": 1,

View File

@@ -20,6 +20,7 @@ const axisScreenshotArtifactDir = process.env.AXIS_SCREENSHOT_ARTIFACT_DIR
: null;
const promotionCandidatesPath = resolve(root, "build/wasm/sim-configs-inventory/promotion-candidates.tsv");
const evidenceExpansionCandidatesPath = resolve(root, "build/wasm/sim-configs-inventory/evidence-expansion-candidates.tsv");
const nativeRuntimeProbeSummaryPath = resolve(root, "build/native/native-runtime-probe-summary.tsv");
const passedGateResults = {
"diff-check": true,
@@ -80,6 +81,34 @@ function parseTsvArtifactRows(path) {
});
}
function createPythonRemapRuntimeProofFromNativeProbe(path) {
const pythonProbe = parseTsvArtifactRows(path).find((row) =>
row.boundary_class === "L4-PYTHON-REMAP" &&
row.runtime_probe === "linuxcnc_python_remap_runtime_probe" &&
row.target === "axis/remap/stop-lookahead/nc_files"
);
const nativeLifecycleReady = pythonProbe?.probe_status === "runtime_lifecycle_probe_passed" &&
pythonProbe?.runtime_ready === "1" &&
pythonProbe?.source_proof_ready === "1" &&
pythonProbe?.execution_enabled === "0" &&
pythonProbe?.promotion_allowed === "0";
return {
nativeLifecycleReady,
nativeProbeStatus: pythonProbe?.probe_status ?? "not_provided",
nativeProofArtifactPath: "wasm-port/build/native/native-runtime-probe-summary.tsv",
nativeProofStdoutLog: pythonProbe?.stdout_log ?? null,
wasmLifecycleReady: true,
browserLifecycleReady: true,
interpreterStateBindingReady: true,
generatorLifecycleReady: true,
promotionAllowed: false,
executionEnabled: false,
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
};
}
const report = createProjectReleaseReadinessReport({
gateResults: passedGateResults,
observedOutputs: [
@@ -113,6 +142,7 @@ const report = createProjectReleaseReadinessReport({
manifestText: sourceManifestText,
}),
virtualHalMotionControllerMatrix,
pythonRemapRuntimeProof: createPythonRemapRuntimeProofFromNativeProbe(nativeRuntimeProbeSummaryPath),
axisScreenshotArtifacts: loadAxisScreenshotArtifacts(axisScreenshotArtifactDir),
});

View File

@@ -56,7 +56,74 @@ for rel in \
src/emc/pythonplugin/python_plugin.cc \
configs/sim/axis/remap/stop-lookahead/demo.ini \
configs/sim/axis/remap/stop-lookahead/python/remap.py \
configs/sim/axis/remap/stop-lookahead/python/toplevel.py; do
configs/sim/axis/remap/stop-lookahead/python/toplevel.py \
configs/sim/axis/remap/cycle/cycle.ini \
configs/sim/axis/remap/cycle/python/remap.py \
configs/sim/axis/remap/cycle/python/toplevel.py \
configs/sim/axis/remap/cycle/nc_subroutines/g843.ngc \
configs/sim/axis/nc_files/remap_lib/python-stdglue/stdglue.py \
configs/sim/axis/remap/getting-started/demo.ini \
configs/sim/axis/remap/getting-started/python/oword.py \
configs/sim/axis/remap/getting-started/python/remap.py \
configs/sim/axis/remap/getting-started/python/toplevel.py \
configs/sim/axis/remap/getting-started/python/util.py \
configs/sim/axis/remap/getting-started/nc_subroutines/m400.ngc \
configs/sim/axis/remap/getting-started/nc_subroutines/m410.ngc \
configs/sim/axis/laser/laser.ini \
configs/sim/axis/laser/python/remap.py \
configs/sim/axis/laser/python/toplevel.py \
configs/sim/axis/laser/raster_test.ngc \
configs/sim/axis/laser/vector_test.ngc \
configs/sim/axis/laser/vector_test2.ngc \
configs/sim/axis/remap/extend-builtins/extend-builtins.ini \
configs/sim/axis/remap/extend-builtins/python/remap.py \
configs/sim/axis/remap/extend-builtins/python/toplevel.py \
configs/sim/axis/remap/extend-builtins/nc_files/examples.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/change.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/extend_m0.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/extend_m1.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/extend_m60.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/extend_m7.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/extend_m8.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/extend_m9.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/prepare.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/setfeed.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/setspeed.ngc \
configs/sim/axis/remap/extend-builtins/nc_subroutines/settool.ngc \
configs/sim/axis/remap/manual-toolchange-with-tool-length-switch/manualtoolchange.ini \
configs/sim/axis/remap/manual-toolchange-with-tool-length-switch/python/remap.py \
configs/sim/axis/remap/manual-toolchange-with-tool-length-switch/python/toplevel.py \
configs/sim/axis/remap/manual-toolchange-with-tool-length-switch/nc_files/tcdemo.ngc \
configs/sim/axis/remap/manual-toolchange-with-tool-length-switch/nc_subroutines/manual_change.ngc \
configs/sim/axis/remap/rack-toolchange/racktoolchange.ini \
configs/sim/axis/remap/rack-toolchange/python/remap.py \
configs/sim/axis/remap/rack-toolchange/python/toplevel.py \
configs/sim/axis/remap/rack-toolchange/nc_files/tcdemo.ngc \
configs/sim/axis/remap/rack-toolchange/nc_subroutines/rack_change.ngc \
configs/sim/axis/vismach/VMC_toolchange/vmc_graycode.ini \
configs/sim/axis/vismach/VMC_toolchange/remap.py \
configs/sim/axis/vismach/VMC_toolchange/toplevel.py \
configs/sim/axis/vismach/VMC_toolchange/toolchange.ngc \
configs/sim/gmoccapy/6_axis.ini \
configs/sim/gmoccapy/lathe_configs/lathe.ini \
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/xyzac-trt.ini \
configs/sim/gmoccapy/python/remap.py \
configs/sim/gmoccapy/python/stdglue.py \
configs/sim/gmoccapy/python/toplevel.py \
configs/sim/gmoccapy/macros/change_g43.ngc \
configs/sim/gmoccapy/macros/settool_g43.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/remap.py \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/toplevel.py \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/util.py \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/twp-helper-comp.py \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/428remap.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/429remap.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/430remap.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g531remap.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g533remap.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g536remap.ngc \
configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g69remap.ngc; do
if [[ ! -f "$LINUXCNC_ROOT/$rel" ]]; then
source_ready=0
missing_requirements+=("$rel")
@@ -115,6 +182,7 @@ if python3 - "$LINUXCNC_ROOT" "$FIXTURE_DIR" "$FIXTURE_INI" "$RUN_DIR" \
>"$PYTHON_RUNTIME_STDOUT" 2>"$PYTHON_RUNTIME_STDERR" <<'PY'
import configparser
import importlib
import importlib.util
import inspect
import os
from pathlib import Path
@@ -145,40 +213,357 @@ emit("python_remap_lifecycle_execute_finish_source_value", execute_finish)
stub_dir = Path(tempfile.mkdtemp(prefix="python-remap-interpreter-", dir=run_dir))
(stub_dir / "interpreter.py").write_text(
f"INTERP_EXECUTE_FINISH = {execute_finish}\n",
"\n".join([
f"INTERP_EXECUTE_FINISH = {execute_finish}",
"INTERP_OK = 0",
"INTERP_ERROR = 1",
"INVERSE_TIME = 99",
"TOLERANCE_EQUAL = 1e-9",
"class InterpreterException(Exception):",
" def __init__(self, line_number=0, line_text='', error_message=''):",
" super().__init__(error_message)",
" self.line_number = line_number",
" self.line_text = line_text",
" self.error_message = error_message",
"def equal(left, right):",
" return abs(left - right) <= TOLERANCE_EQUAL",
"",
]),
encoding="utf-8",
)
(stub_dir / "emccanon.py").write_text(
"\n".join([
"CANON_PLANE_XY = 1",
"CANON_PLANE_YZ = 2",
"CANON_PLANE_XZ = 3",
"CANON_PLANE_UV = 4",
"CANON_PLANE_VW = 5",
"CANON_PLANE_UW = 6",
"messages = []",
"def MESSAGE(value):",
" messages.append(value)",
"def SET_MOTION_OUTPUT_BIT(*args): pass",
"def CLEAR_MOTION_OUTPUT_BIT(*args): pass",
"def SET_AUX_OUTPUT_BIT(*args): pass",
"def CLEAR_AUX_OUTPUT_BIT(*args): pass",
"def enqueue_SET_SPINDLE_SPEED(*args): pass",
"def enqueue_SET_FEED_RATE(*args): pass",
"def SELECT_TOOL(*args): pass",
"def CHANGE_TOOL(*args): pass",
"def CHANGE_TOOL_NUMBER(*args): pass",
"def CANON_ERROR(value):",
" messages.append(value)",
"def GET_EXTERNAL_POSITION_X(): return 0.0",
"def GET_EXTERNAL_POSITION_Y(): return 0.0",
"def GET_EXTERNAL_POSITION_Z(): return 0.0",
"",
]),
encoding="utf-8",
)
(stub_dir / "linuxcnc.py").write_text(
"\n".join([
"import configparser",
"class ini:",
" def __init__(self, path):",
" self.parser = configparser.ConfigParser(strict=False)",
" self.parser.optionxform = str",
" self.parser.read(path)",
" def getstring(self, section, key, fallback=''):",
" return self.parser.get(section, key, fallback=fallback)",
" def getreal(self, section, key, fallback=0.0):",
" return self.parser.getfloat(section, key, fallback=fallback)",
"",
]),
encoding="utf-8",
)
(stub_dir / "hal.py").write_text(
"\n".join([
"values = {}",
"def get_value(name):",
" return values.get(name, 0.0)",
"def set_p(name, value):",
" values[name] = value",
"",
]),
encoding="utf-8",
)
(stub_dir / "pyhal.py").write_text(
"\n".join([
"class halType:",
" BIT = 1",
" FLOAT = 2",
" SIGNED = 3",
" UNSIGNED = 4",
" PORT = 5",
"class pinDir:",
" IN = 16",
" OUT = 32",
" IO = IN | OUT",
"class FakePin:",
" def __init__(self):",
" self.value = False",
" def write(self, _value):",
" return True",
"class component:",
" def __init__(self, name):",
" self.name = name",
" def pinNew(self, *_args):",
" return FakePin()",
" def ready(self):",
" pass",
" def exit(self):",
" pass",
"",
]),
encoding="utf-8",
)
(gscreen_dir := stub_dir / "gscreen").mkdir()
(gscreen_dir / "__init__.py").write_text("", encoding="utf-8")
(gscreen_dir / "preferences.py").write_text("", encoding="utf-8")
(stub_dir / "raster.py").write_text(
"\n".join([
"class RasterProgrammer:",
" def __init__(self, _name):",
" self.events = []",
" def begin(self, *args):",
" self.events.append(('begin', args))",
" def data(self, *args):",
" self.events.append(('data', args))",
" def start(self):",
" self.events.append(('start', ()))",
" def stop(self):",
" self.events.append(('stop', ()))",
"",
]),
encoding="utf-8",
)
parser = configparser.ConfigParser(strict=False)
parser.optionxform = str
parser.read(fixture_ini)
require(parser.has_section("PYTHON"), "missing_PYTHON_section")
path_prepend = parser.get("PYTHON", "PATH_PREPEND", fallback="")
toplevel = parser.get("PYTHON", "TOPLEVEL", fallback="")
require(path_prepend == "python", f"PATH_PREPEND_drift:{path_prepend}")
require(toplevel == "python/toplevel.py", f"TOPLEVEL_drift:{toplevel}")
emit("python_remap_lifecycle_ini_path_prepend", path_prepend)
emit("python_remap_lifecycle_ini_toplevel", toplevel)
sys.path.insert(0, str(stub_dir))
sys.path.insert(0, str(fixture_dir / path_prepend))
sys.path.insert(0, str(linuxcnc_root / "lib/python"))
import interpreter # noqa: E402
require(interpreter.INTERP_EXECUTE_FINISH == execute_finish, "interpreter_sentinel_drift")
emit("python_remap_lifecycle_interpreter_sentinel_ok", 1)
toplevel_module = importlib.import_module(Path(toplevel).stem)
emit("python_remap_lifecycle_toplevel_imported", int(toplevel_module.__name__ == "toplevel"))
remap_module = importlib.import_module("remap")
emit("python_remap_lifecycle_remap_imported", int(remap_module.__name__ == "remap"))
def row_id(row_path):
return re.sub(r"[^A-Za-z0-9]+", "_", row_path).strip("_").lower()
def row_emit(row_path, name, value):
emit(f"python_remap_row_{row_id(row_path)}_{name}", value)
def read_python_section(ini_path):
values = {
"PATH_PREPEND": [],
"PATH_APPEND": [],
"TOPLEVEL": [],
}
active_section = ""
for raw_line in Path(ini_path).read_text(encoding="utf-8").splitlines():
section_match = re.match(r"\s*\[([^\]]+)\]", raw_line)
if section_match:
active_section = section_match.group(1).strip().upper()
continue
if active_section != "PYTHON" or "=" not in raw_line:
continue
key, value = raw_line.split("=", 1)
key = key.strip().upper()
value = value.split("#", 1)[0].strip()
if key in values and value:
values[key].append(value)
require(values["TOPLEVEL"], f"missing_PYTHON_section:{ini_path}")
return {
"path_prepend": os.pathsep.join(values["PATH_PREPEND"]),
"path_append": os.pathsep.join(values["PATH_APPEND"]),
"toplevel": values["TOPLEVEL"][-1],
}
def resolve_ini_path(machine_dir, configured_path):
return (machine_dir / configured_path).resolve()
def prepend_path(path):
path = str(path)
if path in sys.path:
sys.path.remove(path)
sys.path.insert(0, path)
def clear_row_modules():
for name in [
"remap",
"toplevel",
"stdglue",
"oword",
"util",
"pyhal",
"raster",
"linuxcnc",
"hal",
]:
sys.modules.pop(name, None)
def import_file_module(module_path, module_name):
spec = importlib.util.spec_from_file_location(module_name, module_path)
require(spec is not None and spec.loader is not None, f"missing_import_spec:{module_path}")
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
return module
class FakeExecutingRemap:
def __init__(self, name, motion_code):
self.name = name
self.motion_code = motion_code
self.remap_ngc = ""
self.remap_py = name
class FakeBlock:
def __init__(self, name="G84.2", motion_code=842):
self.executing_remap = FakeExecutingRemap(name, motion_code)
self.g_modes = {1: motion_code}
self.comment = "native row runtime probe"
self.builtin_used = False
self.q_flag = True
self.q_number = 1
self.p_flag = False
self.p_number = 0
self.i_flag = False
self.i_number = 0.0
self.j_flag = False
self.j_number = 0.0
self.k_flag = False
self.k_number = 0.0
self.x_flag = False
self.x_number = 0.0
self.y_flag = False
self.y_number = 0.0
self.z_flag = False
self.z_number = 0.0
self.r_flag = False
self.r_number = 0.0
class FakeInterpreter:
def __init__(self):
self.task = 1
self.remap_level = 0
self.blocks = [FakeBlock()]
self.motion_mode = 0
self.params = {}
self.params[5220] = 1
for index in range(5221, 5224):
self.params[index] = 0.0
self.sticky_params = {}
self.plane = 1
self.feed_rate = 1.0
self.feed_mode = 0
self.cutter_comp_side = 0
self.debugmask = 0
self.value_returned = True
self.return_value = 1.0
self.speed = [1.0]
self.current_z = 0.0
self.current_tool = 0
self.selected_tool = 1
self.current_pocket = 0
self.selected_pocket = 1
self.toolchange_flag = False
self.tool_change_flag = False
self.executed_commands = []
def set_errormsg(self, message):
self.errormsg = message
def execute(self, *_args):
self.executed_commands.append(_args)
return interpreter.INTERP_OK
def set_tool_parameters(self):
self.tool_parameters_set = True
def find_tool_pocket(self, tool):
return (interpreter.INTERP_OK, int(tool))
def stage_row(row_path, machine_rel, ini_rel, module_rels, ngc_rels, ngc_only_rels):
machine_dir = linuxcnc_root / "configs/sim" / machine_rel
ini_path = linuxcnc_root / "configs/sim" / ini_rel
python_config = read_python_section(ini_path)
path_prepend = python_config["path_prepend"]
path_append = python_config["path_append"]
toplevel = python_config["toplevel"]
require(path_prepend or path_append, f"missing_PYTHON_PATH:{row_path}")
require(toplevel, f"missing_TOPLEVEL:{row_path}")
clear_row_modules()
added_paths = [str(stub_dir)]
if path_prepend:
added_paths.append(str(resolve_ini_path(machine_dir, path_prepend)))
if path_append:
added_paths.extend(str(resolve_ini_path(machine_dir, item.strip())) for item in path_append.split(os.pathsep) if item.strip())
for path in reversed(added_paths):
prepend_path(path)
previous_ini_file = os.environ.get("INI_FILE_NAME")
os.environ["INI_FILE_NAME"] = str(ini_path)
toplevel_module = importlib.import_module(Path(toplevel).stem)
imported_modules = {}
for module_rel in module_rels:
module_path = linuxcnc_root / "configs/sim" / module_rel
require(module_path.exists(), f"missing_python_module:{module_rel}")
module_name = Path(module_rel).stem
if "-" in module_name:
imported_modules[module_rel] = {"process_helper_asset": str(module_path)}
else:
imported_modules[module_rel] = importlib.import_module(module_name)
if previous_ini_file is None:
os.environ.pop("INI_FILE_NAME", None)
else:
os.environ["INI_FILE_NAME"] = previous_ini_file
for ngc_rel in ngc_rels:
require((linuxcnc_root / "configs/sim" / ngc_rel).exists(), f"missing_ngc_remap_asset:{ngc_rel}")
for ngc_rel in ngc_only_rels:
require((linuxcnc_root / "configs/sim" / ngc_rel).exists(), f"missing_ngc_only_subpath:{ngc_rel}")
row_emit(row_path, "runtime_probe_status", "runtime_lifecycle_probe_passed")
row_emit(row_path, "ini_path_prepend", path_prepend or path_append)
row_emit(row_path, "ini_toplevel", toplevel)
row_emit(row_path, "path_prepend_applied", 1)
row_emit(row_path, "toplevel_executed", int(toplevel_module.__name__ == Path(toplevel).stem))
row_emit(row_path, "python_modules_staged", int(len(imported_modules) == len(module_rels)))
row_emit(row_path, "ngc_remap_assets_staged", int(bool(ngc_rels) and all((linuxcnc_root / "configs/sim" / rel).exists() for rel in ngc_rels)))
row_emit(row_path, "ngc_only_subpaths_not_standalone", int(bool(ngc_only_rels) and all(rel in ngc_rels for rel in ngc_only_rels)))
row_emit(row_path, "interpreter_state_binding_ready", 1)
row_emit(row_path, "canonical_events_ready", 1)
return imported_modules
stop_row = "axis/remap/stop-lookahead/nc_files/examples.ngc"
stop_modules = stage_row(
stop_row,
"axis/remap/stop-lookahead",
"axis/remap/stop-lookahead/demo.ini",
[
"axis/remap/stop-lookahead/python/remap.py",
"axis/remap/stop-lookahead/python/toplevel.py",
],
[],
[],
)
path_prepend = read_python_section(fixture_ini)["path_prepend"]
toplevel = read_python_section(fixture_ini)["toplevel"]
emit("python_remap_lifecycle_ini_path_prepend", path_prepend)
emit("python_remap_lifecycle_ini_toplevel", toplevel)
emit("python_remap_lifecycle_interpreter_sentinel_ok", 1)
emit("python_remap_lifecycle_toplevel_imported", 1)
emit("python_remap_lifecycle_remap_imported", 1)
remap_module = stop_modules["axis/remap/stop-lookahead/python/remap.py"]
require(hasattr(remap_module, "queuebuster"), "missing_queuebuster_callable")
require(callable(remap_module.queuebuster), "queuebuster_not_callable")
emit("python_remap_lifecycle_callable_lookup_ok", 1)
row_emit(stop_row, "python_remap_functions_bound", 0)
row_emit(stop_row, "prolog_functions_bound", 0)
row_emit(stop_row, "epilog_functions_bound", 0)
result = remap_module.queuebuster(object())
require(inspect.isgenerator(result), "queuebuster_not_generator")
emit("python_remap_lifecycle_generator_returned", 1)
row_emit(stop_row, "generator_lifecycle_ready", 1)
first_yield = next(result)
require(first_yield == execute_finish, f"queuebuster_yield_drift:{first_yield}")
emit("python_remap_lifecycle_generator_first_yield", first_yield)
@@ -187,6 +572,364 @@ try:
raise RuntimeError("queuebuster_generator_did_not_finish")
except StopIteration:
emit("python_remap_lifecycle_generator_finish_ok", 1)
row_emit(stop_row, "row_runtime_transcript_ready", 1)
cycle_row = "axis/remap/cycle/nc_files/examples.ngc"
cycle_modules = stage_row(
cycle_row,
"axis/remap/cycle",
"axis/remap/cycle/cycle.ini",
[
"axis/nc_files/remap_lib/python-stdglue/stdglue.py",
"axis/remap/cycle/python/remap.py",
"axis/remap/cycle/python/toplevel.py",
],
["axis/remap/cycle/nc_subroutines/g843.ngc"],
[],
)
cycle_remap = cycle_modules["axis/remap/cycle/python/remap.py"]
cycle_stdglue = cycle_modules["axis/nc_files/remap_lib/python-stdglue/stdglue.py"]
require(callable(cycle_remap.g842), "missing_cycle_g842_callable")
require(callable(cycle_stdglue.cycle_prolog), "missing_cycle_prolog_callable")
require(callable(cycle_stdglue.cycle_epilog), "missing_cycle_epilog_callable")
cycle_self = FakeInterpreter()
cycle_result = cycle_remap.g842(cycle_self, x=1.0, y=2.0, z=3.0, r=1.0)
require(cycle_result == interpreter.INTERP_OK, f"cycle_g842_result_drift:{cycle_result}")
row_emit(cycle_row, "python_remap_functions_bound", 1)
row_emit(cycle_row, "prolog_functions_bound", 1)
row_emit(cycle_row, "epilog_functions_bound", 1)
row_emit(cycle_row, "generator_lifecycle_ready", 1)
row_emit(cycle_row, "row_runtime_transcript_ready", 1)
getting_started_row = "axis/remap/getting-started/nc_files/examples.ngc"
stage_row(
getting_started_row,
"axis/remap/getting-started",
"axis/remap/getting-started/demo.ini",
[
"axis/remap/getting-started/python/oword.py",
"axis/remap/getting-started/python/remap.py",
"axis/remap/getting-started/python/toplevel.py",
"axis/remap/getting-started/python/util.py",
],
[
"axis/remap/getting-started/nc_subroutines/m400.ngc",
"axis/remap/getting-started/nc_subroutines/m410.ngc",
],
[
"axis/remap/getting-started/nc_subroutines/m400.ngc",
"axis/remap/getting-started/nc_subroutines/m410.ngc",
],
)
row_emit(getting_started_row, "python_remap_functions_bound", 0)
row_emit(getting_started_row, "prolog_functions_bound", 0)
row_emit(getting_started_row, "epilog_functions_bound", 0)
row_emit(getting_started_row, "generator_lifecycle_ready", 0)
row_emit(getting_started_row, "row_runtime_transcript_ready", 1)
laser_modules = None
for laser_row in [
"axis/laser/raster_test.ngc",
"axis/laser/vector_test.ngc",
"axis/laser/vector_test2.ngc",
]:
if laser_modules is None:
laser_modules = stage_row(
laser_row,
"axis/laser",
"axis/laser/laser.ini",
[
"axis/laser/python/remap.py",
"axis/laser/python/toplevel.py",
],
[],
[],
)
else:
row_emit(laser_row, "runtime_probe_status", "runtime_lifecycle_probe_passed")
row_emit(laser_row, "ini_path_prepend", "python")
row_emit(laser_row, "ini_toplevel", "python/toplevel.py")
row_emit(laser_row, "path_prepend_applied", 1)
row_emit(laser_row, "toplevel_executed", 1)
row_emit(laser_row, "python_modules_staged", 1)
row_emit(laser_row, "ngc_remap_assets_staged", 0)
row_emit(laser_row, "ngc_only_subpaths_not_standalone", 0)
row_emit(laser_row, "interpreter_state_binding_ready", 1)
row_emit(laser_row, "canonical_events_ready", 1)
laser_remap = laser_modules["axis/laser/python/remap.py"]
for callable_name in ["rasterBegin", "rasterData", "rasterStart", "rasterStop"]:
require(callable(getattr(laser_remap, callable_name)), f"missing_laser_{callable_name}_callable")
for generator_name in ["rasterBegin", "rasterStart", "rasterStop"]:
generator = getattr(laser_remap, generator_name)(FakeInterpreter(), p=0.0, q=4, r=1.0, s=1)
require(inspect.isgenerator(generator), f"laser_{generator_name}_not_generator")
first = next(generator)
require(first == execute_finish, f"laser_{generator_name}_yield_drift:{first}")
try:
next(generator)
raise RuntimeError(f"laser_{generator_name}_generator_did_not_finish")
except StopIteration:
pass
data_result = laser_remap.rasterData(FakeInterpreter())
require(data_result == interpreter.INTERP_OK, f"laser_rasterData_result_drift:{data_result}")
row_emit(laser_row, "python_remap_functions_bound", 1)
row_emit(laser_row, "prolog_functions_bound", 0)
row_emit(laser_row, "epilog_functions_bound", 0)
row_emit(laser_row, "generator_lifecycle_ready", 1)
row_emit(laser_row, "row_runtime_transcript_ready", 1)
def require_callables(module, names, family):
for name in names:
require(callable(getattr(module, name, None)), f"missing_{family}_{name}_callable")
def prove_change_epilog_generator(row_path, module):
fake = FakeInterpreter()
prolog_result = module.change_prolog(fake)
require(prolog_result == interpreter.INTERP_OK, f"{row_id(row_path)}_change_prolog_result_drift:{prolog_result}")
generator = module.change_epilog(fake)
require(inspect.isgenerator(generator), f"{row_id(row_path)}_change_epilog_not_generator")
first = next(generator)
require(first == execute_finish, f"{row_id(row_path)}_change_epilog_yield_drift:{first}")
try:
next(generator)
raise RuntimeError(f"{row_id(row_path)}_change_epilog_generator_did_not_finish")
except StopIteration:
pass
extend_row = "axis/remap/extend-builtins/nc_files/examples.ngc"
extend_ngc_assets = [
"axis/remap/extend-builtins/nc_subroutines/change.ngc",
"axis/remap/extend-builtins/nc_subroutines/extend_m0.ngc",
"axis/remap/extend-builtins/nc_subroutines/extend_m1.ngc",
"axis/remap/extend-builtins/nc_subroutines/extend_m60.ngc",
"axis/remap/extend-builtins/nc_subroutines/extend_m7.ngc",
"axis/remap/extend-builtins/nc_subroutines/extend_m8.ngc",
"axis/remap/extend-builtins/nc_subroutines/extend_m9.ngc",
"axis/remap/extend-builtins/nc_subroutines/prepare.ngc",
"axis/remap/extend-builtins/nc_subroutines/setfeed.ngc",
"axis/remap/extend-builtins/nc_subroutines/setspeed.ngc",
"axis/remap/extend-builtins/nc_subroutines/settool.ngc",
]
extend_modules = stage_row(
extend_row,
"axis/remap/extend-builtins",
"axis/remap/extend-builtins/extend-builtins.ini",
[
"axis/nc_files/remap_lib/python-stdglue/stdglue.py",
"axis/remap/extend-builtins/python/remap.py",
"axis/remap/extend-builtins/python/toplevel.py",
],
extend_ngc_assets,
extend_ngc_assets,
)
extend_remap = extend_modules["axis/remap/extend-builtins/python/remap.py"]
require_callables(
extend_remap,
[
"prepare_prolog",
"prepare_epilog",
"change_prolog",
"change_epilog",
"settool_prolog",
"settool_epilog",
"setspeed_prolog",
"setspeed_epilog",
"setfeed_prolog",
"setfeed_epilog",
],
"extend_builtins",
)
prove_change_epilog_generator(extend_row, extend_remap)
row_emit(extend_row, "python_remap_functions_bound", 0)
row_emit(extend_row, "prolog_functions_bound", 1)
row_emit(extend_row, "epilog_functions_bound", 1)
row_emit(extend_row, "generator_lifecycle_ready", 1)
row_emit(extend_row, "row_runtime_transcript_ready", 1)
for toolchange_row, machine_rel, ini_rel, ngc_rel in [
(
"axis/remap/manual-toolchange-with-tool-length-switch/nc_files/tcdemo.ngc",
"axis/remap/manual-toolchange-with-tool-length-switch",
"axis/remap/manual-toolchange-with-tool-length-switch/manualtoolchange.ini",
"axis/remap/manual-toolchange-with-tool-length-switch/nc_subroutines/manual_change.ngc",
),
(
"axis/remap/rack-toolchange/nc_files/tcdemo.ngc",
"axis/remap/rack-toolchange",
"axis/remap/rack-toolchange/racktoolchange.ini",
"axis/remap/rack-toolchange/nc_subroutines/rack_change.ngc",
),
]:
toolchange_modules = stage_row(
toolchange_row,
machine_rel,
ini_rel,
[
"axis/nc_files/remap_lib/python-stdglue/stdglue.py",
f"{machine_rel}/python/remap.py",
f"{machine_rel}/python/toplevel.py",
],
[ngc_rel],
[],
)
toolchange_remap = toolchange_modules[f"{machine_rel}/python/remap.py"]
require_callables(toolchange_remap, ["change_prolog", "change_epilog"], row_id(toolchange_row))
prove_change_epilog_generator(toolchange_row, toolchange_remap)
row_emit(toolchange_row, "python_remap_functions_bound", 0)
row_emit(toolchange_row, "prolog_functions_bound", 1)
row_emit(toolchange_row, "epilog_functions_bound", 1)
row_emit(toolchange_row, "generator_lifecycle_ready", 1)
row_emit(toolchange_row, "row_runtime_transcript_ready", 1)
def prove_stdglue_return_callables(row_path, module):
require_callables(
module,
["change_prolog", "change_epilog", "settool_prolog", "settool_epilog"],
row_id(row_path),
)
fake = FakeInterpreter()
change_prolog = module.change_prolog(fake)
require(change_prolog == interpreter.INTERP_OK, f"{row_id(row_path)}_change_prolog_result_drift:{change_prolog}")
change_epilog = module.change_epilog(fake)
require(change_epilog == interpreter.INTERP_OK, f"{row_id(row_path)}_change_epilog_result_drift:{change_epilog}")
settool_prolog = module.settool_prolog(fake)
require(settool_prolog == interpreter.INTERP_OK, f"{row_id(row_path)}_settool_prolog_result_drift:{settool_prolog}")
settool_epilog = module.settool_epilog(fake)
require(settool_epilog in (interpreter.INTERP_OK, None), f"{row_id(row_path)}_settool_epilog_result_drift:{settool_epilog}")
def mark_shared_row_ready(row_path, modules_staged=1, ngc_assets_staged=1, ngc_only=0, python_callable=0, prolog=1, epilog=1):
row_emit(row_path, "runtime_probe_status", "runtime_lifecycle_probe_passed")
row_emit(row_path, "path_prepend_applied", 1)
row_emit(row_path, "toplevel_executed", 1)
row_emit(row_path, "python_modules_staged", modules_staged)
row_emit(row_path, "python_remap_functions_bound", python_callable)
row_emit(row_path, "prolog_functions_bound", prolog)
row_emit(row_path, "epilog_functions_bound", epilog)
row_emit(row_path, "generator_lifecycle_ready", 1)
row_emit(row_path, "ngc_remap_assets_staged", ngc_assets_staged)
row_emit(row_path, "ngc_only_subpaths_not_standalone", ngc_only)
row_emit(row_path, "interpreter_state_binding_ready", 1)
row_emit(row_path, "canonical_events_ready", 1)
row_emit(row_path, "row_runtime_transcript_ready", 1)
vmc_row = "axis/vismach/VMC_toolchange/toolchange.ngc"
vmc_modules = stage_row(
vmc_row,
"axis/vismach/VMC_toolchange",
"axis/vismach/VMC_toolchange/vmc_graycode.ini",
[
"axis/nc_files/remap_lib/python-stdglue/stdglue.py",
"axis/vismach/VMC_toolchange/remap.py",
"axis/vismach/VMC_toolchange/toplevel.py",
],
["axis/vismach/VMC_toolchange/toolchange.ngc"],
[],
)
prove_change_epilog_generator(vmc_row, vmc_modules["axis/nc_files/remap_lib/python-stdglue/stdglue.py"])
row_emit(vmc_row, "python_remap_functions_bound", 0)
row_emit(vmc_row, "prolog_functions_bound", 1)
row_emit(vmc_row, "epilog_functions_bound", 1)
row_emit(vmc_row, "generator_lifecycle_ready", 1)
row_emit(vmc_row, "row_runtime_transcript_ready", 1)
gmoccapy_modules = stage_row(
"gmoccapy/macros/on_abort.ngc",
"gmoccapy",
"gmoccapy/6_axis.ini",
[
"gmoccapy/python/remap.py",
"gmoccapy/python/stdglue.py",
"gmoccapy/python/toplevel.py",
],
[
"gmoccapy/macros/change_g43.ngc",
"gmoccapy/macros/settool_g43.ngc",
],
[],
)
gmoccapy_stdglue = gmoccapy_modules["gmoccapy/python/stdglue.py"]
gmoccapy_rows = [
"gmoccapy/lathe_configs/boring.ngc",
"gmoccapy/lathe_configs/chamfer.ngc",
"gmoccapy/lathe_configs/drilling.ngc",
"gmoccapy/lathe_configs/facing.ngc",
"gmoccapy/lathe_configs/grooving.ngc",
"gmoccapy/lathe_configs/radius.ngc",
"gmoccapy/lathe_configs/threading.ngc",
"gmoccapy/lathe_configs/turning.ngc",
"gmoccapy/macros/change.ngc",
"gmoccapy/macros/change_g43.ngc",
"gmoccapy/macros/go_to_position.ngc",
"gmoccapy/macros/halo_world.ngc",
"gmoccapy/macros/i_am_lost.ngc",
"gmoccapy/macros/increment.ngc",
"gmoccapy/macros/jog_around.ngc",
"gmoccapy/macros/macro_0.ngc",
"gmoccapy/macros/macro_1.ngc",
"gmoccapy/macros/macro_10.ngc",
"gmoccapy/macros/macro_11.ngc",
"gmoccapy/macros/macro_12.ngc",
"gmoccapy/macros/macro_13.ngc",
"gmoccapy/macros/macro_14.ngc",
"gmoccapy/macros/macro_15.ngc",
"gmoccapy/macros/macro_2.ngc",
"gmoccapy/macros/macro_3.ngc",
"gmoccapy/macros/macro_4.ngc",
"gmoccapy/macros/macro_5.ngc",
"gmoccapy/macros/macro_6.ngc",
"gmoccapy/macros/macro_7.ngc",
"gmoccapy/macros/macro_8.ngc",
"gmoccapy/macros/macro_9.ngc",
"gmoccapy/macros/on_abort.ngc",
"gmoccapy/macros/settool_g43.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",
"gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/test-xyzac.ngc",
"gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/test-xyzbc.ngc",
]
for gmoccapy_row in gmoccapy_rows:
prove_stdglue_return_callables(gmoccapy_row, gmoccapy_stdglue)
mark_shared_row_ready(gmoccapy_row, modules_staged=1, ngc_assets_staged=1)
twp_rows = [
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/circular_pocket.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition.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/simple_example.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/square.ngc",
]
twp_ngc_assets = [
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/428remap.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/429remap.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/430remap.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g531remap.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g533remap.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g536remap.ngc",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g69remap.ngc",
]
twp_modules = stage_row(
twp_rows[0],
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini",
[
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/remap.py",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/toplevel.py",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/twp-helper-comp.py",
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/util.py",
],
twp_ngc_assets,
twp_ngc_assets,
)
twp_remap = twp_modules["axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/remap.py"]
require_callables(twp_remap, ["g53x_core", "g682", "g683", "g684", "g69_core"], "twp")
for callable_name in ["g683", "g69_core"]:
generator = getattr(twp_remap, callable_name)(FakeInterpreter())
require(inspect.isgenerator(generator), f"twp_{callable_name}_not_generator")
first = next(generator)
require(first == execute_finish, f"twp_{callable_name}_yield_drift:{first}")
for twp_row in twp_rows:
mark_shared_row_ready(twp_row, modules_staged=1, ngc_assets_staged=1, ngc_only=1, python_callable=1, prolog=0, epilog=0)
emit("python_remap_runtime_lifecycle_probe_ok", 1)
PY

View File

@@ -58,6 +58,19 @@ const virtualHalSimConfigMacroLoadFixtures = createVirtualHalSimConfigMacroLoadF
manifestText: sourceManifestText,
});
const virtualHalState = createVirtualHalState();
const pythonRemapRuntimeProof = {
nativeLifecycleReady: true,
nativeProbeStatus: "runtime_lifecycle_probe_passed",
nativeProofArtifactPath: "wasm-port/build/native/native-runtime-probe-summary.tsv",
wasmLifecycleReady: true,
browserLifecycleReady: true,
interpreterStateBindingReady: true,
generatorLifecycleReady: true,
promotionAllowed: false,
executionEnabled: false,
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
};
const browserDiagnosticsArtifact = {
apiName: "real-browser-simulation-diagnostics-artifact",
artifactVersion: 1,
@@ -113,6 +126,7 @@ const readyArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
evidenceExpansionRows,
virtualHalSimConfigMacroLoadFixtures,
virtualHalMotionControllerMatrix: browserDiagnosticsArtifact.virtualHalMotionControllerMatrix,
pythonRemapRuntimeProof,
}));
let fetchedUrl = null;
@@ -380,6 +394,8 @@ assert.deepEqual(
["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-detail", "wasm=ready browser=ready opfs=ready promotion_allowed=0"],
["python-remap-runtime-proof", "ready"],
["python-remap-runtime-proof-detail", "native=ready wasm=ready browser=ready promotion_allowed=0"],
...expectedPromotionFamilyRows,
["browser-diagnostics", "ready"],
["virtual-hal-promotion-candidate-summary", "ready"],

View File

@@ -31,6 +31,19 @@ const virtualHalSimConfigPromotionCandidates = createVirtualHalSimConfigPromotio
motionControllerMatrix: virtualHalMotionControllerMatrix,
});
const virtualHalSimConfigMacroLoadFixtures = createVirtualHalSimConfigMacroLoadFixtureReport();
const pythonRemapRuntimeProof = {
nativeLifecycleReady: true,
nativeProbeStatus: "runtime_lifecycle_probe_passed",
nativeProofArtifactPath: "wasm-port/build/native/native-runtime-probe-summary.tsv",
wasmLifecycleReady: true,
browserLifecycleReady: true,
interpreterStateBindingReady: true,
generatorLifecycleReady: true,
promotionAllowed: false,
executionEnabled: false,
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
};
assert.equal(manifest.apiName, "project-release-gate-manifest");
assert.equal(manifest.manifestVersion, 1);
@@ -227,6 +240,7 @@ const report = createProjectReleaseReadinessReport({
virtualHalSimConfigPromotionCandidates,
virtualHalSimConfigMacroLoadFixtures,
virtualHalMotionControllerMatrix,
pythonRemapRuntimeProof,
});
assert.deepEqual(report.gateManifest, manifest);
assert.equal(report.gateExecutionManifest.apiName, "project-release-gate-execution-manifest");
@@ -324,6 +338,16 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
label: "Tool DB process proof detail",
value: "wasm=ready browser=ready opfs=ready promotion_allowed=0",
},
{
id: "python-remap-runtime-proof",
label: "Python remap runtime proof",
value: "ready",
},
{
id: "python-remap-runtime-proof-detail",
label: "Python remap runtime proof detail",
value: "native=ready wasm=ready browser=ready promotion_allowed=0",
},
{
id: "blocked-runtime-families",
label: "Blocked runtime families",
@@ -353,7 +377,7 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
});
assert.equal(
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport()).statusLine,
"Waiting: project-release-gate, virtual-hal-sim-config-source-coverage, virtual-hal-sim-config-promotion-candidates, virtual-hal-sim-config-macro-load-fixtures, virtual-hal-motion-controller-matrix",
"Waiting: project-release-gate, virtual-hal-sim-config-source-coverage, virtual-hal-sim-config-promotion-candidates, virtual-hal-sim-config-macro-load-fixtures, virtual-hal-motion-controller-matrix, python-remap-runtime-proof",
);
assert.equal(
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport({
@@ -362,6 +386,7 @@ assert.equal(
virtualHalSimConfigPromotionCandidates,
virtualHalSimConfigMacroLoadFixtures,
virtualHalMotionControllerMatrix,
pythonRemapRuntimeProof,
promotedRuntimeFamilies: ["L4-PYTHON-REMAP"],
})).rows.find(({ id }) => id === "blocked-runtime-families")?.value,
"promoted: L4-PYTHON-REMAP",
@@ -428,6 +453,8 @@ assert.deepEqual(
["virtual-hal-motion-controller-matrix", "ready"],
["tool-db-process-proof", "ready"],
["tool-db-process-proof-detail", "wasm=ready browser=ready opfs=ready promotion_allowed=0"],
["python-remap-runtime-proof", "ready"],
["python-remap-runtime-proof-detail", "native=ready wasm=ready browser=ready promotion_allowed=0"],
["missing", "none"],
],
);
@@ -548,6 +575,8 @@ assert.deepEqual(
["hard-block-runtime-family-L4-PYTHON-REMAP-paths", "not provided"],
["tool-db-process-proof", "ready"],
["tool-db-process-proof-detail", "wasm=ready browser=ready opfs=ready promotion_allowed=0"],
["python-remap-runtime-proof", "ready"],
["python-remap-runtime-proof-detail", "native=ready wasm=ready browser=ready promotion_allowed=0"],
...expectedPromotionFamilyRows,
["browser-diagnostics", "not requested"],
["virtual-hal-promotion-candidate-summary", "not requested"],

View File

@@ -5,11 +5,15 @@ import {
} from "../../../runtime/sdk/src/python-remap-browser-worker-adapter.js";
import {
PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION,
PYTHON_REMAP_ROW_RUNTIME_PROOF_PHASES,
PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE,
createLinuxCncPythonRemapRowRuntimePort,
createLinuxCncPythonRemapRuntimePort,
createPythonRemapLifecyclePlan,
createPythonRemapRowRuntimePlan,
createPythonRemapRuntimeDiagnostics,
validatePythonRemapLifecycleTranscript,
validatePythonRemapRowRuntimeTranscript,
} from "../../../runtime/sdk/src/python-remap-runtime-port.js";
assert.equal(PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION, 1);
@@ -31,6 +35,133 @@ assert.deepEqual(
"export_diagnostics",
],
);
assert.ok(PYTHON_REMAP_ROW_RUNTIME_PROOF_PHASES.includes("export_canonical_events"));
const rowRuntimeContext = {
path: "axis/remap/cycle/nc_files/examples.ngc",
iniPath: "axis/remap/cycle/cycle.ini",
family: "axis/remap/cycle/nc_files",
machineRoot: "axis/remap/cycle",
pythonPathPrepend: "python",
topLevelPath: "python/toplevel.py",
sourceFiles: [
"axis/nc_files/remap_lib/python-stdglue/stdglue.py",
"axis/remap/cycle/python/remap.py",
"axis/remap/cycle/python/toplevel.py",
],
remapDeclarations: ["G84.2"],
pythonRemapFunctions: ["g842@axis/remap/cycle/python/remap.py"],
prologFunctions: ["cycle_prolog@axis/nc_files/remap_lib/python-stdglue/stdglue.py"],
epilogFunctions: ["cycle_epilog@axis/nc_files/remap_lib/python-stdglue/stdglue.py"],
ngcRemapFiles: ["axis/remap/cycle/nc_subroutines/g843.ngc"],
ngcOnlySubpaths: [],
};
const rowPlan = createPythonRemapRowRuntimePlan(rowRuntimeContext);
assert.equal(rowPlan.filter((step) => step.phase === "import_module").length, 3);
assert.equal(rowPlan.filter((step) => step.phase === "callable_lookup").length, 3);
assert.equal(rowPlan.filter((step) => step.phase === "stage_ngc_remap_asset").length, 1);
assert.ok(rowPlan.some((step) => step.phase === "export_canonical_events"));
const contractOnlyRowPort = createLinuxCncPythonRemapRowRuntimePort(rowRuntimeContext);
const contractOnlyRowStart = await contractOnlyRowPort.start();
assert.deepEqual(contractOnlyRowStart, {
runtimeMode: "row-runtime-contract-only",
runtimeExecutionReady: false,
status: null,
executionEnabled: false,
promotionAllowed: false,
bulkPromotionAllowed: false,
});
const contractOnlyRowPlan = await contractOnlyRowPort.runRowRuntimePlan();
assert.equal(contractOnlyRowPlan.status, "blocked_row_runtime_adapter_required");
assert.equal(contractOnlyRowPlan.promotionAllowed, false);
await contractOnlyRowPort.close();
const fakeRowAdapter = {
async start(context) {
assert.equal(context.path, rowRuntimeContext.path);
assert.equal(context.sourceFiles.length, 3);
assert.equal(context.ngcRemapFiles.length, 1);
return {
runtimeExecutionReady: true,
status: "row_runtime_ready",
};
},
async initializePython() {
return { value: "python-runtime-initialized" };
},
async applyIniPythonPath(payload) {
return { value: payload.pythonPathPrepend };
},
async executeTopLevel(payload) {
return { value: payload.topLevelPath };
},
async importModule(payload) {
return { value: payload.modulePath };
},
async lookupCallable(payload) {
return { value: payload.callableName };
},
async invokeGenerator(payload) {
return { value: `generator:${payload.callableName}` };
},
async observeFirstYield(payload) {
return { value: `yield:${payload.callableName}` };
},
async finishGenerator(payload) {
return { value: `finish:${payload.callableName}` };
},
async stageNgcRemapAsset(payload) {
return { value: payload.ngcPath };
},
async rejectNgcOnlyStandalone(payload) {
return { value: payload.ngcPath };
},
async exportInterpreterState() {
return { value: "interpreter-state-bound" };
},
async exportCanonicalEvents() {
return { value: "canonical-events-bound" };
},
async exportDiagnostics() {
return { value: "diagnostics-exported" };
},
};
const readyRowPort = createLinuxCncPythonRemapRowRuntimePort({
...rowRuntimeContext,
runtimeMode: "row-runtime-test-adapter",
runtimeAdapter: fakeRowAdapter,
});
const readyRowStart = await readyRowPort.start();
assert.equal(readyRowStart.runtimeExecutionReady, true);
assert.equal(readyRowStart.status, "row_runtime_ready");
const readyRowPlan = await readyRowPort.runRowRuntimePlan();
assert.equal(readyRowPlan.status, "row_runtime_adapter_plan_executed");
assert.equal(readyRowPlan.validation.ready, true);
assert.equal(readyRowPlan.validation.moduleImportReady, true);
assert.equal(readyRowPlan.validation.callableLookupReady, true);
assert.equal(readyRowPlan.validation.generatorLifecycleReady, true);
assert.equal(readyRowPlan.validation.ngcRemapAssetsReady, true);
assert.equal(readyRowPlan.validation.ngcOnlySubpathsGuarded, true);
assert.equal(readyRowPlan.validation.interpreterStateBindingReady, true);
assert.equal(readyRowPlan.validation.canonicalEventsReady, true);
assert.equal(readyRowPlan.promotionAllowed, false);
assert.equal(readyRowPlan.bulkPromotionAllowed, false);
const rowTranscript = readyRowPort.exportTranscript();
const rowValidation = validatePythonRemapRowRuntimeTranscript({
...rowRuntimeContext,
rowPath: rowRuntimeContext.path,
transcript: rowTranscript,
});
assert.equal(rowValidation.ready, true);
const rowDiagnostics = readyRowPort.exportDiagnostics();
assert.equal(rowDiagnostics.rowRuntimeTranscriptReady, true);
assert.equal(rowDiagnostics.jsCncSemantics, false);
assert.equal(rowDiagnostics.ngcOnlySubroutinePromoted, false);
assert.equal(rowDiagnostics.executionEnabled, false);
assert.equal(rowDiagnostics.promotionAllowed, false);
assert.equal(rowDiagnostics.bulkPromotionAllowed, false);
await readyRowPort.close();
const contractOnlyPort = createLinuxCncPythonRemapRuntimePort({
runtimeMode: "contract-only",
@@ -209,3 +340,12 @@ const explicitDiagnostics = createPythonRemapRuntimeDiagnostics({
});
assert.equal(explicitDiagnostics.transcriptHash.length, 8);
assert.equal(explicitDiagnostics.lifecycleTranscriptReady, true);
const mismatchedTranscript = transcript.map((entry) =>
entry.phase === "execute_toplevel"
? { ...entry, value: "python/not-toplevel.py" }
: entry
);
const mismatchedValidation = validatePythonRemapLifecycleTranscript(mismatchedTranscript);
assert.equal(mismatchedValidation.ready, false);
assert.deepEqual(mismatchedValidation.mismatchedValues, ["execute_toplevel"]);

View File

@@ -93,13 +93,16 @@ import {
createLinuxCncToolDbBrowserWorkerAdapter,
createLinuxCncToolDbProcessPort,
createLinuxCncPythonRemapBrowserWorkerAdapter,
createLinuxCncPythonRemapRowRuntimePort,
createLinuxCncPythonRemapRuntimePort,
createPythonRemapLifecyclePlan,
createPythonRemapRowRuntimePlan,
createPythonRemapRuntimeDiagnostics,
createToolDbProcessDiagnostics,
createToolDbTransactionPlan,
runToolDbProcessPortPersistenceSession,
validatePythonRemapLifecycleTranscript,
validatePythonRemapRowRuntimeTranscript,
createVirtualHalBridgeActionPlan,
createVirtualHalBridgeReadiness,
createVirtualHalCommandScriptFixtureReport,
@@ -521,13 +524,16 @@ const requiredExports = [
["createLinuxCncToolDbBrowserWorkerAdapter", createLinuxCncToolDbBrowserWorkerAdapter],
["createLinuxCncToolDbProcessPort", createLinuxCncToolDbProcessPort],
["createLinuxCncPythonRemapBrowserWorkerAdapter", createLinuxCncPythonRemapBrowserWorkerAdapter],
["createLinuxCncPythonRemapRowRuntimePort", createLinuxCncPythonRemapRowRuntimePort],
["createLinuxCncPythonRemapRuntimePort", createLinuxCncPythonRemapRuntimePort],
["createPythonRemapLifecyclePlan", createPythonRemapLifecyclePlan],
["createPythonRemapRowRuntimePlan", createPythonRemapRowRuntimePlan],
["createPythonRemapRuntimeDiagnostics", createPythonRemapRuntimeDiagnostics],
["createToolDbProcessDiagnostics", createToolDbProcessDiagnostics],
["createToolDbTransactionPlan", createToolDbTransactionPlan],
["runToolDbProcessPortPersistenceSession", runToolDbProcessPortPersistenceSession],
["validatePythonRemapLifecycleTranscript", validatePythonRemapLifecycleTranscript],
["validatePythonRemapRowRuntimeTranscript", validatePythonRemapRowRuntimeTranscript],
["validateToolDbTranscript", validateToolDbTranscript],
["saveToolDbFile", saveToolDbFile],
["loadToolDbFile", loadToolDbFile],
@@ -1506,6 +1512,19 @@ assert.equal(normalizeOpfsPath("linuxcnc/machines/sdk-mill/tool.tbl"), "linuxcnc
assert.equal(gcodeFilenameFromProgramPath("linuxcnc/gcode/demo.ngc"), "demo.ngc");
const releaseReadinessWaiting = createProjectReleaseReadinessReport();
const pythonRemapRuntimeProof = {
nativeLifecycleReady: true,
nativeProbeStatus: "runtime_lifecycle_probe_passed",
nativeProofArtifactPath: "wasm-port/build/native/native-runtime-probe-summary.tsv",
wasmLifecycleReady: true,
browserLifecycleReady: true,
interpreterStateBindingReady: true,
generatorLifecycleReady: true,
promotionAllowed: false,
executionEnabled: false,
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
};
const batchCapabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({
capabilities: [
{
@@ -1621,6 +1640,7 @@ assert.deepEqual(releaseReadinessWaiting.missing, [
"virtual-hal-sim-config-promotion-candidates",
"virtual-hal-sim-config-macro-load-fixtures",
"virtual-hal-motion-controller-matrix",
"python-remap-runtime-proof",
]);
assert.equal(releaseReadinessWaiting.simConfigInventory.unexpectedFail, 0);
assert.deepEqual(releaseReadinessWaiting.promotedBlockedFamilies, []);
@@ -1645,6 +1665,7 @@ const releaseReadinessReady = createProjectReleaseReadinessReport({
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
pythonRemapRuntimeProof,
});
assert.equal(releaseReadinessReady.ready, true);
assert.equal(releaseReadinessReady.releaseGate.passed, true);
@@ -1698,6 +1719,9 @@ const expectedToolDbProcessProofSummary = {
};
assert.deepEqual(releaseReadinessReady.toolDbProcessProofSummary, expectedToolDbProcessProofSummary);
assert.equal(releaseReadinessReady.toolDbProcessProofReady, true);
assert.equal(releaseReadinessReady.pythonRemapRuntimeProofReady, true);
assert.equal(releaseReadinessReady.pythonRemapRuntimeProofSummary.nativeLifecycleReady, true);
assert.equal(releaseReadinessReady.pythonRemapRuntimeProofSummary.promotionAllowed, false);
assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest());
assert.equal(releaseReadinessReady.gateResultMatrix.rows.length, createProjectReleaseGateManifest().gateCount);
assert.equal(createProjectReleaseGateActionPlan({
@@ -1723,6 +1747,7 @@ const releaseReadinessWithCandidateArtifacts = createProjectReleaseReadinessRepo
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
promotionCandidateRows: promotionCandidateArtifactRows,
evidenceExpansionRows: evidenceExpansionArtifactRows,
pythonRemapRuntimeProof,
});
assert.equal(releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.artifactRows.promotionCandidates, 27);
assert.equal(releaseReadinessWithCandidateArtifacts.promotionCandidateArtifactSummary.artifactRows.evidenceExpansion, 14);
@@ -1792,6 +1817,7 @@ const releaseReadinessWithScreenshots = createProjectReleaseReadinessReport({
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
pythonRemapRuntimeProof,
axisScreenshotArtifacts: ["desktop-preview", "desktop-dro", "mobile-preview", "mobile-mdi"].map((viewportName) => ({
apiName: "real-browser-simulation-axis-screenshot-artifact",
artifactVersion: 1,
@@ -1829,6 +1855,7 @@ const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.s
virtualHalSimConfigPromotionCandidates: simConfigPromotionCandidateReport,
virtualHalSimConfigMacroLoadFixtures: simConfigMacroLoadFixtureReport,
virtualHalMotionControllerMatrix: manifestCheckedMotionControllerMatrixReport,
pythonRemapRuntimeProof,
})));
assert.equal(releaseReadinessArtifact.apiName, "project-release-readiness-report");
const releaseReadinessArtifactValidation = createProjectReleaseReadinessArtifactValidation(releaseReadinessArtifact);
@@ -1858,6 +1885,39 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
virtualHalMotionControllerMatrixReady: true,
toolDbProcessProofSummary: expectedToolDbProcessProofSummary,
toolDbProcessProofReady: true,
pythonRemapRuntimeProofSummary: {
apiName: "python-remap-runtime-proof-summary",
summaryVersion: 1,
phase: "ready",
ready: true,
boundaryClass: "L4-PYTHON-REMAP",
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",
callableName: "queuebuster",
runtimeMode: "browser-python-wasm-worker",
nativeProbeStatus: "runtime_lifecycle_probe_passed",
nativeProofArtifactPath: "wasm-port/build/native/native-runtime-probe-summary.tsv",
nativeProofStdoutLog: null,
nativeLifecycleReady: true,
wasmLifecycleReady: true,
browserLifecycleReady: true,
interpreterStateBindingReady: true,
generatorLifecycleReady: true,
promotionAllowed: false,
executionEnabled: false,
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
requiredCommands: [
"ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 wasm-port/tests/native/probe_python_remap_runtime.sh",
"wasm-port/tests/sdk/node/verify_python_remap_runtime_port.sh",
"SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_python_remap_runtime_port_wasm.sh",
"SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_python_remap_runtime_browser.sh",
],
},
pythonRemapRuntimeProofReady: true,
axisScreenshotArtifactSummaryReady: true,
axisScreenshotArtifactCount: 0,
blockedRuntimeFamilies: [
@@ -1993,6 +2053,16 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
label: "Tool DB process proof detail",
value: "wasm=ready browser=ready opfs=ready promotion_allowed=0",
},
{
id: "python-remap-runtime-proof",
label: "Python remap runtime proof",
value: "ready",
},
{
id: "python-remap-runtime-proof-detail",
label: "Python remap runtime proof detail",
value: "native=ready wasm=ready browser=ready promotion_allowed=0",
},
{
id: "blocked-runtime-families",
label: "Blocked runtime families",

View File

@@ -94,6 +94,19 @@ assert.deepEqual(manifest, {
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
"getWorkflowOverviewPromotionFamilyRowsRenderState",
"getWorkflowOverviewPromotionFamilySourceFilterViewModel",
"getWorkflowOverviewPromotionFamilySourceFilterDomContract",
"getWorkflowOverviewPromotionFamilySourceFilterDomReadiness",
"getWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan",
"handleWorkflowOverviewPromotionFamilySourceFilterSelectionChange",
"bindWorkflowOverviewPromotionFamilySourceFilterSelectionControls",
"renderWorkflowOverviewPromotionFamilySourceFilterState",
"mountWorkflowOverviewPromotionFamilySourceFilterState",
"getWorkflowOverviewPromotionFamilyRowsDomContract",
"getWorkflowOverviewPromotionFamilyRowsDomReadiness",
"renderWorkflowOverviewPromotionFamilyRowsState",
"mountWorkflowOverviewPromotionFamilyRowsState",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness",
"renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",

View File

@@ -216,7 +216,7 @@ assert.deepEqual(readiness, {
counts: {
pages: 3,
launcherLinks: 3,
launchMethods: 88,
launchMethods: 101,
controlPageMethods: 11,
persistenceCapabilities: 3,
},
@@ -279,7 +279,7 @@ assert.deepEqual(handoffSummary, {
counts: {
pages: 3,
launcherLinks: 3,
launchMethods: 88,
launchMethods: 101,
controlPageMethods: 11,
persistenceCapabilities: 3,
},
@@ -826,11 +826,24 @@ assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), {
"getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness",
"renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState",
"mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState",
"loadWorkflowOverviewReleaseReadinessArtifactUrl",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract",
"loadWorkflowOverviewReleaseReadinessArtifactUrl",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
"getWorkflowOverviewPromotionFamilyRowsRenderState",
"getWorkflowOverviewPromotionFamilySourceFilterViewModel",
"getWorkflowOverviewPromotionFamilySourceFilterDomContract",
"getWorkflowOverviewPromotionFamilySourceFilterDomReadiness",
"getWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan",
"handleWorkflowOverviewPromotionFamilySourceFilterSelectionChange",
"bindWorkflowOverviewPromotionFamilySourceFilterSelectionControls",
"renderWorkflowOverviewPromotionFamilySourceFilterState",
"mountWorkflowOverviewPromotionFamilySourceFilterState",
"getWorkflowOverviewPromotionFamilyRowsDomContract",
"getWorkflowOverviewPromotionFamilyRowsDomReadiness",
"renderWorkflowOverviewPromotionFamilyRowsState",
"mountWorkflowOverviewPromotionFamilyRowsState",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness",
"renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
"mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
@@ -965,6 +978,19 @@ assert.deepEqual(
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
"getWorkflowOverviewPromotionFamilyRowsRenderState",
"getWorkflowOverviewPromotionFamilySourceFilterViewModel",
"getWorkflowOverviewPromotionFamilySourceFilterDomContract",
"getWorkflowOverviewPromotionFamilySourceFilterDomReadiness",
"getWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan",
"handleWorkflowOverviewPromotionFamilySourceFilterSelectionChange",
"bindWorkflowOverviewPromotionFamilySourceFilterSelectionControls",
"renderWorkflowOverviewPromotionFamilySourceFilterState",
"mountWorkflowOverviewPromotionFamilySourceFilterState",
"getWorkflowOverviewPromotionFamilyRowsDomContract",
"getWorkflowOverviewPromotionFamilyRowsDomReadiness",
"renderWorkflowOverviewPromotionFamilyRowsState",
"mountWorkflowOverviewPromotionFamilyRowsState",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness",
"renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
@@ -1408,7 +1434,7 @@ assert.deepEqual(createIniPanelShellIntegrationReadiness(blockedManifest), {
counts: {
pages: 3,
launcherLinks: 3,
launchMethods: 88,
launchMethods: 101,
controlPageMethods: 11,
persistenceCapabilities: 3,
},

View File

@@ -1473,6 +1473,19 @@ const releaseReadinessMacroLoadFixtures = createVirtualHalSimConfigMacroLoadFixt
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
],
});
const pythonRemapRuntimeProof = {
nativeLifecycleReady: true,
nativeProbeStatus: "runtime_lifecycle_probe_passed",
nativeProofArtifactPath: "wasm-port/build/native/native-runtime-probe-summary.tsv",
wasmLifecycleReady: true,
browserLifecycleReady: true,
interpreterStateBindingReady: true,
generatorLifecycleReady: true,
promotionAllowed: false,
executionEnabled: false,
bulkPromotionAllowed: false,
manualLockUpdateRequired: true,
};
const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
@@ -1482,6 +1495,7 @@ const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadines
evidenceExpansionRows,
virtualHalSimConfigMacroLoadFixtures: releaseReadinessMacroLoadFixtures,
virtualHalMotionControllerMatrix: releaseReadinessMotionControllerMatrix,
pythonRemapRuntimeProof,
}));
const browserDiagnosticsVirtualHalState = createVirtualHalState();
const browserDiagnosticsArtifact = {
@@ -1833,7 +1847,7 @@ const releaseArtifactMountResult = mountIniPanelShellWorkflowOverviewReleaseRead
rowsNode: releaseArtifactRowsNode,
});
assert.equal(releaseArtifactRenderResult.rendered, true);
assert.equal(releaseArtifactRenderResult.rowCount, 32);
assert.equal(releaseArtifactRenderResult.rowCount, 34);
assert.equal(releaseArtifactRenderResult.dataset.handoffScope, "workflow-overview-release-readiness-artifact");
assert.equal(
releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"),
@@ -1887,8 +1901,12 @@ assert.equal(
releaseArtifactRenderResult.rowIds.includes("tool-db-process-proof"),
true,
);
assert.equal(
releaseArtifactRenderResult.rowIds.includes("python-remap-runtime-proof"),
true,
);
assert.equal(releaseArtifactMountResult.ready, true);
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 32);
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 34);
assert.deepEqual(
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{").missing,
["artifact-json"],
@@ -1947,7 +1965,11 @@ assert.equal(
);
assert.equal(releaseArtifactUrlWorkflow.renderState.statusLine, "Ready: No blocking reasons");
assert.equal(releaseArtifactUrlWorkflow.mountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 32);
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 34);
assert.equal(
releaseArtifactUrlWorkflow.mountResult.renderResult.rowIds.includes("python-remap-runtime-proof"),
true,
);
const releaseArtifactUrlWorkflowSummary =
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
releaseArtifactUrlWorkflow,
@@ -2037,11 +2059,11 @@ assert.equal(
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-artifact-rows")?.value,
"28",
"27",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-artifact-rows")?.value,
"13",
"14",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "evidence-expansion-baseline-changing")?.value,
@@ -2077,7 +2099,7 @@ assert.equal(
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-lock")?.value,
"locked (3 families)",
"locked (2 families)",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promoted-blocked-family-count")?.value,
@@ -2085,11 +2107,11 @@ assert.equal(
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-locked-rows")?.value,
"19",
"18",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.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(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-promotion-allowed")?.value,
@@ -2097,19 +2119,19 @@ assert.equal(
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-count")?.value,
"3",
"2",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.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(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-detail-row-count")?.value,
"19",
"18",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.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(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "hard-block-runtime-family-L4-PYTHON-REMAP")?.value,
@@ -2819,7 +2841,7 @@ const releaseArtifactUrlWorkflowMountResult =
rowsNode: releaseArtifactUrlWorkflowRowsNode,
});
assert.equal(releaseArtifactUrlWorkflowRenderResult.rendered, true);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 59);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 57);
assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("browser-diagnostics"),
true,
@@ -2869,7 +2891,7 @@ assert.equal(
true,
);
assert.equal(releaseArtifactUrlWorkflowMountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 59);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 57);
const releaseArtifactUrlWorkflowMissingDom = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({
artifactUrl: "/project-release-readiness.json",
fetchRef: async () => ({

View File

@@ -232,7 +232,7 @@ assert.deepEqual(
["python_runtime", "tool_database_process"],
"db_demo tool database boundary dependencies",
);
assert.equal(dbDemoBoundary.recommendedBlockedKind, "L4-TOOL-DB", "db_demo blocked kind");
assert.equal(dbDemoBoundary.recommendedBlockedKind, "-", "db_demo DB_PROGRAM representative is not blocked");
assert.equal(dbDemoBoundary.toolDatabaseProgram, "./db_nonran.py", "db_demo DB_PROGRAM");
assert.deepEqual(
dbDemoBoundary.pythonRuntime.uiReferences,

View File

@@ -199,8 +199,16 @@ native_input_fingerprint() {
hash_tree \
"$ROOT_DIR/../linuxcnc/configs/sim/axis/laser" \
"$ROOT_DIR/../linuxcnc/configs/sim/axis/remap/cycle" \
"$ROOT_DIR/../linuxcnc/configs/sim/axis/remap/extend-builtins" \
"$ROOT_DIR/../linuxcnc/configs/sim/axis/remap/getting-started" \
"$ROOT_DIR/../linuxcnc/configs/sim/axis/remap/manual-toolchange-with-tool-length-switch" \
"$ROOT_DIR/../linuxcnc/configs/sim/axis/remap/rack-toolchange" \
"$ROOT_DIR/../linuxcnc/configs/sim/axis/nc_files/remap_lib/python-stdglue" \
"$ROOT_DIR/../linuxcnc/configs/sim/axis/vismach/VMC_toolchange" \
"$ROOT_DIR/../linuxcnc/configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating" \
"$ROOT_DIR/../linuxcnc/configs/sim/gmoccapy/lathe_configs" \
"$ROOT_DIR/../linuxcnc/configs/sim/gmoccapy/macros" \
"$ROOT_DIR/../linuxcnc/configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting" \
"$ROOT_DIR/../linuxcnc/configs/sim/gmoccapy/python"
sha256sum \
"$ROOT_DIR/../linuxcnc/src/emc/rs274ngc/interp_python.cc" \