Skip to content

Commit dc1202c

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-18801-sharing-rule-note-quotation-rot
2 parents 897ddd2 + d8b12fc commit dc1202c

31 files changed

Lines changed: 238310 additions & 119 deletions
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
`api-surface-declarations/<entry>.txt` — every export of every published entry point now ships a readable pin of the `.d.ts` declaration text the packed build actually emits for it, and the 27-entry `api-surface-signatures.json` hash it subsumes is retired (#16045).
6+
7+
`Clause-②: yes (widening)`
8+
9+
Until now this package pinned its public surface on one axis. `api-surface/<entry>.json` records each export as `name (kind)` — 5336 rows across 17 entry points, re-derived on the landing tree — and a signature change, a renamed interface field and a dropped union member move **none** of them. The only shape pin was `api-surface-signatures.json`: 27 rows, 0.5% of the surface, and reference-level even there, because it hashed `checker.typeToString()`, which prints `z.input<typeof ActionSchema>` without expanding it. A breaking shape change to a ratified public type could pass every witness green.
10+
11+
- **Text, ⛔ not a hash, deliberately.** A digest answers "did the bytes move" with one opaque bit whose known failure at scale is that a red one gets *accepted* rather than investigated. Each shard holds one block per declaration — `// ── Name (kind) ──` followed by the declaration verbatim — so a diff names the export and shows the change, and the existing review discipline is what guards it.
12+
- **The input is the packed `.d.ts` reached through the `exports` map**, i.e. the declarations a consumer installs, never `src/`. Two of the manifest's 19 `exports` entries are asset subpaths with no declaration (`./openapi.json`, `./package.json`), which is why this artifact and `api-surface/` both hold 17 shards.
13+
- **What it costs, measured on the landing tree**: 12,661,943 bytes (12.08 MiB) of text across 17 shards, 237,706 lines, 1.02 MiB gzipped against this package's ~17.6 MiB compressed `dist`. The skew is extreme — the median declaration is 81 bytes and the 20 largest hold ~65% of the bytes, because a Zod schema's packed declaration is its fully expanded structural type. That expansion is exactly what makes an inner field rename visible; it also means four declarations exceed 20,000 lines each.
14+
- **Leading TSDoc is excluded**, so a re-worded `.describe()` does not churn this artifact — documentation drift stays `check:docs`'s axis.
15+
- **The retirement is a strict superset, proven before it landed**: all 27 factory names resolve to a declaration block in `api-surface-declarations/root.txt`, 0 missing. For those 27 declarations text and hash discriminate the same amount (both print a type reference); what is *gained* is the 5309 other declarations, including the schemas those factories point at, whose expanded blocks are where an inner-key narrowing shows up. Nothing published read the retired file: it was not in this package's `files[]`.
16+
- **Sharded per entry point from day one**, for the reason `api-surface/` is: the merge queue rebuilds server-side where no custom merge driver runs, so two PRs sharing one generated file evict the second.
17+
18+
Regenerate with `pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec gen:api-surface-declarations`; `check:api-surface-declarations` names that command when it fails. It reads the built dist, so a missing or stale one is a hard refusal in both modes rather than a green run over nothing.

‎.gitattributes‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,10 @@ packages/spec/authorable-surface.base.json merge=os-regen
144144
packages/spec/authorable-defaults/** merge=os-regen
145145
packages/spec/json-schema.manifest/** merge=os-regen
146146
packages/spec/api-surface/** merge=os-regen
147+
packages/spec/api-surface-declarations/** merge=os-regen
147148
packages/spec/src/meta-spelling/meta-url-data.generated.ts merge=os-regen
148149
packages/spec/export-origins/** merge=os-regen
149150
packages/spec/declaration-map/** merge=os-regen
150-
packages/spec/api-surface-signatures.json merge=os-regen
151151
docs/protocol-upgrade-guide.md merge=os-regen
152152
docs/audits/2026-07-unknown-key-strictness-ledger.counts.md merge=os-regen
153153
content/docs/references/** merge=os-regen

‎.github/workflows/lint.yml‎

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5161,11 +5161,16 @@ jobs:
51615161

51625162
# The authorable KEY surface — what a metadata author may write, which for
51635163
# this platform is the third-party API. `api-surface/` records exported
5164-
# names and `api-surface-signatures.json` hashes factory types as TypeScript
5165-
# PRINTS them (a reference, never structurally expanded), so neither sees a
5166-
# key added to or removed from a schema. #3883 removed three authorable keys
5167-
# with every witness green; #3733 did it by accident. ADR-0059 §5 deferred
5168-
# this gate until a narrowing actually slipped both — it has.
5164+
# names only, so it does not see a key added to or removed from a schema;
5165+
# the shape sibling that used to sit beside it (`api-surface-signatures.json`,
5166+
# retired at #16045) hashed factory types as TypeScript PRINTS them — a
5167+
# reference, never structurally expanded — so it did not see one either.
5168+
# #3883 removed three authorable keys with every witness green; #3733 did it
5169+
# by accident. ADR-0059 §5 deferred this gate until a narrowing actually
5170+
# slipped both — it has. `check:api-surface-declarations` (consumer-gates
5171+
# lane) now records the declaration TEXT of every export, which DOES move on
5172+
# such a key; this gate stays the authority on the AUTHORABLE key set, which
5173+
# is a different question from the declared TypeScript shape.
51695174
#
51705175
# ⚠ ORDER: this step must stay ABOVE the `check:docs` step below. Its
51715176
# `--check` run of scripts/build-schemas.ts writes the gitignored
@@ -6087,6 +6092,29 @@ jobs:
60876092
- name: Check @objectstack/spec public API surface
60886093
run: pnpm --filter @objectstack/spec run check:api-surface
60896094

6095+
# [#16045] The SHAPE half of the same surface, and the step the card above
6096+
# exists for: `api-surface/` pins 5336 `name (kind)` rows and a signature
6097+
# change, a renamed interface field and a dropped union member move NONE of
6098+
# them, so 99.5% of the pinned surface could not go red on a breaking shape
6099+
# change to a ratified public type. The declaration-text snapshot records
6100+
# what the packed `.d.ts` actually declares for every export, per entry
6101+
# point. Ruled at #16045 (director batch #60, maintainer 「同意」): text and
6102+
# ⛔ NOT a hash, because a red hash gets accepted rather than investigated
6103+
# and a readable diff is what makes contract review a guard.
6104+
#
6105+
# WHY THIS LANE. It resolves each entry point through the `exports` map to
6106+
# the BUILT `.d.ts` — the declarations a consumer installs — so it is
6107+
# build-dependent and sits after the two build steps above with its family
6108+
# (`check:api-surface`, `check:published-readme-exports`). A missing or
6109+
# stale dist is a HARD REFUSAL in both of the script's modes, never a skip:
6110+
# a build-dependent gate that silently reads nothing reports "not measured"
6111+
# as if it were "measured and clean" (#4690).
6112+
#
6113+
# It adds no required context: a step in an existing lane, so no open PR
6114+
# waits on a check whose name no head has ever reported (#9325).
6115+
- name: Check @objectstack/spec declaration text (the shape half)
6116+
run: pnpm --filter @objectstack/spec run check:api-surface-declarations
6117+
60906118
# [#11350] Consumer-shaped declaration-emit pin against the BUILT root
60916119
# entry (an un-annotated `export default defineStack(...)` must compile
60926120
# with `declaration: true` — the TS2883 class). The pin is environment-

‎docs/spec-generated-artifact-sharding.md‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@ arithmetic composes on a merge and the judgement does not).
4141

4242
- **`spec-changes.json`** — keyed by version, so two PRs append under different majors.
4343
Never a conflict surface worth splitting.
44-
- **`api-surface-signatures.json`** — 1.3KB, one line per `defineX` factory.
44+
- **`api-surface-signatures.json`** — RETIRED at #16045, and the one row here whose reason
45+
did not survive its own artifact. It was 1.3KB, one line per `defineX` factory, so it was
46+
never worth splitting. Its replacement is the opposite shape: `api-surface-declarations/`
47+
holds the declaration TEXT of every export (12 MiB across 17 shards on the tree that
48+
landed it), so it is sharded per entry point from the day it arrived, for the same
49+
merge-queue reason `api-surface/` is.
4550
- **`authorable-surface.base.json`** — the #5235 deletion-gate anchor. Nothing but an
4651
explicit `gen:authorable-surface-base` writes it (#5358), so it was never on the churn
4752
path that made the other three the queue's serialization point. It also carries **one**

0 commit comments

Comments
 (0)