ci(spec): public API-surface snapshot gate to catch silent export drift (#2035)#2092
Merged
Conversation
…ft (#2035) For a metadata-driven platform the spec package IS the third-party API surface. A removed/renamed/kind-changed export silently breaks every consumer pinned to a published release the moment they upgrade (the #2023 class) — and no in-repo consumer catches it, because they all co-evolve with the spec in the same commit. `scripts/build-api-surface.ts` enumerates every exported `name (kind)` from the built `.d.ts` of each public entry point (`.`, `./ui`, `./data`, … 16 entries, 4251 exports), resolving re-export aliases to their real kind. The result is committed at `packages/spec/api-surface.json`. pnpm --filter @objectstack/spec gen:api-surface # regenerate + write pnpm --filter @objectstack/spec check:api-surface # CI: fail on any drift Wired into lint.yml's TypeScript Type Check job (after the build step). A REMOVED export fails as breaking (bump major); an ADDED export still requires regenerating the snapshot, so every change to the public surface is deliberate, never silent. Complements the downstream-contract gate: that proves exercised exports still ACCEPT real third-party metadata (depth); this proves the whole export set still EXISTS (breadth). Dev tooling only — the script and snapshot are not in the package `files`, so nothing ships to consumers. Sort is code-unit (not locale) for cross-platform determinism; verified the check passes clean and rejects a simulated removal. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 89 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Jun 21, 2026
xuyushun441-sys
added a commit
that referenced
this pull request
Jun 21, 2026
…ates (#2035) (#2098) Documents the layered strategy built across #2088/#2089/#2092/#2093/#2095/#2097 in one authoritative place: why in-repo consumers can't witness backward compat (they co-evolve with the spec), the six gates and each one's job, and — crucially — the FREEZE CONTRACT: a change that requires editing the fixtures or removing a snapshot entry is by definition breaking (bump major), never a mechanical test-update. Also formalizes `objectstack validate` as the third party's authoritative self-gate. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
xuyushun441-sys
added a commit
that referenced
this pull request
Jun 21, 2026
…owing (#2035) (#2100) The api-surface snapshot (#2092) catches add/remove/rename but not NARROWING — a `.default()` that flips an input field to required, or a `string` tightened to an enum, keeps the same `name (kind)`. The downstream-contract fixtures catch narrowing only for the fields they happen to set. Adds api-surface-signatures.json: a stable hash of each defineX factory's resolved signature (27 factories). The factory signature embeds the full accepted authoring shape, so any narrowing of what a domain accepts flips its hash. Scoped to the factories — the authoring contract — to stay low-noise: the breadth file (4251 exports) is unchanged, and full per-export signatures would churn on every internal type tweak. Rides the existing check:api-surface CI step (one command checks both). Verified: check clean, deterministic across regens (code-unit/structural, no locale), breadth snapshot byte-identical to main, and a flipped hash is reported as a breaking "signature changed" with exit 1. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.
Follow-up to #2035 / #2088 / #2089. The tightest pure-type backward-compat gate: catch a removed/renamed/kind-changed
@objectstack/specexport at PR time, before it breaks a published-spec third party.Why
For a metadata-driven platform the spec package is the third-party API. A silently dropped export breaks every consumer pinned to a published release the moment they upgrade — the #2023 class of break. No in-repo consumer catches it, because the examples and
@objectstack/dogfoodco-evolve with the spec in the same commit.What
scripts/build-api-surface.tsenumerates every exportedname (kind)from the built.d.tsof all 16 public entry points (.,./ui,./data, … — 4251 exports), resolving re-export aliases to their real kind (sodefinePagereads asfunction,PageInputastype). The snapshot is committed atpackages/spec/api-surface.json.Wired into lint.yml's TypeScript Type Check job (after the build step that produces the dist). A removed export fails as breaking; an added export still requires regenerating the snapshot — so every change to the public surface is deliberate, never silent.
How it fits the third-party validation strategy
objectstack validate(loader)Verification (local)
check:api-surface→ clean immediately aftergen(unchanged ✓)- defineLegacyThing (function)+ breaking-change guidancelocaleCompare); regenerated twice → byte-identicalNotes
api-surface.jsonis in the packagefiles, so nothing ships to consumers (no changeset needed; the gate's pending-changeset count is already > 0).🤖 Generated with Claude Code