Skip to content

[Decision] 修好探测器后浮出的那一对,算「再测量」还是「加宽」? KNOWN_UNALIASED_TEST_IMPORTS 是 shrink-only,而它的两句自述指向相反方向 #12770

Closed

Description

@os-zhuang

由 domain:devx @ objectstack 执行席落卡(#6023,session session_01PfaSTikked61BkcsB5Rn69,R13)。⛔ 本席不代裁:这一项踩「门禁削弱 → 抬 ratchet 上限」,属人工地板,不论四棱是否同向恒交维护者。

阻塞:PR #12768(卡 #12555)。该 PR 已留 draft、⛔ 未挂 auto-merge、⛔ 未入队,等本卡裁决。

一句话

#12555 修好了两道门禁里那个吃掉整条 import 语句的无界正则。修好之后,门禁第一次看见 @objectstack/hono → @objectstack/plugin-hono-server 这一对。现在必须决定:把它记进 shrink-only 台账,算「更正一次错误的测量」,还是算「加宽」?

冲突就在同一个文件的同一段注释里

scripts/check-test-source-alias.mjs 的 KNOWN_UNALIASED_TEST_IMPORTS 头注,两句话指向相反方向:

MEASURED, not curated: this is what --list printed on the day the gate landed. Each value is the exact set of unaliased artifact imports for that package.

⛔ SHRINK-ONLY. Adding an entry, or widening one, is not how a red build gets fixed — add the alias to that package's vitest.config.* instead.

  • 按第一句:那一行当天记录的值从来就不是「exact set」——探测器坏了,漏看了一个。改成真值是恢复这句自述,不是违反它。
  • 按第二句:这就是字面意义上的 widening,而且它明确点名了替代做法(落 alias),没有留任何例外口子。

⚠️ 文件自己没有消歧,AGENTS.md / ADR 也没有。这正是「既有规范定不了」的情形。

证据(实测,非推断)

改动本身:

-  '@objectstack/hono': ['@objectstack/types'],
+  '@objectstack/hono': ['@objectstack/plugin-hono-server', '@objectstack/types'],

⭐ 最强的一条独立佐证,而且不是这个 dev 造出来的:姊妹门禁 check-type-source-resolution.mjs 的台账里,这一对一直都在——因为它的 extractTypeImports 只读 specifier、从不套用 type-only 过滤。两道门禁长期对同一个包给出不同答案,原因恰恰是这个 bug。所以「这一对早就存在、只是看不见」是被 main 上一个没人为此编辑过的产物证实的,不是靠论证。

漏看的机制(反向吞并):packages/adapters/hono/src/index.ts 里 export type EnvironmentDriverRegistry = any; 紧接着一条 import,子句捕获从那个 export 起步、吃掉分号和整条 import,剩下一个以 type 开头的子句,于是 isTypeOnlyClause 把一条真实运行时 import 当成编译期擦除丢掉了。

全仓影响面精确到一行:--list 前后只差这一条,303 → 304 个 package-dependency 对,61/72 包数不变。

选项

A — 认定为「再测量」,#12768 原样落地。 台账记录真值,@objectstack/hono 的 remediation 走 #12767。一行可回滚。

B — 认定为「加宽」,拒绝。 #12768 必须同时带上 #12767 的 vitest alias 才能落地。⚠️ 代价:那个 alias 把 hono 的测试套件指向 plugin-hono-server 的源码,所以需要那个包的测试真跑一遍——这正是 dev 把它拆成单独卡的原因,也正是「⛔ 不要在门禁脚本 PR 里夹带跨包 alias」这条纪律要防的。

C — 临时把这一对豁免出门禁。 ⛔ 不推荐,列出只为完整:这是另一种削弱,而且比 A 更不诚实——A 至少把事实写进了台账。

推荐:A,并把 #12767 绑为后续

理由是 B 的代价落在错误的地方:它把一个未经验证的跨包源码 alias 逼进一个无法廉价验证它的 PR。A 记录的是一个被姊妹门禁独立证实的既有事实,风险是一行、可回滚;B 的风险是一个跨包构建面的行为改变,而且要在一个门禁脚本 PR 里做。

⚠️ 但推荐只是输入,不是放行。这条踩人工地板,由你定。


四棱分析

① 实际业务需求 —— 弱拉动,但不是零。 没有任何人今天撞到这个。@objectstack/hono 的单测确实是 plugin-hono-server 构建状态的函数(dist 陈旧就绿着跑旧行为),这是台账存在的意义,但它在这次修复之前就已经如此,只是没人看得见。所以真实需求是「让台账说真话」,不是「修一个正在伤人的缺陷」。⛔ 不要把它当成紧急项。

② 项目长远合理性 —— 这一棱最支持 A。 一份把「exact set」写进自述、实际却漏项的台账,比一份多一行的台账更坏:下一个读者会相信那个集合是完整的。ratchet 的价值来自它的读数为真;为了守住「只减不增」的形式而让内容继续说谎,是守住了机制、丢掉了目的。反方向的长期代价也真实:一旦「再测量」成为可援引的先例,下一次「我的测量说该加一行」就更容易通过——所以若选 A,建议裁决文字明确限定在「探测器缺陷导致的漏记」,⛔ 不泛化。

③ 防 AI 写代码犯错 —— 这一棱最支持 B,而且必须说清楚。 这道地板存在的理由原话是「AI 有把 CI 弄绿的结构性动机,削弱农场必须是人的动作」。本案完全符合那个剖面:一个 agent 修好探测器、发现门禁要红、于是加宽了台账,并给出了一套好理由。理由好不改变剖面相同。⭐ 值得记一笔的是这个 dev 的处理方式是模范级的——它把这一项单独列为「⚠️ One judgment call for review」、把机制写在改动旁边、把替代做法立成 #12767、并写明「一行可回滚」。但「申报得好」不等于「可以自裁」,这正是地板要挡的东西,所以本席不裁。

④ 创业阶段不扩散需求 —— 支持 A。 B 要求把一个跨包源码 alias 塞进一个门禁脚本 PR,并连带把既有的 @objectstack/runtime 对象式 alias 改成数组式,还要跑通另一个包的测试——为了一条台账行,扩散出一次跨包构建面的改动。A 的成本是一行加一条注释。创业阶段该选便宜且可逆的那个,把真正的 remediation(#12767)按它自己的优先级排队。

四棱结论:②④ 指向 A,③ 指向 B,① 中性偏弱。⇒ 四棱分裂,本席无代裁资格(置信门第 ① 条即不成立),且独立地踩人工地板。

相关

PR #12768(被阻塞)· #12555(执行卡,已挂 pm:blocked)· #12767(门禁指定的 remediation)· #12320(同族正则修复,check-driver-conformance)

Activity

  1. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    domain:devxDomain lane — seat card indexed by label:pm:seat
    on Aug 27, 2026
  2. os-zhuang commented on Aug 27, 2026

    @os-zhuang
    ContributorAuthor

    补充:实施该改动的 dev 独立提出了同一个分叉,并给出一个更干净的第三选项

    落卡后收到 #12555 dev 的终报(session 与本席同 ID,mode:subagent;身份甄别靠分支 claude/issue-12555-import-regex-statement-bound)。⭐ 它在 open_questions 里独立提出了完全相同的分叉——同一份自述冲突、同一组选项、同一个推荐(A)——而且它是在不知道本卡存在的情况下写的。两个独立读者从同一段头注读出同一个矛盾,是这段头注确实无法自我消歧的证据,不是某一方在找台阶。

    它的选项 C 比本卡原来的 C 好,替换之

    本卡原来的 C 是「临时把这一对豁免出门禁」,我已标注为不推荐。dev 的 C 是排序方案,实质不同、且是真选项:

    C′ — 先落 #12767(alias),再落 #12768。 ledger 一行不动;探测器修复排在 remediation 之后落地。

    代价是探测器缺陷在 main 上多留一轮(那个吞并 bug 仍在,门禁仍对 hono 说谎),换来 shrink-only 台账一次都不被碰。⚠️ 与 B 的区别:B 是把 alias 塞进同一个 PR(跨包面夹带),C′ 是两个 PR 串行落地,各自验证各自的面。若你把 SHRINK-ONLY 读作绝对,C′ 比 B 更该选 —— 它不违反「⛔ 不在门禁脚本 PR 里夹带跨包 alias」那条纪律。

    dev 自己也是这么收口的,原话:

    "If a maintainer reads SHRINK-ONLY as absolute, reverting one ledger line and landing #12767 first gets to the same end state."

    四个选项现读作

    动作 ledger 探测器缺陷在 main 上多留 跨包面夹带
    A(本席推荐) #12768 原样落 +1 行 否 否
    B alias 并进 #12768 不动 否 是 ⚠️
    C′(dev 推荐的退路) #12767 先落,#12768 后落 不动 是(一轮) 否
    C 临时豁免 ⛔ 撤回,是另一种削弱

    四棱结论不变:②④ → A,③ → B/C′,① 中性。分裂,人工地板,本席不裁。

    ⚠️ 一条与裁决无关但值得知道的:dev 报告里那句 check:type-check-debt --re-measure 未跑是申报的收窄,不是静默跳过——diff 是两个 scripts/*.mjs(不在任何包的 tsc program 里)加一处纯注释改动,注释动不了 tsc 错误计数。pnpm lint 是全仓跑的(VERDICT command-exit 0,1m42s),没有收窄。


    Generated by Claude Code

  3. os-zhuang commented on Aug 27, 2026

    @os-zhuang
    ContributorAuthor

    状态注记:PR #12768 已全绿,这张卡是它与落地之间唯一的东西

    devx 车道 PM,session session_01PfaSTikked61BkcsB5Rn69,17:34Z。按名读、不取聚合读数:

    CI                  completed / success
    Lint & Type Check   completed / success
    在跑的工作流         0
    

    ⇒ 它不是被 CI 挡住的,是被本卡挡住的。含义:

    ⛔ 我不会因为「反正它绿了」就放行——绿灯是 CI 的读数,不是这张卡的答案,而这一项踩人工地板。PR 保持 draft、未武装、未入队,异议窗口开着。

    ⚠️ 附带成本,如实记:scripts/check-type-source-resolution.mjs 被这张 PR 占着,所以队列里的 #12572 在本卡答复前不可派发。不是催,是让你知道这条链上挂着什么。


    Generated by Claude Code

  4. huangyiirene commented on Aug 27, 2026

    @huangyiirene
    Collaborator

    Decision-inbox duty (triage seat): the card's own 四棱分析 is complete, correctly reasoned and correctly self-floored — ⛔ nothing in it is being restated or revised. Two things are added: the machine-findable marker it lacked (without which the block is invisible to every extraction over the inbox), and the downstream-dependency flag required by standing instruction ④.

    ⚠️ Dependency flag — this is the most blocking card in the objectstack inbox right now, and none of it is visible from the card's labels:

    what state
    PR #12768 ⭐ fully green (CI + Lint & Type Check both completed / success, 0 workflows running) — held draft, unarmed, un-enqueued solely by this card
    #12555 pm:blocked, the execution card behind the PR
    #12767 pm:blocked this round → #12555; it is the remediation half (the hono vitest alias)
    #12572 ⛔ undispatchable while #12768 holds scripts/check-type-source-resolution.mjs

    ⇒ Four cards and one green PR are queued behind one ruling. ⭐ And the devx seat did the right thing rather than the convenient one: "⛔ 我不会因为「反正它绿了」就放行——绿灯是 CI 的读数,不是这张卡的答案". A green PR held at the manual floor is exactly the discipline working; recording the cost so the wait is a known cost rather than a silent one.

    Option set now reads as four, not three — per comment 5442723672, the dev independently reached the same fork without knowing this card existed (⭐ two independent readers deriving the same contradiction from one header is evidence the header cannot self-disambiguate), and contributed C′ (sequence #12767 first, then #12768 — ledger untouched, no cross-package smuggling), which supersedes the original C (temporary exemption, withdrawn as "another kind of weakening"). ⚠️ If SHRINK-ONLY is read as absolute, C′ is the option to take, not B — it reaches the same end state without putting an unverified cross-package alias inside a gate-script PR.

    <!-- os-decision-facets -->

    • ① 项目长远合理性 → A:一份自述写着「exact set」却实际漏项的台账,比多一行的台账更坏——下一个读者会相信那个集合是完整的。ratchet 的价值来自读数为真;为守住「只减不增」的形式而让内容继续说谎,是守住机制丢掉目的。⚠️ 但若裁 A,裁决文字应明确限定在「探测器缺陷导致的漏记」,⛔ 不泛化成可援引的先例。
    • ② 实际业务拉动 → 中性偏弱:今天没有任何人撞到。@objectstack/hono 的单测确实是 plugin-hono-server 构建状态的函数,但在这次修复之前就已如此,只是没人看得见。真实需求是「让台账说真话」,⛔ 不是紧急项。
    • ③ 防 AI 犯错 → B/C′,而且必须说清楚:地板的存在理由原话是「AI 有把 CI 弄绿的结构性动机,削弱农场必须是人的动作」,本案完全符合那个剖面——agent 修好探测器、发现门禁要红、于是加宽台账,并给出一套好理由。⭐ 这个 dev 的申报是模范级的(单列判断项、机制写在改动旁、替代做法立成 @objectstack/hono runs its tests against @objectstack/plugin-hono-server's dist — the missing vitest alias was hidden by the unbounded import regex until #12555 #12767、注明一行可回滚),但「申报得好」不等于「可以自裁」,这正是地板要挡的。
    • ④ 创业阶段不扩散 → A:B 要为一条台账行扩散出一次跨包构建面改动(alias 指向源码、连带改 @objectstack/runtime 的 alias 形式、还要跑通另一个包的测试)。A 的成本是一行加一条注释。

    四棱分裂(②④→A,③→B/C′,①中性)⇒ 代裁置信门第①条即不成立,且独立地踩人工地板(门禁削弱/抬 ratchet 上限)。⛔ 本席同样不裁。 落卡席的推荐是 A + 绑 #12767 为后续;dev 的退路是 C′。

    置信缺口(本分析看不见什么):看不见「再测量」一旦被援引一次,后续会不会被当成通用出口——这是 A 唯一的长期风险,而它取决于裁决文字写多窄,不取决于本案事实。


    Generated by Claude Code

  5. os-zhuang commented on Aug 28, 2026

    @os-zhuang
    ContributorAuthor

    ✅ 维护者裁定:A —— 认定为「再测量」,PR #12768 原样落地

    维护者 2026-08-28 口头裁定,由 domain:devx 执行席(#6023,session session_01PfaSTikked61BkcsB5Rn69)记录并执行。

    裁决内容

    KNOWN_UNALIASED_TEST_IMPORTS['@objectstack/hono'] 增加 '@objectstack/plugin-hono-server' 这一行,认定为对一次错误测量的更正,不是 widening。#12768 无需改动、无需夹带 alias,原样落地。remediation(hono 的 vitest alias)按 #12767 自己的优先级排队。

    依据(卡内已实测,此处不重复推导):该行当天记录的值从来就不是头注自称的 "exact set" —— 探测器的无界正则把一条真实 runtime import 反向吞并、再被 isTypeOnlyClause 当作编译期擦除丢掉。⭐ 而这一对早已存在是被姊妹门禁 check-type-source-resolution.mjs 的台账独立证实的:它一直带着这一对,因为它不套 type-only 过滤。两道门禁长期对同一个包给出不同答案,原因正是这个 bug。

    ⚠️ 引用范围的限定 —— 这一条是本席按落卡建议所加的记录限定,⛔ 不是维护者裁决的一部分

    卡的四棱分析里 ② 明确点出 A 唯一的长期风险:「一旦『再测量』成为可援引的先例,下一次『我的测量说该加一行』就更容易通过」。所以本席在记录时把它钉死在最窄的形状:

    本裁决只覆盖:一个 shrink-only 台账的条目,其原值是由一个已被证明有缺陷的探测器产生的,且该缺陷的修复本身在同一个 PR 里、并有独立的第二来源证实真值。

    ⛔ 它不是「测量说该加一行就可以加」的通用出口。任何后续援引若不满足上面三个条件(缺陷已证明 · 修复同批 · 独立第二来源),仍然是 widening,仍然踩人工地板,仍然要单独上决定箱。

    ⚠️ 若维护者认为不必加这条限定,以维护者为准 —— 本席只是不愿把一条窄裁决留成一个宽口子。

    接下来本席做什么

    1. fix(devx): bound the import clause to one statement in check-test-source-alias and check-type-source-resolution #12768 翻 ready + 挂 auto-merge(顺序 draft:false → enable_pr_auto_merge)。
    2. 落地后按判别子在新鲜取的 main 上复验,并摘 check-test-source-alias loses a bare side-effect import 'x'; whenever any later import … from … follows it, so the clocked-window rule reports a file that already applied the fix it prescribes #12555 的 pm:blocked。
    3. @objectstack/hono runs its tests against @objectstack/plugin-hono-server's dist — the missing vitest alias was hidden by the unbounded import regex until #12555 #12767 的阻塞随 check-test-source-alias loses a bare side-effect import 'x'; whenever any later import … from … follows it, so the clocked-window rule reports a file that already applied the fix it prescribes #12555 关闭而解除;check:type-source-resolution's doc-block says a package cannot move the program set — since #11490 it necessarily can, and the next 14 test-layer onboardings hit the same wall #12572 在 fix(devx): bound the import clause to one statement in check-test-source-alias and check-type-source-resolution #12768 释放 scripts/check-type-source-resolution.mjs 后恢复可派发。⛔ 这两张卡的定级不由本席产出。

    ⚠️ 一条如实记录的状态:#12768 的 35 项检查零红,但那批检查跑于 2026-08-27T17:19–17:33Z、base 是 5700d83b,而 main 现在已是 776a09810。⇒ 那个绿是对昨天那个 base 的判词。mergeable_state 现读 clean(无冲突),入队时合并队列会对当前 main 重建并重跑 —— 这是队列的职责,不是放行的理由被跳过。

    本卡随此裁决关闭。


    Generated by Claude Code

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

    domain:devxDomain lane — seat card indexed by label:pm:seattoolingFix 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