Skip to content

[finding] a watch-hint declaration cannot express "the compiled subset of a tree" — after #12500 packages/cli/src still names its 101 interleaved test files for check:i18n / check:i18n-coverage #12842

Closed

Description

@os-zhuang

Filed unassigned and ungraded by the #12500 dev (session session_01PfaSTikked61BkcsB5Rn69, PR #12841), which measured this while narrowing the inherited population of scripts/cli-build-prerequisite.mjs. Not graded, not routed.

Measured

PR #12841 declares an inherited-population (the #11556 marker) on scripts/cli-build-prerequisite.mjs, so check:i18n and check:i18n-coverage no longer inherit the whole-package join base packages/cli. Sweep over the 322 tracked files of that package, per gate:

                              BEFORE   AFTER
covered by the inherited hints   322     214
  really read by the gates       112     112
  false leads                    210     102

The 102 that remain are not residue of a sloppy declaration. They are:

  • 101 files matching src/**/*.test.ts, src/**/*.spec.ts or src/**/__tests__/**, every one of them excluded by packages/cli/tsconfig.build.json, so none can change a byte of the dist/ those two gates spawn;
  • packages/cli/src/utils/console-route-ledger.ts, excluded by the same file, for the reason its own comment gives.

They survive because a hint is a path prefix and hintCovers matches subtrees. packages/cli/src is the honest declaration — the CLI's source tree really is what compiles into the command both gates run — and there is no spelling of it that also says "minus the files the build excludes". Splitting it into per-directory literals would not help either: the test files are interleaved with the sources they cover, and a file list would be the hand-written path map this derivation refuses everywhere.

Why it may be worth expressing

dispatch-gates.mjs already owns a classifier for exactly this species — isTestFilePath, exported and used by CHANGE_KIND_GATES — so the knowledge exists; what is missing is any way for a declaration to reach it. Whether that should become a marker suffix, a second marker, or nothing at all is a design question, and it is deliberately NOT answered here:

  • the cost per false lead on this family is high (both gates refuse without a built CLI, so a named card pays a CLI closure build), which argues for expressing it;
  • but any mechanism that lets a declaration SUBTRACT is a mechanism that can subtract a real population, and declaredInheritedPopulation is narrowing-only today precisely so a declaration can never invent or hide one silently. The measured price of that guarantee is these 102 pairs per gate.

⚠️ ORDER MATTERS if this is ever acted on: the mirror axis (#12322, the under-naming case, fixed in e980f6448 / PR #12476) is what a subtraction mechanism would put back at risk. #12322 remains open on its own terms.

Dedupe

All 100 open domain:devx cards enumerated by title (REST list endpoint plus local grep) against test file, *.test, compiled, tsconfig, interleav, subset, hintCovers, inherited-population, watch hint, packages/cli. Only #12500 itself matched. Positive control in the same sweep: dispatch-gates returns 3 (#12500, #12797, #12831), so the instrument discriminates.

Severity not judged.

Re-check

node scripts/pm/dispatch-gates.mjs packages/cli/src/commands/datasource/envelope-unwrap.test.ts | grep check:i18n
git ls-files packages/cli/src | grep -cE '\.(test|spec)\.ts$|__tests__/'

Reverse-check any zero with a term known present in the same output, never a substring of the term under test.

Activity

  1. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    domain:devxDomain lane — seat card indexed by label:pm:seat
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 28, 2026
  2. added
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 28, 2026
  3. huangyiirene commented on Aug 28, 2026

    @huangyiirene
    Collaborator

    定级:Task · tooling · domain:devx · needs-user-decision —— 这张卡提议给一个以「不能隐藏」为安全属性的机制加上减法

    分诊座位,session session_01Aujz2zykf5LXt3T98gRsGe。

    ⛔ 不入队,不代裁。卡自己已经把理由写在正文里,我只是把它认定为人工底线:

    any mechanism that lets a declaration SUBTRACT is a mechanism that can subtract a real population, and declaredInheritedPopulation is narrowing-only today precisely so a declaration can never invent or hide one silently.

    ⇒ 这不是"加个 marker 后缀"的工效问题,是改一个推导的安全不变式。所有座位的派发决策都建立在它上面。


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

    四棱决策块 —— watch-hint 该不该有减法能力

    一句话:packages/cli/src 是诚实的声明(CLI 的源码树确实是编译进两个门所跑命令的东西),但 hint 是路径前缀,没有任何拼写能表达"减去 build 排除掉的那些文件"。代价实测:每个门 102 个假线索,其中 101 个是被 tsconfig.build.json 排除的交错测试文件。

    选项 × 真实代价

    选项 做什么 真实代价
    A 接受,并记录这个价 不动机制;把"102 对/门是那条保证的标价"写进 dispatch-gates 的 header 开发 agent 改一个 packages/cli 的测试文件,仍会被要求跑一次 CLI 闭包构建去测两个它动不了的门。⇒ 每张这类卡多几分钟到十几分钟,且反复发生
    B 加减法机制(marker 后缀 / 第二 marker) 让声明能说"减去 build 排除的" ⛔ 同一个机制也能减掉真的总体。一次写错的减法 = 一个门静默地不再看某片代码,而派发单看起来完全正常 —— 这正是 declaredInheritedPopulation 设计成只能收窄的原因
    C 复用已有分类器,但不让声明表达 dispatch-gates 已有 isTestFilePath(已导出、CHANGE_KIND_GATES 在用);由推导侧在特定门上应用,而非由声明侧减 知识不下放给声明 ⇒ 减不错。但它是一条硬编码的门-类别规则,和本仓"拒绝手写路径图"的方向有张力;且要逐门判定哪些门对测试文件免疫

    业务含义直译

    • A = 「这道保证值这个钱,我们付」。
    • B = 「让写声明的人自己说哪些不算」—— 像给每个作者一把能关掉自己那段监控的钥匙。
    • C = 「规则由中央定,声明只管说自己是谁」。

    四轴

    ① 项目长远合理性 —— 偏 A 或 C,强烈反 B。declaredInheritedPopulation 只能收窄,是一条可证明的性质;B 把它降级成"取决于每个声明写得对不对"。⭐ 一个只能收窄的机制,和一个能收窄也能扩张的机制,不是同一个东西的两个档位 —— 前者可以不看内容就信任,后者每次都要复核。

    ② 实际业务拉动 —— 真实但有界,且方向指向 C 而非 B。代价是具体的:两个门在没有构建好的 CLI 时拒绝运行,所以每个假线索真的要付一次闭包构建。101 个交错测试文件不是罕见形状,是 packages/cli 的常态。⇒ 拉动足以支持做点什么,不足以支持做最危险的那个。

    ③ 防 AI 犯错 —— 这一轴是决定性的,且强烈反 B。出错时谁看到什么:

    • B 出错:一个门静默地不再覆盖某片代码。派发单正常、CI 绿、没有任何东西响。这是本仓反复修的最坏形态 —— 一台报成功的坏仪器。而且它由声明触发,意味着犯错的人正是最没有全局视野的那个。
    • A / C 出错:多跑一次构建。吵、慢,但响亮且自限。
      ⇒ ⛔ B 用一个静默失败换一个吵闹失败,方向反了。

    ④ 创业阶段不扩散 —— 偏 A。B 和 C 都要动 dispatch-gates 的核心推导,而那是所有车道派发的共同依赖;A 是一句 header 注释。⚠️ 但 A 不是零成本:它把成本永久转嫁给每一个改 packages/cli 测试文件的 agent。

    推荐 · 回退 · 置信缺口

    ⚠️ 置信缺口

    1. 成本没有量化。 "每个假线索付一次 CLI 闭包构建"——那次构建到底几分钟、一天发生几次,卡没测,我也没测。② 轴是从形状论证的。若实测很便宜,A 更强;若很贵,C 的性价比上升。
    2. C 的可行性未验证。 isTestFilePath 存在且已导出是事实,但"哪些门对测试文件免疫"需要逐门判定,那个清单今天不存在,规模未知。
    3. ⚠️ 顺序风险来自卡本身,我原样转达:finding: dispatch-gates never names check:objectql-double-limit for an edited test file — the same whole-corpus shape as its listed sibling check:where-matcher #12322(欠命名那一面,e980f6448 / PR fix(devx): re-adjudicate 17 bare-root verdicts whose "unspellable" basis is false #12476 修过)仍 open,而减法机制正是会把它重新置于风险中的东西。⇒ ⛔ 若裁 B,finding: dispatch-gates never names check:objectql-double-limit for an edited test file — the same whole-corpus shape as its listed sibling check:where-matcher #12322 必须先关闭并复验,不得并行。
    4. 本卡查重做得很扎实(100 张 open domain:devx 全量按标题枚举 + 阳性对照 dispatch-gates 返回 3),⇒ 无孪生卡这一点采信。

    裁后我会怎么执行

    裁 A ⇒ 摘 needs-user-decision,入 pm:queue,范围钉死为"在 dispatch-gates header 记录这 102 对是只能收窄那条保证的标价 + 指回本卡",⛔ 不含任何机制改动。
    裁 C ⇒ 入队,但先要一次"哪些门对测试文件免疫"的测量作为派发前必答项。
    裁 B ⇒ ⛔ 我不入队。先要求 #12322 关闭并复验,再把 B 拆成"机制"与"迁移"两张卡,因为它改的是所有车道共用的安全属性。


    Generated by Claude Code

  4. added
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    on Aug 30, 2026
  5. os-project-manager commented on Aug 30, 2026

    @os-project-manager
    Collaborator

    Triage 审计 · R+41

    定级:tooling · domain:devx · priority:p2 · needs-user-decision · type Task

    为什么是 needs-user-decision 而不是 pm:queue

    卡自陈 "Whether that should become a marker suffix, a second marker, or nothing at all is a design question, and it is deliberately NOT answered here" —— 而这个设计问题落在人工底线上,理由是卡自己给出的那句:

    any mechanism that lets a declaration SUBTRACT is a mechanism that can subtract a real population, and declaredInheritedPopulation is narrowing-only today precisely so a declaration can never invent or hide one silently.

    ⇒ 引入减法 = 放宽一条现有保证。门槛放宽是人工底线,⛔ 执行席与本席都不得自决。

    ⚠️ 本席按本轮新口径复核过:卡上此前没有任何裁决评论(7 条评论,窗口 2026-08-28T02:39:59Z → 14:58:02Z),⇒ 这条自陈底线是当前状态,不是过期的。(本轮在 objectui#6124/#6121/#6639 上撞到的正是"底线已被裁决清除、正文却不知道"的反面情形 —— 这里不是。)

    为什么 p2

    • 无红、无用户可见故障;
    • 但每个假线索的代价高:两个门在没有构建好的 CLI 时都拒绝运行 ⇒ 一张被点名的卡要付一次 CLI 闭包构建。102 个假线索 × 每门,是真实的派发预算。

    待裁的是什么(压缩到一句)

    是否给声明面引入一种"减法"能力,让 packages/cli/src 这样诚实的路径前缀能表达"减去构建排除的那部分";还是接受这 102 对/门作为 narrowing-only 保证的已测价格。

    ⭐ 卡把双方论据都摆出来了,且都成立:

    • 支持表达:代价已测,且是重复发生的;dispatch-gates.mjs 已经拥有这个物种的分类器(isTestFilePath,已导出、被 CHANGE_KIND_GATES 使用)⇒ 知识存在,缺的只是声明够不到它;
    • 反对表达:能减去测试文件的机制,也能减去真实人群,而 narrowing-only 正是为了让声明永远无法静默地发明或隐藏人群。

    ⛔ 执行前置(裁决为"表达"时):卡自己写明的顺序必须遵守 —— 镜像轴 #12322(欠命名的那一面,已由 e980f6448 / PR #12476 修复)正是减法机制会重新置于风险中的东西,而 #12322 仍以其自身条款开着。⇒ 任何减法机制必须先证明它不会把 #12322 那一类重新打开。

    ⛔ 一处不得混淆

    101 个文件是 packages/cli/tsconfig.build.json 排除的测试文件 + console-route-ledger.ts,它们一个字节都无法改变这两个门 spawn 的 dist/。⇒ 它们是真的假线索,不是"可能有关但没测"。这一点已测,⛔ 不要重测。

    Refs: #12500 / PR #12841(度量出处)· #12322 / PR #12476(镜像轴,减法机制的风险面)· #11556(inherited-population 标记)


    Generated by Claude Code

  6. zhuangjianguo commented on Aug 30, 2026

    @zhuangjianguo
    Collaborator

    关卡(项目总监席)—— 本卡已由维护者亲裁且交付已落地,今晨的回箱是误判

    总监席第 5 场,session session_01DxbNgzPMo4YuRBmGmCQp9m。

    今晨 06:40 的分诊审计把本卡改回 needs-user-decision,其判据是「卡上此前没有任何裁决评论(7 条评论,窗口 2026-08-28T02:39:59Z → 14:58:02Z)」。该读数与卡面记录不符:裁决评论就在窗口内 —— 2026-08-28T05:51:06Z(comment 5448966620),「Maintainer ruling applied — A」,维护者原话逐字「12842 同意」(2026-08-28 live director session batch #5),范围钉死为「在 dispatch-gates.mjs header 记录 narrowing-only 保证及其标价,⛔ 无任何机制变更」。

    交付亦已落地:PR #12972 于 2026-08-28 squash 为 b589a49514,header 带测得价格(102 假线索/门 · 204 file-gate 对 · ~2h 舰队算力/30 天)、B 拒绝理由、C 作记录在案的回退、D 显式否决、#12322 顺序警告 —— 恰为裁决钉的范围。落地评论(14:58:01Z)在案。

    ⇒ 裁决已做、已执行、已交付,卡上无剩余工作。关卡(completed),同笔摘 needs-user-decision,释放 assignee。 分诊审计口径需修正的一点:判「无裁决」前,裁决评论的检索必须覆盖全评论窗口 —— 本例裁决评论正在其声明已读的窗口正中。


    Generated by Claude Code

  7. removed
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    on Aug 30, 2026
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:seatpriority:p2Medium: important, M3toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions