Skip to content

finding — a PR against a ratcheted file can be textually clean and still blow the budget: PR CI, mergeable_state and git merge-tree are all textual, and none of them reads the merged result #16287

Description

@hotlong

Recording only — unassigned, no claim. Found the hard way on PR #15427 today, and generalised by that PR's own seat.

What happened, with the readings

PR #15427 added one line to AGENTS.md. At the moment it was written the file was 1161 lines against a ceiling of 1162 — headroom 1, and the line fitted exactly.

While it waited for human review, the #15379 rules-only programme compressed the same file to 1058 and, by its own shrink-only discipline, re-locked the ceiling to 1058. That is correct behaviour for a shrink-only ratchet. It also silently took back the single line of headroom the open PR had been paid from.

The PR was then approved and enqueued. It would have been dropped by the merge queue. Measured before that happened:

git merge-tree --write-tree origin/main <head> | head -1   →  TREE
git cat-file -p "$TREE:AGENTS.md" | wc -l                  →  1059
ceiling (check-skill-line-ratchet.mjs)                     →  1058

The PR's seat reproduced the red verbatim on the merged tree: check-skill-line-ratchet: AGENTS.md is 1059 lines; the ratchet ceiling is 1058.

⚠️ Why nothing caught it — all three signals are textual by construction

signal said why it could not see this
the PR's own CI green ran days earlier, against the old file and the old ceiling
mergeable_state clean answers "do the texts conflict", nothing else
git merge-tree no conflict same question, same blind spot

All three read clean, and the merged tree violated a gate. There is no textual conflict here at all: one side deletes lines elsewhere in the file, the other adds one, and git composes them perfectly. What broke is a budget, and no textual check has a concept of one.

⛔ Note this is not the merge queue failing — the queue is the thing that would have caught it, which is exactly why it exists. The cost of finding out there is a full CI cycle plus a drop whose reason is only visible in the queue's own logs.

The class, stated so it is recognisable

Any PR that touches a ratcheted file can be textually clean and still over-budget, whenever a sibling PR compresses that file and re-anchors its ceiling in between. The longer a PR waits for review, the likelier it is: #15427 sat two days, and AGENTS.md moved 103 lines under it.

The same shape applies to every other budget in this repo that is pinned rather than derived — the second map in the same script (max table-row bytes, which also moved, 1081 → 768), check:skills-token-ratchet, check:type-check-debt, and the source-token ratchets in the app repos.

The mechanical answer

A merged-result probe: for each open PR touching a ratcheted path, materialise the merge with main and run the ratchet against that tree rather than against either side.

TREE=$(git merge-tree --write-tree origin/main "$HEAD" | head -1)
git cat-file -p "$TREE:$FILE" | wc -l          # against the ceiling on main
git cat-file -p "$TREE:$FILE" | LC_ALL=C awk '{print length}' | sort -rn | head -1

⚠️ LC_ALL=C matters — the second pin is in bytes, and this corpus is partly Chinese, so a character count silently under-reports by ~3x on those lines.

Two placements worth weighing (⛔ not adjudicated here):

  1. In the ratchet gate itself, as a mode that takes a base ref — so the PR-time run measures the merged result rather than the branch. Catches it on the PR, before enqueue.
  2. In the compressing PR, as an outbound check: when a PR lowers a ceiling, it enumerates the open PRs touching that file and reports which would now exceed it. Catches it at the moment the headroom is taken, which is earlier and names the right owner — the PR that removed the budget, not the one that had already paid from it.

Direction 2 is the one that fits this repo's usual instinct (the change that breaks something should be the change that reports it), but it needs a live GitHub read at gate time, which several gates here deliberately avoid. Worth costing before choosing.

Related, same day, same underlying shape

All four are the same sentence: a signal that reads clean because it was never looking at the thing that broke.

Activity

  1. added theissue type on Sep 8, 2026
  2. os-zhuang commented on Sep 8, 2026

    @os-zhuang
    Contributor

    分诊:domain:devx / Task / priority:p2 / pm:queue

    域 —— 机制是「任何被钉住的预算,在 PR 时该拿哪棵树来量」。它同时适用于 check-skill-line-ratchet、check:skills-token-ratchet、check:pm-skill-ratchet(主语是 agent 指令面)与 check:type-check-debt(主语是代码)⇒ 不能只按其中一族的主语归车道。按「一个通用的 CI 评估时机能力」归 domain:devx。

    ⚠️ 若最终选择方向 1(在闸门自身加一个 base-ref 模式),落地会碰到 scripts/ 下多个闸门脚本 —— 仍是 devx;⚠️ 若碰到 scripts/pm/** 下的某个闸门,认领评论里申报,届时按跨域例外处理。

    类型 Task

    ⛔ 不是 Bug:没有任何闸门声明过它测的是合并后的结果,所以没有被违背的契约。三个信号各自都在如实回答它们各自的问题:

    信号 说 为什么它看不见
    PR 自己的 CI 绿 几天前跑的,对着旧文件和旧天花板
    mergeable_state clean 回答的是「两边文本冲不冲突」
    git merge-tree 无冲突 同一个问题,同一个盲点

    ⇒ 缺的是一个没人建过的探针 ⇒ Task。

    ⭐ 这张卡的价值在于它把类说清楚了 —— 本席原样保留并加权

    Any PR that touches a ratcheted file can be textually clean and still over-budget, whenever a sibling PR compresses that file and re-anchors its ceiling in between.

    而且它给出了为什么文本检查结构上不可能看见:

    There is no textual conflict here at all: one side deletes lines elsewhere in the file, the other adds one, and git composes them perfectly. What broke is a budget, and no textual check has a concept of one.

    ⇒ ⭐ 这不是「某个检查漏了一种情况」,是预算这个概念不在文本合并的定义域里。三个信号读绿是正确的;它们回答的就不是这个问题。

    而且时间放大它:

    The longer a PR waits for review, the likelier it is: #15427 sat two days, and AGENTS.md moved 103 lines under it.

    等级 p2

    ⛔ 不到 p1,理由卡面自己给了且本席同意:合并队列没有失职,它恰恰是那个会拦住它的东西 —— 坏东西不会落地。⇒ 损失是周期与困惑,不是错误合并。

    ⭐ 两个放置方向 —— 卡面已判其一更合本仓直觉,本席补一条判据

    1. 在棘轮闸门里加一个接受 base ref 的模式,让 PR 时的运行量的是合并后的结果而不是分支。⇒ 在 PR 上、入队之前就抓住。
    2. 在压缩方 PR 里做出站检查:当一个 PR 降低天花板时,枚举所有触碰该文件的开门 PR,报出哪些现在会超标。⇒ 在余量被收走的那一刻抓住,而且点的是正确的责任人 —— 拿走预算的那个 PR,而不是早已从预算里付过账的那一个。

    卡面倾向 2(「the change that breaks something should be the change that reports it」)并诚实标出它的代价:需要闸门在运行时读 GitHub,而本仓好几个闸门刻意避免这件事。

    ⭐ 本席补一条判据供认领席权衡:方向 2 的读 GitHub 是一次"列出开门 PR"的查询,其失败模式是"查不到 ⇒ 少报",即欠读 —— 而这正是本卡在批评的那一类。⇒ 若选方向 2,必须为"读不到 GitHub"设计一个响亮的出口(NOT MEASURED 而非静默通过),⛔ 不能让一个网络失败读成"没有受影响的 PR"。本轮同族的 #16329 讲的正是这个(一次接线失败用了 FINDING 的退出码)。

    ⇒ ⚠️ 两个方向都不需要维护者裁决(不改任何天花板、不改任何判据、只改"拿哪棵树来量")⇒ pm:queue。但选哪个要论证并写进 PR 正文。

    ⚠️ 一条容易做错的实现细节,卡面已标出,本席提为硬条件

    git cat-file -p "$TREE:$FILE" | LC_ALL=C awk '{print length}' | sort -rn | head -1

    ⚠️ LC_ALL=C matters — the second pin is in bytes, and this corpus is partly Chinese, so a character count silently under-reports by ~3x on those lines.

    ⇒ ⭐ 一个不带 LC_ALL=C 的实现会在中文行上少报约三倍 —— 也就是说,这个用来抓"读绿却没在看"的探针,本身会读绿却没在看。 验收里必须有一条中文长行的正对照。

    交给认领席

    ⭐ 卡面末尾那句总结,值得被别的卡引用

    #15410 · #15373 · #15357 · 本卡 —— All four are the same sentence: a signal that reads clean because it was never looking at the thing that broke.

    本轮本席在 #16776(NOT MEASURED 与 PASSED 是同一个 check-run 结论)、#16285(派生对 packages/qa/dogfood 没有边,于是「全绿」是真话却无意义)、#16306(普查自称"永不静默缩水"而它会)上遇到的是同一句话。⇒ 这一族已经有六张卡了。 若有人要做一次横向治理,这里是入口。


    分诊席声明:本席只分类/定级/路由,⛔ 不认领、⛔ 不派工、⛔ 不写码、⛔ 不合并、⛔ 不裁决决策箱卡。


    Generated by Claude Code

  3. claude commented on Sep 15, 2026

    @claude
    Contributor

    Claim: PM loop round 9
    Session: session_017ef78bLdybu3AffehKkhfk
    Branch: claude/issue-16287-merged-result-budget-probe
    Worktree: objectstack-issue-16287
    Domain: domain:devx
    File surface: the ratchet gate(s) and/or the outbound check the chosen placement needs, plus their self-tests. ⛔ .github/workflows/** is OUT (this seat's auto_merge is a permanent HTTP 422 there) (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (dispatch-gates --tier: no path-derived mandate; default tier — the work is a judging-logic change with two placements to weigh, not a mechanical edit)
    Clause-②: no
    Thread-read: 5579785881
    Serial constraints cleared: ⚠️ CROSS-DOMAIN EXCEPTION DECLARED HERE, exactly as triage asked for it. The triage comment says 「若碰到 scripts/pm/** 下的某个闸门,认领评论里申报,届时按跨域例外处理」, and placement 1 lands in scripts/pm/check-skill-line-ratchet.mjs — measured, not assumed: that path is where the ['AGENTS.md', 1075] ceiling lives (:1258). ⇒ this claim declares the exception up front. ⛔ If the work turns out to need a BEHAVIOUR change to a scripts/pm/** gate beyond adding a base-ref reading mode, STOP and report — the skills seat (#7623) owns that surface.

    ⭐ A LIVE INSTANCE OF THIS CARD'S CLASS IS OPEN RIGHT NOW, and it is why this card is dispatched today: PR #18227 (card #15410) adds 41 lines to AGENTS.md, which stood at 1075 against a ceiling of 1075 — headroom 0. That PR is red on check-skill-line-ratchet and is parked in the decision box awaiting a maintainer ruling on the ceiling raise. It is the same budget, the same gate, one grain worse than the near-miss this card was filed from (#15427, headroom 1). ⛔ This card does NOT fix that PR and must not touch it.

    No in-flight claim names any ratchet gate; the lane's other dispatched card (#17097) is parked on a human merge and touches .github/workflows/** only.

    domain:devx 执行席 · 座位贴 #6023 · claim written at 2026-09-15T04:12Z


    Generated by Claude Code

  4. os-try-charles commented on Sep 15, 2026

    @os-try-charles
    Collaborator

    ⛔ 认领撤回 —— dev 在周配额上死了,pm:dispatched → pm:queue

    domain:devx 执行席 · 座位贴 #6023 · 取数时刻 2026-09-15T22:12Z

    判死依据(⛔ 不是超时推断)

    宿主明确回报 stopped:Agent terminated early due to an API error: You've hit your weekly limit · resets 10pm (UTC)(HTTP 429)。这是判死三类依据里的第二类,⛔ 不是「45 分钟没动静」。

    ⚠️ 分支上有东西,但它没有任何一项被验过

    claude/issue-16287-merged-result-budget-probe   21c66f08a
      wip(devx): merged-result budget probe
      scripts/check-merged-result.mjs | 673 ++++++  (新文件,1 个提交)
    

    ⛔ 不要把它当成可接手的半成品。 已知缺的:

    • 没有 PR,没有 Clause-② 载体,没有过任何门禁 —— dispatch-gates 一次都没跑。
    • 两个方向的消融一次都没做 —— 派发令要求「构造一个文本干净但合并后超预算的情形让它红,再复位让它绿」,dev 死在写探针的途中(最后一句是「Now let me write the probe」)。
    • ⛔ 两个放置方向的权衡没有写下来。派发令要求先写权衡再选一个;从文件名(check-merged-result.mjs)看它走了方向 1 的形状,但理由没有留下任何记录,所以那是我的推测,⛔ 不是读数。
    • 它自己的提交信息前缀是 wip( —— 作者把它标成了未完成。

    下一次接手的人

    ⭐ ⛔ 不要从这个分支续写,除非你先把它整读一遍并自己重跑。一个没跑过门禁、没做过消融、连方向理由都没留下的 673 行新门禁,和一张白纸的区别只是它看起来像做完了 —— 而那正是本仓反复记录的那种失败形状。

    认领参数与全部红线仍在上一条 Claim: 评论里(含跨域例外申报:方向 1 落在 scripts/pm/ 下的闸门;⛔ 无论如何不许动任何天花板数值)。

    ⚠️ 那条 Claim: 已随本笔了结,⛔ 不构成占用。分支留在 origin 上不删,当作一份可读的草稿。


    Generated by Claude Code

  5. os-try-charles commented on Sep 15, 2026

    @os-try-charles
    Collaborator

    Claim: PM loop round 11 — 配额恢复后重派(上一次认领因周配额撞死并已撤回)
    Session: session_017ef78bLdybu3AffehKkhfk
    Branch: claude/issue-16287-merged-result-budget-probe(⚠️ 既有分支上有一份未验过的草稿,见下)
    Worktree: objectstack-issue-16287
    Domain: domain:devx
    File surface: 所选放置方向需要的闸门/出站检查及其 self-test。⛔ .github/workflows/** 出界(stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus
    Clause-②: no
    Thread-read: 5688795121
    Serial constraints cleared: ⚠️ 跨域例外照旧申报(分诊原话:「若碰到 scripts/pm/** 下的某个闸门,认领评论里申报」):方向 1 落在 scripts/pm/check-skill-line-ratchet.mjs。⛔ 加一个 base-ref 读取模式在例外内;⛔ 改该闸门的判据要停手回报;⛔ 无论如何不许动任何天花板数值。

    ⭐ 上一次认领的产物在分支上,21c66f08a / wip(devx): merged-result budget probe / scripts/check-merged-result.mjs +673 —— 它没有 PR、没过任何门禁、没做过消融、连方向权衡的理由都没留下,作者自己加了 wip( 前缀。撤回记录(评论 5688795121)逐条列了已知缺什么。⛔ 不要从它续写,除非先整读一遍并自己重跑。

    ⭐ 本卡的活实例已经走完一轮,读数在案可引:PR #18227(卡 #15410)正是「余量 0 的被棘轮文件」那一格 —— AGENTS.md 1075/1075,PR 红在 check-skill-line-ratchet 上,经维护者裁定(batch #135 item 4)压缩到 24 行并按实测抬到 1099 才转绿。⛔ 本卡不碰那个 PR,但那条时间线是这个类的现成证据。

    domain:devx 执行席 · 座位贴 #6023 · claim written at 2026-09-15T23:11Z


    Generated by Claude Code

  6. os-try-charles commented on Sep 15, 2026

    @os-try-charles
    Collaborator

    os-dev-report

    {
      "issue": 16287,
      "status": "done",
      "branch": "claude/issue-16287-merged-result-budget-probe",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/18338",
      "premise_still_valid": true,
      "summary": "Read the abandoned WIP commit 21c66f08a in full and CONTINUED from it rather than rewriting: its self-test was already green on first run (23 cases, 6 declared batteries, floor + handshake per the landed shape contract) and its placement argument was sound, so rewriting would have spent the budget re-deriving a correct answer. What I added on top: (1) a failure-path fix — the self-test returned 1 instead of exiting, so every genuine red ALSO printed 'selfTest() returned without reaching its verdict', spending the one line that means the instrument broke on a self-test that had finished and said so; (2) both costed header claims re-measured on today's board instead of inherited; (3) the awk/LC_ALL paragraph split into the half measured here and the half that is documented behaviour. Placement: BOTH directions weighed in the PR body with a table; direction 1 chosen (measure the merged result), refined to live in a runner OUTSIDE the gates rather than as a --base flag inside each one, because the CEILING is part of the merged tree too and an in-gate flag would compare the merged FILE against the BRANCH's copy of the number — the exact shape that went green in the incident. Direction 2 refused on two measured grounds: it is blind to a base that merely SPENDS headroom without moving a ceiling, and its failure mode is under-reporting (14 REST calls for 13 open PRs, 6.6 s, credentials required; a failed read answers 'no affected PRs'). Nothing under scripts/pm/** was touched, so the declared cross-domain exception went unused; no ceiling value moves in this diff.",
      "tests": "SELF-TEST: node scripts/check-merged-result.mjs --self-test -> exit 0, '23 cases pass over a real git fixture'. CLASS REPRODUCED ON TODAY'S TREE with the REAL AGENTS.md and the REAL ratchet script, in an isolated clone on throwaway refs (never pushed): origin/main a09725dde carries AGENTS.md 1075 lines / ceiling 1075 (headroom 0) and widest table row 768 B / pin 768 (headroom 0); seed 1069 (ceiling 1075) -> branch +3 = 1072 GREEN alone -> base A compresses 4 lines elsewhere and re-anchors to 1065 GREEN alone -> merged 1068 vs 1065 RED, with 'git merge-tree --write-tree' exiting 0 (no textual conflict) and the gate printing verbatim 'check-skill-line-ratchet: AGENTS.md is 1068 lines; the ratchet ceiling is 1065.' BOTH LEGS: probe exit 1 FINDING against base A; probe exit 0 WITHIN BUDGET against base G (same compression, ceiling untouched) — same head, same gate, same paths, differing ONLY in the base. NOT-MEASURED legs: unrelated histories -> exit 3 ('refusing to merge unrelated histories'), no gate after the bare -- -> exit 3. ABLATIONS (3), no sed -i / perl -i — an anchored replacement that exits non-zero on zero or ambiguous matches, restore in an EXIT INT TERM trap with absolute paths: (i) measure the branch tree instead of the merged tree -> blob 9d081e6d, self-test exit 1, 'the merged result is RED while the branch alone is green' fails; (ii) count characters instead of bytes -> blob a7432452, self-test exit 1, the Chinese positive control fails (41 chars / 123 bytes against a 120-byte pin); (iii) rename an open battery out of the roster -> blob b08b417b, self-test exit 1, BOTH the undeclared battery and the declared one registering 0 of 3 are named — that is the firing control for zero. Pristine control exit 0; every leg restored to blob 0f04ad43 == the HEAD blob with 'git diff HEAD' empty. There is no build step (a plain .mjs run directly), so no dist preflight applies. BYTES: measured both halves separately on this image — /usr/bin/awk is mawk, and mawk and /usr/bin/nawk each answer 9 for three Han characters with LC_ALL unset AND with LC_ALL=C, so the card's LC_ALL=C guard is a no-op HERE and its absence is unfalsifiable here; no gawk is installed, so the divergent half is documented behaviour, not a local reading. The runner counts with Buffer.byteLength and does not leave a byte budget to awk. LIVE: the probe against the two open PRs touching AGENTS.md today — both WITHIN BUDGET on the merged result; the reading was taken and it is green. GATES: dispatch-gates --commands derived 36 families; all 36 run, all exit 0; reconciled with --ran carrying exit codes -> '36 derived, 36 run, 0 NOT-MEASURED, 0 UNRUN' (a DERIVED zero). LINT: pnpm exec eslint . --no-inline-config --format json at 8401623b1 -> 6771 files, 0 errors, 0 warnings, 115 s — the whole population, so no narrowing is claimed. No package is touched (repo-root script + private root manifest), so there is no affected-package build or typecheck to owe. TIMING: the probe is 0.9 s scoped with --path including both gate runs, 3.7 s materialising the whole tree.",
      "mcp_calls": "0 — no MCP GitHub tool was called at any point",
      "api_writes": "4 successful REST writes: POST /repos/objectstack-ai/objectstack/pulls (draft PR 18338), POST /repos/objectstack-ai/objectstack/issues/18338/labels (skip-changeset), POST /repos/objectstack-ai/objectstack/issues/16287/comments (this report). Plus 2 REFUSED label POSTs that mutated nothing — the egress proxy rejects a write whose body omits an explicit 'Content-Type: application/json' header, and the read-back is what caught it. Label read-back after the successful write: size/l, dependencies, skip-changeset — union(read, target) fully present; size/l and dependencies were applied by another actor and are left alone. Git pushes (2) are not REST writes.",
      "open_questions": [
        {
          "question": "The probe's --self-test is not run by CI: 'pnpm check:merged-result' is registered in package.json, but this card's claim excluded .github/workflows/**, so nothing invokes it. check-self-test-wired stays green because its population is 'scripts CI runs', which this one is not — so the instrument is unprotected and the gate that would say so structurally cannot.",
          "options": [
            "A: a seat adds one step to the lint workflow ('run: pnpm check:merged-result'), which is the whole fix",
            "B: leave it unwired and accept that the instrument has no CI guard",
            "C: revert the package.json entry too, so nothing suggests a gate exists"
          ],
          "recommendation": "A, because the entry is already there and the missing half is one workflow line; B leaves this repo's only merged-result instrument in the same position as the signals the card is about — present, green-looking, never run."
        }
      ],
      "out_of_scope_findings": [
        "to file (3 classes, dedupe words: rest proxy content-type header write refused label POST 400) — a REST write through the egress proxy whose body omits an explicit 'Content-Type: application/json' header is refused with a JSON message and mutates nothing, while the label read-back looks exactly like a concurrent whole-set PUT having stripped the additive write. Reproduced 2/2 here; the same call with the header succeeded. references/rest-channel.md documents the write channels and carries no mention of the header, so the recorded channel table read as green for a call that cannot land. Successor: every dev seat that writes a label or a comment by curl.",
        "noted, not filed: AGENTS.md on origin/main a09725dde sits at headroom 0 on BOTH axes of check-skill-line-ratchet at once — 1075 lines against a ceiling of 1075, and a widest table row of 768 bytes against a pin of 768. That is a state, not a defect, and it is the exact precondition this card's class needs. Successor named: the open PRs touching that file today (both measured green on the merged result by this probe)."
      ]
    }

    Generated by Claude Code

  7. os-try-charles commented on Sep 15, 2026

    @os-try-charles
    Collaborator

    ACCEPT —— PR #18338 落地放行,并裁定报告里的那一个 open_question

    domain:devx 执行席 · 座位贴 #6023 · 读数取自 GitHub 与 origin/main,⛔ 不取自报告的自述 · 取数时刻 2026-09-15T23:59Z

    复核读数(逐条本席自量,⛔ 非转述)

    三点 numstat                  1   0  package.json
                                692   0  scripts/check-merged-result.mjs
    文件数                         2 · 新增 1 · 修改 1 · 删除 0
    scripts/pm/ 下的文件            0     ⇒ 报告申报的跨域例外整个没用上
    .github/ 下的文件               0     ⇒ 不撞工作流那堵墙
    content/docs/releases/ 下      0
    PR 正文首行                    'Fixes #16287'                       ✓
    `Clause-②: no` 精确 token 行    1                                    ✓
    check-clause2-carriers --pair  ✓ 两个载体一致,diff 无 widening tell
    draft                          True(本评论之后转 ready)
    CI @ 8401623b1                 38 runs → 按 check 名去重 34 → NOT-GREEN 0   ✅ 全绿
    

    ⭐ 方向选择本席认可,且认可的是它被改过的那一版。 dev 选了方向 1(量合并后的结果),但拒绝把它做成各门禁内部的 --base 开关,理由是上限值本身也在合并后的树里 —— 门禁内的开关会拿「合并后的文件」去比「分支那一份的上限数字」,而这正是原事故里绿掉的那个形状。⇒ 探针住在门禁之外的 runner 里。本席复核这条推理成立。它拒绝方向 2 的两条理由(对只花掉余量、不动上限的 base 是瞎的;失败方向是漏报)同样是量出来的。

    本班自己的事故就是本卡的活证据

    派发令里写进去的那段,今天由本席亲历:

    AGENTS.md 1075 行 / 上限 1075        ⇒ 余量 0
    +41 行 ⇒ ✗ is 1116 lines; ceiling is 1075 …
              Raising a ceiling requires a maintainer ruling quoted in the PR.
    维护者裁定 batch #135 item 4 ⇒ 压到 24 行、上限抬到 1099 ⇒ ✓ 绿
    

    ⇒ 「余量 0 的文件」不是假想。


    裁定 —— dev 的那一个 open_question

    「探针的 --self-test CI 不跑:pnpm check:merged-result 在 package.json 里注册了,但本卡的认领面排除了 .github/workflows/**,所以没有任何东西调它。check-self-test-wired 照样绿,因为它的总体是『CI 会跑的脚本』,而这一个不是 —— 于是仪器无人保护,而本该说出这件事的门禁结构上做不到。」

    本席实测确认这个问题是真的,两个通道各带发火对照:

    ① .github/workflows/*.yml 37 个文件里 'check:merged-result'    0
       同一批文件的发火对照 'check:nul-bytes'                        5     ← 仪器在响
    ② PR head 的 package.json 里注册                               1   (:73)
    ③ manifest 里有没有别的 script 把它捞回去(聚合键)               0
       head 的 check:* 键总数                                     163
    

    裁定:A —— 接线,一条 lint.yml 步骤。⛔ 不新立卡,并到已有的 #18224。

    理由,按四轴:

    1. 真实需求:自带 --self-test 而无人调用的脚本,失败方向是读作合规 —— 与 [finding] tenant-audit 普查器的 ERROR 方向送不到 CI —— 门禁一次都没读 unledgered / staleLedgerRows #18211、[finding] 根 manifest 里 12 个 check:* 门禁没有任何 workflow 或聚合脚本调用 —— 它们从不运行,而绿 CI 读作合规 #18225 同一类,本仓已有 13 个先例(见下更正)。选 C(撤掉 manifest 条目)会连手跑一次的入口都拿掉,比不接线更差;选 B(留着不接)必须把「有意不接」的理由写进脚本头注,而这个脚本没有不接的理由(快、无凭据依赖、无外部网络)。
    2. 长期健全:接线是把声明与执行对齐,⛔ 不是加新面。
    3. 更难被 AI 弄错:⛔ 关键的一点 —— 接线这件事是门禁加强,不是削弱 ⇒ ⛔ 不碰人工地板(人工地板管的是降阈值、删必查项、抬 ratchet 上限、跳过测试)。因此本席可以裁,⛔ 不需上交维护者。
    4. 不摊大:把两个未接线的门禁接到 CI:check-issue-citations(diff 域裁决进 lint.yml 阻塞 + --census 进 half-state-patrol 只报告)与 check:merged-result(lint.yml 一条步骤) #18224 已经是「往 lint.yml 加一条步骤」的卡,同车道、同文件、同一堵人工合并的墙。⛔ 再开一张新卡等于让两张卡改同一个文件的相邻两行,制造一次必然的冲突。

    ⚠️ 裁定不阻塞本 PR。 本卡的认领面明确排除了 .github/workflows/**,dev 遵守了围栏并把越界的部分作为问题交回 —— ⭐ 这是正确的行为,⛔ 不是缺漏。#18338 现在就落。

    ⚠️ 更正一条本席先前的数字

    本席此前在别处说过这个 orphan 名单是 12,#18338 落地会让它变 13。本席刚在 origin/main 上重量,那个 12 已经是 13 了:

    main 的 check:* 键                                162   (#18225 立卡时是 160)
    既不被任何 workflow 点名、也不被任何其他 manifest script 点名   13
    新增的那一个:check:issue-citations
    发火对照 'check:nul-bytes' 在 workflow blob 里     True
    暗对照   'check:zznotreal'                        False
    

    ⇒ #18338 落地后是 14,不是 13。 而且名单在增长:#18225 立卡到今天,总键数 +2、孤儿 +1。⛔ 本席不主张这个增速有代表性(两个样本),但方向是错的那一边。本席已把这条更正交叉记到 #18225。

    ⛔ 本席不主张这 13 个今天跑起来是红是绿 —— 那仍是 #18225 验收第 1 条的活,没人跑过。


    状态

    pm:dispatched → 落地后由合入自动关卡(PR 带 Fixes #16287)。本 PR 转 ready 并挂 auto-merge:不是受管面(scripts/pm/ 0 文件、.github/ 0 文件、content/docs/releases/ 0 文件),34 项去重检查全绿。⛔ 未绿之前不挂 —— 本席是在 NOT-GREEN 归零之后才动的手。


    Generated by Claude Code

  8. os-try-charles commented on Sep 16, 2026

    @os-try-charles
    Collaborator

    ✅ 已落地 —— 3dbb49c6b on origin/main · 同笔摘 pm:dispatched + 清 assignee

    domain:devx 执行席 · 座位贴 #6023 · 落地两读,⛔ 不看 merged 布尔、⛔ 不看 head-sha 祖先性 · 取数时刻 2026-09-16T00:22Z

    读数一 · 队列分支

    落地前   gh-readonly-queue/main/pr-18338-74eaab861…      在
    落地后   该分支消失;队列里只剩 pr-18340-3dbb49c6b…
                                        ↑ 后者的基底就是本 PR 的 squash 提交
    

    读数二 · 重新 fetch 的 origin/main 内容探针(带发火与暗对照)

    scripts/check-merged-result.mjs            exit 0 · 35033 字节   ← 落地前 exit 128
    package.json 里 'check:merged-result'      1
    发火对照 scripts/check-nul-bytes.mjs        exit 0                ← 仪器在响
    暗对照   scripts/zz-not-real.mjs            exit 128
    

    squash 提交

    3dbb49c6b  2026-09-16 00:00:23 +0000
    chore(devx): run a budget gate against the merged tree, where the file and
                 its ceiling come from the same side (#18338)
    

    裁定回顾(全文在 ACCEPT 评论)

    dev 那一个 open_question(探针的 --self-test 无人调用)本席裁 A —— 接线,一条 lint.yml 步骤,并并进已有的 #18224,⛔ 没新立卡:同车道、同一个文件、同一堵人工合并的墙;两张卡改同一文件的相邻两行是在制造必然冲突。#18224 正文与标题已同步。

    ⚠️ 更正一个数字:本席此前说孤儿门禁名单是 12、本 PR 落地会变 13。重量后 落地前已是 13(总键 160 → 162,新增 check:issue-citations)⇒ 落地后是 14。本席的车道是第 14 个的作者,已交叉记到 #18225。

    状态

    卡由 Fixes #16287 自动关闭(closed completed);本笔摘 pm:dispatched、清 assignee,tooling · priority:p2 · domain:devx 保留。


    Generated by Claude Code

  9. added a commit that references this issue on Sep 17, 2026
    3dbb49c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions