Skip to content

spec 的 #4650 删除闸门在「按 SHA 钉住的消费者构建」里无法锚定 origin/main,硬失败 —— cloud 的镜像构建与 pin bump 全线卡死 #5235

Description

@os-zhuang

这条挡住了 cloud 的所有 framework pin bump(实测:objectstack-ai/cloud#1091,把 pin 从 ad047d2e 挪到 586d6f70 后,verify-objectos-ee-image 立刻红)。

现象

cloud 的 EE 镜像构建里,framework 的 @objectstack/spec build 直接失败:

@objectstack/spec:build: ─── Summary ───
@objectstack/spec:build:   Generated: 1654 (135 as input shape)
@objectstack/spec:build:   Skipped:   23 (unsupported types: function, date, bigint, custom)

@objectstack/spec:build: ❌ Cannot resolve origin/main to anchor the authorable-surface deletion check (#4650).

@objectstack/spec:build:    Deleted baseline lines are validated against authorable-surface.json at the
@objectstack/spec:build:    merge base with origin/main — a baseline this commit cannot rewrite. Without
@objectstack/spec:build:    that anchor the tombstone gate can be bypassed by hand-editing the file, so
@objectstack/spec:build:    this build fails instead of silently skipping the check.

@objectstack/spec:build:    Fix: `git fetch origin main` (or point refs/remotes/origin/main at your
@objectstack/spec:build:    upstream main) and re-run.
@objectstack/spec:build:  ELIFECYCLE  Command failed with exit code 1.

@objectstack/spec#build:  ERROR  command (/repo/objectstack/packages/spec) pnpm run build exited (1)
 Tasks:    0 successful, 3 total
Failed:    @objectstack/spec#build

归因(已逐条核过,不是推断)

检查 结果
该报错字符串在 cloud 当前 pin ad047d2e 上存在吗 不存在(git grep 零命中)
在 586d6f70 上呢 存在于 packages/spec/scripts/build-schemas.ts
同一个 CI job 在别的 cloud PR 上(仍是旧 pin)通过吗 全部通过(近 10 次运行只有改 pin 的这次红)
本地在有 origin/main 远程引用的 framework worktree 里构建 586d6f70 通过 —— 所以本地验证看不见它

也就是说:闸门是本区间新增的,且只在「没有 origin/main 远程引用」的构建环境里触发。

为什么消费者构建必然踩到

resolveSurfaceBase() 先 git rev-parse origin/main^{commit},失败则自愈式 git fetch --depth=1 origin +refs/heads/main:refs/remotes/origin/main,再失败就 fatal。

cloud(以及任何按 SHA 消费 framework 的下游)拿到的 framework 树来自 actions/checkout@v4 + ref: <pinned SHA>(默认 fetch-depth: 1):只有那一个 commit,没有 refs/remotes/origin/main。cloud 三个 workflow 全是这个形状:

  • .github/workflows/verify-objectos-ee-image.yml(已实测红)
  • .github/workflows/pin-smoke.yml
  • .github/workflows/test.yml

镜像那条更硬:framework 是被 COPY objectstack/ /repo/objectstack/ 抄进 buildx 构建阶段再构建的,自愈 fetch 在那里也没成功。

为什么这不只是「cloud 自己加个 fetch」就完事

可以让 cloud 三处 checkout 都 fetch-depth: 0(或显式 fetch main)绕过去,但那给这个开源产物加了一条很难看的性质:

「构建一个按 SHA 钉死的 @objectstack/spec 发布树,必须能联网访问它自己 main 分支的当前状态。」

这会打断离线 / 气隙构建、fork 构建、以及任何按 tag 复现历史版本的构建——而在这些场景里闸门要防的事根本不存在:树是不可变的、已经合并过的,没有「我这个 PR 相对 main 删了什么」这个问题可问。闸门在消费者构建里既问不出答案,也没有要防的对象。

同时也不该简单加个 SKIP=1 环境变量——那正是 #4650 要堵的旁路。

建议方向(留给维护者定)

  1. 闸门只在「开发式检出」跑:判据不是环境变量,而是可验证的事实——例如 authorable-surface.json 相对已提交状态有改动、或存在多于一个 commit / 存在分支引用。消费者构建里没有可删的基线,就没有要证明的删除。
  2. 或者把基线锚点变成树内产物:把 merge-base 那份基线在发布时固化进包内(如 authorable-surface.base.json),让闸门比对树内文件而不是远程分支——保持「PR 改不动的基线」这个性质,同时不依赖网络。

两条都保留 #4650 的防旁路性质;第 2 条对下游最友好。

影响面

  • cloud 的 .objectstack-sha 今天无法往前挪到任何含该闸门的 SHA,pin 会一直停在 ad047d2e(现已落后 main 119+ commit),连带 staging 部署与所有等 pin 的 issue(如 cloud#1012)。
  • 同样的形状会打到 release-objectos-ee.yml 与 deploy-* 系列(都在 pin 住的 framework 检出上构建镜像)。

出处

cloud#1012 的 pin bump(PR objectstack-ai/cloud#1091,会话 session_015W6nhsDrz6zWQc8je12a1t)。失败 run:https://github.com/objectstack-ai/cloud/actions/runs/30905564626 。未指派,按 Prime Directive #10 归档。

Activity

  1. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    订正影响面:坏的是「在 buildx 阶段构建 framework」的那条路,不是所有钉住检出

    正文里我按「三个 workflow 的 checkout 形状相同」推断三条都会红。再等了 ~50 分钟看实际结果,这个推断只对了三分之一,据实更正:

    cloud CI job framework 检出 结果
    verify-objectos-ee-image(buildx 里构建 framework) actions/checkout + COPY objectstack/ 进 Docker 红,闸门 fatal(2.5 分钟内)
    build-and-test(runner 上直接构建) 同样的 actions/checkout@v4 + ref: <SHA> 跑过了 spec 构建阶段(50 分钟仍在后续步骤)
    console-boots / pin-smoke(runner 上直接构建) 同上 同样跑过了 spec 构建阶段

    差别不在 checkout,而在闸门的自愈 fetch 能不能成功:

    let tipProbe = git('rev-parse', '--verify', '--quiet', 'origin/main^{commit}');
    if (tipProbe.status !== 0) {
      git('fetch', '--quiet', '--depth=1', 'origin', '+refs/heads/main:refs/remotes/origin/main');
      tipProbe = git('rev-parse', '--verify', '--quiet', 'origin/main^{commit}');
    }

    在 runner 上这条 fetch 成功(有网、有 checkout 留下的凭据),所以闸门自愈、构建通过——正文引的那句注释「CI's typecheck job checks out shallow with no branch refs, so fetch the one ref this check needs」写的就是这个场景,它是有效的。

    在 buildx 构建阶段里这条 fetch 没成功,于是 fatal。我没有进一步把「无网 / 无凭据 / 私有仓」区分开——这需要在 Docker 里插桩复现,属于修复者该做的一步,我只报实测到的事实。

    对结论的影响

    • 不是「任何钉住检出都构建不了 spec」,收窄成:任何在无法访问 GitHub 的构建环境里构建 framework 的下游都会 fatal。cloud 的镜像构建(verify-objectos-ee-image、release-objectos-ee、deploy-* 都在 buildx 里构建 framework)全在此列。
    • 「离线 / 气隙 / fork 构建会被打断」这一条仍然成立,而且现在有了实测证据——cloud 的镜像构建就是一个真实存在的、构建期不便联网的环境。
    • 建议方向不变,第 2 条(把基线锚点固化成树内产物)依然是对下游最友好的:它让闸门不再依赖构建期网络,同时保留 authorable-surface 的 tombstone 门禁可被手编基线绕过 —— 删掉基线行就删掉了证据(#4638 / #4643 已两次这样过绿) #4650 的防旁路性质。

    Generated by Claude Code

  2. added
    bugSomething isn't working
    and removed on Aug 4, 2026
  3. self-assigned this
    on Aug 4, 2026
  4. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    认领:PM 循环第 2 批(spec 车道,维护者已核准 batch:5;本单列 A1 —— 正在阻断 cloud 发布链)
    会话:session_01ErbEDVAg1No9gdg1pgDAGB
    分支:claude/issue-5235-offline-surface-anchor
    Worktree:objectstack-issue-5235
    域:domain:spec(spec 构建工具面)
    文件面:packages/spec/scripts/build-schemas.ts(resolveSurfaceBase 一段)、树内基线锚点新产物(形如 packages/spec/authorable-surface.base.json,命名以实现为准)、packages/spec/package.json(若需纳入发布 files)、配套测试。⛔ 不动 schema 源与其它生成物语义。

    执行依据:维护者指示按方向 2(基线锚点固化为树内产物)执行,保留 #4650 的防旁路性质;PM 已核实 origin/main 上闸门 fatal 路径仍在、树内无锚点产物,前提成立。落地后 cloud pin bump(cloud#1012 / PR cloud#1091)解锁。


    Generated by Claude Code

  5. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    验收:ACCEPT(PM 复核毕,PR #5304,第二轮 CI 24/24 全绿)

    复核依据(GitHub 实据):

    • 方向 2 落地完整:树内锚点 authorable-surface.base.json(8045 keys)+ 双重真实性校验(祖先 + keys 逐行);写入侧只准 git 解析基线、且在删除闸门判定之后才写(顺序承重:被拒的删除永远推不进锚点);两种锚点都缺时仍 exit 1,无环境变量旁路 —— authorable-surface 的 tombstone 门禁可被手编基线绕过 —— 删掉基线行就删掉了证据(#4638 / #4643 已两次这样过绿) #4650 防旁路性质完好;
    • CI 红自愈质量高:首轮 CI 的浅检出假阳性(88b9b2d 明明是祖先、浅仓判不了)被 dev 在我落返工简报的同窗内自行诊断修复(e80df68:非浅仓才判祖先,浅仓验 keys 半边),并加了用 $GIT_DIR/shallow 忠实截断的两条 pin —— 与 PM 返工方向完全一致,返工轮未消耗;「生产环境替你跑了反向验证」这条记录进 PR 正文,诚实;
    • 7 文件对账吻合(含 .gitattributes + regen-artifacts.mjs 的驱动登记,注明「陈旧合法」与邻居的差异),check:merge-driver 双向自检过,changeset patch 档合理;20/20 测试(12 存量 + 8 新增);
    • RED→GREEN 逐字节复刻 cloud 现场报错,反篡改两式(削行、指本地 commit)均红。

    后续:本 PR 与 #5289/#5293/#5300 同批四单全绿,按生成物纪律串行入队(每落地一单,下一单先 merge-main + 整体重生成再入队)。落地后:cloud pin bump(cloud#1012 / PR #1091)解锁;cloud 的 .git-入上下文绕法(cloud#1098 / PR #1106,~20 分钟冷构建/次)可撤,缓存买回(cloud#1102)—— 已在 PR 正文记录,cloud 车道见 Blocked-by 解除自会跟进,验收时不另开单。


    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