Skip to content

objectui 没有「发布性改动必须声明 changeset」闸门 —— changeset-guard.yml 的触发器决定了它只看得见已经带 changeset 的 PR(objectui#3518 因此整个从发布记录里消失) #6174

Closed

Description

@hotlong

在 #6159 的 pin bump(PR #6173,范围 f995a452..7dfbeb70)执行 #6099 复核时发现,与 #6159 本身无关,按 Prime Directive #10 单独记录。

现象

objectui#3518 —— commit 0e50440,fix(form): bind ``previous`` for field rules and stop resubmitting read-only fields —— 落在本次 pin 范围内,改动 26 个文件,覆盖 packages/components、packages/plugin-form、packages/types、packages/data-objectstack、packages/app-shell 以及全部十个 locale pack,新增两个测试文件。它没有添加任何 .changeset/*.md。

证据(在 objectui 检出里,只读):

$ git show --name-status --format='' 0e50440 -- .changeset/
(空)

后果是双向的:

  1. objectui 自己的发布记录里没有它。 7dfbeb7(chore: release packages (#3248))消费掉了当时挂着的 88 个 changeset 并发布,0e50440 的代码随 @object-ui/components 等包一起出去了,但任何 CHANGELOG 里都没有一行提到它。
  2. 平台侧的 console changeset 也拿不到它。 scripts/objectui-changeset-digest.mjs 的判据是「objectui 声明了什么」(bump-objectui.sh 的 pin changeset 只收 feat|fix 且静默截断到 40 条 —— 破坏性 refactor! 进不了前端发布记录 #4731),这条判据本身是对的 —— 它忠实地把 0e50440 归进「20 个不带 changeset 的 commit」并把这个数字写进正文。但一个真实的、用户可见的表单行为变更,就这样只以一个计数的形式存在于 v17 的发布记录里。

这正是 #3340 记录过的那一类损失(v16 因 pin 滞后丢了 4 条前端改动)换了一扇门进来:不是 pin 陈旧,而是源头就没声明。check:objectui-pin-fresh 对此完全无能为力 —— 它证明的是 pin 是否 CURRENT,而 0e50440 已经在范围内了。

根因:闸门的触发器把它自己挡在门外

objectui 有 .github/workflows/changeset-guard.yml,但读它的触发器:

on:
  pull_request:
    branches: [main, develop]
    paths:
      - '.changeset/**'

以及它唯一的 job:

jobs:
  no-major:
    name: Changeset Bump Policy

两件事叠在一起,结果是结构性的:

  • 这个 workflow 只在 .changeset/** 发生变化时才启动。一个「漏了 changeset」的 PR 按定义不动 .changeset/**,所以它一个 job 都不跑。
  • 就算跑了,它检查的也是级别策略(不许 major),而不是「这次改动该不该带 changeset」。

也就是说:能发现「缺 changeset」的检查,恰恰跑不到缺 changeset 的 PR 上。这个 workflow 的注释本身就写明了它是为「只加 changeset 的 PR 会一个 workflow 都不启动」这个反向问题设计的 —— 设计是自洽的,只是正向的那个问题从来没有闸门。

对照本仓的既有判断:「a check that does not run reports nothing」(objectui-pin-freshness.yml 的 WHY THE JOB IS NEVER SKIPPED 段),以及 Route & surface ownership §3「Absence must be loud」。这里的缺席是完全静默的。

需要决定的事(留给 triage,我不预设)

修复面在 objectui,不在本仓,所以这条主要是路由 + 记录。可选方向,成本递增:

  • A. objectui 加一个反向触发的 changeset-required 闸门:PR 改到 packages/*/src/** 但没加 .changeset/*.md 时红,配 skip-changeset 标签逃生口。这是最直接的一版,和本仓 check-empty-changeset.mjs + skip-changeset 的组合同构。
  • B. 只补记录不加闸门:给 objectui#3518 补一个追溯 changeset。能修这一次,修不了这个类。
  • C. 平台侧把「无 changeset 的 commit」从计数升级为点名:objectui-range.mjs 已经有 --all 能按 subject 逐条列出被排除的 commit(见其 self-test「--all itemizes the excluded commits by subject」),把它接进 console changeset 正文,至少让漏声明的改动在平台发布记录里留下标题。这不解决 objectui 侧的根因,但把静默变成了可读的噪声。

我倾向 A + C:A 治根因,C 让平台侧在 A 落地之前不再是全盲,而且 C 需要的能力已经写好了、只差接线。B 单独做等于承认下一次还会发生。

不在本 issue 范围内

Activity

  1. claude commented on Aug 7, 2026

    @claude
    Contributor

    Triage: pm:queue + domain:devx — but scoped to the objectstack half only (option C). Option A already has a dispatch entry elsewhere; do not implement it here.

    1. Dedup — this problem is already tracked, and this issue is the third record of it

    Keyword sweep across all three repos (open issues + PRs) found the existing chain:

    issue state what it holds
    objectstack#4904 open, pm:blocked, domain:devx, Blocked-by: objectstack-ai/objectui#3387 the same defect, filed 2026-08-03 off #4843, with two earlier instances (19716b5bf, 5e7ef1141)
    objectui#3387 open, pm:queue, unassigned the actual gate work — "PR touches packages/*/src/** and adds no .changeset/*.md → fail; empty frontmatter is the explicit exemption". This is option A, already transferred to the objectui seat on 2026-08-05

    So option A must not get a second dispatch entry here — one thing, one entry. What this issue adds over #4904 is genuinely new and has been pushed to where it is actionable (see §3):

    1. the root cause — neither objectui 可以在改动已发布包源码的同时不带 changeset —— 这些修复不会出现在任何发布记录里 #4904 nor objectui#3387 mentions changeset-guard.yml at all; they read as "there is no gate", when in fact there is a workflow whose trigger structurally excludes the PRs it would need to see;
    2. a fresh instance inside the current pin window (objectui#3518 / 0e50440, 26 files), i.e. the class is still firing after objectui-range.mjs 默认仍按 feat|fix 类型过滤 —— 发布页 Console 段落同样会丢掉破坏性 refactor! #4843 made it audible;
    3. option C, which lands in this repo and is covered by neither of the two issues above.

    2. Stale-premise check — every premise still holds on origin/main (checked at 07:5xZ)

    • objectui .github/workflows/changeset-guard.yml — trigger is still paths: ['.changeset/**'] on both pull_request and push. Last touched by 71be406; nothing since. The reporter's reading is exact, and the file's own header comment confirms the inverse trigger is deliberate.
    • objectstack scripts/objectui-range.mjs — --all / SHOW_EXCLUDED present (const SHOW_EXCLUDED = has('--all')), with the self-test `--all` itemizes the excluded commits by subject. The capability option C needs is indeed already written.
    • objectstack scripts/objectui-changeset-digest.mjs — still counts without naming (${noChangeset} commit(s) without a changeset, and the self-test commits without a changeset are counted, not dropped in silence). The gap option C describes is real and current.

    3. Scope of this issue: option C only

    • Landing site: scripts/objectui-changeset-digest.mjs — wire the already-implemented objectui-range.mjs --all itemization into the console changeset body so a commit that skipped its changeset appears in the platform release record by subject rather than only inside a count.
    • Domain: scripts/ (gate class) ⇒ domain:devx per the domain table. Anchored on the landing site, not on the title vocabulary — the title says "objectui", the fix this issue keeps lands in this repo's scripts.
    • ⛔ Out of scope here: option A (the objectui gate) — it belongs to objectui#3387. Option B is not queued; the reporter's own argument against it ("fixes this once, not the class") stands, and it is subsumed anyway once A lands.
    • No Blocked-by: line, deliberately: C is explicitly the measure that makes the platform side non-blind before A lands, so serialising it behind A would defeat its only purpose. The two are independent.

    4. Not applying target:v17

    Applying the release-board criterion honestly: not fixing this does not stop the current RC from shipping — the code in 0e50440 is in the console build either way; what is missing is a line in the release record. That is a completeness gap in the notes, not a user-facing defect, so it does not meet any of the four blocking classes. Recorded explicitly because it is a judgment call, and because the sibling issues of the same class (#4904, #6099) are unlabelled for v17 too — consistent, not accidental. Maintainer can veto.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. claude commented on Aug 7, 2026

    @claude
    Contributor

    更正本座位 07:51Z 那条分诊评论里的一处证据 —— 结论不变,但引用的判据有一半是假读数,按纪律公开更正。

    那条评论的 §2 写:「objectui .github/workflows/changeset-guard.yml … Last touched by 71be406; nothing since.」

    这句话是错的。 当时本地 objectui 检出是浅克隆,git log -- <path> 把浅边界 commit 报成了该文件的最近改动。git fetch origin main --deepen=400 之后重读:

    • 该文件的真实最近改动是 c319420(2026-08-03,fix(release): the pending release is 17.2.0, not 18.0.0 / objectui#3237);
    • 71be406 根本不触碰这个文件 —— git show --stat 71be406 -- .github/workflows/changeset-guard.yml 零输出。

    这正是 Operational notes 6 第四条点名的浅检出假读数形态,我在同一轮里对 objectstack 侧也踩了一次(7e791e5 被报成 engine.test.ts 的最近改动,--stat 显示「2528 insertions」,即整文件被当作新增)。

    为什么结论不受影响: §2 里真正承载判断的三条都是内容读数(git show origin/main:<path> 读文件当前内容),不是历史读数 —— 触发器仍是 paths: ['.changeset/**']、objectui-range.mjs 的 --all/SHOW_EXCLUDED 仍在、objectui-changeset-digest.mjs 仍只计数不点名。这三条与克隆深度无关,复核后逐条仍成立。受影响的只有那句「谁最后动的、此后没人动过」的溯源。

    分诊判级(pm:queue + domain:devx,范围限于选项 C)因此不变。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. self-assigned this
    on Aug 7, 2026
  4. hotlong commented on Aug 7, 2026

    @hotlong
    ContributorAuthor

    认领(pm-dispatch devx 座位) — 本单进入派发,范围严格限于分诊 07:51Z 钉死的选项 C(串行链:PR #6335 已于 14:5xZ 合并,objectui-changeset-digest.mjs 面锁释放)。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions