跳到主要内容

ISSUE-037: Phase 1 守恒模型与拆分模式的设计层限制 (Phase 2 重做)

发现日期:2026-06-05 状态已修复 (Phase 2A 完成:F001 跨边界 cbfc_residue + F002 重做拆分用标准 mod + must_use 防御) 修复日期:2026-06-05 类型:建模误差 + 实现 bug (代码本身正确,但探测能力 / 拆分模式与 plan 设计意图有偏差) 影响范围:G5 测试加固 Phase 1 完成后的两类已知限制。不影响当前代码功能,仅影响 Phase 1 守恒断言层的探测覆盖范围与代码组织品质——Phase 2 plan 必须吸收。


问题现象

来自 Phase 1 完成后 in-flow code-review (opus, 2026-06-05) 报告的两个非阻塞 finding:

1. Credit 守恒等式由构造保证 (F001,原 P0)

rc_link_tx.rs Task 1 守恒断言 credit_imbalance() = credits_issued - credits_consumed - sum(credits.values()) 设计为"held 派生不维护字段",初衷防漏插桩——任何对 credits map 的新写入路径都会让 held 变化,从而让守恒等式破坏。

实际效果:credits map 字段是 private,所有 mutation 都强制经过 3 个现有更新点(init/return/consume),每个更新点同时更新 creditsissued/consumed。守恒等式由构造满足

$\sum credits = issued - consumed \quad \text{恒成立}$

即使 try_arbitrate 整体逻辑有 RX 端未返还 credit 类型的 "CBFC 黑洞" bug,只要走的还是那三个 TX 内部更新点,守恒等式仍为 0。

真实探测覆盖:当前实现只能 detect"未来开发者新增第 4 个 credit 路径但忘了同步更新 issued/consumed"——即 TX 内部簿记错位。不能 detect Task 1 立项时想抓的真 CBFC 黑洞 (例如 RX 路径漏 emit CreditReturn)。

2. include! 宏注入子文件 (F002,原 P1)

rc_link_tx.rs:536include!("rc_link_tx_diag.inc.rs") 把第二个 impl RcLinkTx 块从主文件注入。Task 8 subagent 选这种做法的理由:用 mod rc_link_tx_diag; 需要把 RcLinkTx 内部字段改 pub(super),会泄露 module 边界。

问题:include!是 Rust 反模式:

  • 文件无法独立编译,IDE 跳转/格式化/lint 行为退化
  • cargo check 报错定位指向 include!行而非真实位置
  • 仅做"行数搬家"——同一编译单元,私有字段访问仍跨文件,没有真正的 module 语义边界

是 worst-of-both:既没真拆分语义边界,又付出 IDE/工具链体验代价。


调查过程

  • [in-flow code-review, opus, 2026-06-05] 独立 reviewer 指出 F001 Task 1 守恒等式由构造保证,F002 include!反模式两个问题
  • [查代码] F001: rc_link_tx.rs:393-398 credit_imbalance 实现 + rc_link_credit.rs 三个更新点确认 reviewer 论断正确
  • [查代码] F002: rc_link_tx.rs:536 include!调用 + rc_link_tx_diag.inc.rs 内含两个 impl 块,确认是 module 边界绕过

根因分析

F001 根因

Task 1 plan 设计时假设"派生 held 自动 catch 漏插桩"——但忽略了 credits map 是 private 这一前提。所有 mutation 必经现有 3 个 helper,守恒由构造恒成立。真正的 CBFC 黑洞发生在跨边界路径 (TX 扣 credit 但 RX 漏返还),Phase 1 没有跨边界对账机制。

F002 根因

Task 8 拆分时未充分讨论 Rust module 系统的 abstraction trade-off。include! 看起来是"最少改动"方案 (不动字段可见性),但牺牲了模块系统的全部价值。


Spec / 文档依据

  • Phase 1 plan(已归档)Task 1 + Task 8 拆分方案
  • code-review report (本 ISSUE 创建者通过 in-flow code-review skill 触发)
  • Rust 社区惯例:include! 主要用于 build-script 生成代码 (OUT_DIR 模式),不用于源码拆分

解决方案 (Phase 2 plan 必须覆盖)

F001 修复方向

采用方案:Phase 2 plan 加独立 RX 对账层。例如:

  • 在 RX 侧维护 credits_returned_total 计数 (每次 CreditReturn 发出时 ++)
  • 在全局守恒断言里加 TX.credits_consumed == RX.credits_returned_total + TX.credits_held_outstanding 跨边界对账
  • 或:TX 维护 sent_bytes / credit_size 比例 推导独立 consumed 路径,与现有 consumed 计数对账

F002 修复方向

采用方案:Phase 2 plan 重做 rc_link_tx 拆分。两选项:

  • A:把内部字段改 pub(super),用 mod rc_link_tx_diag; 正常子模块,接受微小可见性 leak
  • B:把 diag 方法收回 rc_link_tx.rs,接受适度超 600 行 (在 code-style.md 加项目级例外条款给特定文件)

验证 (Phase 2A 完成实测)

F001 跨边界 Credit 对账

  • 新增 stat multi_chip.cbfc_residue = tx_consumed_total - rx_returned_total
  • 收尾时严格 == 0 (守恒等式)
  • 3 单测验证:
    • test_cbfc_residue_normal_run_converges_to_zero:正常路径 PASS
    • test_cbfc_residue_detects_rx_omission: mock RX 漏返还 → 守恒断言 panic ✓
    • test_cbfc_residue_detects_tx_undercounting: mock TX 少计数 → 守恒断言 panic ✓
  • 3 mutation test 验证 RX 3 个 CreditReturn 决定点 (accept/nak/dup) 漏插桩都被探测到 ✓
  • 全 413 个 cargo test 在 Phase 2A 完成后均未触发 cbfc_residue 真 bug — 说明 Phase 1 by-construction 守恒在 G5 当前实现下没掩盖隐藏 CBFC 黑洞

F002 重做拆分

  • rc_link_tx_diag.inc.rs 删除,新 rc_link_tx_diag.rs 作为 tier6 子模块用标准 mod rc_link_tx_diag; 引入
  • 7 个字段改 pub(super) (不泛 pub),接口签名不变
  • bit-identical 验证通过:406 baseline 测试全保留,集合无差异
  • 覆盖率 51% → 94% (远高于 ≥85% 目标),整体覆盖率 80.53% → 81.27% (微升不回退)

#[must_use] 防御:

  • 7 个方法加 must_use: paxi::{submit, on_ack, on_credit_return, on_tx_done} + RcLinkTx::{try_arbitrate, on_tx_done, submit_frame}
  • 编译期 warning 收集:生产代码 0 warning (ISSUE-036 修复后生产事件链 100% schedule 正确)
  • 测试模块 52 warning 通过模块级 #![allow(unused_must_use)] + SAFETY 注释处理 (测试无 kernel, fire-and-forget 合理)

修复后状态 (Phase 2A 完成)

维度Phase 1Phase 2A
cargo test377/0/3 (修复 ISSUE-036 后)413/0/4 (+36 新测试)
整体覆盖率 (regions)80.53%81.27%
整体覆盖率 (lines)83.88%84.54%
rc_link_tx_diag 覆盖率51% (反模式)94% (标准 mod + inline 单测)
Credit 守恒 detect 能力由构造保证 (不能 detect 真黑洞)跨边界对账 detect RX 漏返还 / TX 少计数 / 静默丢帧
ISSUE-034/036 类回归编译期防御7 方法 #[must_use]

遗留问题

  • 守恒探测器的 mutation testing 机制未通用化:仅 Phase 2A Task 1 反例测试一次性出现,未沉淀到 testing-conventions。Phase 2B 可考虑写入 docs/issues 或新建测试公约文档。
  • Phase 2B 还需补的部分:P1 缺口 (Switch 优先级 / SwitchTick 自驱动) + P2 缺口 (诊断 stat 字段) + 蜕变 MR + proptest + golden + CI 三级分层