Skip to content

[finding] check-adr-0087-registration.mjs 的 CLI 派发在模块顶层执行 —— 任何 import 都会真的把门跑一遍,判红时还会 process.exit(1) 掐死调用方 #6566

Description

@hotlong

发现于 #6494 / PR #6556 实施期间(devx 车道执行座位)。按 Prime Directive #10 只记录不修,未认领不自评级别(定级是分诊座位的单一通道)。

事实

scripts/check-adr-0087-registration.mjs 的 CLI 派发写在模块顶层,没有 import.meta.url === process.argv[1] 这类入口守卫(同仓 scripts/objectui-changeset-digest.mjs 末尾就有一个)。文件尾部形如:

const argv = process.argv.slice(2);
if (argv.includes('--self-test')) { selfTest(); }
else if (argv.includes('--list')) { list(REPO_ROOT, 'HEAD'); }
else if (argv.includes('--audit-stock')) { auditStock(REPO_ROOT, ...); }
else { /* 真正的门:解析 base、assertInputs、scan、打印判决 */ }

四个分支没有一个是 no-op。因此 import 它 = 运行它(对导入方进程的 REPO_ROOTprocess.argv)。

实测(不是从代码形状推断的)

临时仓 + 从 origin/main 取出的门脚本副本,写一个只想借一个纯函数的消费方:

// probe.mjs
import { readDisposition } from './scripts/check-adr-0087-registration.mjs';
console.log('PROBE OWN OUTPUT: readDisposition ->', JSON.stringify(readDisposition('nothing here')));

第一次(门无可判) —— 门的判决先于消费方自己的那行打出来:

=== everything below this line came from a bare `import` of the gate module ===
✓ check-adr-0087-registration: this PR adds no declared-breaking changeset (0 non-breaking changeset(s) seen).
PROBE OWN OUTPUT: readDisposition -> {"ok":false,"reason":"no `adr-0087:` disposition marker"}
PROBE_EXIT=0

第二次(同一个 import,但仓里有一份声明破坏、没带标记的 changeset,默认 base 落在 HEAD 之前) —— 门判红并 process.exit(1)消费方自己的代码一行都没执行到

✗ check-adr-0087-registration: 1 problem(s).

  • .changeset/new-breaking.md
      declares a breaking change (major, BREAKING) but no `adr-0087:` disposition marker.
      …
PROBE_EXIT=1
--- did the importer's own line ever run? ---
NO — the gate called process.exit(1); the importer never reached its own code

即:导入方拿不到函数、拿不到控制权、还会被按门的判决决定退出码,而门判的是导入方的 REPO_ROOT,与导入方想问的问题无关。

这正是 PR #6556 绕开它的原因

#6494 要让 objectui-changeset-digest.mjs 生成的 changeset 携带一份「门仍会判红」的 ADR-0087 处置脚手架。想证明「门确实把这份占位认成 present-but-unanswered」,最直接的写法是 import 门的 parseChangeset / breakingDeclaration / readDisposition —— 一个判据、两个消费方、不会漂移,正是本仓一贯偏好的形状(objectui-range.mjs 与 digest 共用 classifyRange() 就是先例)。

这条堵死了这个写法。PR #6556 改为子进程 + 临时仓驱动门的二进制来钉一致性。附带一条也值得记:即便可以 import,也要权衡「给发布关键路径(bump-objectui.sh 调用链)加一个『门被改名/移动就崩』的耦合点」;两条理由叠加,子进程是对的,但第一条本不该存在

代价

一个不能被安全 import 的门,等于强迫每一个想与它保持一致的调用方自己起进程。 具体开销:

  • 每个消费方都要自建临时仓 + 复制门脚本 + 造够 assertInputs 的输入(两份 ledger 源、spec-changes.json、一份存量 breaking changeset),只为问一句「这段正文算不算带了处置标记」。PR fix(scripts): objectui digest 声明破坏性时写入门仍判红的 ADR-0087 处置脚手架 (#6494) #6556 为此写了约 40 行 fixture。
  • 这些 fixture 复制了门的输入契约:门以后改输入要求,邻居脚本的自测会跟着红。子进程把耦合从「模块路径」换成了「输入契约」,没有消灭它。
  • 反向作用同样存在:任何工具(未来的门、报表、审计脚本)想复用 extractIds / hasMigrationPrescription / readDisposition 这些明显是纯函数的导出,都会踩同一颗雷,而且踩到时的现象是「我的脚本莫名其妙打印了一段 ADR-0087 判决然后退出 1」,第一眼根本不像 import 的锅。

它们全部导出了(export function readDisposition 等),形状上就是给人复用的;只是现在没有一个可以被使用

相邻(非重复)

去重检索:adr-0087 importimport.meta.url top-level CLI dispatchcheck-adr-0087-registration 三次 search_issues 均无同形单。

发现会话:session_01BDmDsu2575gDxeMCxXhDE3(devx 车道执行座位)。严重度与路由留分诊座位判定。


Generated by Claude Code

Activity

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