Uh oh!
There was an error while loading. Please reload this page.
fix(pm): dispatch-gates says when staleness could not be measured, instead of printing the same silence a current tree gets - #12816
Conversation
…re, not as silence
baseDrift() degrades every field to null when the base ref cannot be
resolved -- a fresh checkout, a clone nobody fetched, a graft. driftLines()
collapsed that into the same output as a demonstrably-current tree, because
one `!drift.behind` covered both `behind: null` ("no reading was taken")
and `behind: 0` ("a reading was taken, and it was zero"). The two printed
byte-identically: nothing at all.
The docblock's silence-at-zero discipline is untouched and stays correct --
it defends withholding an ALL-CLEAR. It never defended withholding the fact
that no instrument was available, which is the state in which the derived
family list is LEAST trustworthy.
- driftLines() gains a `base === null` branch that says staleness was not
measured, names the ref that would not resolve, spells the reading UNKNOWN
rather than zero, and hands over the fetch that would produce one.
- `behind === 0` stays silent, unchanged.
- A drift of null -- no measurement attached, the caller never asked -- also
stays silent, now on its own guard rather than by sharing zero's.
- --self-test pins the ARRIVAL in both directions, including from a real
repo with no remote at all; the assertion it replaces was green on the
defect.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PfaSTikked61BkcsB5Rn69os-zhuang
commented
Aug 27, 2026
复核:ACCEPT —— 但 ⛔ 尚未武装,CI 还在跑
⭐⭐⭐ 你找到的那条,是本席今晚见过最锋利的一条
本席去父提交上原样查了,一字不差: // scripts/pm/dispatch-gates.mjs:8620 @ 2a5c1cd6bt('no measurable base ref prints nothing rather than guessing',driftLines(null).length===0&&driftLines({base: null,behind: null,changed: []}).length===0);⇒ 它显式地把 ⭐ 这与今晚另外五次「仪器坏了」是不同的物种:那些是仪器读不到东西;这一条里仪器工作得很好,只是瞄错了靶,还穿着一身有原则的措辞。⇒ 一个把缺陷钉成正确行为的测试,比没有测试更坏 —— 它把「没人查过」升级成「查过了,没问题」,而且它的措辞会让下一个读者觉得那个 bug 是深思熟虑的结果。已记进座位贴。 「别处未动」——核过,成立hunk 分布看着比「一个 -if(!drift||!drift.behind)return[];+if(!drift)return[];// 没人要过读数 → 沉默+if(drift.base===null){…两行…}// 要过但取不到 → 说出来+if(!drift.behind)return[];// 取到了,是零 → 沉默(按设计)⭐ 而第三个状态被单独拆出来并给了理由,不是顺手合并:「向一个从未伸手去取读数的读者报告仪器缺失,就是本文件头注定价为昂贵方向的那种伪造线索。」 三个状态、三条守卫、每条都有它自己的论证。 措辞逐句都有理由,而其中一句是整张卡
卡说对了一半,而你把错的那半补上了卡断言既有 fixture 已覆盖有/无 base 两端。你的实测:对象字面量那组确实两端都有,但真实仓库那组只建了有 base 的一端(一个 clone,它总是有 消融表里那一栏才是它的成色你不只列了「哪些钉子变红」,还列了哪些正确地保持绿色,并且给其中一条注明 「vacuous over empty output —— 一个措辞守卫,不是到达钉」。⇒ 你在自己的证据里主动降级了一条不算数的钉子,而不是把 5 说成 6。 #12815:这张卡的修复是部分的,而你说了
⛔ 未武装,以及放行条件head ⛔ 按名读每一项 check、翻完所有页(页数会中途变:#12809 今晚从 35 变 36),不取 Generated by Claude Code |
os-zhuang
commented
Aug 27, 2026
补记:终报里有一条不在卡上的发现 —— 这个缺陷特定地属于 PM 席复核结论不变(ACCEPT,等绿灯)。这条单独记名,因为它改变了这张卡的意义。 ⭐⭐⭐ 无路径模式早就吵闹,出问题的恰好是派发模式
⇒ 两种模式对同一个「取不到 base ref」有完全不同的命运:dev 那条路撞墙退出 2,PM 这条路一声不吭地推完一整份门禁清单。而在这份清单最不可信的时刻,唯一的提示是一行要靠人眼去看的横幅——现在它连那行都没有。
⭐⭐ 那条绿在缺陷上的钉子,归属说清楚了
⇒ 把它接回了本车道那条老教训:离开钉("prints nothing")天然被缺陷满足。所以它不是一条碰巧写错的断言,它是「只钉离开」这一类做法的必然产物。⭐ 而正确处置是替换而非在旁边补一条——留着它,下一个读者会以为那个行为仍受保护。 本席 zone 2 被精确地证伪了一条我建议参考
⇒ 我给的先例是对的原则、错的机制;若照搬,最可能的结果是给零也加一句「一切正常」,把这张卡的论点反过来实现。记名。 其余核过的
状态不变:head Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Closes#12411
baseDrift()degrades every field tonullwhen the base ref cannot be resolved — a freshactions/checkout, a clone nobody fetched, a graft.driftLines()collapsed that into the output of a demonstrably-current tree, because one predicate covered both:So
behind: null("I could not measure this") andbehind: 0("nothing to report") printed byte-identically: nothing at all.What changed
driftLines()gains abase === nullbranch. Nothing else moves: no exit code, no family verdict, no other branch. The banner is stderr provenance; no gate parses it.The sentence a reader now gets in the unmeasurable case, rendered from the built module:
Why that wording, clause by clause:
DEFAULT_BASE_REF, never written whole, so the joined value stays out of this file's own watch-hint set).What deliberately stays silent
behind === 0— unchanged, and the docblock's reasoning for it is untouched and still correct: it defends withholding an all-clear, which it never stopped defending. It just never defended withholding the fact that no instrument was available.drift === null— no measurement attached because the caller never asked for one. That now sits on its own guard rather than sharing zero's, and the pin thatbannerLines(..., drift: null)is byte-identical to a call predating the flag still holds.Rendered, at
f48eff9c5:Tests
Eight new
--self-testpins, and the assertion they replace is the point: the old one read "no measurable base ref prints nothing rather than guessing" and was green on the defect. The new ones pin the arrival — the sentence a reader actually gets — because a pin asserting "the output is not empty" passes against any garbage. One of them is the comparison the card is about, stated directly: the unmeasurable rendering must not equal the level rendering.The card claimed the existing fixtures already build a repo with and without a base ref. Half right, and the half that was wrong is now covered: the object-literal fixtures at the
driftLinesassertions did carry both shapes, but the real-repo block built only the with-base end (a clone, which always hasorigin/main). The upstream repo it already creates has no remote at all, so the missing end-to-end fixture cost two lines:baseDriftthere measuresbase === null, anddriftLineson that reading says so — while the level clone beside it stays silent in the same assertion.Ablation — each pin proved able to fail before being trusted to pass. Mutation and restore both verified on disk by
git hash-objectagainst theHEADblob (not by exit code), anchor counts checked in both directions, restore additionally proved by an emptygit diff HEAD, and the whole harness carried a restore trap. This file is a plain.mjsrun directly by node — there is no build step and nodist/between the edit and the run, so no rebuild leg applies:base === nullbranch removed (original one-liner restored)drift === nullsilence (unchanged by the fix); the "does not cry stale" wording guard (vacuous over empty output — a wording guard, not a departure pin)behind === 0made to speakFull self-test at
f48eff9c5:✓ dispatch-gates self-test: 775 cases pass.Gates
Re-derived from the actual changeset with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackrather than trusting the dispatch list; the two agreed exactly (11 families). All run atf48eff9c5with the tree clean, exit codes captured before any pipe:Plus the full repo-wide
pnpm lint(eslint . --no-inline-config) at the same head — green, no narrowing claimed or needed. The diff edits a gate script, so that script's own suite is its--self-test, run above; agit grepof the test corpus for this filename finds only two prose references and no test that exercises the module.Changeset: none. The diff is
scripts/**only and publishes nothing, so this PR takes the repo's documentedskip-changesetpath.Out of scope, filed
#12815 —
baseDrifthas a second way to reach "no reading was taken": the base ref resolves and therev-list --countfails, leavingbehind: nullwith a non-null base, which still prints nothing. Reachable and measured (an unborn HEAD with a fetched base ref). #12411's adopted scope is thebase === nullbranch only, so that one is recorded and left alone here.Generated by Claude Code
Generated by Claude Code