Skip to content

Commit 04fab5e

Browse files
os-zhuangclaude
andauthored
fix(spec): #4650 删除闸门改用树内基线锚点,按 SHA 钉住的离线消费者构建不再硬失败 (#5235) (#5304)
* fix(spec): anchor the #4650 deletion gate in-tree so a pinned build needs no network (#5235) `gen:schema` resolved the authorable-surface deletion baseline only out of git — `origin/main`, with a self-heal fetch — and exited 1 when neither worked. That is right for a developer who forgot to fetch and wrong for every build environment with no route to GitHub: image-build stages that COPY a SHA-pinned framework tree into a container, air-gapped builds, forks, historical-tag reproductions. Those trees are immutable and already merged, so the question the gate asks ("what did this commit delete relative to main?") has no subject there — yet it failed them. The baseline is now also committed as `packages/spec/authorable-surface.base.json`: the keys of `authorable-surface.json` at `baseRev`, a commit on origin/main. - origin/main reachable: unchanged. The gate anchors on the merge base exactly as before, and additionally verifies the committed anchor against it — `baseRev` must be an ancestor of origin/main and its keys must BE that commit's baseline. So the environments that can police the anchor do, and a commit cannot edit it to hide a deletion. - origin/main unreachable: the gate anchors on the committed file and the build proceeds. It still runs — a recorded key this build no longer emits is as fatal as before — and only a git-resolved baseline may write the file, so an offline build can never advance the anchor to its own state. No env-var skip: a deletion check that can be switched off is the bypass #4650 closes. With neither anchor available the build still exits 1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ErbEDVAg1No9gdg1pgDAGB * fix(spec): do not read a shallow checkout as a forged anchor baseRev (#5235) First CI run on this branch failed every job: `merge-base --is-ancestor` answers "not an ancestor" in a depth-1 checkout, because the anchor's commit is fetched as its own shallow root and there is no history to walk between it and the tip. The ancestry half of the anchor verification therefore rejected a baseRev that demonstrably IS on main, exactly the way the merge-base fallback a few lines above already anticipates for the baseline itself. Ancestry is now judged only where it can be: `rev-parse --is-shallow-repository` gates it, and a shallow run says so and verifies the recorded KEYS alone — the half truncation cannot take away, since the fetched commit's tree is present. A full clone (every dev checkout — where the anchor is regenerated, and where a hand-edit is therefore caught) still checks both. Two tests pin it, using `$GIT_DIR/shallow` to truncate the sandbox repo exactly as `--depth=1` does: a lagging-but-authentic anchor stays green, and a shed line still goes red there. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ErbEDVAg1No9gdg1pgDAGB * chore(spec): refresh the in-tree surface anchor onto the post-merge baseline (#5235) `git merge origin/main` brought #5289 / #5293 / #5296 / #5300, three of which change the authorable surface (theme token tombstones, the HttpServerConfig removal, the NotificationAction / EmbedConfig removals). The anchor is written from the merge base, so it moves with it: baseRev 88b9b2d26e1029, 8045 → 8016 keys, regenerated by `gen:schema` rather than text-merged — this file is on the os-regen list precisely because a textual merge of it means nothing. Verified line-for-line against `git show 26e1029:packages/spec/authorable-surface.json`, and the siblings' entries survived in it: `ui/Theme:animation [RETIRED]` / `ui/Theme:zIndex [RETIRED]` are carried, HttpServerConfig / NotificationAction / EmbedConfig are gone from every generated witness at once. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ErbEDVAg1No9gdg1pgDAGB --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 26e1029 commit 04fab5e

7 files changed

Lines changed: 8687 additions & 44 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
fix(spec): the authorable-surface deletion gate no longer needs network to build (#5235)
6+
7+
`gen:schema` anchors the #4650 deletion check on `authorable-surface.json` at the merge
8+
base with `origin/main` — the one version of that file the commit under test cannot
9+
rewrite. When `origin/main` could not be resolved (and the self-heal `git fetch` could not
10+
make it resolvable), the build failed:
11+
12+
```
13+
❌ Cannot resolve origin/main to anchor the authorable-surface deletion check (#4650).
14+
```
15+
16+
That is correct for a developer who forgot to fetch, and wrong for an entire class of
17+
build environments that have no route to GitHub at all: image-build stages that copy a
18+
SHA-pinned framework tree into a container and build it there, air-gapped builds, forks,
19+
and historical-tag reproductions. Those trees are immutable and already merged — there is
20+
no "what did this PR delete relative to main" question to ask — yet the gate failed them
21+
anyway. It blocked every downstream consumer that builds `@objectstack/spec` from a pinned
22+
checkout without network.
23+
24+
The baseline is now also committed to the tree as `packages/spec/authorable-surface.base.json`:
25+
the keys of `authorable-surface.json` as of `baseRev`, a commit on `origin/main`.
26+
27+
- Where `origin/main` is reachable (every dev checkout, every CI run) nothing changes: the
28+
gate still anchors on the merge base, and it additionally verifies the committed anchor
29+
against it — `baseRev` must be an ancestor of `origin/main` and the recorded keys must
30+
be that commit's baseline. So a commit cannot edit the anchor to hide a deletion; the
31+
environments that can check, do.
32+
- Where `origin/main` is not resolvable, the gate anchors on the committed file and the
33+
build proceeds. It still runs: a key the anchor records that the build no longer emits
34+
is as fatal as before. Only an authoritative anchor may write the file, so an offline
35+
build can never advance it to its own state.
36+
37+
There is deliberately no environment-variable skip — a deletion check that can be switched
38+
off is the bypass #4650 exists to close. With neither anchor available the build still
39+
fails.

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
packages/spec/spec-changes.json merge=os-regen
3939
packages/spec/authorable-surface.json merge=os-regen
40+
packages/spec/authorable-surface.base.json merge=os-regen
4041
packages/spec/json-schema.manifest.json merge=os-regen
4142
packages/spec/api-surface.json merge=os-regen
4243
packages/spec/api-surface-signatures.json merge=os-regen

0 commit comments

Comments
 (0)