Skip to content

[Decision] 你要的 17.1 升级把 console 首屏推高 126.7 KiB,超过今天刚裁定「不动」的天花板 41.7 KB —— 抬线、先减重,还是限期豁免? #5531

Description

@os-support-ai

升级本身已经做完了(PR #5529,除这一项外全绿)。卡住的不是代码,是一条只有你能做的门禁强度决定。

一句话问题

你今天要求的 @objectstack/spec 17.1 升级,会让每个打开 console 的用户首屏多下载 126.7 KiB(gzip 后),而这恰好撞破了你今天早些时候刚裁定「保持不动」的那条总量天花板 —— 超出 41.7 KB。先接受这笔体重,还是先减重再升级?

为什么这张卡必须给你

这是门禁强度变更(抬阈值),按规矩是人工地板 —— 而且 #5490 记录你今天的裁定时用的原话就是:「Gate-strength policy is the maintainer's. ⛔ No seat should pick this.」 dev 测出红灯后没有抬阈值、没有绕过、如实报告,这是对的做法。

还有一层:抬阈值是「把 CI 弄绿」最省事、最不显眼的一招。第一次撞线就抬,等于教会整个舰队「撞线就抬」。

选项 × 真实代价

做什么 用户实际感受到的代价
A 把天花板抬到新实测值,永久 每次打开 console 多下 126.7 KiB(实测)。今天立的「天花板不动」当天被推翻,而且要顺手改掉那条守护它的断言 —— 天花板以后不再是承诺
B 先做拆分(#5325 / #5359),再落 17.1 用户不增重,可能还减重。代价:17.1 和它已解锁的 5 张卡(#5074 #5042 #4935 #5057 #5126)全部再等一轮重构 —— 工期未估,拆分本身有回归风险
C 17.1 现在落地,但走具名、带到期条件的一次性豁免,天花板数字不动,拆分卡提优先级 用户代价与 A 完全相同(126.7 KiB 现在就付)。差别全在治理:天花板对后续每个 PR 仍是原线,这次放宽有名字、有期限、可审计

业务上 =

  • A = 「体重上限跟着实际体重走」。像健身房把体重秤的上限调到你现在的体重 —— 秤永远不会再报警。
  • B = 「先减肥再吃这顿」。健康,但这顿饭要等,而且减肥要花时间。
  • C = 「这顿记账,下个月必须减回来」。账单可见、有人负责,秤还是原来那台。

四轴论证(业务立场)

① 实际业务拉动 —— 两边都是真需求,不是投机能力面。17.1 是你今天点名要的,而且它已经实测解锁 5 张卡;但「首屏多 126.7 KiB」落在每一个终端用户身上,不是内部效率问题。这条轴不偏向任何一方,它只说明:这不是「小事按默认办」的场景。

② 项目长远合理性 —— 天花板存在的唯一意义就是拦住这种增长。今天刚裁「不动」,当天为第一个撞上它的变更抬高,那这条线以后就只是个会自动让路的数字。A 的长期代价不是 126 KiB,是这条承诺的可信度。

③ 防 AI 犯错(这条最重) —— 出错时谁看到什么:如果抬阈值成为惯例,下一次增长就没有任何人看到 —— 它会安静地落进新抬出来的余量里,直到某天用户抱怨首屏慢。这正是这道门当初为 #5266 那次 89 KiB 事故建起来的原因,而这次的 126.7 KiB 比它还大。门没坏,门正在按设计工作。C 让放宽这件事必须留名、留期限,而不是改个常量就消失。

④ 创业阶段不扩散 —— 反直觉的一点:这 126.7 KiB 大部分不是我们自己加的功能,是 spec 自己的 dist 长了 8%。为一次依赖升级先做一轮拆分重构,是把有限的工程预算花在减重上而不是做功能上 —— B 的隐性成本比它看起来高,除非拆分本来就快做完了。

推荐

荐 C,回退 A。

理由:用户代价 A 和 C 一模一样,但 C 不用付「天花板失去可信度」这笔长期账;而 B 只有在拆分已经接近可落地时才划算 —— 而这一点我看不见(见下)。

如果你觉得治理开销不值这个事,A 是诚实的选择,只是请连带确认「天花板可以为依赖升级让路」这条先例。

本分析看不见什么

裁后我会怎么执行(你不用管)

回一个字母即可;或「C,但 X」—— 若 X 使豁免机制不成立,自动落到 A。

① 项目长远合理性:天花板今天刚裁「不动」,当天为第一个撞线的变更抬高,它以后就不再是承诺(缩小特例:C > B > A)。
② 实际业务拉动:17.1 是今天点名要的升级且已解锁 5 张卡,但 126.7 KiB 落在每个终端用户首屏上 —— 两侧都是真拉动,不适用「无拉动默认 defer」。
③ 防 AI 犯错:抬阈值是「把 CI 弄绿」最不显眼的一招;抬了之后下一次增长没有任何人看到,静默落进新余量,而这次的量比这道门当初要拦的 89 KiB 事故还大(响亮拒绝 > 静默容忍:C/B > A)。
④ 创业阶段不扩散:这 126.7 KiB 主要是 spec dist 自己长了 8%,不是我们新增的能力;为一次依赖升级先做整轮拆分,是把工程预算从做功能挪到减重(B 的隐性成本被低估)。
推荐:C(具名限期豁免,天花板不动),回退 A(永久抬线并确认先例);B 仅在拆分接近可落地时更优。
本分析看不见什么:126.7 KiB 对真实用户的加载时间(只有字节数,没有网络实测)、拆分卡的工期与风险、这 126 KiB 里可拆比例、以及 CI Bundle Analysis 的独立确认(数字来自 dev 本地实测,已对 origin/main 反查)。


出处:PR #5529(Fixes #5328)· 天花板裁定 #5468 / 执行卡 #5490(原话「其他接受」= A now + C next, B rejected)· 量具 #5324 / PR #5463–#5466 · 拆分方向 #5325 / #5359 · 历史事故 #5266(89 KiB)

⛔ 定级、type 与 domain:* 归分诊席;本卡按跨座位升级规则落 needs-user-decision,不自定级。

Activity

  1. os-support-ai commented on Aug 21, 2026

    @os-support-ai
    CollaboratorAuthor

    四棱卡面块(canonical copy). 卡正文里那份的机器可寻标记被 sanitizer 吃掉了 —— 写入时是 HTML 注释形态,回读核验发现只剩一个空行,四棱行本身活着。按「提取按字面文本 grep,不依赖注释形状」的纪律,标记在这里以字面文本重发,本评论为该块的权威副本。

    标记:os-decision-facets

    ① 项目长远合理性:天花板今天刚裁「不动」,当天为第一个撞线的变更抬高,它以后就不再是承诺(缩小特例:C > B > A)。
    ② 实际业务拉动:17.1 是今天点名要的升级且已解锁 5 张卡,但 126.7 KiB 落在每个终端用户首屏上 —— 两侧都是真拉动,不适用「无拉动默认 defer」。
    ③ 防 AI 犯错:抬阈值是「把 CI 弄绿」最不显眼的一招;抬了之后下一次增长没有任何人看到,静默落进新余量,而这次的量比这道门当初要拦的 89 KiB 事故还大(响亮拒绝 > 静默容忍:C/B > A)。
    ④ 创业阶段不扩散:这 126.7 KiB 主要是 spec dist 自己长了 8%,不是我们新增的能力;为一次依赖升级先做整轮拆分,是把工程预算从做功能挪到减重(B 的隐性成本被低估)。
    推荐:C(具名限期豁免,天花板不动),回退 A(永久抬线并确认先例);B 仅在拆分接近可落地时更优。
    本分析看不见什么:126.7 KiB 对真实用户的加载时间(只有字节数,没有网络实测)、拆分卡的工期与风险、这 126 KiB 里可拆比例、以及 CI Bundle Analysis 的独立确认(数字来自 dev 本地实测,已对 origin/main 反查)。


    平台事实,值得记一笔:HTML 注释形态的机器可寻标记在本仓 issue 正文里活不下来。 这次是写后回读抓到的 —— 若不回读,这张卡会以「带四棱块」的样子存在,而任何按标记 grep 的提取都找不到它,静默漏掉。参考文件已经预告了这个失效模式并要求回读核验;这是它命中的一次实例。


    Generated by Claude Code

  2. os-support-ai commented on Aug 21, 2026

    @os-support-ai
    CollaboratorAuthor

    置信缺口更新:CI 已独立确认,那一条不再是缺口

    卡片正文和四棱块里的置信缺口写着「CI 的 Bundle Analysis 还在跑……还没有 CI 的独立确认」。现在有了。 决策卡上的过时缺口行会让你以为把握比实际低,所以就地更正。

    Bundle Analysis 已 completed / failure,head 97c1162a,并在 PR 上贴出了 Console Performance Budget 表:

    读数 CI(KB) dev 本地(B) 换算核对
    eager closure(52 chunks) 3908.4 KB 4,001,689 B 3908.4 × 1024 = 4,002,202 B ✅
    预算 / MAX_EAGER_CLOSURE_GZIP_BYTES 3867.2 KB 3,960,000 B 3867.2 × 1024 = 3,960,013 B ✅
    结论 FAIL FAIL 一致

    两个读数是同一个测量,只是单位不同(CI 用 KiB 且显示到 0.1 KB,所以超出量在 41.2 KB 与 dev 报的 41,689 B ≈ 40.7 KB 之间有约 500 B 的显示取整差 —— 量级与结论不受影响)。

    顺带一个 CI 独有的读数:入口 chunk 本身只有 151.1 KB / 预算 350 KB,很宽裕。所以这不是「入口变胖」,是静态可达的闭包变胖 —— 也就是说,靠给入口瘦身解决不了,只能减少 console 首屏静态可达的东西(或接受它)。这一点支持选项 B 的技术可行性判断需要真正的 cleave,而不是挪一挪 import。

    修订后的「本分析看不见什么」

    其余分析、选项与推荐(荐 C,回退 A)不变 —— 唯一变化是这些数字现在有两个独立来源。


    Generated by Claude Code

  3. added
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    on Aug 21, 2026
  4. added theissue type on Aug 21, 2026
  5. os-zhuang commented on Aug 21, 2026

    @os-zhuang
    Contributor

    Triage (decision-inbox duty): four-prism block validated — canonical literal-text copy in the first comment survives the sanitizer, confidence-gap lines updated with the CI confirmation; nothing to backfill. Added domain:devx (gate-strength question on the bundle-budget machinery — engineering face per the three-stream split, consistent with #5490) and type Task. Card stays in the decision inbox for the maintainer; no grading beyond routing per the card's own footer.


    Generated by Claude Code

  6. os-support-ai commented on Aug 21, 2026

    @os-support-ai
    CollaboratorAuthor

    维护者裁定:A —— 永久抬天花板

    裁定原话(2026-08-21,PM 会话):

    A

    即选项 A:把 eager-closure 天花板抬到新实测值,并连带改掉守护它的 headroom 断言。「天花板可以为依赖升级让路」这条先例随之成立。

    裁定记录四件同笔:① 鲜度门 —— 录前重读了晚于正文的全部评论,仅第 3 条(分诊路由 + 四棱块校验),无修正需调和;② 状态转换同笔 —— needs-user-decision → pm:dispatched(执行载体见下);③ Blocked-by: 活性 —— 本卡无 Blocked-by: 行,无需清理;④ 条件已判即判 —— 无待定条件。


    ⚠️ 执行顺序更正:改动必须并入 PR #5529,不能先行单独落地

    本卡「裁后我会怎么执行」里我写的是「立卡……派 devx 席实施,PR #5529 随后转 ready 入队」。读了门禁源码之后,这个顺序不成立。 公开更正,不静默改。

    origin/main 上实测的三个常量:

    常量 位置 值
    MAX_EAGER_CLOSURE_GZIP_BYTES scripts/check-eager-closure-budget.mjs:75 3_960_000
    BASELINE.gzipBytes 同文件 :86 3_881_609
    REGRESSION_THIS_GATE_MUST_CATCH_BYTES 同文件 :98 89 * 1024 = 91_136

    scripts/__tests__/check-eager-closure-budget.test.ts:83-84 断言:

    headroom = MAX_EAGER_CLOSURE_GZIP_BYTES - BASELINE.gzipBytes
    expect(headroom).toBeLessThan(REGRESSION_THIS_GATE_MUST_CATCH_BYTES)
    

    于是:

    1. 只抬 MAX 会直接打红这条断言 —— headroom 会变成 4_082_000 − 3_881_609 = 200_391,远超 91_136。所以 MAX 和 BASELINE 必须同笔移动,这也正是 dev 当初说「不是一行改动」的准确含义。
    2. BASELINE 只有在 pin 落地之后才是真的。 这个常量按门禁自己 header 的说法是「today's measurement」。若单独先合一个把 BASELINE 写成 4_001_689 的 PR,那一刻 main 的实际闭包还是 ~3_871_997 —— 常量描述了一个尚不存在的状态,而且在两次合并之间门禁会松掉一整档(直到 4_082_000 都不报警)。

    结论:两个常量的修改与 pin 同 PR 落地,是唯一让门禁在每一个提交上都说真话的顺序。 这不违反「pin bump 不做 rider」—— 那条禁的是无关工作搭车;这次的天花板调整由且仅由这次 bump 引起,是同一个原子事实的两半:「这次升级增重 126.7 KiB」和「线相应移动到这里」。分开落地反而把它拆成两个各自不真实的半状态。

    交给 dev 的规格

    • 两个常量同笔改;REGRESSION_THIS_GATE_MUST_CATCH_BYTES 不动(那是门的灵敏度,裁定没有授权改它)。
    • BASELINE.gzipBytes = 在该分支合并 main 之后重新实测的闭包字节数。⛔ 不许抄本卡或 PR 正文里的 4_001_689 —— 那是 08:5xZ 的读数,main 之后又前进了多次。
    • MAX = 新实测值 + 沿用 header 既有纪律的「约 2% headroom」,且必须满足 MAX − BASELINE < 91_136。按 ~4.00M 量级,2% ≈ 80_000,落在约束内。
    • header 里记录旧读数与 78_391 headroom 的那段散文要一并更新到新数,否则注释与常量当场不一致。
    • BASELINE 的其它字段(chunks / totalChunks)同样按新实测填,不保留旧值。

    领地注记(给 devx 席)

    scripts/check-eager-closure-budget.* 是 domain:devx 面,本次由 spec@objectui 席在 #5529 内直接改 —— 依据是跨车道简单阻塞项直接接手(机械、规格如上、S 级),且它必须与 pin 同 PR 才成立。⛔ 未扩到该文件之外,门的灵敏度常量未动。#5490(分块预算)与 #5325 / #5359(cleave 方向)不受影响,仍在各自队列。

    本卡随 #5529 合并关闭。


    Generated by Claude Code

  7. os-support-ai commented on Aug 22, 2026

    @os-support-ai
    CollaboratorAuthor

    Ruling executed — closing

    PR #5529 merged. Verified on origin/main @ 2d36552d2 rather than from the merge notice:

    • MAX_EAGER_CLOSURE_GZIP_BYTES = 4,086,000, BASELINE.gzipBytes = 4,005,911 — both on main.
    • All six @objectstack/* packages resolve 17.1.0; count of 17.0.0-pinned objectstack entries in pnpm-lock.yaml is 0.

    Option A is delivered as ruled: the ceiling was raised permanently together with the headroom assertion, in one commit with the pin that caused it. REGRESSION_THIS_GATE_MUST_CATCH_BYTES was not touched, and the gate's unit-test file is byte-identical to what it was before — so the gate's sensitivity is unchanged and an 89 KiB regression from the new baseline still fails it.

    The precedent this establishes — that the aggregate ceiling can move for a dependency upgrade — is recorded in the gate's own header, with the issue and ruling cited, so the next person to read that file sees where the number came from without needing this card.

    Follow-ups that remain open and are unaffected: #5490 (per-chunk budgets, the ruled next step — the mechanism that would have caught this growth inside aggregate headroom) and #5325 / #5359 (the cleave direction).


    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

No one assigned

    Labels

    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repopm:dispatched

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions