Skip to content

bump-objectui.selftest.sh 把 digest 的 stderr 丢进 /dev/null,而失败措辞写死为「范围不可走」—— 崩掉的探针被报成一个关于 objectui 范围的判决 #18354

Closed

Description

@os-try-charles

scripts/bump-objectui.selftest.sh 的两处探针调用把 digest 的 stderr 丢掉,而失败措辞写死为**「the range does not walk」** —— 于是一个崩掉的 digest(缺模块、语法错、被杀)在自检里呈现为一个关于 objectui 范围的判决。措辞对不上原因,而唯一的线索(stderr)已经没了。

⚠️ priority: 与 domain: 故意留空 —— 分诊的活,不是本席的。

⛔ 先更正报这条的 dev 的定位:bump-objectui.sh 没有这个缺陷

交回这条的 dev(卡 #16421 / PR #18346)把它写成「bump-objectui.sh runs the digest with >/dev/null 2>&1 and maps ANY non-zero exit onto 'the objectui range does not walk'」。本席在 origin/main 实测,这句对那个文件为假:

bump-objectui.sh 里 digest 调用带 >/dev/null 2>&1 的数量 : 0
  (发火对照:该文件里 'objectui' 命中 85 次,'zzNOPEzz' 0 次)

而且它恰恰已经做了这条发现所要求的区分,逐字(:492-499):

if [[ "$WALK_RC" -ne 2 && "$WALK_RC" -ne 3 ]]; then
  # The probe did not ANSWER. 2 and 3 are its two verdicts; anything else
  # means it never reached one (no node, a syntax error, a killed process).
  # Deepening would be a remedy applied to a diagnosis nobody made, so it is
  # not attempted — and the refusal below says which of the two this was.
  DEEPEN_NOTE="  no deepen was attempted: the probe never returned a verdict, …"

⇒ ⭐ bump-objectui.sh 不吞 stderr、区分「判决」与「没答上」、且拒绝对一个崩溃开 --unshallow 的药方。⛔ 本卡不主张动它。

缺陷的真实位置:自检脚本的两处

scripts/bump-objectui.selftest.sh:

:369-370   node "$DIGEST_SCRIPT" … --check-walkable >/dev/null 2>&1 || walk_rc=$?
:371-372   bad "fixture: the range does not walk BEFORE breaking the blob (rc=${walk_rc}) — not this card's state"

:380-382   node "$DIGEST_SCRIPT" … --check-walkable >/dev/null 2>&1 || walk_rc=$?
:387       bad "fixture: --check-walkable now exits ${walk_rc} — the blob deletion broke the WALK, not just the blob read"

⭐ 数字是对的,句子是错的。 两条 bad 都把 rc 打了出来 ⇒ 崩溃(rc=1)与判决(rc=2/3)在数字上可分;但两句话都断言「范围不可走 / 是 blob 删除弄坏了 WALK」,而 rc=1 时两句都不成立。⇒ 读到它的人得到一个语法正确、指向错误的诊断,而能纠正它的那行 stderr 已经被 2>&1 丢进 /dev/null。

代价 —— 量过的,不是假想

卡 #16421 的 dev 在本轮真的踩了:它给 check-adr-0087-registration.mjs 加了一个 import,自检沙箱的拷贝面没跟上,digest 以 ERR_MODULE_NOT_FOUND 崩掉 —— 而 pnpm check:objectui-bump 报给它的是objectui 范围不可走。它在报告里写这「cost the longest part of the round」。

⇒ 失败方向是把诊断指向另一个子系统。⛔ 比单纯报错贵得多:它把人送去查 shallow clone 与 --unshallow,而真因在三个目录外的一个拷贝清单里。

验收(⛔ 不规定实现)

  1. 让崩溃可见。要么不吞 stderr,要么在 bad 之前把子进程的 stderr 回放出来。⛔ 只把 rc 打出来不够 —— 本卡的要害是句子,不是数字。
  2. ⭐ 按 bump-objectui.sh:492-499 已有的判据分叉:2 和 3 是判决,其余是「没答上」。⛔ 不要新造一套判据 —— 同一个探针在同一个仓里已经有一套,两套会漂开。
  3. ⭐ 两个方向都要量:构造一次真的「范围不可走」(rc=2 或 3)⇒ 措辞应说范围;构造一次崩溃(例如抽掉 digest 的一个 import)⇒ 措辞必须说探针没答上,且 stderr 可见。
  4. ⛔ 不许顺手改 bump-objectui.sh —— 上面已证它是对的,动它是纯风险。

⛔ 没量的部分,不许当读数用

Refs:卡 #16421 / PR #18346(踩中并交回)· scripts/bump-objectui.selftest.sh:370 :382 · scripts/bump-objectui.sh:492-499(正确的判据,可照抄)

domain:devx 执行席 · 座位贴 #6023 · 读数取自 origin/main 与 PR #18346 的三点 diff


Generated by Claude Code

Activity

  1. os-try-charles commented on Sep 18, 2026

    @os-try-charles
    CollaboratorAuthor

    Claim: PM loop round 50
    Session: session_017ef78bLdybu3AffehKkhfk
    Branch: claude/issue-18354-selftest-digest-stderr-and-verdict-split
    Worktree: objectstack-issue-18354
    Domain: domain:devx
    File surface: scripts/bump-objectui.selftest.sh(stop on breach; explain in the report)。⛔ 面外:scripts/bump-objectui.sh(卡面验收第 4 条明写「不许顺手改」)、治理面、packages/*/CHANGELOG.md。
    Container & model: M, mode:subagent, model: opus (当次 dispatch-gates.mjs --tier scripts/bump-objectui.selftest.sh 输出逐字:「no path-derived mandate: the surface hits none of the 3 declared glob(s)… floor sonnet · default opus · ceiling fable」)
    Clause-②: no
    Thread-read: 5713022109
    Serial constraints cleared: 32 个 open PR 的 files 逐个读过(共读到 452 个变更文件 ⇒ 读法有反应)。触到 objectui 字样的只有 PR #18723(draft),面是 .objectui-sha 与 packages/sdui-parser/objectui-lockstep.json —— 与本卡的 scripts/bump-objectui.selftest.sh 不重叠。

    ⚠️ 派发前重测:卡面行号已经移位,按内容定位

    卡面写 :369-370 / :371-372 / :380-382 / :387。本席在 origin/main 现读,同样的两处探针在:

    :425    --check-walkable >/dev/null 2>&1 || walk_rc=$?
    :437    --check-walkable >/dev/null 2>&1 || walk_rc=$?
    :439  ok  'fixture: --check-walkable still exits 0 after the blob is deleted …'
    :441  bad "fixture: --check-walkable now exits ${walk_rc} — the blob deletion broke the WALK, not just the blob read"
    

    ⇒ 整体下移约 55 行。⛔ 行号当线索,不当读数 —— 按内容定位。

    ⚠️ 另:该文件里 dev/null 共 8 处,而卡面只主张其中的两处 digest 探针。⛔ 不要把八处一起改 —— 卡面没量过另外六处,施工席要自己判哪些在面内并申报。

    ⭐ 本席差一点误报卡面有错,记下来

    本席复核卡面那条对照(「bump-objectui.sh 里 digest 调用带 >/dev/null 2>&1 的数量:0」)时,先跑了 grep -c ">/dev/null 2>&1" scripts/bump-objectui.sh,读到 1,一度以为卡面被证伪。

    读那一处才发现::301 是 git -C "$OBJECTUI_ROOT" rev-parse --verify --quiet origin/main >/dev/null 2>&1 —— 一次存在性探测,⛔ 不是 digest 调用。

    ⇒ 卡面的主张按其原文完全成立,错的是本席的对照 —— 它数的是「整个文件里的 >/dev/null 2>&1」,而卡面问的是「digest 调用上的」。同一个仓、同一个文件、同一个字符串,答的是两个问题。 这正是本班反复栽的那一形状,本席这次在断言之前抓住了。

    读数时刻 2026-09-18T12:19Z


    Generated by Claude Code

  2. os-try-charles commented on Sep 18, 2026

    @os-try-charles
    CollaboratorAuthor

    os-dev-report

    {
      "issue": 18354,
      "status": "done",
      "branch": "claude/issue-18354-selftest-digest-stderr-and-verdict-split",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/19034",
      "premise_still_valid": true,
      "summary": "scripts/bump-objectui.selftest.sh ran the digest's --check-walkable probe with both streams discarded and then hard-coded the failure wording as a verdict about the objectui range, so a probe that crashed was reported as 'the range does not walk' with its only clue already gone. Both probe sites in case_5 now go through a shared check_walkable helper that keeps the child's stderr in a file under the existing TMPROOT, and fork on walk_answered: exits 2 and 3 are the probe's two verdicts, anything else means it never reached one. That criterion is taken from bump-objectui.sh:492-499 rather than invented a second time. On a no-answer the child's stderr is replayed before the bad line and the wording says the probe never answered and that this says nothing about the objectui range. scripts/bump-objectui.sh was not touched. The card's line numbers had moved (:425/:437 at origin/main); everything else in the card held when measured, and the file header's dangling 'See this card for what IS carded' pointer was retargeted at the fork that now exists.",
      "tests": "GATE BEFORE/AFTER — pnpm check:objectui-bump on the pre-change file (restored from merge base 72c164050 into the worktree, then restored to HEAD): EXIT=0, '20 assertions across 5 cases'; restore proof: disk blob b689c89a7f60e180adaf9ecc6340b5fab2645055 == HEAD blob, git diff HEAD empty. Same gate after the change: EXIT=0, '20 assertions across 5 cases'. check:objectui-bump IS 'bash scripts/bump-objectui.selftest.sh', so the direct run is that same run. ACCEPTANCE ITEM 3, BOTH DIRECTIONS x BOTH PROBE SITES (4+4 legs; each leg proved its mutation reached disk by an occurrence count, ran the real self-test, restored under a trap, and ended with both touched files hashing byte-identical to HEAD; final git status --porcelain and git diff HEAD --stat empty). A (real verdict, rc=2, built by deleting the from-endpoint's commit object): BEFORE printed 'fixture: the range does not walk BEFORE breaking the blob (rc=2) - not this card's state'; AFTER printed the same sentence preceded by the probe's own stderr ('PREREQUISITE NOT MET - an endpoint of ... is not present as a commit object', the fetch --unshallow / fetch origin remedies, and git's fatal line). No regression. B (crash, rc=1, built by importing a name ./invoked-as.mjs does not export): BEFORE printed 'fixture: the range does not walk BEFORE breaking the blob (rc=1) - not this card's state' - the wrong sentence, nothing else; AFTER printed the replayed SyntaxError stack followed by 'fixture: the walkability probe never ANSWERED before the blob was broken (exit 1; its verdicts are 2 and 3) - this says nothing about the objectui range; read the probe's stderr above'. Site 2 (mutations injected BETWEEN the two probes): A gave 'fixture: --check-walkable now exits 2 - the blob deletion broke the WALK, not just the blob read' before and after (after: with stderr above it); B gave that same sentence with 'exits 1' BEFORE (wrong) and AFTER gave 'fixture: the walkability probe never ANSWERED after the blob deletion (exit 1; its verdicts are 2 and 3) - the WALK is UNMEASURED here, not broken; read the probe's stderr above' with the SyntaxError replayed. VEHICLE NOTE: removing an import SPECIFIER outright cannot reach these probes - firstPartyModuleClosure throws on an absent first-party module, so new_framework_with_digest dies first (the earlier hardening working). A missing NAMED EXPORT from an existing module is the same ERR-class load failure and does reach them. GATE FAMILIES - derived in-worktree with 'node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack' (1 changed path vs merge base 72c164050): 26 commands. All 26 ran, all EXIT=0, including check:objectui-bump, check:nul-bytes, check:parse-guard, check:entry-guard, check:self-test-wired, check:scripts-symbol-anchors, check:bash32-floor, check:agent-test-spelling, check:cross-package-test-inputs and check:pnpm-filter-targets. check:pm-dispatch-gates was run detached and waited on with 'tail --pid': EXIT=0, 'dispatch-gates self-test: 1849 cases pass', 'the battery took 999.5s on this box' (contended box; the prescription quotes 430-450s). The tool's own warning applies: not a complete account of CI. LINT - not claimed as a reading and not run as one: eslint's universe is JS/TS and the only changed file is a .sh file it does not lint, so there is no narrowed-eslint measurement to declare here. CONTROL-CHARACTER SELF-SCAN - grep -naP over the changed file for the control-byte class: no match (exit 1), on top of check:nul-bytes EXIT=0. CLAUSE-2 - readClause2Line on the exact body printed {\"kind\":\"declared\",\"value\":\"no\"} BEFORE the PR was created; 'node scripts/pm/check-clause2-carriers.mjs --pair 19034' EXIT=0, with pair.1.pr-body.clause2-line: DECLARED `no` and pair.1.claim.clause2-line: DECLARED `no`. SKIP-CHANGESET - measured, not asserted: root manifest is private:true with no files[], and no package's files[] names anything under the repo-root scripts/ directory (positive control: the same loop prints 'dist' for 5+ packages). Label applied and read back: ['size/s', 'skip-changeset'] - nothing stripped. PR BODY READBACK - full body re-read from the API and diffed against what was sent: identical except a stripped trailing newline; exactly 1 attribution footer; the Clause line survives at line start.",
      "mcp_calls": "0 - no MCP GitHub tool was called, read or write",
      "api_writes": "3 - POST /repos/objectstack-ai/objectstack/pulls (draft, created PR 19034); POST /repos/objectstack-ai/objectstack/issues/19034/labels (skip-changeset); POST /repos/objectstack-ai/objectstack/issues/18354/comments (this os-dev-report). Plus one git push of the branch, which is not a REST write.",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: no repo-wide sweep was done for the same shape (both streams discarded plus a hard-coded failure sentence that is a verdict about another subsystem). The card claims this one file's two sites and not a population, and the PM's dispatch says measuring a population would be a new finding rather than a licence to widen. Carrier: none identified (承接者:无) - no queued PR or file touches this shape.",
        "noted, not filed: case_5 calls new_framework_with_digest as a bare statement, so a closure-derivation failure is consumed by set -e and aborts the whole self-test before the per-case summary line. It is not silent - the helper prints its own named diagnostic plus the child's stderr first - so this is an ergonomics observation, not a defect. Carrier: the next PR that touches the staging helper.",
        "noted, not filed: the two probe sites now differ only in which side of the blob deletion they sit on. Collapsing the surrounding assertions further would change what case 5 pins, so it was left alone. Carrier: none identified (承接者:无)."
      ]
    }

    Generated by Claude Code

  3. removed their assignment
    on Sep 18, 2026
  4. os-try-charles commented on Sep 18, 2026

    @os-try-charles
    CollaboratorAuthor

    落地两读(卡 #18354 · PR #19034)—— 卡随 Fixes 自动 CLOSE。

    读① 队列侧 —— ready_for_review 2026-09-18T13:11Z → added_to_merge_queue 2026-09-18T13:13Z → merged 2026-09-18T13:39Z。对照:同一次 git ls-remote --heads origin 'gh-readonly-queue/*' 读到其他队列分支 1 条 ⇒ 列表是活的。

    读② 内容侧 —— 重取 origin/main(b22db516d,取数 2026-09-18T13:40Z),探 scripts/bump-objectui.selftest.sh:

    探什么 读到
    walk_answered() { [[ "$1" -eq 2 || "$1" -eq 3 ]]; } 1 ✅
    never ANSWERED 措辞 2(两处探针各一) ✅
    stderr 被留下(2>"$WALK_ERR") 1 ✅
    发火对照 CHANGESET_NAME diff 内 0 / 文件内 6 ✅

    对照是当场机械挑的:TMPROOT(1/15)、case_5(2/3)、DIGEST_SCRIPT(3/2)因 in-diff > 0 全部作废,换到 CHANGESET_NAME。⛔ 没有硬凑。

    ⛔ 两读都不取 merged 布尔,也不取 head-sha 祖先关系。

    四条验收,本席逐条自读

    读到
    ① 让崩溃可见 2>"$WALK_ERR" —— 只丢 stdout,stderr 留到文件;walk_replay_stderr() 在 bad 之前回放
    ② ⛔ 不新造判据 bump-objectui.sh:494 是 [[ "$WALK_RC" -ne 2 && "$WALK_RC" -ne 3 ]],新 helper 是它的补集写法,同一条 2-or-3
    ③ 两个方向都量 施工席给了 4+4 条腿(真判决 rc=2 / 崩溃 rc=1,各跑两处探针),每条都证明变异落盘、跑真自检、trap 还原、末了两文件 hash 与 HEAD 逐字节相同
    ④ ⛔ 不碰 bump-objectui.sh diff 只有 1 个文件

    ⭐ 落地措辞比本席要求的更准。:496 逐字:

    the WALK is UNMEASURED here, not broken; read the probe's stderr above

    原缺陷是「把没测出来说成测出来了」,而修法把这个区分写进了失败行本身 —— 下一个撞上的人不必再推。

    ⭐ 另一条它自己挖出来、值得留档的夹具知识:构造崩溃时直接删掉 import 说明符行不通 —— firstPartyModuleClosure 会先抛,夹具在探针之前就死了(那是更早一次加固在起作用)。改用「从存在的模块里 import 一个不存在的具名导出」,同一类装载失败,且能走到探针。

    其余六处 dev/null,施工席逐个给了不动的理由

    242 / 261 / 265 / 273 是 git 存在性探测(退出码就是全部答案,且消息描述的是脚本自己刚造的夹具,⛔ 不是关于别的子系统的判决)· 374 是容忍缺失的 pin 读、⛔ 无判决句 · 431 只丢 stdout,stderr 本就流出且 break_changeset_blob 自带证据。

    ⇒ 六处都不带卡面那个形状,均未改动。dev/null 计数 8 → 7 是两处探针并进共享 helper,⛔ 不是有人被顺手改掉。

    ⚠️ 全仓同形未做扫描,施工席按卡面与派发令刻意不做 —— 卡面只主张这一个文件的两处,⛔ 不主张一个总体。

    席位状态随此清理:撤下 pm:dispatched,解除指派。

    读数时刻 2026-09-18T13:42Z


    Generated by Claude Code

  5. added a commit that references this issue on Sep 28, 2026
    b22db51
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

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions