chore(ci): ADR-0087 台账存量一次性回补审计口 --audit-stock —— 238 条 breaking 逐条过一遍门禁自己的判定逻辑 (#6350) - #6500
Conversation
#6148 的门禁是**前向**的(#6129 的理由,本单不推翻它),代价是 v17 列车 已有的 238 条 breaking changeset 一条都没被同一套逻辑看过。本次给同一个 脚本加一个**算子调用**的一次性审计口,把门禁**自己的**判定函数 (breakingDeclaration / readDisposition / findMigrationPrescription / workspacePackagesAt)对着存量跑一遍 —— 没有第二份逻辑,只有换一批被判对象。 刻意不接 CI:`package.json` 的 `check:adr-0087-registration` 与 Check Changeset 工作流都是不带旗标地调门禁,`--audit-stock` 只能手敲。它是 `--list` 的邻居,不是第二道门。 分桶把 238 收窄成人可核的残差:answered / exempt-unpublished / exempt-no-prescription / RESIDUE(已发布的破坏 + 正文带改写指令 ⇒ 只剩 registered / already-registered,两者都要台账条目)。残差再叠一条机械 事实:动过这条 changeset 的 commit 里有没有哪个同时动了台账源。 浅克隆下该信号会**说谎**(嫁接 commit 携带整棵树、含两个台账源,实测 104 commit 的默认检出把 92 条残差里的 91 条报成「作者登记过」,条条错), 因此浅克隆时**抑制**而不是近似 —— 按 #4690 的口径,报不出来就说报不出来。 self-test 加 S1-S6 钉住分类器四个臂与其间的优先级;反向验证:删掉 `bumps.length > 0` 守卫后恰好 S5 转红(`[].every()` 真空为真)。
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
PM 验收:仪器这一半通过,已开启自动合并进队列。 判据问题已升维护者,#6350 保持 open(本 PR 用 独立复核过的四点
一条工具链陷阱值得所有座位记下:容器默认检出是浅克隆(104 commits,graft 于 未回补台账是遵守派单、不是偷懒。 派单原文写了例外条款:「若审计发现的真实漏登记多于少数几条,先报数量与形态」。实测 92 条残留里 51 条从无登记提交(剔除带墓碑/D2 转换的还剩 34),远超少数几条,且每一条的判定都悬在同一个未答的判据问题上 —— migrations registry 是声明为永久、发布给消费者的产物,错加一条的撤回成本很高。先问后写是对的顺序。 抽样三条已落到结论,并且推翻了 issue 自己的排序:穷举两个台账确认三条均无覆盖条目;但 另记一条本 PR 之外的发现 #6497(live bypass,不是观察级): Generated by Claude Code |
Refs #6350
Fixes。 本 PR 落地的是 #6350 的器械半边(一次性存量审计口),没有动任何一条台账。原因见下「为什么这一版不带回补」—— 抽样那 2 条以及审计新翻出来的另外 49 条,判定全部悬在同一个未回答的口径问题上,而登记决定是维护者的。#6350 需要保持 open 直到那个口径被裁定,否则 51 条待判清单会随 issue 一起消失。一、落地了什么
scripts/check-adr-0087-registration.mjs新增--audit-stock,把 #6148 门禁自己的判定函数(breakingDeclaration/readDisposition/findMigrationPrescription/workspacePackagesAt)对着存量跑一遍。这里没有第二份逻辑 —— 只是换一批被判对象。这一点是刻意的:审计若自己抄一份判定,它就会和门禁各自漂移,那样得出的清单谁也不敢信。
刻意不接 CI,也不该接。
package.json的check:adr-0087-registration与 Check Changeset 工作流都是不带旗标地调门禁;--audit-stock只能手敲。它是--list的邻居 —— 算子按需跑的常设审计面 —— 不是第二道门。⛔ 把门禁改成回溯全量比对是 #6129 明确拒绝的那条路(会给每个 PR 加上与其改动无关的历史负担),本 PR 一个字都没往那个方向动:scan()与 CI 的调用点原样未改。分桶:238 收窄成人可核的残差
存量 changeset 一条都没有处置标记(从来没有东西问过它们),所以把门禁逐字重放只会把 238 条全报一遍、什么也没说。有信息量的是它本来有资格拿哪条处置:
private: true,破坏不出门registered/already-registered,两者都要台账条目残差再叠一条机械事实,把「作者确实登记过」和「从来没人问过」分开:动过这条 changeset 的 commit 里,有没有哪个同时动了台账源?这正是 #6350 手工抽样用的信号,只是算到了全量而不是三个点上。结果:41 条动过台账(标
~,需核对该条目是否覆盖本次面),51 条一次都没动过(标!)。一条踩到的坑,已写进脚本
浅克隆下这个信号会说谎,而且谎得很像真的:嫁接历史里每个 pre-graft 文件都读作「由嫁接 commit 添加」,而那个 commit 携带整棵树、含两个台账源。本容器默认检出就是浅的(104 commit,graft 在
2bc187641,该 commit 5978 个文件),实测 92 条残差里 91 条被报成「作者登记过」—— 条条都是错的,而输出在纸面上完全合理。所以浅克隆时该信号被抑制,而不是被近似:打印
?与一段说明,并告诉算子跑git fetch --unshallow --filter=blob:none(只读 tree、不拉 blob 就够)。照 #4690 的口径 —— 报不出来就说报不出来,不许拿一个像样的答案顶上。self-test
新增 S1-S6 钉住分类器四个臂与其间的优先级(
unpublished优先于 prescription;不声明任何包的 changeset 买不到 unpublished 豁免 —— 门禁本身也拒)。反向验证(事前判定方向:删守卫 ⇒ 恰好 S5 转红):删掉
parsed.bumps.length > 0守卫后,[].every()真空为真,空 frontmatter 的 changeset 会被静默判成 exempt。实测恰好 S5 一条转红,方向与事前判定一致。二、抽样那 3 条:逐条落到结论
先说事实层(穷举 grep 两个 registry,不是抽查):
runtime-httpserver-wrapper-retired.md(#5122)HttpServer仅 2 处,均为别的条目正文里顺带提到HttpServerConfig(#4938)record-details-sections-object-form.md(#5611)RecordDetails/hideFields/record:details两个 registry 各 0 命中data-driver-query-omit-object.md(#5181)DriverQuery仅 1 处,在 aggregate 别名条目正文里(作为参数变成的类型),不是被登记的面;IDataDriver的命中全是findStream与无关行三条都没有覆盖它们的条目。 这一层是可核的事实,#6350 的抽样成立。
判定层见第三节 —— 三条的方向不一致,而且分歧全部来自同一个口径。
三、为什么这一版不带回补(#6350 派单里那条例外条款)
派单写明:「若审计发现的真实漏登记多于寥寥几条,不要闷头批量修 —— 先报数量和形状」。实测 51 条(去掉正文自带 tombstone / D2 conversion 的 17 条后仍有 34 条),不是 2 条。数量已经改变了这件事的性质。
更要紧的是:51 条(含抽样那 2 条)的判定全部悬在同一个未回答的问题上。 台账里五条同族条目(
data-driver-find-stream-retired#4484、storage-service-list-retired#5540、actor-user-roles-to-positions#6011、spec-type-alias-input-suffix-retired#6083、driver-aggregate-undeclared-key-aliases-removed#6321)各自写下了两条触发口径,措辞几乎逐字相同:按 T1/T2 的字面读法,
HttpServer退役(T2:TypeError不会说「改成直接注册适配器实例」)与RecordDetails.sections(#5611 当时properties根本不被 parse ⇒ T1;#5068 接上门后 safeParse 报expected object也不说「每个 ID 换成{ fields: [...] }」⇒ T2)都该登记;而IDataDriver那条反而最弱 —— 它的运行期行为零变化,无类型 JS 调用方多传object:会被静默忽略、什么都不用做,TS 侧 TS2353 又逐字点名了要删的键(对「改成什么」并不哑)。同一条口径,三条给出三个方向,而按字面读法要补的是 ~34 条而不是 2 条。 台账是被声明的 forever artifact(每次发布 CI 全链重放,条目喂
migrate meta/spec-changes.json/ 已发布的升级指南),一条错加的条目会直接发到消费者手里且很难撤回。这个不对称让「先问」明显优于「先猜」,故本 PR 不带回补,问题连同三轴分析回给维护者裁。四、顺带发现,已另立单:#6497
#6350 抽样的第 1 条(
runtime-httpserver-wrapper-retired.md),门禁的探测器今天根本够不着它。 它的迁移说明写成## Migration标题下的无箭头两列表,而findMigrationPrescription()两条分支都要求出现箭头 —— 于是它落进exempt-no-prescription,压根进不了残差面(它是人眼发现的,不是门禁)。这是前向缺口而不只是存量问题:今后任何 PR 把迁移说明写成表格,就能合法写下
not-required (no-migration-prescription)并被批准 —— #6419 那个形状的第三种拼写。实测存量 13 条带此形状,其中 4 条声明为 breaking 且正靠这条盲区拿豁免。按派单口径,门禁本身的改动另立一单,不在本 PR 内落地 ⇒ #6497。
五、验证(真实输出)
检测器自身的标定:审计在没有被告知的情况下,把
tidy-donkeys-yawn.md(#6048,唯一已确证的那次漏登记)独立地标成了!。它同时说明!不等于漏登记 —— 该面早已由 PR #6138 另行补登,今天正确的处置是not-required (already-registered actor-user-roles-to-positions)。六、changeset
没有,故需
skip-changeset:本 PR 只改一个仓内工具脚本(scripts/**),不发布任何东西,读者/消费者可见面零变化。标签已由本座位手动打上。🤖 Generated with Claude Code
https://claude.ai/code/session_01AZgRyPVwi1jLb1mNNuUQ9o
Generated by Claude Code