fix(spec): 重锚 authorable-surface.base.json 改为显式动作 —— 构建不再顺手推进删除门的锚点 - #5807
Merged
baozhoutao merged 1 commit intoAug 6, 2026
Merged
Conversation
packages/spec/authorable-surface.base.json 是 #4650 删除门在无法访问 origin/main 时 使用的基线锚点(#5235)。它不是本包源码的投影,而是某个**上游 commit** 的快照 —— 正因为受测 commit 改不了它,它才有资格当基线。 在此之前,只要它与 git 解析出的基线有差异,每一次 gen:schema 都会重写它。而 gen:schema 是 pnpm build 的第一步,于是任何一个依赖闭包里含 @objectstack/spec 的包 的构建、以及 check:docs,都会触发。三位 dev 在三个互不相关的任务里各自撞上 (#4990 净删 110 键、#5155 同样 110 键、#5660 +3 键),那 110 个正是 #4988/#5321 刚 退役的 ui/ComponentAnimation 族 —— 锚点越过退役点之后,删除门就再也看不见那次退役, 而且推进前后两种状态门禁**都判绿**。三次都只是靠提交前逐行读 git status 拦下的。 改法是给锚点一个属于它自己的模式: - 新增 --update-base(脚本 gen:authorable-surface-base),是唯一写入该文件的路径; - gen:schema 与任何构建都不再写它,滞后只打印一行 ℹ️ 并给出显式命令(滞后本来就不 是错误:main 上 merge base 即 HEAD,该文件必然落后自身 surface 一个 PR); - --check 保持严格只读,缺文件仍然致命;--check 与 --update-base 互斥并在生成前拒绝。 锚点真实性语义完全未动:baseRev 仍须是 origin/main 的祖先且键集与该 commit 一致, 仍只从 git 解析的基线写入(绝不从被检查的构建),写入仍发生在删除门裁决之后 —— 所以显式模式同样无法把基线推过一次未获证明的删除。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01559M8FVm6W6vDLABL3jvdW
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 110 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
baozhoutao
marked this pull request as ready for review
August 6, 2026 06:17
This was referenced Aug 6, 2026
baozhoutao
deleted the
claude/issue-5358-check-authorable-surface-readonly
branch
August 6, 2026 06:29
This was referenced Aug 6, 2026
Closed
This was referenced Aug 6, 2026
Closed
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 #5358
前提核验:一半已过期,一半仍然成立
按「issue 是线索不是规格」的要求,先对
origin/main(1624f4ad)复核了本单的两条现象。第 1 条(
--check会重写工作区)已经不成立。build-schemas.ts的锚点写入分支在当前 main 上已经是if (drifted && !CHECK),--check不写。干净树实测:第 2 条(构建/
gen:schema路径)完全成立,也正是三条 issue 评论(#4990 / #5155 / #5660)复现的那条。同一棵干净树:所以本 PR 修的是写入分支的触发条件,而不是
--check分支 —— 与评论区三位 dev 的结论一致(「修法覆盖 build 路径而不只是--check路径」)。--check的只读性作为不变量被补了自证测试,但它今天本来就绿,下面「反向核验」一节如实标注了这一点。为什么这个锚点和它的两个邻居不同
gen:schema写三个受版本控制的产物。其中json-schema.manifest.json与authorable-surface.json是本地源码的投影 —— 重新生成永远是对的,它们的 diff 就是被 review 的改动本身。authorable-surface.base.json不是任何本地东西的投影:它是上游某个 commit 的快照,是 #4650 删除门的基线,而它之所以有资格当基线,恰恰因为受测 commit 改不了它。把它做成「每次构建顺手刷新」,等于让这条保证依赖于「没有人跑过构建」。代价是可量化的:一次触发 = 110 条删除(#4990 / #5155),而那 110 个正是 #4988/#5321 刚退役的
ui/ComponentAnimation族。锚点越过退役点之后,删除门就再也看不见那次退役 —— 而且推进前后两种状态门禁都判绿,因为两者各自自洽(#5660 的观察)。三次都只靠人眼逐行读git status拦下。滞后则是安全方向的:锚点越旧,离线构建需要交代的键只会更多,不会更少。
改法
给锚点一个属于它自己的模式,而不是加一条更聪明的启发式:
gen:schema/ 任何pnpm build/check:docscheck:authorable-surface(--check)gen:authorable-surface-base(--update-base)--check --update-base真实性语义一个字没动:
baseRev仍须是origin/main的祖先且键集与该 commit 逐行一致;仍只从 git 解析出的基线写入(绝不从被检查的构建 —— 离线时--update-base同样无能为力);写入仍在删除门裁决之后,所以新的显式命令也无法把基线推过一次未获证明的删除(有测试)。顺带修正的处方:所有指向该文件的错误信息此前都写「运行
gen:schema」,而gen:schema已经不再碰它 —— 全部改为指向新命令。这正是本单描述的缺陷类别(声明与实际不符),留着就是新的一处。三处不在改动范围、但被显式处理的相邻面
SURFACE_BASE_DESCRIPTION常量刻意逐字未改。 该字符串是锚点文件规范形式的一部分(readCommittedSurfaceBase把任何差异判为手改并致命),改它就必须同时重锚该文件 —— 而本单的验收标准要求该文件与origin/main逐字节一致。因此描述里那句 "Written only bygen:schema" 现在是欠描述而非错误:写入者仍是同一个生成器scripts/build-schemas.ts,只是收窄到--update-base一种模式。收窄方向是安全的。代码里留了注释说明,建议下一次真正重锚时把这句一并带上(那会是一个干净的、被 review 的 diff)。scripts/regen-artifacts.mjs只改注释,gen字段仍是gen:schema。 合并驱动打印的处方会照抄这个字段;若改成gen:authorable-surface-base,驱动就会在 merge 未 commit 时指示用户跑那条命令 —— 那正是 os-regen 驱动指示的gen:schema在 merge 未 commit 时运行,会把 authorable-surface 锚点倒退回旧 merge-base —— 生成器写入、门全绿、静默撤销 main 的锚点推进 #5370 的重演(MERGE 态下merge-base(HEAD, origin/main)仍是分支旧分叉点,锚点会向后倒退,且倒退后依然 authentic,没有门会拦)。保留gen:schema:对该文件的两个邻居正确,对它自己是无害空操作。注释写清了这个取舍。check:generated的账本新增第三类EXPLICIT_GENERATORS。UNGATED_GENERATORS会谎称「没有门核验它」(check:authorable-surface核验其真实性),GATED又会把它放进--fix的射程(那正是本次移除的副作用)。新类别要求声明gatedBy,且该门必须是本账本已声明的门,否则 reconcile 失败。测试
新增
build-schemas-check-mode.test.ts的#5358块(7 例),复用该文件已有的沙箱:把scripts/拷进临时目录、真建一个带refs/remotes/origin/main的 git 仓库,生产代码路径逐字节不变。fixture 是真实形状 —— 一个滞后但真实的锚点(指向更早的上游 commit,键集与该 commit 的 baseline 逐行一致),提交到干净树后再跑,断言就是本单要求的那条:git status --porcelain -uno为空。pnpm --filter @objectstack/spec test:319 文件 / 8152 用例全绿;typecheck绿;check:merge-driver绿;check:generated --reconcile-only绿(18 check: + 13 gen: scripts, all classified)。反向核验(方向先说,再跑)
把
build-schemas.ts换回origin/main的版本、只留新测试,预测「构建路径的两条红、--check那条绿」。实际 5 红 22 绿,与预测一致:核心那条的断言就是本单的机制本身:
如实说明:
a --check run leaves the anchor byte-identical这一条在旧代码下也是绿的。 它不是修复的证据,而是不变量的钉子 —— 本单第 1 条现象在派单前就已被别的改动修掉了。把它写成红是不诚实的,所以这里标出来。真仓库上的验收证据
本 PR 中
packages/spec/authorable-surface.base.json与origin/main逐字节一致(上面第三步之后已git checkout --还原),不含任何一次性重锚。check:api-surface在本地报红,原因是这个全新 worktree 从未build过、packages/spec/dist不存在(该门读 built.d.ts,即 AGENTS.md 记的 stale-dist 幻影)。本 diff 不含任何packages/spec/src/**文件,导出面不可能变。对三条相邻单的影响(必答)
gen:schema在 merge 未 commit 时运行,会把 authorable-surface 锚点倒退回旧 merge-base —— 生成器写入、门全绿、静默撤销 main 的锚点推进 #5370(merge 未 commit 时gen:schema让锚点倒退):大幅变简单,但没有变得不必要。该单复现路径的字面触发点(照抄 os-regen 驱动的处方,在 merge 未 commit 时跑gen:schema)在本 PR 之后消失了 ——gen:schema已经完全不碰锚点。但根因(resolveSurfaceBase()在 MERGE 态下把merge-base(HEAD, origin/main)解析成分支的旧分叉点)一个字没动,只是现在唯一能走到它的入口变成一条需要人手敲的显式命令。本 PR 特意不把驱动的处方改成指向新命令,以免把它重新变成一条被指示的路径。authorable-surface.json在 main 上不是gen:schema的输出 —— 基线手编的字节级实锤(#4650 加固建议) #4663(authorable-surface.json字节级 round-trip 门):不受影响。那是另一个文件(.json,不是.base.json),而它的写入路径(surfaceChanged && !CHECK)本 PR 完全没动。锚点文件自己的字节级 round-trip 检查(readCommittedSurfaceBase比对serializeSurfaceBase())也原样保留 —— 只是现在写它的模式变少了一种,这只会让「文件里的每个字节都来自生成器」更容易成立。.type就能让一个 tombstone 冒充「已登记迁移」 #4659(检查 (b) 的叶名匹配):不受影响。registeredClauseMajors()/registeredRetirementSurfaces()的.split(' / ')+endsWith('.' + prop)逻辑本 PR 一行未改,判定谁调用它们的时机也没变。越界发现
无。改动严格限于锚点写入分支及其处方,外加两处为保持一致必须同步的账本/注释(见上文第 2、3 点)。
🤖 Generated with Claude Code
https://claude.ai/code/session_01559M8FVm6W6vDLABL3jvdW
Generated by Claude Code