Skip to content

#6148 门禁的迁移说明探测器读不到「无箭头的两列改写表」—— not-required (no-migration-prescription) 可被合法豁免绕过,存量已见 4 条同形 #6497

Closed

Description

@os-project-manager

在 #6350 的存量回补审计(PR 见下)里测出来的。未认领。

事实

scripts/check-adr-0087-registration.mjs 的 findMigrationPrescription() 是 not-required (no-migration-prescription) 这条豁免的唯一守门人:正文带改写指令 ⇒ 拒绝该豁免。它有两条分支,#6419 修过一次:

  1. FROM / TO 标签约定;
  2. 带迁移框架的改写 —— REWRITE_RE 要求一个 X 箭头 Y,两侧都是代码形。

两条分支都要求出现箭头。而本仓相当一批 changeset 把改写指令写成 ## Migration 标题下的两列 markdown 表,一个箭头都没有:

## Migration

| Wrote | Write instead |
| --- | --- |
| `new HttpServer(new HonoHttpServer(port))` registered as `http.server` | register the `HonoHttpServer` directly |
| `import { HttpServer } from '@objectstack/runtime'` | remove it — tsc reports this one |

这是 runtime-httpserver-wrapper-retired.md(#5122)的真实正文。探测器判它 无迁移说明,于是这条 changeset 落进 exempt-no-prescription 桶 —— 它够不着门禁的残差面。

实测口径(当前存量,1372 条 changeset)

  • 13 条 changeset 带「迁移框架标题下的无箭头两列改写表」;
  • 其中 4 条声明为 breaking 且当前正是靠这条盲区拿到豁免:runtime-httpserver-wrapper-retired.md、etl-author-shape-aliases.md、retire-three-deprecated-aliases.md、unknown-key-strictness-ui-batch15.md。修好探测器后这 4 条会从 exempt-no-prescription 移入残差面。

复现:node scripts/check-adr-0087-registration.mjs --audit-stock(PR #6350 半边新增的一次性审计口),runtime-httpserver-wrapper-retired.md 不出现在残差列表里。

为什么这是前向问题,不只是存量问题

门禁是前向的,而这条盲区同样前向:今后任何一个 PR,只要把迁移说明写成表格而不是箭头,就可以合法地写下 not-required (no-migration-prescription),门禁会批准这条自相矛盾的豁免 —— 正是 #6419 那个形状的第三种拼写。#6419 的教训逐字适用:「作者写得越认真,探测器越看不见」—— 一张写清了「原来怎么写 / 改成怎么写」的表,比一行箭头信息量更大,却恰恰是探测器唯一读不到的形状。

顺带:脚本正文里那段「残余盲区」自述目前只说 未加框架 的改写表读不到(128 changesets ... 21 of them declared-breaking)。上面这 4 条是加了框架的,所以那段自述本身也不完整,应一并改写。

明确不主张具体改法

候选各有代价,需要先量化误报面:把表格行(| 分隔、两侧各带反引号代码段)在迁移框架下也算作改写,是最直接的一条,但 changeset 正文里的表格用途很杂(能力矩阵、行为对照、版本对照),放宽之前要照 #6419 的做法把误报数先测出来 —— 那次的结论是「一条诚实收窄的探测器胜过一条吵闹的」,因为误报没有诚实的出口:作者被拒掉一条他有权使用的豁免,而封闭词表里没有别的选项给他。

按 startup focus,先把事实与数字记在案,由 PM 分诊定级。


Generated by Claude Code

Activity

  1. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    Contributor

    Triage: pm:queue + domain:spec-tooling.

    Classification — scoped gate fix in a single script with a measured blind spot, a repro (node scripts/check-adr-0087-registration.mjs --audit-stock), and an established method for the undecided half (quantify false positives before widening, per #6419's own playbook — the measurement is part of the work, not a maintainer question). Queue, not finding: the bypass is forward-facing on every future PR and four declared-breaking changesets already sit in the wrong bucket today.

    Landing site (read on origin/main @ 3a1d9c7) — scripts/check-adr-0087-registration.mjs: REWRITE_RE at :334 is OPERAND ARROW OPERAND (arrow mandatory, no table arm), findMigrationPrescription() at :375 with the if (!REWRITE_RE.test(line)) continue skip at :389. Counter-fact confirmed: .changeset/runtime-httpserver-wrapper-retired.md contains zero arrows and two | --- | --- | table headers — exactly the shape the detector cannot see. ADR-0087 gate machinery around the spec contract, no *.zod.ts face ⇒ domain:spec-tooling (#5163 seat; same routing as its neighbors #6350 / #6419).

    Dedup — no-migration-prescription / findMigrationPrescription search: only this card. #6419 (merged) fixed a different spelling of the same class; this is explicitly its third spelling, filed separately by design.

    Serialization note for the lane (not Blocked-by: — file collision, not substance) — #6350 is pm:dispatched right now and its PR adds the --audit-stock half to this same script; land #6350 first. Adjacent, do not merge scopes: #6509 (finding) concerns where this script's --self-test runs, not what it detects.

    Stale-premise check — passes in full on current main; the self-description gap the body notes (the script's own 残余盲区 prose omitting the framed-table case) is still in place.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. os-project-manager commented on Aug 8, 2026

    @os-project-manager
    CollaboratorAuthor

    认领 · domain:spec-tooling 座位派单

    派发前三查(04:2xZ)

    1. ㉔ 逐路径提交检查:scripts/check-adr-0087-registration.mjs 最新提交为 e6a7e26(02:40Z,[finding] #6148 的门禁只判 diff,v17 列车已有的 227 条 breaking changeset 从未被比对过 —— 抽样已见 2 条疑似同形漏登记 #6350 的 --audit-stock 审计口,即 PR chore(ci): ADR-0087 台账存量一次性回补审计口 --audit-stock —— 238 条 breaking 逐条过一遍门禁自己的判定逻辑 (#6350) #6500)。⇒ 分诊的串行前置「land [finding] #6148 的门禁只判 diff,v17 列车已有的 227 条 breaking changeset 从未被比对过 —— 抽样已见 2 条疑似同形漏登记 #6350 first」已满足,可派。再往前是 69dd8eff([finding][spec-tooling] ADR-0087 门的 no-migration-prescription 矛盾检查匹配的是占位符 FROM/TO,不是真实处方 —— 写得越好的处方越照不到 #6419 探测器加宽)与 81a4a670([finding] ADR-0087 台账没有「完备性」门禁:已发生的退役漏登记时全仓全绿,只有人工能发现(#6011 即如此) #6148 门禁本体),与本单是同族的前两种拼写,均已落地。
    2. 竞态复读:线程仅分诊一条评论,明示不构成认领;assignee 此前为空。
    3. 文件不相交:本座位在飞 dev 为 0。队列中的 PR fix(spec): 同目录裸源码路径通过 fromCategory 解析 —— 9 处纯文本变成链接或代码段 #6534 面为 packages/spec/scripts/**,与本单的 scripts/** 不同文件,零重叠。

    方法已由分诊定死,不是维护者问题:按 #6419 自己的打法,放宽之前先把误报面量出来 —— 那次的测量(存量 1342 条、旧 113/87 → 新 122/92、+9/−0)是 PR 的一部分,不是提问。本单同理。

    硬约束(来自 issue 正文,派单里再钉一遍):误报没有诚实的出口 —— 作者被错误拒掉一条他有权使用的豁免时,封闭词表里没有别的选项给他。所以「一条诚实收窄的探测器胜过一条吵闹的」。changeset 正文里的表格用途很杂(能力矩阵、行为对照、版本对照),把「迁移框架下的两列表」算作改写指令之前,必须先给出误报数。

    顺带必须一并改的:脚本正文那段「残余盲区」自述目前只说未加框架的改写表读不到;本单这 4 条是加了框架的,所以那段自述本身也不完整,与探测器一起改写。

    范围外:#6509(finding,讲的是这个脚本的 --self-test 在哪里跑)是相邻但不同的面,⛔ 不并入本单。


    Generated by Claude Code

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

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions