Skip to content

fix(pipeline): violation-precedence 去重键在 run.at 缺失时会把不同 run 的违规合并 #461

Open

Description

@modusensus

现象

src/pipeline.js 的 suggestRetirements 里,前置违规的去重键是:

const vk = `${run.at ?? ""}|${step.id}|${need}`;

两条 run.at 都缺失(null/undefined)的 run,即使步骤证据完全独立,同一 (step, need) 组合的第二条违规会被键冲突吞掉——只报一条,because 里还写着第一条的时间。

现网风险:低

写入侧 normalizeRun(src/pipeline-runs.js)已强制 run.at 必填(缺失直接抛,不做补默认),合法账本里 at 恒存在。这是防御深度问题:只有读侧被独立喂入非法形状(或未来写入契约放宽)时才会触发。

发现来源:CodeRabbit 对 PR #458 的评审意见,已用最小复现脚本验证键冲突真实存在。

建议修法

为每条 run 保留数组索引,run.at 缺失时把索引进去重键(如 `${run.at ?? `#${i}`}|...`),at 存在时行为不变。补一条读侧回归测试锁住。

Activity

  1. modusensus commented on Oct 20, 2026

    @modusensus
    MemberAuthor

    现状(2026-10-21 对照 main 实测)

    代码里一行没动,问题仍在。

    dsh-mneme/src/pipeline.js:127:

    const vk = `${run.at ?? ""}|${step.id}|${need}`;

    run.at 缺失时该键塌缩成 |step|need,不同 run 的同类违规会被合并——去重本意是「同一轮里同一条违规只报一次」,塌缩后变成「所有无时间戳的轮次共享一条」。现网风险低(run.at 正常都有值),属于防御深度缺口。

    还缺什么

    1. 缺失 run.at 时用 run.id 或进程内单调序号兜底,而不是空串;
    2. 加一条回归用例:两个 run.at 均为 undefined 的 run 产生同 step.id 违规,断言产出两条。

    接手建议

    三行以内可修,test/pipeline-activities.test.js 里已有同类断言可照抄。这条不需要设计讨论。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions