Repository navigation
feat(spec)!: split the assembled-stage package API declarations off ./api into @objectstack/spec/api-assembled - #20052
Conversation
…api into ./api-assembled The four Package API declarations that embed the assembled package body (AssembledInstalledPackageSchema, InstalledPackageAtEitherStageSchema, the List/Get installed-package responses) and the PackageApiContracts map that names them move from src/api/package-api.zod.ts to src/api/package-api-assembled.zod.ts, published from the new @objectstack/spec/api-assembled entry. ./api no longer imports stack.zod, so its graph no longer reaches the datasource declaration or the driver-config validators; it drops its browser condition, which ./api-assembled takes. In-repo importers (client type import, runtime conformance test, client return-type pins, spec unit test) move to the new path; the route-ledger responseSchema resolver learns both API entries. Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d Co-authored-by: Claude <noreply@anthropic.com>
…y of the api category A new scripts/lib/split-entries.ts declares ./api-assembled as publishing part of the api protocol. build-schemas walks it as part of API (JSON Schema ids stay api/<Name>), build-docs treats its missing json-schema directory as declared and expires the declaration on the same walk, and the reference pages spell their import line from whichever entry exports the page's names. The browser-reachable ledger classifies the new entry as unjudged, the docs subpath sentence lists it, and the category title marks it an entry rather than a protocol namespace. Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d Co-authored-by: Claude <noreply@anthropic.com>
…s for the ./api-assembled split; register the ADR-0087 semantic entry api-surface/api.json loses the thirteen moved names and api-surface/api-assembled.json carries them; export-origins follows. The reference page for the moved declarations imports from @objectstack/spec/api-assembled. The D3 semantic entry api-assembled-entry-split records the import-path move (an import path is not metadata, so no D2 conversion applies). PROTOCOL_MAP lists the new source file. Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d Co-authored-by: Claude <noreply@anthropic.com>
…assembled split; client patch for its type import Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d Co-authored-by: Claude <noreply@anthropic.com>
…home category's origins The schema manifest keys the moved declarations api/<Name>, while export-origins now records them under ./api-assembled; the composition joins the split entry's origins into its home category (SPLIT_ENTRIES), so the declaration map stays byte-identical. Reference docs regenerated after the docblock wording fix. Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d Co-authored-by: Claude <noreply@anthropic.com>
…led-stage names at ./api-assembled Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d Co-authored-by: Claude <noreply@anthropic.com>
…, not a level-1 heading Keeps the reference renderer's level-1-opener census (file-description.test.ts) where it was; the page is regenerated. Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d Co-authored-by: Claude <noreply@anthropic.com>
…ce page (32) Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d Co-authored-by: Claude <noreply@anthropic.com>
…5, or TS2724 with a misleading did-you-mean) Measured against the rebuilt declarations from packages/client's resolution root. Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d Co-authored-by: Claude <noreply@anthropic.com>
… the pre-split reading) Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 3 package(s): 13 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 4 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 143 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 918e01b6167da74ed3d17114b8a24fcb3f52649f && git checkout 918e01b6167da74ed3d17114b8a24fcb3f52649f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3557f85fa5af6c8a7793475bce399d7a9414eb08 12822df4fb855451b8bf2c72c6a90c865da66b8d && git checkout -B drift-repro 3557f85fa5af6c8a7793475bce399d7a9414eb08 && git merge --no-ff 12822df4fb855451b8bf2c72c6a90c865da66b8d
node scripts/docs-audit/affected-docs.mjs --json 3557f85fa5af6c8a7793475bce399d7a9414eb08
|
Contract reviewServed-tier: 104/104 Isolated at-tier reviewer subagent, run by the ① Derived judgments(a) Ruling's order — measurement first, and true. PR #20052 body §1 is the export-usage measurement; the cut follows it in the same PR, as ruling
(b) The cut is exactly the tree-reaching set, and (c) Grade and Clause ②. 13 names leave (d) ADR-0087 D3 entry. (e) Surface artefacts, mutually consistent. (f) Size proof and pin. Method stated (base (g) Scope. Client ( ② Semver levelPublished-surface changes the diff produces: (1) ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
… split left behind
- The spec changeset declares `Clause-②: yes (narrowing)`: the diff widens one
surface (the new ./api-assembled) and narrows another (13 names leave ./api).
- package-registry.zod.ts and package-lifecycle.zod.ts cite the moved
declarations at package-api-assembled.zod.ts; package-api.zod.ts no longer
{@link}s a symbol it does not have in scope.
- api/index.ts and package-lifecycle.zod.ts say the route-ledger resolver reads
both API entries, which it does since the split.
- The ./api ledger row counts sixteen unjudged entries and anchors the fifteen
of its weight scan to the tree it was taken on.
Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d
Co-authored-by: Claude <noreply@anthropic.com>
… pointer fix Generated by gen:schema + gen:docs; check:docs was red on 05383b5 for exactly this page. Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: 63/63 Isolated at-tier reviewer subagent, run by the Second review of this PR, isolated and adversarial, scoped to patch round 1 ( ① Derived judgments(a) Round 1 is exactly the 7 listed files, comments/prose only. (b) Every edited sentence is true at the head.
(c) The page is the generator's output. (d) (e) The seat-written PR body. "Patch round 1" and "Verification" sentences checked one by one against the head: changeset + body line, the (f) Sweep for what the cut left false that round 1 could still have missed. The 13 moved names at their old file ② Semver levelUnchanged from round 0 and still right: ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #18576
Clause-②: yes (narrowing)
Ruling
5714238181(batch #145 item 1, letter B, 「同意,其他也同意」): split./apiso the browser-facing half no longer carries the assembled-package (datasource/driver) declarations. This PR does the export-usage measurement first, finds that no objectui consumer uses a moved name, and then makes the cut.Patch round 1 (after the at-tier PASS
5824354899on4c6babc4ae)Its non-blocking recommendations, and nothing else. Head is now
12822df4fb.Clause-②: yes (narrowing). The diff widens one surface (the new./api-assembled) and narrows another (13 names leave./api), which is theyes (narrowing)combination inscripts/pm/clause2-line.mjs.check-adr-0087-registrationnow reads the changeset as[BREAKING+bang+clause-②-narrowing]. The client changeset carries no Clause line and needs none: its published types are unchanged, and only their import specifier moved.packages/spec/browser-reachable-entries.json: the./apirow'snotAnOutliernow says one of sixteen unjudged entries (the ledger'sunjudgedarray holds 16). The fifteen/twelve/fourteen figures of its weight scan are anchored to the tree that scan ran on, and./api-assembledis named as the sixteenth, not in that scan. No gate reads this prose:check-browser-reachable-entries.tsreads only the section keys.packages/spec/src/kernel/package-registry.zod.ts:AssembledInstalledPackageSchemais cited at../api/package-api-assembled.zod.ts.packages/spec/src/api/package-lifecycle.zod.ts:PackageApiContracts.rollbackPackageis cited at./package-api-assembled.zod.ts. The same header also said the route-ledger resolver looks names up "only in@objectstack/spec/api", which is false since the split, and now names both entries.content/docs/references/api/package-lifecycle.mdxwas regenerated bygen:schema+gen:docs.packages/spec/src/api/index.ts(the book-tree re-export comment said the resolver "searches only@objectstack/spec/api", now both entries). Alsopackages/spec/src/api/package-api.zod.ts, which had two{@link InstalledPackageAtEitherStageSchema}references to a symbol no longer in that file's scope; they are now a code span plus the sibling file.responseSchemafield docs of the rest, auth, i18n and storage route ledgers say names resolve against@objectstack/spec/apiexports. That is still true of every row they hold, and they name no moved symbol. The historical migration-registry entries and.changeset/*stock record the past and were not edited.05383b5e9c, fixed:Type Check · source gatesfailedcheck:docswithcontent/docs/references/api/package-lifecycle.mdx (out of date). That was the pointer edit landing before its page was regenerated.12822df4fbcarries the generated page, andcheck:docsanswers226 generated files in sync with packages/spec.1. Export-usage measurement (first deliverable)
objectui at
62597c5880, which is the pinned.objectui-sha(62597c588072636e9c30ea35b3d89b1e46fd765d). Every non-testimport … from '@objectstack/spec/api'/import('@objectstack/spec/api')in objectui:core/src/utils/column-sortability.tsFIELD_UNSORTABLE_VIRTUAL_TYPE,FIELD_SORTABLE_UNPROVISIONED_ANCHOR; typesFieldSortability,ObjectSortabilityapi/sortability.zod.tsdata-objectstack/src/metadata-client.tsGetMetaItemLayeredResponseSchema; typesGetMetaItemLayeredResponse,PublishPackageDraftsResponse,RuntimeAuthoringIssueapi/protocol.zod.tsapp-shell/src/views/metadata-admin/clientValidation.tsApiEndpointSchemaapi/endpoint.zod.tsplugin-chatbot/src/usePendingActions.tsApproveAiPendingActionResponse,RejectAiPendingActionResponseapi/protocol.zod.tsreact/src/utils/error-message.tsApiErrorapi/contract.zod.tstypes/src/data.tsExportJobStatus,ExportFormat,ImportJobStatus,ImportRowResult,ImportWriteModeapi/export.zod.tsRe-scan beyond the card's six: two more type-only sites —
data-objectstack/src/index.ts(ApiError) andtypes/src/index.ts(export type * as API from '@objectstack/spec/api', a type-only namespace re-export). The card called all six "value imports"; three of them are type-only and cost a bundle nothing.Which
./apinames pull the tree. An esbuild metafile walk of every modulesrc/api/index.tsre-exports: exactly one,package-api.zod.ts, reachedstack.zod→data/datasource.zod→ ninedata/driver/*modules, through one import (RecordStagePackageBodySchema). Inside it, exactly five declarations need that import:AssembledInstalledPackageSchema,InstalledPackageAtEitherStageSchema,ListInstalledPackagesResponseSchema,GetInstalledPackageResponseSchema, andPackageApiContracts(which names the two responses). None of them is imported anywhere in objectui (0 hits across the whole objectui tree; controlGetMetaItemLayeredResponseSchema: 14 files).Stop conditions: neither fired. No objectui site imports a moved name, so no objectui import changes and no
@object-ui/*API change is needed for the six sites to keep resolving. One transitive note:@object-ui/types's type-onlyAPInamespace loses the moved names when the pin bumps. objectui itself reads none of them through it.2. The cut
src/api/package-api.zod.ts→ the five declarations (and theirX/XParsedtypes) move verbatim tosrc/api/package-api-assembled.zod.ts.package-api.zod.tsno longer imports../stack.zod.@objectstack/spec/api-assembled(src/api-assembled/index.ts, oneexport *), carrying thebrowsercondition (its graph still reaches the pg URL grammar, so it gets the existingswapServerOnlyGrammarArmtwin)../apidrops itsbrowsercondition: nothing in its graph links a server-only module any more, andtsup.config.tssays the conditioned list must equal the poisoned set../data,./api, …) plus one fine-grained hyphenated entry (./meta-spelling). This one is a packaging split of the API protocol, named by the property that sets its members apart: they carry the assembled package body.api-keeps it next to./apiin every listing. It is not calledserver: the browser-reachability gate still requires it to be bundler-feasible (its browser condition), and the browser SDK's.d.tsimports a type from it, so "server-only" would be a claim nothing enforces.scripts/lib/split-entries.tsdeclaresapi-assembledas a split entry ofapi, so JSON Schema ids stayapi/NAME(the publishedjson-schema/api/ids are unchanged;json-schema.manifest/,authorable-surface/,declaration-map/byte-identical).build-docsspells the page's import line from the entry that exports it,declaration-mapjoins the split entry's origins into its home, and a missingjson-schema/api-assembled/is a declared state that expires if the tree stops matching.packages/client/src/index.ts(type import),packages/client/src/return-type-precision.test.ts,packages/runtime/src/domains/packages-read-delete-response-conformance.test.ts,packages/spec/src/api/package-api.test.ts. The route-ledgerresponseSchemafield (packages/runtime/src/route-ledger.ts) now names an export of either API entry, and its resolver test resolves against both. It also pins that the two entries share no name.examples/**andapps/**: 0 importers.Does an existing import path stop resolving? Yes. The 13 names (5 declarations plus their types) no longer resolve from
@objectstack/spec/api. That is major in the ruling's terms. Under the launch-window convention (check-changeset-no-major) it ships as@objectstack/specminor with a BREAKING banner, a FROM → TO table, and one ADR-0087 marker (registered api-assembled-entry-split, a new D3 semantic entry; an import path is not metadata, so there is no D2 conversion).@objectstack/clientgets apatch, because its published.d.tsnow imports the type from the new entry. Runtime publishes nothing changed: its builtdist/holds 0 occurrences of the edited ledger text, against the positive controlHttpDispatcherin 4 files.3. Size proof
Base
fc6ddb87a4vs head12822df4fb(the consumer probes read byte-identically at5f845af5c4), both built from source. esbuild 0.28.2,platform: browser, conditionsbrowser+import, minified, gzip -9, spec resolved through itsexportsmap:core/src/utils/column-sortability.ts, bundled as-ismetadata-client.ts's value import, verbatimclientValidation.ts's dynamicimport(), verbatim (pulls the whole namespace)./apinamespace (contrast)./apientry bundle (esm)./apisource graph: 171 → 120 modules. The stack, datasource and driver modules went from 11 to 0.pg-connection-stringis linked 4× in the base node bundle and 0× in the head. For history, #17535 measured the regression as 132,121 → 261,221 on the narrowest consumer. The remaining gap to 166,529 is the rest of./apigrowing since then, not the assembled tree. Thebrowser-reachable-entries.json./apiweight row is replaced with these readings, and its ablation was re-taken at5f845af5c4: 2 problems (zod ×2), down from 4 (zod ×2 pluspg-connection-string×2).4. Verification (head
12822df4fb)Gates were derived by
dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat12822df4fb: 126 families, the same set as round 0. All 126 were run on that head with exit codes recorded, and--rananswered126 derived famil(ies) accounted for — 126 run, 0 NOT-MEASURED (a DERIVED zero …). All 126 exited 0;check:pm-dispatch-gatestook 933.7s (1925 self-test cases). The dist-reading gates ran against a fullturbo run build --filter='./packages/*' --filter='./packages/*/*'of this head (72 tasks). Highlights:check:docs:226 generated files in sync with packages/spec, which is the CI red on05383b5e9c, fixed.check:generated,check:api-surface,check:export-origins,check:declaration-map,check:authorable-surface,check:browser-reachable-entries(5 browser-conditioned subpaths, positive control held),check:entry-nameability,check:dual-source-exports,check:exported-any,check:llms-txt,check:skill-examples: exit 0.check:dual-build-cjs-loads,check:published-files,check:lean-entry-closure,check:docs-spec-enumerations,check:quick-reference-counts,check:type-check-debt,check:nul-bytes: exit 0.check-adr-0087-registration --base origin/main:.changeset/18576-api-assembled-entry-split.md [BREAKING+bang+clause-②-narrowing] registered api-assembled-entry-split.check-changeset-no-major:This diff introduces no major bump.@objectstack/speclocal project 534 files / 15665 passed. Spec repo project 30 of 31 files / 492 tests passed (the 31st is under NOT MEASURED). The client (7) and runtime (17) targeted tests and the spec/client/runtime typechecks were green at4c6babc4ae; this round changes no code they compile (comments, prose, a ledger string, one generated page).5f845af5c4; this round touches neither the pin nor the graph).src/api/api-entry-graph.pin.test.tsturned red, 2 failed and 2 passed, whensrc/api/index.tsre-exported./package-api-assembled.zod. Restored blob404586349bequals HEAD..d.ts).ListInstalledPackagesResponseSchemafrom/apifails withTS2724 … Did you mean 'InstallPackageResponseSchema'?. From/api-assembledit compiles with 0 diagnostics.12822df4fb:mergeable: true. Main moved about 20 commits, touchingpackages/spec(including the generatedsrc/migrations/registry.ts) but none of the lines this diff owns, so the branch was not merged. CI's merge ref covers it.NOT MEASURED, declared to CI:
scripts/build-schemas-check-mode.test.ts(spec repo project). This round it queued three times and never acquired the shared verify lock (3 × 540s, other seats' suites and builds held it). In round 0 it did not finish inside the foreground window. This round's diff does not touchbuild-schemas.tsor anything it reads.Acceptance notes
cloudis NOT MEASURED (not attached). Its consumers of the 13 moved names are unknown.scripts/engine-double-contract.pinned.json(unrelated to this diff), so the branch was not merged. CI's merge ref covers it.Generated by Claude Code