ops-tune-workbench-plugin 0.0.0-stage → 0.0.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/NOTICE.md +14 -0
  2. package/README.md +39 -2
  3. package/cordis.patch.yml +20 -0
  4. package/lib/client.js +4253 -0
  5. package/lib/host.js +2518 -0
  6. package/package.json +53 -4
  7. package/skills/tool-ops-tune/INSTALL.md +52 -0
  8. package/skills/tool-ops-tune/README.md +167 -0
  9. package/skills/tool-ops-tune/SKILL.md +93 -0
  10. package/skills/tool-ops-tune/agent/AGENTS.md +195 -0
  11. package/skills/tool-ops-tune/agent/agents/perf-implementer.md +68 -0
  12. package/skills/tool-ops-tune/agent/agents/perf-reviewer.md +86 -0
  13. package/skills/tool-ops-tune/agent/workflows/task-prompts.md +362 -0
  14. package/skills/tool-ops-tune/agent/workflows/troubleshooting.md +43 -0
  15. package/skills/tool-ops-tune/docs/figures/opstune-architecture.png +0 -0
  16. package/skills/tool-ops-tune/docs/figures/opstune-architecture.svg +253 -0
  17. package/skills/tool-ops-tune/docs/ops-tune-design.md +404 -0
  18. package/skills/tool-ops-tune/docs//347/224/250/344/276/213/346/236/204/345/273/272.md +714 -0
  19. package/skills/tool-ops-tune/docs//347/237/245/350/257/206/345/272/223/350/247/204/350/214/203.md +103 -0
  20. package/skills/tool-ops-tune/evals/evals.json +242 -0
  21. package/skills/tool-ops-tune/ops-collect/SKILL.md +146 -0
  22. package/skills/tool-ops-tune/ops-collect/references/tool-routing.md +86 -0
  23. package/skills/tool-ops-tune/ops-collect/scripts/collect_board.sh +444 -0
  24. package/skills/tool-ops-tune/ops-collect/scripts/collect_cases.py +368 -0
  25. package/skills/tool-ops-tune/ops-collect/scripts/collect_sim.sh +134 -0
  26. package/skills/tool-ops-tune/ops-collect/scripts/csv_to_db.py +314 -0
  27. package/skills/tool-ops-tune/ops-collect/scripts/db_bench.py +545 -0
  28. package/skills/tool-ops-tune/ops-collect/scripts/facts_bridge.py +1063 -0
  29. package/skills/tool-ops-tune/ops-collect/scripts/isolation_test.py +454 -0
  30. package/skills/tool-ops-tune/ops-collect/scripts/jsonl_to_cases.py +163 -0
  31. package/skills/tool-ops-tune/ops-family/SKILL.md +76 -0
  32. package/skills/tool-ops-tune/ops-family/references/broadcast/advanced_tiling.md +92 -0
  33. package/skills/tool-ops-tune/ops-family/references/broadcast/broadcast_design.md +160 -0
  34. package/skills/tool-ops-tune/ops-family/references/broadcast/code/broadcast_add_kernel.cpp +126 -0
  35. package/skills/tool-ops-tune/ops-family/references/broadcast/code/broadcast_add_tiling.cpp +144 -0
  36. package/skills/tool-ops-tune/ops-family/references/broadcast/code/broadcast_tiling_common.h +401 -0
  37. package/skills/tool-ops-tune/ops-family/references/broadcast/code/onedim_add_kernel.cpp +107 -0
  38. package/skills/tool-ops-tune/ops-family/references/broadcast/code/onedim_add_tiling.cpp +64 -0
  39. package/skills/tool-ops-tune/ops-family/references/broadcast/datacopypad_design.md +46 -0
  40. package/skills/tool-ops-tune/ops-family/references/broadcast/guide.md +14 -0
  41. package/skills/tool-ops-tune/ops-family/references/broadcast/nddma_design.md +53 -0
  42. package/skills/tool-ops-tune/ops-family/references/broadcast/onedim_design.md +114 -0
  43. package/skills/tool-ops-tune/ops-family/references/broadcast/ub_broadcast_design.md +67 -0
  44. package/skills/tool-ops-tune/ops-family/references/common/datacopy_optimization_design.md +351 -0
  45. package/skills/tool-ops-tune/ops-family/references/common/index.md +9 -0
  46. package/skills/tool-ops-tune/ops-family/references/common/tail_block_design.md +268 -0
  47. package/skills/tool-ops-tune/ops-family/references/common/timing_harness/index.md +38 -0
  48. package/skills/tool-ops-tune/ops-family/references/common/timing_harness/timing_harness_aclrt_event.cpp +199 -0
  49. package/skills/tool-ops-tune/ops-family/references/common/timing_harness/timing_harness_torch_event.py +130 -0
  50. package/skills/tool-ops-tune/ops-family/references/common/ub_resident_design.md +342 -0
  51. package/skills/tool-ops-tune/ops-family/references/conversion/guide.md +69 -0
  52. package/skills/tool-ops-tune/ops-family/references/conversion/index.md +4 -0
  53. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_base.template +81 -0
  54. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_big_dim.template +217 -0
  55. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_cut_one_axis.template +336 -0
  56. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_cut_two_axis.template +781 -0
  57. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_n_last.template +340 -0
  58. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_small_shape.template +321 -0
  59. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_tensor_move.template +102 -0
  60. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_transdata_5hd.template +480 -0
  61. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_transdata_5hd_021axis.template +533 -0
  62. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_with_gather.template +688 -0
  63. package/skills/tool-ops-tune/ops-family/references/conversion/templates/transpose_tiling_data.template +192 -0
  64. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_big_dim.md +220 -0
  65. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_cut_one_axis.md +175 -0
  66. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_cut_two_axis.md +177 -0
  67. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_fusion_design.md +398 -0
  68. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_gather.md +191 -0
  69. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_n_last.md +276 -0
  70. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_small_shape.md +315 -0
  71. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_tensor_move.md +203 -0
  72. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_tiling_data.md +176 -0
  73. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_vconv_021.md +206 -0
  74. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_vconv_5hd.md +179 -0
  75. package/skills/tool-ops-tune/ops-family/references/elementwise/double_buffer_design.md +288 -0
  76. package/skills/tool-ops-tune/ops-family/references/elementwise/guide.md +46 -0
  77. package/skills/tool-ops-tune/ops-family/references/elementwise/index.md +10 -0
  78. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/CMakeLists.txt +131 -0
  79. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/README.md +55 -0
  80. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_extension/add_custom_torch.cpp +80 -0
  81. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_extension/ops.h +23 -0
  82. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_extension/register.cpp +30 -0
  83. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_host/add_custom.asc +149 -0
  84. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_host/data_utils.h +97 -0
  85. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_kernel/add_custom_kernel.asc +136 -0
  86. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_kernel/add_custom_tiling.h +31 -0
  87. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/run.sh +86 -0
  88. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/scripts/gen_data.py +70 -0
  89. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/scripts/golden.py +34 -0
  90. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/scripts/test_torch.py +99 -0
  91. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/scripts/verify_result.py +56 -0
  92. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/usage_guide.md +61 -0
  93. package/skills/tool-ops-tune/ops-family/references/elementwise/vector_efficiency_design.md +203 -0
  94. package/skills/tool-ops-tune/ops-family/references/fa/fa_mixed_kernel_design.md +174 -0
  95. package/skills/tool-ops-tune/ops-family/references/fa/fa_roofline_design.md +141 -0
  96. package/skills/tool-ops-tune/ops-family/references/fa/guide.md +87 -0
  97. package/skills/tool-ops-tune/ops-family/references/knowledge_gaps.json +56 -0
  98. package/skills/tool-ops-tune/ops-family/references/matmul/best-practices-guide.md +14 -0
  99. package/skills/tool-ops-tune/ops-family/references/matmul/fullload_design.md +344 -0
  100. package/skills/tool-ops-tune/ops-family/references/matmul/guide.md +52 -0
  101. package/skills/tool-ops-tune/ops-family/references/matmul/mte2_preload_design.md +320 -0
  102. package/skills/tool-ops-tune/ops-family/references/matmul/pingpong_design.md +254 -0
  103. package/skills/tool-ops-tune/ops-family/references/matmul/scale_coalescing_design.md +282 -0
  104. package/skills/tool-ops-tune/ops-family/references/matmul/streamk_design.md +374 -0
  105. package/skills/tool-ops-tune/ops-family/references/matmul/swat_design.md +423 -0
  106. package/skills/tool-ops-tune/ops-family/references/matmul/templates/matmul_basic_blaze.template +165 -0
  107. package/skills/tool-ops-tune/ops-family/references/matmul/templates/usage_guide.md +29 -0
  108. package/skills/tool-ops-tune/ops-family/references/mc2/best-practices-guide.md +43 -0
  109. package/skills/tool-ops-tune/ops-family/references/mc2/guide.md +42 -0
  110. package/skills/tool-ops-tune/ops-family/references/mc2/local_matmul_design.md +130 -0
  111. package/skills/tool-ops-tune/ops-family/references/mc2/pipeline_balancing_design.md +502 -0
  112. package/skills/tool-ops-tune/ops-family/references/reduce/guide.md +178 -0
  113. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/kernel_utils.template +75 -0
  114. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ar_full_load.template +429 -0
  115. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ar_recompute.template +485 -0
  116. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ar_small_r.template +348 -0
  117. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ara_full_load.template +574 -0
  118. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ara_online.template +324 -0
  119. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ara_recompute.template +436 -0
  120. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_base.template +950 -0
  121. package/skills/tool-ops-tune/ops-family/references/reduce/templates/fused_attention_online_softmax_design.md +428 -0
  122. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ar_full_load.md +137 -0
  123. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ar_recompute.md +135 -0
  124. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ar_small_r.md +122 -0
  125. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ara_full_load.md +144 -0
  126. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ara_online.md +185 -0
  127. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ara_recompute.md +163 -0
  128. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_tiling.template +460 -0
  129. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_tiling_data.template +278 -0
  130. package/skills/tool-ops-tune/ops-family/references/reduce/templates/state_resident_design.md +337 -0
  131. package/skills/tool-ops-tune/ops-family/references/reduce/templates/usage_guide.md +253 -0
  132. package/skills/tool-ops-tune/ops-family/references/scalar/coding_principles.md +476 -0
  133. package/skills/tool-ops-tune/ops-family/references/scalar/conv_scalar.md +163 -0
  134. package/skills/tool-ops-tune/ops-family/references/scalar/guide.md +30 -0
  135. package/skills/tool-ops-tune/ops-family/references/simt/guide.md +91 -0
  136. package/skills/tool-ops-tune/ops-family/references/simt/optimization-guide.md +110 -0
  137. package/skills/tool-ops-tune/ops-family/references/sort/guide.md +83 -0
  138. package/skills/tool-ops-tune/ops-family/references/sort/radix_sort.md +354 -0
  139. package/skills/tool-ops-tune/ops-family/scripts/family_asset_audit.py +68 -0
  140. package/skills/tool-ops-tune/ops-family/scripts/tiling_candidates.py +419 -0
  141. package/skills/tool-ops-tune/ops-mechanism/SKILL.md +23 -0
  142. package/skills/tool-ops-tune/ops-mechanism/cases/ascendc/case_groupedmatmul_pipeline.md +23 -0
  143. package/skills/tool-ops-tune/ops-mechanism/cases/ascendc/case_triton_atomic_revert.md +19 -0
  144. package/skills/tool-ops-tune/ops-mechanism/cases/mc2/case_mc2_fusion.md +19 -0
  145. package/skills/tool-ops-tune/ops-mechanism/references/optimize/api-usage.md +70 -0
  146. package/skills/tool-ops-tune/ops-mechanism/references/optimize/ascendc-regbase-vf.md +45 -0
  147. package/skills/tool-ops-tune/ops-mechanism/references/optimize/ascendc-tiling.md +51 -0
  148. package/skills/tool-ops-tune/ops-mechanism/references/optimize/data-copy.md +67 -0
  149. package/skills/tool-ops-tune/ops-mechanism/references/optimize/index.md +45 -0
  150. package/skills/tool-ops-tune/ops-mechanism/references/optimize/mc2-balancing.md +77 -0
  151. package/skills/tool-ops-tune/ops-mechanism/references/optimize/mc2.md +103 -0
  152. package/skills/tool-ops-tune/ops-mechanism/references/optimize/memory-hierarchy.md +40 -0
  153. package/skills/tool-ops-tune/ops-mechanism/references/optimize/pipeline.md +95 -0
  154. package/skills/tool-ops-tune/ops-mechanism/scripts/case_backfill.py +170 -0
  155. package/skills/tool-ops-tune/ops-mechanism/scripts/log_gap.py +145 -0
  156. package/skills/tool-ops-tune/ops-mechanism/scripts/mc2_search.py +367 -0
  157. package/skills/tool-ops-tune/ops-report/SKILL.md +77 -0
  158. package/skills/tool-ops-tune/ops-verdict/evals/run_evals.py +837 -0
  159. package/skills/tool-ops-tune/ops-verdict/evals/smoke/install-hooks.sh +65 -0
  160. package/skills/tool-ops-tune/ops-verdict/evals/smoke/run_smoke.sh +405 -0
  161. package/skills/tool-ops-tune/ops-verdict/fixtures/core_tail_a5.csv +25 -0
  162. package/skills/tool-ops-tune/ops-verdict/fixtures/few_cores_a5.csv +5 -0
  163. package/skills/tool-ops-tune/ops-verdict/fixtures/mte2_bound_a2.csv +17 -0
  164. package/skills/tool-ops-tune/ops-verdict/fixtures/multi_kernel_mix_a5.csv +7 -0
  165. package/skills/tool-ops-tune/ops-verdict/fixtures/no_bound_a5.csv +9 -0
  166. package/skills/tool-ops-tune/ops-verdict/fixtures/serial_no_overlap_a2.csv +9 -0
  167. package/skills/tool-ops-tune/ops-verdict/fixtures/vec_bound_a5.csv +9 -0
  168. package/skills/tool-ops-tune/ops-verdict/references/io-contracts.md +94 -0
  169. package/skills/tool-ops-tune/ops-verdict/references/rule-schema.md +130 -0
  170. package/skills/tool-ops-tune/ops-verdict/references/scope-schema.md +61 -0
  171. package/skills/tool-ops-tune/ops-verdict/references/soc-spec.yaml +140 -0
  172. package/skills/tool-ops-tune/ops-verdict/rules/L0-instruction.yaml +67 -0
  173. package/skills/tool-ops-tune/ops-verdict/rules/L1-single-core.yaml +316 -0
  174. package/skills/tool-ops-tune/ops-verdict/rules/L2-inter-core.yaml +173 -0
  175. package/skills/tool-ops-tune/ops-verdict/rules/L3-inter-card.yaml +156 -0
  176. package/skills/tool-ops-tune/ops-verdict/scripts/batch_pipeline.py +444 -0
  177. package/skills/tool-ops-tune/ops-verdict/scripts/calibrate.py +249 -0
  178. package/skills/tool-ops-tune/ops-verdict/scripts/delta_compare.py +244 -0
  179. package/skills/tool-ops-tune/ops-verdict/scripts/render_html.py +274 -0
  180. package/skills/tool-ops-tune/ops-verdict/scripts/snapshot.py +200 -0
  181. package/skills/tool-ops-tune/ops-verdict/scripts/trace_bubble.py +331 -0
  182. package/skills/tool-ops-tune/ops-verdict/scripts/verdict.py +1072 -0
  183. package/skills/tool-ops-tune/scripts/diff_audit.py +281 -0
@@ -0,0 +1,444 @@
1
+ #!/usr/bin/env bash
2
+ # -----------------------------------------------------------------------------------------------------------
3
+ # Copyright (c) 2026 Huawei Technologies Co., Ltd.
4
+ # This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5
+ # CANN Open Software License Agreement Version 2.0 (the "License").
6
+ # Please refer to the License for details. You may not use this file except in compliance with the License.
7
+ # THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8
+ # INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9
+ # See LICENSE in the root of the software repository for the full text of the License.
10
+ # -----------------------------------------------------------------------------------------------------------
11
+ # collect_board.sh — 上板采集统一入口(标准 7 组指标 / quick / MC² 多 rank)
12
+ #
13
+ # 设计对齐(docs/算子调优AGENT设计文档.md §4 总体架构):
14
+ # - 复用不重做:底层调 msprof(CANN 官方),不封装私有格式;产物以
15
+ # collection_manifest 契约落盘(采集块状态机真源,M2 对接可视化管线)。
16
+ # - 采集模式路由(决策树 + 「先探测本机帮助再选命令」门禁):
17
+ # MC²/fork 多 rank → 强制 msprof --aic-mode=task-based(msprof op 对 fork 行为未定义)
18
+ # 单卡算子 → msprof op(若本机支持)或 msprof 7 组指标
19
+ # - 每次采集输出 collection_manifest.json(采集块状态机真源:ok/partial/failed/skipped),
20
+ # 禁止「重采已有 ok 块」——已有 manifest 的目录只允许重解析,不允许重复采集。
21
+ #
22
+ # 用法:
23
+ # bash collect_board.sh --exe ./build/demo --args "8448 4096 4096" --output ./prof/baseline
24
+ # bash collect_board.sh --exe ./build/demo --mode quick --output ./prof/quick_check
25
+ # bash collect_board.sh --exe ./build/timing_harness --mode timing --output ./prof/timing_base # 跑 event 计时 harness(P0-2)
26
+ # bash collect_board.sh --exe ./build/mc2_demo --mode mc2 --ranks 4 --output ./prof/mc2_base
27
+ # bash collect_board.sh --exe ./build/demo --mode npu-compute --output ./prof/npc_base # 950/A5 DB 格式采集(Roadmap #4)
28
+ # 可选:--kernel-name <名>(单卡 msprof op 分支过滤,本机 --help 为门禁)
29
+ # --timeout <秒>(msprof 单块超时,默认 1800)
30
+ # --sections <A,B>(npu-compute 模式追加 Section,如 PmSampling——本机
31
+ # npu-compute --list-sections 为门禁,不支持者跳过并告警)
32
+ # 注:稳态 warm-up 由被测程序自身循环保证(循环 ≥10 次前 5 warm-up),
33
+ # 采集脚本不做应用层 warmup(F17:原 --warmup 为死参数,已删除)。
34
+
35
+ set -euo pipefail
36
+
37
+ MODE=standard
38
+ EXE=""
39
+ EXE_ARGS=""
40
+ OUTPUT="./prof/run"
41
+ RANKS=1
42
+ KERNEL_NAME=""
43
+ MSPROF_TIMEOUT=1800 # msprof 单块采集超时(秒);挂起程序不再无限阻塞(检视 F23)
44
+
45
+ usage() {
46
+ sed -n '2,32p' "$0"; exit 1
47
+ }
48
+
49
+ while [[ $# -gt 0 ]]; do
50
+ case "$1" in
51
+ --exe) EXE="$2"; shift 2 ;;
52
+ --args) EXE_ARGS="$2"; shift 2 ;;
53
+ --mode) MODE="$2"; shift 2 ;;
54
+ -o|--output) OUTPUT="$2"; shift 2 ;;
55
+ --timeout) MSPROF_TIMEOUT="$2"; shift 2 ;;
56
+ --ranks) RANKS="$2"; shift 2 ;;
57
+ --kernel-name) KERNEL_NAME="$2"; shift 2 ;;
58
+ --sections) NPU_SECTIONS="$2"; shift 2 ;;
59
+ -h|--help) usage ;;
60
+ *) echo "unknown arg: $1" >&2; usage ;;
61
+ esac
62
+ done
63
+
64
+ [[ -x "$EXE" || -f "$EXE" ]] || { echo "ERROR: --exe 不存在或不可执行: $EXE" >&2; exit 1; }
65
+
66
+ # ---- 工具探测(先探测再选命令:本机帮助为门禁,不复制跨版本命令)----
67
+ # npu-compute 模式(Roadmap #4):950/A5 单卡 DB 格式采集——工具探测与
68
+ # msprof 分开(不同二进制);timing 模式两者都不依赖。
69
+ if [[ "$MODE" == "npu-compute" ]]; then
70
+ command -v npu-compute >/dev/null 2>&1 || {
71
+ echo "ERROR: npu-compute 不可用(本机未安装或不在 PATH)。" >&2
72
+ echo " npu-compute 为 950/A5 平台采集器(随本仓 npu_tools 构建);" >&2
73
+ echo " 非 950 环境请回退 --mode standard(msprof 7 组)路径。" >&2
74
+ exit 1
75
+ }
76
+ npu-compute --list-sets >/dev/null 2>&1 || {
77
+ echo "ERROR: npu-compute --list-sets 探测失败(工具异常)。" >&2
78
+ exit 1
79
+ }
80
+ fi
81
+ # P0-2:timing 模式跑 event harness 不依赖 msprof,探测跳过(harness 直跑)
82
+ if [[ "$MODE" != "timing" && "$MODE" != "npu-compute" ]] && ! command -v msprof >/dev/null 2>&1; then
83
+ echo "ERROR: msprof 不可用。检查 CANN 安装与 source set_env.sh" >&2
84
+ exit 1
85
+ fi
86
+ if [[ "$MODE" != "timing" && "$MODE" != "npu-compute" ]]; then
87
+ msprof op --help >/dev/null 2>&1 && MSPROF_OP_OK=1 || MSPROF_OP_OK=0
88
+ echo "[probe] msprof op 支持: $MSPROF_OP_OK"
89
+ # F17:MSPROF_OP_OK 真正参与路由(tool-routing 决策树:单卡且本机 msprof op
90
+ # 可用 → msprof op 分支;不可用 → 完整 msprof 7 组)。kernel-name 仅
91
+ # msprof op 形态有效(本机 --help 实测),完整 msprof 透传无此参数。
92
+ MSPROF_KERNEL_FILTER_OK=0
93
+ if [[ $MSPROF_OP_OK -eq 1 ]] && msprof op --help 2>&1 | grep -q -- "--kernel-name"; then
94
+ MSPROF_KERNEL_FILTER_OK=1
95
+ fi
96
+ fi
97
+
98
+ mkdir -p "$OUTPUT"
99
+ MANIFEST="$OUTPUT/collection_manifest.json"
100
+
101
+ # ---- 已有 manifest 的目录禁止重复采集(只允许重解析)----
102
+ if [[ -f "$MANIFEST" ]]; then
103
+ echo "ERROR: $MANIFEST 已存在 —— 该目录只允许解析(verdict.py),禁止重复采集。" >&2
104
+ echo " 如需重新采集,请指定新的 --output 目录。" >&2
105
+ exit 2
106
+ fi
107
+
108
+ # ---- 空闲卡选择(多卡环境尽量避开占用卡)----
109
+ # P2-2/G20 ①:结构化解析优先(npu-smi info -t usages -i <id> 的
110
+ # Aicore/Aivector Usage Rate(%) 字段),字面 "100%" 匹配退化为兜底——
111
+ # npu-smi 输出格式随版本漂移时不再误判(此前 index($0,"100%")==0 会把
112
+ # Health 列、温度 100 等任何含 100% 的行错杀,或漏判真实满载卡)。
113
+ pick_device_structured() {
114
+ command -v npu-smi >/dev/null 2>&1 || { echo "0"; return; }
115
+ local total dev util
116
+ # 设备总数探测(-l 输出 "Total Count : N";探测失败按 1 台。注:不用
117
+ # grep -c 探测——无匹配时 grep 退 1,pipefail+errexit 会终止整个脚本)
118
+ total=$(npu-smi info -l 2>/dev/null | sed -n 's/.*Total Count *: *\([0-9]*\).*/\1/p' | head -1)
119
+ [[ "$total" =~ ^[0-9]+$ ]] || total=1
120
+ for ((dev = 0; dev < total; dev++)); do
121
+ # npu-smi usages 输出行首为 TAB(本机 25.5.0 实测):sed 同时容忍 TAB 与空格
122
+ util=$(npu-smi info -t usages -i "$dev" 2>/dev/null | \
123
+ sed -n 's/^[ \t]*Aicore Usage Rate(%) *: *\([0-9]*\).*/\1/p' | head -1)
124
+ if [[ "$util" =~ ^[0-9]+$ ]]; then
125
+ # 结构化字段可解析:占用 < 50% 视为可用(留余量给并行流)
126
+ if (( util < 50 )); then
127
+ echo "$dev"; return
128
+ fi
129
+ else
130
+ # 结构化解析失败(版本差异):退化到字面启发(原实现),并在 stderr 提示
131
+ echo "[warn] npu-smi usages 结构化解析失败(设备 $dev)——退化到 info 表字面匹配" >&2
132
+ local fallback
133
+ fallback=$(npu-smi info 2>/dev/null | awk -F'|' -v d="$dev" 'NR>3 && NF>3 {
134
+ gsub(/ /,"",$2);
135
+ if ($2 == d && index($0,"100%")==0) { print $2; exit }
136
+ }')
137
+ if [[ -n "$fallback" ]]; then
138
+ echo "$fallback"; return
139
+ fi
140
+ fi
141
+ done
142
+ echo "0"
143
+ }
144
+ pick_device() {
145
+ if command -v npu-smi >/dev/null 2>&1; then
146
+ local dev
147
+ dev=$(npu-smi info 2>/dev/null | awk -F'|' 'NR>3 && NF>3 {
148
+ gsub(/ /,"",$2);
149
+ if ($2 ~ /^[0-9]+$/ && index($0,"100%")==0) { print $2; exit }
150
+ }')
151
+ echo "${dev:-0}"
152
+ else
153
+ echo "0"
154
+ fi
155
+ }
156
+ DEVICE=$(pick_device_structured)
157
+ # F17:选卡后注入 ASCEND_RT_VISIBLE_DEVICES——实际执行设备与 manifest 记录一致
158
+ # (口径指纹 device_id 源头保真;此前选了空闲卡却未注入,程序仍可能跑在 0 号卡)
159
+ export ASCEND_RT_VISIBLE_DEVICES="$DEVICE"
160
+ echo "[env] device=$DEVICE ranks=$RANKS mode=$MODE"
161
+
162
+ TIMESTAMP=$(date +%Y%m%d_%H%M%S)
163
+ declare -a BLOCKS=() # manifest 采集块记录
164
+
165
+ record_block() { # name status path note
166
+ # 记录间以单元分隔符 \x1f 分隔(note 可含逗号/引号,最终由 json.loads 逐条解析)
167
+ BLOCKS+=("$(python3 -c 'import json,sys; print(json.dumps({"name": sys.argv[1], "status": sys.argv[2], "path": sys.argv[3], "note": sys.argv[4]}, ensure_ascii=False))' "$1" "$2" "$3" "$4")")
168
+ }
169
+
170
+ # ---- 采后产物有效性断言(P2-1 / G18:消灭「manifest ok 但数据不可用」的伪 Green)----
171
+ # msprof 退出 0 ≠ 产物可用。断言四项(任一不满足 → 状态降级 partial,note 记原因):
172
+ # ① op_summary CSV 存在且非空(≥2 行 = 表头 + ≥1 数据行)
173
+ # ② Task Duration 列存在且有 ≥1 数值(verdict/isolation 的主消费字段)
174
+ # ③ PipeUtilization 块:pipe 比例列 ≥1 列存在(判定引擎主输入)
175
+ # ④ OpBasicInfo 存在且含 kernel 行(多 kernel 混采时提示 --kernel-name,G15 联动)
176
+ assert_block_artifacts() { # prof_dir metrics_name
177
+ local dir="$1" metric="$2"
178
+ local notes=()
179
+ # 找 op_summary CSV(完整 msprof 布局 PROF_*/mindstudio_profiler_output/ 或顶层导出);
180
+ # msprof op 形态产出的是 OPPROF_*/ 下的扁平 <Metric>.csv(无 op_summary)——
181
+ # E2E 真机验证发现(square_add@910B3,msprof 9.0):两种布局都合法,按序探测。
182
+ local csv
183
+ csv=$(find "$dir" -name "op_summary_*.csv" 2>/dev/null | head -1)
184
+ if [[ -z "$csv" ]]; then
185
+ csv=$(find "$dir" -name "${metric}.csv" 2>/dev/null | head -1)
186
+ fi
187
+ if [[ -z "$csv" ]]; then
188
+ # msprof op 形态:任意指标 CSV 存在即有产物(如 PipeUtilization.csv)
189
+ csv=$(find "$dir" -name "PipeUtilization.csv" -o -name "OpBasicInfo.csv" 2>/dev/null | head -1)
190
+ fi
191
+ if [[ -z "$csv" ]]; then
192
+ echo "产物缺失:无 op_summary_*.csv / ${metric}.csv / OPPROF 指标 CSV"
193
+ return 1
194
+ fi
195
+ local rows
196
+ rows=$(wc -l < "$csv")
197
+ if [[ "$rows" -lt 2 ]]; then
198
+ echo "产物空(${rows} 行 < 2:表头+数据)"
199
+ return 1
200
+ fi
201
+ # Task Duration 列只在 op_summary/OpBasicInfo 形态存在;OPPROF 逐核明细 CSV
202
+ # 无此列(时间在 OpBasicInfo.csv)——该形态下检查 OpBasicInfo 存在即可
203
+ if grep -q "Task Duration" "$csv"; then
204
+ :
205
+ elif [[ -n "$(find "$dir" -name "OpBasicInfo.csv" 2>/dev/null | head -1)" ]]; then
206
+ :
207
+ else
208
+ echo "Task Duration 列缺失且无 OpBasicInfo.csv(verdict/isolation 主消费字段)"
209
+ return 1
210
+ fi
211
+ if [[ "$metric" == "PipeUtilization" ]] || [[ "$metric" == "msprof_op" ]]; then
212
+ local pipe_csv
213
+ pipe_csv=$(find "$dir" -name "PipeUtilization.csv" 2>/dev/null | head -1)
214
+ if [[ -z "$pipe_csv" ]]; then
215
+ pipe_csv="$csv"
216
+ fi
217
+ if ! grep -qE "vec_ratio|cube_ratio|mte2_ratio|VectorUtilization|CubeUtilization|Mte2Utilization" "$pipe_csv"; then
218
+ echo "pipe 比例列缺失(判定引擎主输入,检查 $pipe_csv)"
219
+ return 1
220
+ fi
221
+ fi
222
+ return 0
223
+ }
224
+
225
+ run_msprof() { # metrics_name aic_metrics_value extra_flags...
226
+ # 统一 msprof 调用形态:timeout 包裹(挂起可诊断)+ 全量日志落盘副本(检视 F23:
227
+ # 此前 >/dev/null 2>&1 把排障现场全部丢弃,troubleshooting.md 第一条就要看采集输出)
228
+ # P2-1:msprof 退出 0 后跑产物有效性断言,断言不过 → 状态 partial(不伪 Green)
229
+ local name="$1" metric="$2"; shift 2
230
+ local out="$OUTPUT/PROF_${name}_${TIMESTAMP}"
231
+ local log="$OUTPUT/msprof_${name}.log"
232
+ if timeout "$MSPROF_TIMEOUT" msprof --output="$out" --ai-core=on --aic-metrics="$metric" \
233
+ --task-time=on "$@" --application="${EXE} ${EXE_ARGS}" >"$log" 2>&1; then
234
+ local assert_note
235
+ assert_note=$(assert_block_artifacts "$out" "$name") || true
236
+ if [[ -z "${assert_note:-}" ]]; then
237
+ record_block "$name" ok "$out" "aic-metrics=$metric"
238
+ else
239
+ record_block "$name" partial "$out" "断言未过:${assert_note}(msprof 退出 0 但产物不可用——伪 Green 防护,P2-1)"
240
+ fi
241
+ else
242
+ local rc=$?
243
+ if [[ $rc -eq 124 ]]; then
244
+ record_block "$name" failed "$out" "msprof 超时(>${MSPROF_TIMEOUT}s),日志: $log"
245
+ else
246
+ record_block "$name" failed "$out" "msprof 退出非 0(rc=$rc),日志: $log"
247
+ fi
248
+ fi
249
+ }
250
+
251
+ run_msprof_metrics() { # metrics_name aic_metrics_value(run_msprof 的兼容薄封装)
252
+ run_msprof "$1" "$2"
253
+ }
254
+
255
+ run_msprof_mc2() { # task-based 多 rank 采集(MC² 专用路径)
256
+ # MC² 多 rank:强制 task-based(msprof op 对 fork 程序行为未定义,数据不可靠)
257
+ # 稳态纪律由运行程序自身保证:循环 ≥10 次前 5 warm-up;跨 rank 取 max 由 verdict/报告侧执行
258
+ local out="$OUTPUT/PROF_MC2_${TIMESTAMP}"
259
+ local log="$OUTPUT/msprof_mc2_taskbased.log"
260
+ if timeout "$MSPROF_TIMEOUT" msprof --output="$out" --ai-core=on --aic-mode=task-based \
261
+ --aic-metrics=PipeUtilization --task-time=on --ascendcl=on \
262
+ --application="${EXE} ${EXE_ARGS}" >"$log" 2>&1; then
263
+ local assert_note
264
+ assert_note=$(assert_block_artifacts "$out" "PipeUtilization") || true
265
+ if [[ -z "${assert_note:-}" ]]; then
266
+ record_block "mc2_taskbased" ok "$out" "aic-mode=task-based ranks=$RANKS"
267
+ else
268
+ record_block "mc2_taskbased" partial "$out" "断言未过:${assert_note}(伪 Green 防护,P2-1)"
269
+ fi
270
+ else
271
+ local rc=$?
272
+ if [[ $rc -eq 124 ]]; then
273
+ record_block "mc2_taskbased" failed "$out" "msprof 超时(>${MSPROF_TIMEOUT}s),日志: $log"
274
+ else
275
+ record_block "mc2_taskbased" failed "$out" "msprof 退出非 0(rc=$rc;检查 rank≤空闲卡数),日志: $log"
276
+ fi
277
+ fi
278
+ }
279
+
280
+ run_msprof_op() { # msprof op 分支(单卡 + 本机支持;tool-routing 决策树 ②③)
281
+ # F17:单卡且本机 msprof op 可用时走 msprof op(含 Roofline/KernelScale 等能力
282
+ # 按本机帮助);--kernel-name 仅此形态有效(多 kernel 程序必须显式过滤)
283
+ local out="$OUTPUT/PROF_msprof_op_${TIMESTAMP}"
284
+ local log="$OUTPUT/msprof_msprof_op.log"
285
+ local -a cmd=(timeout "$MSPROF_TIMEOUT" msprof op --output="$out"
286
+ --aic-metrics=PipeUtilization,ArithmeticUtilization,Memory,MemoryL0,MemoryUB,L2Cache,ResourceConflictRatio)
287
+ if [[ -n "$KERNEL_NAME" ]]; then
288
+ cmd+=(--kernel-name="$KERNEL_NAME")
289
+ fi
290
+ cmd+=("--application=${EXE} ${EXE_ARGS}")
291
+ if "${cmd[@]}" >"$log" 2>&1; then
292
+ local assert_note
293
+ assert_note=$(assert_block_artifacts "$out" "msprof_op") || true
294
+ if [[ -z "${assert_note:-}" ]]; then
295
+ record_block "msprof_op" ok "$out" "kernel_name=${KERNEL_NAME:-<all>}"
296
+ else
297
+ record_block "msprof_op" partial "$out" "断言未过:${assert_note}(伪 Green 防护,P2-1)"
298
+ fi
299
+ else
300
+ local rc=$?
301
+ if [[ $rc -eq 124 ]]; then
302
+ record_block "msprof_op" failed "$out" "msprof op 超时(>${MSPROF_TIMEOUT}s),日志: $log"
303
+ else
304
+ record_block "msprof_op" failed "$out" "msprof op 退出非 0(rc=$rc),日志: $log"
305
+ fi
306
+ fi
307
+ }
308
+
309
+ case "$MODE" in
310
+ standard)
311
+ # 采集模式路由(tool-routing 决策树):MC² 强制 task-based;单卡在本机
312
+ # msprof op 可用时优先 msprof op(F17:探测结果真正参与路由,不再只是打印)
313
+ if [[ $RANKS -le 1 && $MSPROF_OP_OK -eq 1 ]]; then
314
+ echo "[route] 单卡 + msprof op 可用 → msprof op 分支"
315
+ run_msprof_op
316
+ else
317
+ echo "[route] 走完整 msprof 7 组 aic-metrics 顺序采集"
318
+ for m in PipeUtilization ArithmeticUtilization Memory MemoryL0 MemoryUB L2Cache ResourceConflictRatio; do
319
+ run_msprof_metrics "$m" "$m"
320
+ done
321
+ fi
322
+ ;;
323
+ quick)
324
+ # 1 轮 kernel 时间(不采 7 指标):用于快速对比 / 迭代轮次中的时间复核
325
+ # F17 真身化:仅 task-time 轮(aic-metrics 取单组,非 7 组全量回放)
326
+ run_msprof_metrics TaskDuration "PipeUtilization"
327
+ ;;
328
+ timing)
329
+ # P0-2:计时 harness 模式——不依赖 msprof,跑工程自带/拷入的 aclrtEvent
330
+ # harness 产 result.json(kernel_avg_us 的第一生产者,G4)。
331
+ # harness 来源:工程自带 benchmark,或拷
332
+ # ops-family/references/common/timing_harness/timing_harness_aclrt_event.cpp
333
+ # (模板拷贝纪律;[MODIFY] 点见模板头注释)。
334
+ # 产物契约:harness 的 --output 必填(本脚本显式指定),结果落 $OUTPUT/ 并
335
+ # manifest 记账;warmup/repeat 是口径字段由 harness 持有。EXE_ARGS 由调用方
336
+ # 提供(含 --output 时以调用方为准;缺省追加本脚本的目标路径)。
337
+ TIMING_JSON="$OUTPUT/result.json"
338
+ TIMING_LOG="$OUTPUT/timing_harness.log"
339
+ # 退出码先捕获再判定(跨 [[ ]] 取 $? 会拿到测试结果而非 harness 退出码——检视 #9 修复)
340
+ timeout "$MSPROF_TIMEOUT" bash -c "cd '$PWD' && '${EXE}' ${EXE_ARGS} --output '$PWD/result.json'" >"$TIMING_LOG" 2>&1
341
+ harness_rc=$?
342
+ if [[ $harness_rc -eq 0 ]] && [[ -f "result.json" ]]; then
343
+ mv "result.json" "$TIMING_JSON"
344
+ record_block "timing_harness" ok "$TIMING_JSON" "harness 直跑(event 口径,warmup/repeat 见 result.json)"
345
+ elif [[ -f "result.json" ]]; then
346
+ mv "result.json" "$TIMING_JSON"
347
+ record_block "timing_harness" failed "$TIMING_JSON" "harness 产出 result.json 但退出非 0(rc=$harness_rc),日志: $TIMING_LOG"
348
+ else
349
+ record_block "timing_harness" failed "$TIMING_LOG" "harness 退出非 0 且无 result.json(rc=$harness_rc),日志: $TIMING_LOG"
350
+ fi
351
+ ;;
352
+ mc2)
353
+ run_msprof_mc2
354
+ ;;
355
+ npu-compute)
356
+ # ---- Roadmap #4:npu-compute DB 格式采集(950/A5 单卡)----
357
+ # 产物形态:.npu-rep(单文件报告:7 Section CSV + Summary.jsonl +
358
+ # summary.db[PcSampling/FieldUnitMap] + PipeTrace.json)→ 解包后喂
359
+ # facts_bridge DB 通道 / verdict --db。
360
+ # npu-compute 采集的指标范围以 --set full 为底座(7 Section 全量);
361
+ # PmSampling 等 Section 以 --sections 追加(本机 --list-sections 为门禁,
362
+ # 先探测再选命令纪律同 msprof 段)。
363
+ NPU_REP="$OUTPUT/report.npu-rep"
364
+ NPU_LOG="$OUTPUT/npu_compute.log"
365
+ # 可选 Section 追加(逗号分隔):仅当本机支持时进命令(探测门禁)
366
+ declare -a npc_cmd=(timeout "$MSPROF_TIMEOUT" npu-compute --set full)
367
+ if [[ -n "${NPU_SECTIONS:-}" ]]; then
368
+ IFS=',' read -r -a EXTRA_SECTIONS <<< "$NPU_SECTIONS"
369
+ for s in "${EXTRA_SECTIONS[@]}"; do
370
+ s="${s//[[:space:]]/}"
371
+ [[ -z "$s" ]] && continue
372
+ if npu-compute --list-sections 2>/dev/null | grep -qx "$s"; then
373
+ npc_cmd+=(--section "$s")
374
+ else
375
+ echo "[warn] 本机 npu-compute 不支持 Section $s(--list-sections 为门禁),跳过" >&2
376
+ fi
377
+ done
378
+ fi
379
+ npc_cmd+=(--export "$NPU_REP" -- "$EXE")
380
+ # EXE_ARGS 追加(bash -c 拼接与 msprof --application 形态对齐)
381
+ if [[ -n "$EXE_ARGS" ]]; then
382
+ npc_cmd+=($EXE_ARGS)
383
+ fi
384
+ if "${npc_cmd[@]}" >"$NPU_LOG" 2>&1 && [[ -f "$NPU_REP" ]]; then
385
+ # 解包(--import;解包目录以终端 unpacked= 输出为准,不猜测)
386
+ UNPACK_LOG="$OUTPUT/npu_compute_import.log"
387
+ UNPACKED_DIR=""
388
+ if timeout "$MSPROF_TIMEOUT" npu-compute --import "$NPU_REP" \
389
+ --export "$OUTPUT/unpacked" >"$UNPACK_LOG" 2>&1; then
390
+ UNPACKED_DIR=$(sed -n 's/.*npu-compute: unpacked= *//p' "$UNPACK_LOG" | tail -1)
391
+ fi
392
+ # 产物断言(P2-1 伪 Green 防护同款):解包目录须含判定引擎主输入
393
+ # (PipeUtilization.csv 或 summary.db 任一)
394
+ ASSERT_NOTE=""
395
+ if [[ -z "$UNPACKED_DIR" || ! -d "$UNPACKED_DIR" ]]; then
396
+ ASSERT_NOTE="解包目录缺失或未解析到 unpacked= 路径(日志: $UNPACK_LOG)"
397
+ elif [[ ! -f "$UNPACKED_DIR/PipeUtilization.csv" && ! -f "$UNPACKED_DIR/summary.db" ]]; then
398
+ ASSERT_NOTE="解包目录无 PipeUtilization.csv / summary.db(判定引擎主输入)"
399
+ fi
400
+ DB_NOTE="无 summary.db"
401
+ [[ -f "$UNPACKED_DIR/summary.db" ]] && DB_NOTE="含 summary.db(DB 通道可用)"
402
+ if [[ -z "$ASSERT_NOTE" ]]; then
403
+ record_block "npu-compute" ok "$NPU_REP" "unpacked=$UNPACKED_DIR($DB_NOTE)"
404
+ else
405
+ record_block "npu-compute" partial "$NPU_REP" "断言未过:$ASSERT_NOTE(伪 Green 防护,P2-1)"
406
+ fi
407
+ else
408
+ rc=$?
409
+ if [[ $rc -eq 124 ]]; then
410
+ record_block "npu-compute" failed "$NPU_REP" "npu-compute 超时(>${MSPROF_TIMEOUT}s),日志: $NPU_LOG"
411
+ else
412
+ record_block "npu-compute" failed "$NPU_REP" "npu-compute 退出非 0(rc=$rc),日志: $NPU_LOG"
413
+ fi
414
+ fi
415
+ ;;
416
+ *) echo "ERROR: 未知 mode: $MODE(standard/quick/timing/mc2/npu-compute)" >&2; exit 1 ;;
417
+ esac
418
+
419
+ # ---- manifest 落盘(采集块唯一真源;verdict.py 与报告按此定位产物)----
420
+ # F23:记录由 json.dumps 逐条生成(含引号/逗号安全),以 \x1f 分隔传递
421
+ BLOCKS_JSON=$(printf '\x1f%s' "${BLOCKS[@]:-}")
422
+ BLOCKS_JSON=${BLOCKS_JSON:1}
423
+ python3 - "$MANIFEST" "$MODE" "$EXE" "$EXE_ARGS" "$DEVICE" "$RANKS" "$KERNEL_NAME" "$BLOCKS_JSON" <<'PYEOF'
424
+ import json, sys
425
+ manifest = {
426
+ "schema": "opstune.collection/1.0",
427
+ "generated_at": __import__("datetime").datetime.now().isoformat(timespec="seconds"),
428
+ "mode": sys.argv[2],
429
+ "exe": sys.argv[3],
430
+ "args": sys.argv[4],
431
+ "device_id": sys.argv[5],
432
+ "ranks": int(sys.argv[6]) if sys.argv[6].isdigit() else sys.argv[6],
433
+ "kernel_name": sys.argv[7] or None,
434
+ "blocks": [json.loads(b) for b in sys.argv[8].split("\x1f")] if sys.argv[8] else [],
435
+ }
436
+ with open(sys.argv[1], "w", encoding="utf-8") as f:
437
+ json.dump(manifest, f, ensure_ascii=False, indent=2)
438
+ f.write("\n")
439
+ PYEOF
440
+
441
+ echo "[done] manifest → $MANIFEST"
442
+ echo "下一步:"
443
+ echo " 1. 在 op_summary/PipeUtilization CSV 上运行 ops-verdict/scripts/verdict.py"
444
+ echo " 2. 基线落盘:ops-verdict/scripts/snapshot.py(口径指纹)"