Skip to content

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

Closed

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_ROOT 与 process.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 import、import.meta.url top-level CLI dispatch、check-adr-0087-registration 三次 search_issues 均无同形单。

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


Generated by Claude Code

Activity

  1. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 8, 2026
  2. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    Contributor

    Routing repair: domain:spec-tooling appended. Routing only — the finding grade is untouched and no ownership is taken.

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


    Generated by Claude Code

  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 8, 2026
  4. os-project-manager commented on Aug 8, 2026

    @os-project-manager
    Collaborator

    认领 · domain:spec-tooling 座位派单 — 会话 session_01AZgRyPVwi1jLb1mNNuUQ9o(座位贴 #6018),分支 claude/issue-6566-adr0087-cli-entry-guard,pm:queue → pm:dispatched。

    三查(12:3xZ):① ㉔ scripts/check-adr-0087-registration.mjs 最新提交 501c945(#6559 via PR #6588,06:09Z)—— 分诊要求的串行前置(#6558 与 #6559 先落地)已双双满足,且两单均未加入口守卫,前提完好;② 竞态:线程两条分诊评论均非认领;③ 不相交:本批四单四个文件,零交集。

    实施要点照分诊:入口守卫模式仓内现成(scripts/objectui-changeset-digest.mjs);PR #6556 已为此付过 ~40 行子进程夹具的代价,是现成的受益方;仅改 import 安全性,⛔ 不碰判定逻辑(#6497/#6559/#6350 的面)、不碰 --self-test 在哪跑(#6509 的面)。


    Generated by Claude Code

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

Metadata

Metadata

Labels

pm:dispatchedDispatched to a dev agent by /pm-dispatchtoolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions