Skip to content

fix: overlap CUDA graph replay preparation on PRO5000 - #1365

Open
siluzhou wants to merge 32 commits into
develop/pro5000-optfrom
feature/pro5000-moe-cuda-graph-async
Open

fix: overlap CUDA graph replay preparation on PRO5000#1365
siluzhou wants to merge 32 commits into
develop/pro5000-optfrom
feature/pro5000-moe-cuda-graph-async

Conversation

@siluzhou

@siluzhou siluzhou commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • keep CUDA Graph replay planning metadata in pinned host memory and refresh capture-stable device mirrors without a per-step D2H synchronization
  • pipeline step N+1 replay preparation with step N GPU execution while protecting reusable preparation copies with CUDA events
  • make FlashInfer CUDA Graph planning use the graph-bound device page-index alias and enforce stable RoPE sequence-length buffers
  • add an RTX PRO 5000 TP2 Qwen3-30B-A3B FP8 MoE smoke case with frontend server count 1, CUDA Graph, stream async/device-input switches, and max sequence length 8192

Validation

  • bazelisk build //rtp_llm:start_server_bin --config=cuda12_9: passed
  • FlashInfer CUDA Graph targeted tests (capture fixed batch, replay metadata refresh, graph-bound buffer contract): passed
  • //rtp_llm/test/smoke:moe_fp8pb_cuda_graph_async_tp2_sm120: passed on GPUs 0-1; 2/2 requests matched the existing synchronous golden (compare diff count: 0)
  • sync/async timeline A/B: next graph launch moved from +1818.8 us after the previous graph ended to -8098.8 us before it ended (8/8 transitions); median true GPU idle fell from 1279.7 us to 62.6 us (-95.1%), and every launch was attributed to the later inference step

Scope

Only production changes, the directly affected unit assertion, and the new SM120 smoke registration are included. Existing managed test/benchmark artifacts are intentionally excluded.

parkerpang and others added 30 commits August 18, 2026 08:31
…_8bit

The kernel read the input twice (absmax pass + quantize pass). For the
common case (bf16 + group_size=128 -> 1 vec/lane) cache the loaded
vector in registers and reuse it in the quantize pass, eliminating the
second HBM read; larger groups fall back to reload. Also emit the 8 fp8
outputs as one packed vec_t<__nv_fp8_e4m3,8> store (if constexpr; int8
path unchanged). Bit-identical output (math unchanged).
quant kernel 304.8->130.9 us/step (-57%); PER_BLOCK bs64 3.91->3.43ms.
Benefits every per_token_group_quant_8bit user (PER_BLOCK / DeepGEMM).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- transport input embeddings through RPC and the C++ engine pipeline
- expose embeddings to PyModel and model descriptors
- support multimodal embedding locations and disable token-only reuse
- start_server: add prompt generator multi-process launch with frontend
  port reuse, guarded by VIT role check and enable_prompt_generator config
- Add MPS support gated by enable_prompt_generator_mps config
- Normalize and validate prompt generator config at startup with graceful
  fallback when internal_source is unavailable
- model_loader: support loading embedding weights independently
- Add normalize_prompt_generator_config unit tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add register_additional_metrics() to MetricReporter for dynamic metric
registration with dedup support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Preserve legacy per-output AuxInfo tensors for rolling upgrades while serializing one stacked tensor for clients that avoid per-output decoding. Rebuilt from the missing RPC portion of 1adf28a.
Bound complete token history by the request output limit, initialize every beam row, delay non-beam return-sequence expansion until sampling, and align logits processor rows. Rebuilt from the develop-missing portions of 28a7fa2.
@rtp-llm-review-bot

Copy link
Copy Markdown
Collaborator

PR #1365 第 1 轮评审 — BLOCKED(1)

  • 标题:fix: overlap CUDA graph replay preparation on PRO5000(@siluzhou,open)
  • head 32497b255b44 · base 27e6930188f6 · delta 14 文件 · discover 5/5 成功

阻塞项(P0/P1,support≥3 且过全部门)

[P1] FusedRopeKVCacheDecodeOp.forward 新增的 is 身份检查会破坏 XQAImpl(sm_90/sm_100 的 decode 路径)

  • rtp_llm/ops/fused_rope_kvcache_op.py:339 · 类别 compatibility · 票数 4/5 · 首见 r1
  • 依据:本 PR 在 FusedRopeKVCacheDecodeOp.forward 新增严格身份断言 if params.sequence_lengths is not self._sequence_lengths_device: raise DecodeRopeContractError(...)(fused_rope_kvcache_op.py:339)。但同类 FusedRopeKVCacheDecodeOp 的另一消费者 XQAImpl 在 prepare_cuda_graph 中显式覆盖了该指针:self.rope_params.sequence_lengths = self._captured_seq_lens(xqa.py:153),其中 self._captured_seq_lens = attn_inputs.sequence_lengths(xqa.py:80),而 refresh_sequence_lengths 内部 self._sequence_lengths_device 永远是另一块 buffer(graph 镜像 sequence_lengths_device 或 torch.empty 分配),二者 data_ptr 必然不同。触发路径:sm_90/sm_100(A100/H100,XQAImpl.support 仅在 is_sm12x() 时返回 False,见 xqa.py:91-96 与 init.py:96 将 XQAImpl 注册在 PyFlashinferDecodeImpl 之前)上,need_rope_kv_cache=True 的 decode 模型开启 CUDA graph 后,XQAImpl.forward(xqa.py:102-103)调用 rope_kvcache_impl.forward 时 params.sequence_lengths is not self._sequence_lengths_device 为 True,直接抛出 DecodeRopeContractError,导致 decode 崩溃。XQADecodeImpl(xqa.py:166/226)同样使用该 op 且 prepare_cuda_graph 不更新 sequence_lengths,存在相同风险。
  • 建议:该 is 身份断言过强,不应假设所有调用方都把 rope_params.sequence_lengths 绑定到 op 内部 buffer。建议:要么删除 params.sequence_lengths is not self._sequence_lengths_device 这条断言(仅保留 is_cuda 与 kv_cache_offset 校验),要么同步修改 XQAImpl/XQADecodeImpl 使其不再覆盖 rope_params.sequence_lengths 为 _captured_seq_lens,并补充 sm_90/sm_100 的回归测试。
  • 事实核验:hold — verified(本 PR 在 FusedRopeKVCacheDecodeOp.forward );verified(XQAImpl 是 FusedRopeKVCacheDecodeOp 的另一消费);verified(XQAImpl.prepare_cuda_graph 显式覆盖指针 self.);verified(self._captured_seq_lens = attn_inputs.s);verified(refresh_sequence_lengths 只会把 _sequence_l);verified(sm_90/sm_100 上 XQAImpl.support 返回 True(i);verified(XQAImpl 注册在 PyFlashinferDecodeImpl 之前,sm);verified(CUDA graph replay 会先调用 prepare_cuda_grap);unverifiable(生产环境确实存在 need_rope_kv_cache=True 且开启 CUD);refuted(XQADecodeImpl 同样存在此身份断言崩溃风险("存在相同风险"))

非阻塞发现(并集报告:单票也保留,降级不丢弃)

  • [P2] need_rope_kv_cache=False 时 CUDA graph replay 不再刷新 rope_params,导致 KV cache offset/位置陈旧 — rtp_llm/models_py/modules/factory/attention/cuda_impl/py_flashinfer_mha.py:1302 · 票数 2/5 · 提议 P1,降级原因 votes(2<3)
  • [P2] RTP_LLM_CHECK_WITH_INFO(next_real_seq_len>0) 对空/刚加入流可能误杀 — rtp_llm/cpp/normal_engine/NormalModelInputGatherer.cc:555 · 票数 1/5
  • [P2] streamAsyncReplayPrepEnabled 与 gatherer/executor 的 stream-async 判定标志不一致 — rtp_llm/cpp/cuda_graph/cuda_graph_runner.cc:56 · 票数 1/5
  • [P2] prepare_copy_event_recorded_ 非原子 bool 且 prepare_copy_event_ 跨线程 record/synchronize 存在数据竞争风险 — rtp_llm/cpp/cuda_graph/cuda_graph_runner.h:186 · 票数 1/5

本轮 KPI

{
 "reps": 5,
 "agentic_reps": 2,
 "diff_truncated": false,
 "patch_bytes": 34169,
 "coverage": 1.0,
 "shards": 1,
 "discover_ok": 5,
 "discover_fail": 0,
 "discover_attempts": 10,
 "clusters": 5,
 "synth_fail": 0,
 "confirm": {
  "candidates": 1,
  "to_block": 0,
  "confirm_fail": 0
 },
 "blocking_dedup_merged": 0,
 "fact_check": {
  "checked": 1,
  "hold": 1,
  "refuted": 0,
  "parse_fail": 0
 },
 "model": "whale/DeepSeek-V4-Pro-0813",
 "round_index": 1,
 "delta_files": 14,
 "full_files": 14,
 "new_findings": 5,
 "merged": 0,
 "recheck": {
  "fixed": 0,
  "still_open": 0,
  "cannot_substantiate": 0,
  "fail": 0
 },
 "blocking": 1,
 "blocking_on_unchanged": 0
}

本报告由 rtp-llm-agent-platform 自动生成,同一 PR 的后续轮次就地更新同一条评论。


rtp-llm-agent-platform review · 第 1 轮 · head 32497b255b44 · BLOCKED(1) · 同一 PR 的结论就地更新这一条评论

@siluzhou siluzhou left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Code Review - PR #1365

Status: BLOCKING

Summary: P0/0 · P1/1 · P2/0 · P3/0

Reviewed: commit 32497b255b44 · 2026-09-01 11:57 UTC+8

Blocking Issues

P1

  • XQA replay 违反新增的 decode RoPE 设备缓冲区所有权契约 @ rtp_llm/ops/fused_rope_kvcache_op.py:339
    • 建议:让 XQA replay 调用 refresh_sequence_lengths(),或通过 prepare(..., forbid_reallocation=True) 刷新并保留 op 管理的稳定设备 tensor,删除 _captured_seq_lens 回写。增加真实 XQA capture/replay 测试,连续改变 batch size 与序列长度,并验证 eager/replay 输出一致、padding 清零及指针稳定。

Checklist Findings (3 fail / 108 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue XQA replay 违反新增的 decode RoPE 设备缓冲区所有权契约
    forward() 要求 params.sequence_lengths 必须是 op 持有的稳定 CUDA buffer;初始化时 prepare() 返回该 buffer,但 XQAImpl.prepare_cuda_graph() 仍将参数改回捕获自原始 attn_inputs.sequence_lengths_captured_seq_lens。启用 RoPE 的 XQA replay 随后会因对象身份不符而抛出 DecodeRopeContractError;若原 tensor 在 CPU,还会先触发设备检查。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue XQA replay 违反新增的 decode RoPE 设备缓冲区所有权契约
    forward() 要求 params.sequence_lengths 必须是 op 持有的稳定 CUDA buffer;初始化时 prepare() 返回该 buffer,但 XQAImpl.prepare_cuda_graph() 仍将参数改回捕获自原始 attn_inputs.sequence_lengths_captured_seq_lens。启用 RoPE 的 XQA replay 随后会因对象身份不符而抛出 DecodeRopeContractError;若原 tensor 在 CPU,还会先触发设备检查。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue XQA replay 违反新增的 decode RoPE 设备缓冲区所有权契约
    forward() 要求 params.sequence_lengths 必须是 op 持有的稳定 CUDA buffer;初始化时 prepare() 返回该 buffer,但 XQAImpl.prepare_cuda_graph() 仍将参数改回捕获自原始 attn_inputs.sequence_lengths_captured_seq_lens。启用 RoPE 的 XQA replay 随后会因对象身份不符而抛出 DecodeRopeContractError;若原 tensor 在 CPU,还会先触发设备检查。

Strengths

  • sequence_lengths_device 已贯通 C++ 构造、CUDA Graph capture/replay、pybind 声明及 Python 消费路径。
  • CUDA Graph replay 会原位刷新设备侧序列长度镜像,并正确清理 padding lane。
  • FlashInfer graph plan 使用图绑定的设备侧 page-index buffer,并通过 CUDA event 保护可复用的 pinned planning buffer。
  • 新增 SM120 smoke 覆盖 TP2、MoE FP8、CUDA Graph 与流异步的关键组合,并复用同步用例的输入与 golden。

raise DecodeRopeContractError(
"decode RoPE sequence_lengths must be on CUDA"
)
if params.sequence_lengths is not self._sequence_lengths_device:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] XQA replay 违反新增的 decode RoPE 设备缓冲区所有权契约

forward() 要求 params.sequence_lengths 必须是 op 持有的稳定 CUDA buffer;初始化时 prepare() 返回该 buffer,但 XQAImpl.prepare_cuda_graph() 仍将参数改回捕获自原始 attn_inputs.sequence_lengths_captured_seq_lens。启用 RoPE 的 XQA replay 随后会因对象身份不符而抛出 DecodeRopeContractError;若原 tensor 在 CPU,还会先触发设备检查。

建议: 让 XQA replay 调用 refresh_sequence_lengths(),或通过 prepare(..., forbid_reallocation=True) 刷新并保留 op 管理的稳定设备 tensor,删除 _captured_seq_lens 回写。增加真实 XQA capture/replay 测试,连续改变 batch size 与序列长度,并验证 eager/replay 输出一致、padding 清零及指针稳定。

Checklist: [6.1] 状态不变量:创建/更新/失败/重试/回滚路径有效;[6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试;[6.1] 边界 case 覆盖(空、单元素、最大值)

@LLLLKKKK LLLLKKKK left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Code Review - PR #1365

Status: LGTM

Summary: P0/0 · P1/0 · P2/3 · P3/3

Reviewed: commit 32497b255b44 · 2026-09-01 12:02 UTC+8

lgtm ready to ci

Non-blocking Suggestions

P2

  • XQA 未迁移新的 RoPE 缓冲区所有权契约 @ rtp_llm/ops/fused_rope_kvcache_op.py:339
    • 建议:保留 _captured_seq_lens 供 XQA 参数原地刷新,但不要赋给 RoPE 参数;改用 refresh_sequence_lengths(..., forbid_reallocation=True),并增加启用 RoPE 的 prepare/forward 与 graph replay 测试。
  • 直连 runner 未构造新增设备镜像 @ rtp_llm/cpp/cuda_graph/cuda_graph_runner.cc:431
    • 建议:在测试 runner 中补齐 sequence_lengths_device;decode replay 缺少该镜像时应 fail-fast,并增加连续两次不同 sequence length 的重放测试。
  • 异步 plan-copy 等待顺序缺少确定性测试 @ rtp_llm/models_py/modules/factory/attention/cuda_impl/test/test_py_flashinfer_mha_decode.py:593
    • 建议:注入可控 event,断言未完成时在下一次 plan() 前同步,并覆盖 event 已完成时不等待的分支。

P3

  • 非图 decode 重复分配并复制 sequence lengths @ rtp_llm/ops/fused_rope_kvcache_op.py:294
    • 建议:非图路径直接采用已有且 dtype/shape 匹配的设备镜像,仅为缺少镜像的独立调用保留分配回退。
  • page indices 别名契约缺少直接断言 @ rtp_llm/models_py/modules/factory/attention/cuda_impl/test/test_py_flashinfer_mha_decode.py:620
    • 建议:增加 capture_call.args[1].data_ptr() == fmha_params.page_indice_d.data_ptr() 断言。
  • 必备绑定字段被当作可选属性访问 @ rtp_llm/ops/fused_rope_kvcache_op.py:209
    • 建议:改为直接访问 attn_inputs.sequence_lengths_device,让缺失绑定明确抛出 AttributeError

Checklist Findings (5 fail / 101 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue 异步 plan-copy 等待顺序缺少确定性测试
    新逻辑要求 event 未完成时先 synchronize(),再复用 planning 缓冲区;当前测试仅检查 plan 次数、参数设备与指针稳定性。若等待被删除或顺序颠倒,单测不会稳定失败,只能依赖 smoke 时序偶现暴露。
  • [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue 必备绑定字段被当作可选属性访问
    sequence_lengths_device 已由 pybind 和 .pyi 定义为必备字段,但代码仍通过字面量 getattr 静默回退。加载不匹配的旧 .so 时不会 fail-fast,反而进入不同的缓冲区路径,掩盖 Python 与绑定版本不一致。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue page indices 别名契约缺少直接断言
    测试只确认传给 plan() 的 page indices 位于 CUDA,未确认其与 fmha_params.page_indice_d 同址。若改传另一块 CUDA tensor,测试仍会通过,但预期的内部 no-op 拷贝将退化为真实 D2D 拷贝。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue 异步 plan-copy 等待顺序缺少确定性测试
    新逻辑要求 event 未完成时先 synchronize(),再复用 planning 缓冲区;当前测试仅检查 plan 次数、参数设备与指针稳定性。若等待被删除或顺序颠倒,单测不会稳定失败,只能依赖 smoke 时序偶现暴露。

Python Static-First Checklist

  • [P.A] 静态结构与类型纪律 — 禁止 getattr/setattr literal 访问 → issue 必备绑定字段被当作可选属性访问
    sequence_lengths_device 已由 pybind 和 .pyi 定义为必备字段,但代码仍通过字面量 getattr 静默回退。加载不匹配的旧 .so 时不会 fail-fast,反而进入不同的缓冲区路径,掩盖 Python 与绑定版本不一致。

Strengths

  • 生产链路已将 sequence_lengths_device 贯通 C++、pybind、类型声明、捕获内存与 replay 刷新。
  • 独立 CUDA event 保护可复用 pinned 规划缓冲区,无需等待整张 graph。
  • 活跃 lane 复制与 padding lane 清零语义在 host/device 两侧保持一致。
  • 新增 SM120 异步 smoke 复用同步用例 golden,可检测输出漂移。

raise DecodeRopeContractError(
"decode RoPE sequence_lengths must be on CUDA"
)
if params.sequence_lengths is not self._sequence_lengths_device:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] XQA 未迁移新的 RoPE 缓冲区所有权契约

forward() 要求 sequence_lengths 必须是 op 自持 CUDA tensor,但 XQAImpl.prepare_cuda_graph() 仍将其覆盖为 pinned CPU _captured_seq_lens。生产 replay 不再调用 Python forward(),所以不会形成 P1;然而对象状态已违反自身不变量,任何 prepare 后的直接 forward 都会失败,且现有 XQA 图测试均关闭 RoPE。

建议: 保留 _captured_seq_lens 供 XQA 参数原地刷新,但不要赋给 RoPE 参数;改用 refresh_sequence_lengths(..., forbid_reallocation=True),并增加启用 RoPE 的 prepare/forward 与 graph replay 测试。


if (!is_prefill_cuda_graph_mode_) {
// D2D copies — collected for single batched kernel launch
tryAddD2DCopy(inputs.attention_inputs.sequence_lengths_device,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 直连 runner 未构造新增设备镜像

sequence_lengths_device 未定义时复制会静默跳过;测试 runner 仅构造 input/prefix 镜像。已注册的 H20 decode-padding 测试启用 RoPE,因此 replay 会继续使用捕获期长度,而 host 输入已更新。生产 PyWrappedModel 会构造该镜像,影响集中于测试封装和直连 runner。

建议: 在测试 runner 中补齐 sequence_lengths_device;decode replay 缺少该镜像时应 fail-fast,并增加连续两次不同 sequence length 的重放测试。

@@ -615,7 +615,9 @@ def test_replay_refreshes_plan_metadata(self):
self.assertEqual(plan_mock.call_count, 1)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 实际位置 rtp_llm/models_py/modules/factory/attention/cuda_impl/test/test_py_flashinfer_mha_decode.py:593(不在 diff 展示范围内,就近挂载)

[P2] 异步 plan-copy 等待顺序缺少确定性测试

新逻辑要求 event 未完成时先 synchronize(),再复用 planning 缓冲区;当前测试仅检查 plan 次数、参数设备与指针稳定性。若等待被删除或顺序颠倒,单测不会稳定失败,只能依赖 smoke 时序偶现暴露。

建议: 注入可控 event,断言未完成时在下一次 plan() 前同步,并覆盖 event 已完成时不等待的分支。

Checklist: [6.1] 状态不变量:创建/更新/失败/重试/回滚路径有效;[6.1] 边界 case 覆盖(空、单元素、最大值)

tuple(sequence_lengths.shape),
device,
)
self._sequence_lengths_device = torch.empty(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] 非图 decode 重复分配并复制 sequence lengths

PyWrappedModel 已生成 sequence_lengths_device,但非图路径仍执行 torch.empty()copy_();普通 forward 又会重新创建 attention impl,使私有缓存无法跨步复用。每步因此增加一次小型设备分配及一次冗余 H2D/D2D 拷贝,但当前没有基准数据证明显著回归。

建议: 非图路径直接采用已有且 dtype/shape 匹配的设备镜像,仅为缺少镜像的独立调用保留分配回退。

self.assertFalse(capture_call.args[1].is_cuda)
# Page indices alias the graph-bound device buffer so FlashInfer's
# internal copy is a no-op instead of a blocking host-to-device copy.
self.assertTrue(capture_call.args[1].is_cuda)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] page indices 别名契约缺少直接断言

测试只确认传给 plan() 的 page indices 位于 CUDA,未确认其与 fmha_params.page_indice_d 同址。若改传另一块 CUDA tensor,测试仍会通过,但预期的内部 no-op 拷贝将退化为真实 D2D 拷贝。

建议: 增加 capture_call.args[1].data_ptr() == fmha_params.page_indice_d.data_ptr() 断言。

Checklist: [6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试

# mirror. Its pointer is capture-stable and CudaGraphRunner refreshes
# it with the other fused metadata copies before every replay, avoiding
# a synchronizing torch.copy_ from the pinned host planning mirror.
graph_sequence_lengths = getattr(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] 必备绑定字段被当作可选属性访问

sequence_lengths_device 已由 pybind 和 .pyi 定义为必备字段,但代码仍通过字面量 getattr 静默回退。加载不匹配的旧 .so 时不会 fail-fast,反而进入不同的缓冲区路径,掩盖 Python 与绑定版本不一致。

建议: 改为直接访问 attn_inputs.sequence_lengths_device,让缺失绑定明确抛出 AttributeError

Checklist: [6.1] 错误语义:fail-fast/retry/fallback/silent 行为显式;[P.A] 禁止 getattr/setattr literal 访问

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

CI dispatcher could not find a native build run for HEAD SHA 32497b25.

This can happen if the PR was opened before the CI architecture change, or if the original run was deleted.

To fix: push any commit (even empty: git commit --allow-empty -m "trigger CI" && git push) to create a native build run, then re-approve or post lgtm ready to ci.

@siluzhou
siluzhou force-pushed the develop/pro5000-opt branch from 27e6930 to 77e9a20 Compare September 1, 2026 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants