fix(scripts): objectui digest 声明破坏性时写入门仍判红的 ADR-0087 处置脚手架 (#6494) - #6556
Merged
Conversation
#6099 之后 digest 会在正文打 `**BREAKING**`,而 #6148 的门要求任何声明破坏性的 changeset 必须携带 `adr-0087:` 处置标记。生成器不写这个标记,`Check Changeset` 判红;PR #6476 手工补了一行而绿,但那份 changeset 是**生成的** —— 下一次 pin bump 重新生成时手工标记消失,同一个红复现。且复发条件是常见情形:objectui 在发布窗口内 禁 `major`,把破坏性一律声明为 `minor` + 正文标注,那是它的常规做法。 生成器现在写入一份**占位**而不是答案: <!-- adr-0087: TODO — the pin bump cannot answer this; a human must (objectstack#6494) --> `TODO` 既不匹配 `registered ...` 也不匹配 `not-required (...) ...`,门因此报 `unparseable disposition: "TODO …"` 并继续判红 —— 「有标记但未作答」与「无标记」 在日志里是两个不同的事实,且会带出门自己的四种合法写法。生成器**不**替人选处置: #6148 的设计就是门从不替你作答,生成器替你作答是同一个洞换了作者。 判定读的是**刚渲染出的产物**而非 `breaking` 计数:计数蕴含标记,反向不成立 (作者自写的强调标签超过 48 字符时不计入 breaking,但文本仍进入渲染行,被门更宽的 `/\*\*BREAKING/` 命中);`major` frontmatter 也会单独触发门。第三个信号 (conventional-commit `!`)在本生成器下不可达,注释里写明原因。 自测新增 8 条,含**通过真实门二进制**的往返:占位 → 门红(引用其原文),换成真实 处置 → 门绿。门模块的 CLI 派发在顶层执行,import 它等于运行它,故以子进程 + 临时仓 驱动,不在发布路径上引入耦合。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
反向验证(把脚手架发射消融掉再跑自测)暴露一条本 PR 自己的毛病:没有占位可替换时 `replace()` 是空操作,随后的 `git commit` 在干净树上退出 1 并抛出,整份自测以一段 `execFileSync` 栈中止,ROUND TRIP (green) 这条根本没来得及报告。消融正是下一个读者 最需要「哪条断言红了」的时刻。 于是:替换先经 `check()` 断言(占位确实在、替换确实改动了文件、前后各恰好一个标记), 提交带 `--allow-empty`。消融后 9 条中 8 条具名判红、无异常;恢复后 9 条全绿。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
hotlong
marked this pull request as ready for review
August 8, 2026 04:31
hotlong
enabled auto-merge
August 8, 2026 04:31
This was referenced Aug 8, 2026
This was referenced Aug 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6494
objectui-changeset-digest.mjs从 #6099 起会在正文打**BREAKING**,而 #6148 的门要求任何声明破坏性的 changeset 必须携带adr-0087:处置标记。生成器不写这个标记,Check Changeset判红。PR #6476 手工补了一行而绿 —— 但那份 changeset 是生成的,下一次 pin bump 重新生成时手工标记消失,同一个红复现。按分诊钉死的方向 1:生成器写入一份门仍会判红的脚手架,逼出一个书面答案。⛔ 方向 2(生成器硬写
not-required)与方向 3(门侧豁免)未被采用,也未被自行替换。锚点自核(未引用卡片,在本工作树
origin/main@3510e4a25重测)分诊在
3a1d9c7上核过的两条,在3510e4a25上仍然成立:BREAKING_MARKER常量位置scripts/objectui-changeset-digest.mjs:331,应用于:336(改前)bump-objectui.sh中adr-0087命中数0/0.objectui-sha0cf8f0f70d10…,正是 issue 所指.changeset/console-0cf8f0f70d10.md那次 bumpcheck-adr-0087-registration.mjs:225-231breakingDeclaration(),三个信号取并集:416收集标记、:423/:430解析两种合法写法改后新常量落在
:349(BREAKING_MARKER)、:392(ADR_0087_SCAFFOLD)、:430(artifactDeclaresBreaking)。脚手架的确切写法
<加字母」的 sanitizer 规则(<加感叹号不在其内),是第三种损坏形态 —— 本 PR 初版正文的三处标记全被渲染成空。因此下面凡展示标记字面处,尖括号一律写成</>实体,且不放进代码围栏(围栏内实体不解码)。权威字节见scripts/objectui-changeset-digest.mjs:392。正文末尾追加恰好一个标记:一行 HTML 注释,注释体是
adr-0087: TODO — the pin bump cannot answer this; a human must (objectstack#6494)。连同注释定界符的完整一行:<!-- adr-0087: TODO — the pin bump cannot answer this; a human must (objectstack#6494) -->
门为什么把它认成「有标记但未作答」而不是「无标记」:
:416用adr-0087\s*:收集标记 —— 这一行命中,所以不会走「noadr-0087:disposition marker」那条。:423的^registered\s+与:430的^not-required\s*\(双双落空(正文以TODO开头),于是落到:437的unparseable disposition: "…",把占位原文逐字引回日志,并带出门自己的四种合法写法。N markers -- exactly one disposition is expected),那条红说的是工具而不是问题,所以正文里不允许出现第二处adr-0087:。用 HTML 注释的理由与门自己头注一致:changeset 正文会逐字进 CHANGELOG.md,标记对终端用户渲染为空,而在 diff、
git grep和门日志里始终可见。生成器不选处置。 写
not-required (…)(哪怕是通常正确的那一类)等于让生成器代替人做 #6148 要提取的判断,而且此后每次 bump 都会被无人阅读地写一遍 —— 那道门的全部设计就是它从不替你作答;生成器替你作答只是同一个洞换了作者。判定读的是「刚渲染出的产物」,不是
breaking计数artifactDeclaresBreaking({ bump, body })镜像门三个信号中本生成器可能触发的两个:major—— RC pre-mode 与CONSOLE_BUMP=major都可达,且它单独就能触发门,正文里可以一个标记也没有。因此breaking > 0这种代理判据会漏掉这类产物(自测里有一条专门钉它)。**BREAKING(或BREAKING CHANGE:行)。!)不可达:产物首行是本脚本自己的固定句Console (objectui) refreshed to …,而门的^[a-z]+(\([^)]*\))?!:区分大小写。注释里写明了理由,而不是默默不实现。为什么读产物而不读计数:两者几乎一致,缺口是单向的。⚠️ 提示句按构造会复述该标记),所以计数蕴含信号;反向不成立 —— 作者自写的强调标签若超过
breaking > 0必然渲染出**BREAKING**(BREAKING_EMPHASIS_LABEL的 48 字符尾巴,就不计入breaking,但其文本仍进入渲染行,被门更宽的/\*\*BREAKING/命中。读产物按构造堵住这个缝,而不是靠两个判据保持一致。没有 import 门的函数:那个模块的 CLI 派发在顶层执行(没有
import.meta.url === argv[1]守卫),import 它等于运行它;而且会让发布路径上的脚本多一个「门被改名就崩」的耦合点。一致性改为在自测里用子进程 + 临时仓驱动真实门来钉。往返实证:真实区间、真实门二进制
用真实 objectui 区间(
7dfbeb704e1e..e6fdbdcc4bb2,6 份 releasing、2 份标注式破坏,正是 objectui 的常规形状),生成一份新增的.changeset/console-e6fdbdcc4bb2.md,提交后跑真实门(该门读已提交树,见 issue 同源观察):RUN 1 —— 带占位(本 PR 的产物):红,且是「未作答」这一条
门列出的四种合法写法,逐字转录(HTML 注释包裹,尖括号为实体):
占位之所以是「未作答」而非「答错」,正是因为它一条都不匹配。
RUN 2 —— 对照组,把标记整行删掉(= 本 PR 之前的产物形状):同样红,但是另一条
两条红的文案不同,这正是「present-but-unanswered」与「absent」在日志里可分的证据 —— 不是「门不再报错」这种会真空通过的负向断言。
RUN 3 —— 把占位换成真实处置:绿
三次探针提交跑完即
git reset --hard丢弃,本 PR 只含 1 个文件的改动。再生存活性(本 issue 的真正交付物)
把 issue 里那份已被手工补过标记的 changeset 原地重新生成一次:
grep的两行(-是被再生冲掉的手工标记,+是再生后的占位;尖括号为实体):-<!-- adr-0087: not-required (already-registered dashboard-widget-action-aria-removed) 本条目声明的两处破坏都落在 objectui 自家 npm 包的 TypeScript 导出面… -->+<!-- adr-0087: TODO — the pin bump cannot answer this; a human must (objectstack#6494) -->手工标记被再生冲掉是实测到的,不是推断;改后它冲掉之后留下的是一个门会判红的问题,而不是一片沉默。(该文件已
git checkout还原,不在本 PR 内。)bump 时刻的提示
生成器同时向 stderr 打一行(shell driver 用
BUMP="$(…)"只截 stdout,所以这行能到操作者眼前):门
pnpm lint(eslint,含 lint.yml 的族门)LINT_EXIT=0;CI 的 ESLint job 亦已 successpnpm check:objectui-changeset(digest 自测 + objectui-range 自测)DIGEST_GATE_EXIT=0,✓ all checks passednode scripts/check-nul-bytes.mjsOK (scanned 6128 tracked text file(s) … no raw ASCII control bytes)grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'自扫改动文件check-adr-0087-registration往返turbo run typecheck未在本地跑:改动是仓根scripts/*.mjs,实测不在任何 TS program 的 include 内(唯一提到 scripts 的是packages/spec/tsconfig.scripts.json,指的是该包自己的scripts/),没有可用的 typecheck 输入;CI 的 TypeScript Type Check job 仍会跑全量,本 PR 会盯到它出结论。反向验证:先声明,再运行 —— 两处与声明不符,如实记录
消融方式:只把发射改回
const body = rendered;,常量、判定、断言全部保留。声明(运行前写下):5 条转红(EXACTLY ONE / PLACEHOLDER / major FRONTMATTER / ROUND TRIP red / ROUND TRIP green),2 条仍绿(负极性那条;「三方一致」那条 —— 理由是两边一起变 false)。
观测:
d.adr0087Scaffold === (d.bump === 'major' || /\*\*BREAKING/i.test(d.body))右侧锚在门的判据上,不随发射消失;于是 flag=false 对 criterion=true,等式破裂。这条断言比我声明的更强,声明的推理只看了第一个子句。replace()空操作 → 干净树上git commit退出 1 →execFileSync抛出,只剩一段栈。方向与声明一致(它确实不会绿),但诊断质量不合格:消融正是下一个读者最需要「哪条断言红了」的时刻。这是本 PR 自己的毛病,就地修好(第二个 commit):替换先经check()断言(占位确实在、文件确实变了、前后各恰好一个标记),提交带--allow-empty。硬化后重跑:消融时 9 条中 8 条具名判红、0 崩溃,仍绿的只有那条声明为负极性的;恢复发射后 9 条全绿。
自测新增 9 条(含真实门往返 2 条 + 替换到位 1 条)。其中「非破坏性产物不带任何标记」一条被显式标注为负极性:消融时它同样不会红,保留是因为它钉的是另一类回归(在门根本不判的产物上留一个永远无人作答的标记,那会教会下一个读者忽略这个标记),并配了三条正向守卫防止真空通过。
文件面与 changeset
只改
scripts/objectui-changeset-digest.mjs(含其内联自测)。⛔ 未触碰scripts/check-adr-0087-registration.mjs(只读)、scripts/bump-objectui.sh、.github/workflows/**、content/docs/releases/**。仓内工具改动,不发布任何包 ⇒ 无 changeset,已贴
skip-changeset。Generated by Claude Code