fix(data-objectstack): createObjectStackAdapter declares the adapter it returns - #7503

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type
Sep 3, 2026
Merged

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns#7503
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#7323

createObjectStackAdapter returned new ObjectStackAdapter(config) while declaring the shared DataSource interface as its return. A wider value is assignable to a narrower annotation, so nothing ever failed to compile — the loss was entirely on the reading side: every adapter-only member was erased from the type the factory hands back, while staying on the object it hands back.

Measured before the change, compiling against the shipped packages/data-objectstack/dist/index.d.ts with the doc-snippet gate's own compiler options (strict, bundler resolution): 9 x TS2339, not the 4 the card lists — getClient, getCacheStats, invalidateCache, clearCache, getConnectionState, isConnected, onConnectionStateChange, onBatchProgress, setSystemCapabilities. Eight of those are exactly the members the README's API Reference — Methods list documents; the ninth is the one the factory's own JSDoc links to ([ADR-0066] See ObjectStackAdapter.setSystemCapabilities). The same probe after this change: 0 diagnostics.

The two questions that decided the shape

The triage set these as a stop-condition and both are answered from the code, on origin/maine17605309.

Q1 — is ObjectStackAdapter exported from the package's public entry? Yes, already.packages/data-objectstack/src/index.ts:2148 reads export class ObjectStackAdapter, and tsup.config.ts has exactly one entry, src/index.ts. The class is in the shipped dist/index.d.ts export list (verified in the built artifact, not inferred). Two pin tests already assert the exported spelling in source — cloud-surface-retired-4152.pin.test.ts:114 and v3-deep-integration-retired-4241.pin.test.ts:135 — and apps/console/src/dataSource.ts:14 re-exports it by name. So this PR exports nothing by implication: the export list in dist/index.d.ts is unchanged, name for name, before and after.

Q2 — is the narrow return deliberate? No, and there are two pieces of evidence against it.

  1. Commit a5d817061 ("Add connection state monitoring, auto-reconnect, and batch progress") added autoReconnect / maxReconnectAttempts / reconnectDelay to the factory's own config bag while leaving the members that observe those features off the factory's declared return. The same change configured a feature it made unobservable through its own entry point. That is an oversight shape, not an encapsulation decision.
  2. The factory's JSDoc, added by 41b7ce3ce, points the reader at ObjectStackAdapter.setSystemCapabilities — a member its declared return hides.

Nothing anywhere pins the return: no comment, no ADR, no test, no Equal assertion, and no swappability note. Searched the export list, package.jsonfiles / exports, ADR-0066, the sibling adapters, and every test that names the factory.

Had either answer come back the other way this would have been a docs-only PR. They did not, so it is option A — and swappability, the property the narrow return was suspected of protecting, is not lost: a wider return is assignable to the narrower annotation, so const ds: DataSource = createObjectStackAdapter(...) still compiles and still gives the narrow surface. There is a control test that says exactly that.

Why A and not B or C

B (add the missing members to DataSource) would make every other DataSource implementation declare caching, connection state and batch progress it does not have — those are this adapter's concerns. C (document a cast) teaches a cast around a declaration that is merely narrower than the value, which is the opposite of declared = enforced. A is one line and makes declared match shipped for every documented member at once.

The pin

packages/data-objectstack/src/adapterFactoryReturn.types.test.ts — the card's TS2339 reproduction, inverted. It reads all nine members through the factory's return, asserts the return's identity with Equal (not mere assignability — two adapter-shaped declarations are mutually assignable, so only identity can tell "returns THE adapter" from "returns something adapter-shaped"), and carries two controls:

  • _NotOnDataSource — the adapter-only members stay ABSENT from the shared DataSource. This is what makes the reads a statement about the factory's return rather than about every data source, and it fires on option B.
  • _StillADataSource — the widened return is still assignable to DataSource.

Both controls are independent of the return annotation, so reverting the source change turns the reads red and leaves the controls green. That split is measured below, not asserted.

Which tree the pin exercises. Two, deliberately. The test is compiled by pnpm --filter @object-ui/data-objectstack type-check, whose program includes the whole src/** (tests included) and resolves ./index from src — proven with --listFiles: 1 hit for the new file, 55 test files, src/index.ts present. @object-ui/types in that same program resolves from packages/types/dist, so the type used for the controls is the shipped one. The README blocks are the dist half: check:doc-snippets compiles them against the built dist/*.d.ts (its own resolution control prints '@object-ui/types' was successfully resolved to packages/types/dist/index.d.ts).

Documentation

The README note naming this card (added by #5174 batch 8) is removed. The four sections built on the adapter-only members — Metadata Caching, Connection State Monitoring, Batch Operation Progress, Troubleshooting → Cache Issues — now continue from Basic Setup's createObjectStackAdapter(...) call instead of hand-declaring the class, so the page teaches one shape and the doc-snippet gate pins the fix against dist. The other README blocks still declare the class where they only need DataSource-level members; that is truthful (the class is public and documented) and rewriting them buys nothing, so they are left alone.

setSystemCapabilities is still absent from the README's Methods list. It was absent before this PR too, it is a separate doc gap, and adding it here would be scope this card did not ask for.

Verification — every number below was observed, at head 922ca54ec

CheckCommandResult
Package type-checkpnpm --filter @object-ui/data-objectstack type-checkexit 0, tsc --noEmit echoed
Package testspnpm exec vitest run --maxWorkers=2 packages/data-objectstack/ (repo root — a package-dir run is refused by objectui#3378's guard)55 files, 737 tests, all passed
check:doc-snippetsnode scripts/check-doc-snippet-types.mjsexit 0 — "Semantic phase: 455 of 455 block(s) judged, 0 failed." / "Every covered documentation snippet compiles against the built types."
check:readme-exportspnpm check:readme-exportsexit 0 — "OK (43 tracked README(s) ...; 414 self-imports judged (414 real, 0 wrong-path, 0 fabricated); 3305 export symbol(s) read from 37 of 40 tracked package(s) (0 unbuilt ...))"
changeset presencenode scripts/check-changeset-presence.mjsexit 0 — "1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)"
check:doc-fencesnode scripts/check-doc-fence-languages.mjsexit 0 — "every TypeScript block in 227 document(s) is fenced ts/tsx/typescript ..."
control bytesnode scripts/check-control-bytes.mjsexit 0 — "OK (scanned 6180 tracked text file(s); skipped 85 binary)"
Package lintpnpm --filter @object-ui/data-objectstack lintexit 0 — 423 problems, 0 errors, 423 warnings, all pre-existing no-explicit-any; the new file contributes 0 findings (eslint --format json on the two changed source files: new test 0/0)

Every gate result above is quoted from the gate's own verdict line; exit codes were captured by redirecting first and reading $? before any pipe.

Dependency closure built first (pnpm --filter '@object-ui/data-objectstack...' build, then the doc-snippet gate's own derived --build-filter: 26 packages, 34 turbo tasks, then --filter='./packages/*' for check:readme-exports, which had reported 2 unbuilt packages — a "could not run", not a defect).

Lint scope, declared: the table's lint row is the changed package only, not the repo. pnpm lint here is turbo run lint (each package's own eslint .), and CI runs it in full regardless. One reading that needs stating: a separate probe with eslint --no-inline-config reports 4 errors in src/index.ts at lines 1369–1389. Those are the documented eslint-disable-next-line no-console sites for the spec Logger binding (objectui#4029), they are ~4300 lines from either of my edits, and they are invisible to the lint CI actually runs because it honours inline config. Not introduced here, and not silently omitted either.

Reverse verification — red/green split, both directions proven on disk

Method: commit first, then mutate packages/data-objectstack/src/index.ts back to the narrow return under trap ... EXIT INT TERM with an absolute restore path, prove the mutation landed by counting the injected and removed text (not by the editor's exit code) and by comparing blob hashes, then restore and prove the restore by state.

Mutation landed: injected-text count 0 → 1, removed-text count 1 → 0; mutated blob 447fffc8 differs from HEAD blob bad31df4.

Leg 1 — the src pin, no rebuild needed (the pin imports ./index from src; the mutation cannot reach packages/types/dist, which is where its DataSource comes from):

  • 13 diagnostics total, every one of them inside the pin file, 0 anywhere else in the package.
  • 10 x TS2339 at lines 75–84: the nine member reads (invalidateCache twice, with and without an argument).
  • TS2344 at 89 and 113: the _HasHiddenMembers and _IsTheAdapter assertions.
  • TS7053 at 157: the runtime member sweep can no longer index the narrowed type.
  • The controls at lines 120 (_NotOnDataSource) and 135 (_StillADataSource) produced ZERO diagnostics — the split the pin was designed for.
  • vitest on the same mutated tree still passes, which is the point: esbuild erases types, so the colour comes from tsc and from nowhere else.

Leg 2 — the dist pin, rebuilt in both directions. Rebuilt from the mutated source, then a dist preflight: the widened marker count in dist/index.d.ts was 0 (the mutation reached dist). The standalone dist probe then reproduced the card's failure exactly — 9 x TS2339 — and check:doc-snippets went to exit 1, "455 of 455 block(s) judged, 4 failed": precisely the four README sections this PR rewired, failing on getCacheStats, invalidateCache x2, clearCache, onConnectionStateChange, getConnectionState, isConnected, onBatchProgress, clearCache, invalidateCache.

Restore, proven by state, both halves. Source: restored blob bad31df4 equals HEAD's blob and git diff HEAD is 0 bytes. dist: rebuilt again, widened marker back to 1 and narrow marker 0, dist probe back to 0 diagnostics, check:doc-snippets back to exit 0, 0 failed. The restore leg is rebuilt on purpose — a mutated marker left in dist would keep acting on every later run in this tree.

Clause ②

Clause-②: yes, declared in the claim comment on #7323 in the fixed machine spelling, and needs:contract-review is on both carriers (this PR and the card). It widens the declared public surface reachable through a published entry point. Q1's answer removes the triage's specific worry — no class is exported by implication — but the declaration limb is judged from content, not from paths or from diff size.

The one compatibility note a reviewer should weigh. The widened return is a class with private members, so it is no longer satisfied by a hand-written structural stand-in: an object literal assigned to the factory's ReturnType will now be rejected where it used to be accepted. Nothing in this repo does that (searched every test and app that names the factory — apps/console/src/dataSource.ts only re-exports it), and the fix for a downstream consumer is to annotate such a fake as DataSource, which is what it was standing in for. It is the only direction in which this change is not purely additive, and it belongs in the contract review rather than in a footnote.

Draft on purpose, not flipped ready, auto-merge not enabled — the seat lands it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…it returns
The factory returned `new ObjectStackAdapter(config)` while declaring
`DataSource<T>`. A wider value is assignable to a narrower annotation, so
nothing ever failed to compile — the loss was entirely on the reading side.
Measured against the shipped `dist/index.d.ts` with the doc-snippet gate's own
compiler options, nine reads through `ReturnType<typeof
createObjectStackAdapter>` failed with TS2339: getClient, getCacheStats,
invalidateCache, clearCache, getConnectionState, isConnected,
onConnectionStateChange, onBatchProgress and setSystemCapabilities. Eight are
exactly the members the package README's API Reference documents; the ninth is
the one the factory's own JSDoc links to. Same probe after this change: 0
diagnostics.
Option A of the card. B (widening `DataSource` itself) would make every other
data source declare caching, connection state and batch progress it does not
have; C (documenting a cast) teaches a cast around a declaration that is merely
narrower than the value. `ObjectStackAdapter` was already exported from the
package's only entry, so nothing is exported by implication, and no comment,
ADR or test pinned the narrow return — the commit that added autoReconnect /
maxReconnectAttempts / reconnectDelay to the factory's config bag left the
members observing those features off its declared return in the same change.
Callers are unaffected: `const ds: DataSource = createObjectStackAdapter(…)`
still compiles and still gives the narrow surface.
The README note saying the page could not yet teach the factory's shape is
removed, and the four sections built on the adapter-only members now continue
from Basic Setup's factory call. `adapterFactoryReturn.types.test.ts` pins the
card's TS2339 reproduction inverted, with a control for option B and a control
for swappability.
Part of #7323
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation data-adapter tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Clause-② in-seat contract review — PASS WITH REQUIRED AMENDMENTS. needs:contract-review stays on.

Commissioned by the domain:ui execution seat (PM session session_01EMrWaQw3XS5DxTHxp4yRyC) per the in-seat review route (维护者 2026-08-31 裁定). This seat runs opus and is not at tier, so it commissioned an isolated reviewer rather than self-reviewing. The reviewer was given only the card, its triage and the PR — ⛔ not the dispatch order and not this seat's conclusions.

Tier verification (维护者 2026-08-27 裁定): every harness-stamped model field in the transcript was read before adopting — 76 stamps, all claude-fable-5-1, no other value, and zero fallback/overload markers. CONTRACT_REVIEW_TIER is claude-fable-5 (objectstack/scripts/pm/dispatch-gates.mjs:8091); claude-fable-5-1 is at or above that floor. ⇒ Verified, so the verdict is adopted verbatim — ⛔ nothing below is rewritten, abridged or polished.


Contract review — objectui PR #7503 (head 922ca54ec, base e17605309)

Everything below was read from the diff, the card, triage 5524408927, and my own runs in a detached worktree at /home/user/objectui-review-7503 (dependency closure built; git diff HEAD 0 bytes at the end). No edits, commits, pushes, labels or GitHub posts.

① Derived judgments

Accept set

  • Config parameter type: byte-identical before/after. No change to what the factory accepts. Right.

Published surface

  • Declared return DataSource<T>ObjectStackAdapter<T>. Ships: built dist/index.d.ts ends the factory }): ObjectStackAdapter<T>;. Export list unchanged (the diff adds no export; the test file is outside tsup's single entry). Right.
  • The true delta is 20 members, not 9. tsc-computed Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>> is exactly: clearCache, connect, getCacheStats, getCached, getClient, getConnectionState, getDiscovery, getItems, invalidateCache, invalidateViewKeys, isConnected, listImportMappings, onBatchProgress, onConnectionStateChange, onSaveAdvisory, onWriteWarning, probeAppAccess, queryDataset, setSystemCapabilities, updateDashboard. Proven with Assert<Equal<Extra, Twenty>> silent, and negative controls red at 19 names, 21 names, and the PR's nine (TS2344 ×3) — on both the src program and the shipped dist/index.d.ts. All 20 are now the factory's promise.
  • Judgment on the other 11: right to publish, but under-described. None is @internal/@deprecated; stripInternal is not set anywhere, so they were already in the shipped class type; and the class type is already the monorepo's working adapter contract — packages/react/src/context/AppShellContext.tsx:8 types AdapterCtx as ObjectStackAdapter | null, and app-shell's AdapterProvider, MetadataProvider, MetadataService all take ObjectStackAdapter. Two are escape-hatch shaped and worth the seat's eye: getCached(key): unknown | undefined (raw cache read) and getDiscovery(): Promise<unknown | null> whose own JSDoc says "accesses an internal property of the ObjectStackClient"; invalidateViewKeys is a cache-key coupling seam. Not blocking — reachable via new ObjectStackAdapter() and every app-shell path today — but the changeset presents "nine" as the delta. → Amendment 2.

Assignability attack — no consumer regressed except the one the PR names

  • Probed at PR head, all compile: const a: DataSource = …; const b: DataSource<User> = createObjectStackAdapter({…}) (T infers through the class); explicit <User>; DataSource[] and DataSource<User>[] mixed arrays; DataSource | null union; take(ds: DataSource<User>) with and without explicit T; function-type assignment (cfg) => DataSource (return covariance); generic→concrete (cfg) => DataSource<User>; satisfies DataSource; callback contravariance withDs((ds: DataSource) => …) against a (ds: FactoryReturn) => void parameter; Record<string, DataSource>; Promise<DataSource>.
  • The only break: a structural stand-in for ReturnType<typeof createObjectStackAdapter> — TS2740, "missing … client, connected, connectPromise, metadataCache, and 64 more". Repo has no typeof createObjectStackAdapter anywhere; the sole vi.mock('@object-ui/data-objectstack') (packages/app-shell/src/chrome/ConditionalAuthWrapper.previewRetired-6654.test.tsx:72) mocks only getSharedDiscovery, untyped. Correctly named by the PR. Not a rejection.
  • Secondary, not new: a class-with-privates return is nominal across duplicate installs; every ObjectStackAdapter-typed seam in @object-ui/react/app-shell already carries that property.

Q1 — class already exported: verified.src/index.ts:2148 export class ObjectStackAdapter; tsup.config.ts single entry src/index.ts; name present in built dist/index.d.ts export block; apps/console/src/dataSource.ts:14 re-exports it. Nothing exported by implication.

Q2 — narrow return not deliberate: verified, with one miss.

  • No Equal/expectTypeOf/satisfies/ReturnType pin on the factory anywhere in packages/apps; no ADR names it. 780a1b993 (introduced ): DataSource) carries no design statement. a5d817061 added autoReconnect/maxReconnectAttempts/reconnectDelay to the factory config (diff +358–360) without touching the return; 41b7ce3ce (feat(data-objectstack,app-shell,i18n): view config is explicitly org-wide — permission-gated write path + console wording #6125) added the [ADR-0066] See {@link ObjectStackAdapter.setSystemCapabilities} JSDoc. Plus the AdapterCtx fact above — the React layer already treats the class as the contract, the opposite of a swappability design.
  • Miss: the PR's "no swappability note anywhere" overlooked content/docs/utilities/data-objectstack.mdx, untouched by the diff: L53 "createObjectStackAdapter returns a DataSource — the same universal interface every ObjectUI renderer consumes"; L109 "Factory returning a DataSource"; the signature fragment at L137 ending }): DataSource<T>; under a marker claiming it was "Checked against the shipped dist/index.d.ts … with the same type"; and L163–200, a whole "Adapter-only (hold the class type to reach these)" section built on the distinction this PR erases. Provenance 92c0b1f40 (docs(data-objectstack): document the real headless surface, not a phantom React API #4129, a docs batch that "verified snippets against the built packages") — it describes the shipped d.ts, it does not prescribe, so Q2's answer stands. But the page is now false about the shipped types and nothing gates it (the fragment is declared, so the doc-snippet gate never compiles the return). → Amendment 1.

README

Unmentioned diff content: the factory JSDoc gains 15 lines including "⛔ Do not narrow this back" — consistent with the change; nothing else hidden. Test file is exactly as described.

② Semver

  • Changeset: '@object-ui/data-objectstack': minor. Policy (scripts/check-changeset-no-major.mjs header): major reserved for the objectstack-synchronised bump; objectui's own breaks ship as minor with the break spelled out. The body spells out the stand-in break. I would assign minor. Matches. Changeset Bump Policy / Fixed Group / Declaration checks all green.
  • Wording nit folded into Amendment 2: the bold "Not a breaking change for callers." heading sits directly over the paragraph describing the break.

③ Boundary flags

  • (a) Q1 yes/already; Q2 not deliberate — both confirmed, with the mdx caveat above.
  • (b) 9 vs 4. The card measured 4 (its snippet) and named 8, saying the other four were "exactly the members" on the same README list; setSystemCapabilities was introduced by the triage, not the card. All nine do fail together — reproduced (mutation B: 10 × TS2339 on the nine reads, invalidateCache twice). So "one tier, not two" is right in substance, and the card was partial rather than wrong. But the implementer's own count is partial too: README-documented adapter-only members are 9 (connect()), and the hidden delta is 20.
  • (c) Lint narrowing hides nothing.eslint.config.js has no parserOptions.project/projectService — rules are syntactic and per-file, so a return-type change cannot alter lint output elsewhere. What the package-only run skipped (//#lint:root, other packages) CI's full Lint ran and passed.

Pin verification (not vacuous)

  • In the program:tsc --listFiles for the package lists src/adapterFactoryReturn.types.test.ts and src/index.ts; DataSource resolves to packages/types/dist/data.d.ts (project-reference redirect), as the implementer said.
  • Mutation B (narrow back to DataSource<T>): 13 diagnostics, all in the pin — lines 75–84 TS2339 ×10, 89 and 113 TS2344, 157 TS7053; 0 elsewhere; controls at 120/135/137: 0 hits. Reproduces the reported split exactly. My stand-in probe starts compiling under B, confirming that is the only direction that moved.
  • Mutation C (return Omit<ObjectStackAdapter<T>,'find'>, i.e. no longer a DataSource): line 135 _StillADataSource TS2344 red, line 137 TS2741 red, line 113 red, line 120 green. _StillADataSource genuinely discriminates; _NotOnDataSource reads keyof DataSource and is independent of the return, so it fires only on option B, as designed.
  • vitest from the repo root on the pin file: 1 file, 4 tests pass.

CI by name at 922ca54ec (32 runs, all completed)

  • success (29): Lint · Type Check · Build & E2E · Test (shard 1/4) · Test (shard 2/4) · Test (shard 3/4) · Test (shard 4/4) · Doc Snippet Type Check · README Export Check · Changeset Bump Policy · Changeset Declaration · Changeset Fixed Group Check · Changeset Overwrite Report · Doc Fence Language Check · Doc Component Type Check · Internal Docs Link Check · Docs Route Eager Closure Check · Build Docs · Bundle Analysis · Control Byte Scan · Shell Escape Residue Scan · Pre-Install Import Graph Check · Governed Surface Queue Guard · Inert vi.mock Specifier Check · Skill Eval Token Check · Skill Example Check · Skill Guide Path Check · Live E2E (informational) · label
  • skipped (3, conditional jobs): Test (coverage) · Test (coverage shard) · dependabot
  • failed: none.

Verdict

PASS WITH REQUIRED AMENDMENTS

  1. Bring content/docs/utilities/data-objectstack.mdx in line with the shipped types — four sites: L53 prose ("returns a DataSource"), L109 ("Factory returning a DataSource"), the fragment's }): DataSource<T>; at L137 together with its marker's "checked against dist … same type" claim, and the L163–200 "hold the class type to reach these" section, which now teaches a distinction the factory no longer has. This PR already carries the docs limb for the same package's README; the docs-site page must not contradict dist/index.d.ts after merge.
  2. Fix the changeset's description of the delta (.changeset/7323-adapter-factory-return.md, mirrored in the PR body and test header if desired): state that the factory's declared return is now the full class — every public member of ObjectStackAdapter, 20 beyond DataSource — rather than presenting the nine measured reads as the delta; correct "Eight of those are exactly the members … the README documents" to nine (connect()); and reword the "Not a breaking change for callers." heading so the CHANGELOG line does not read as "no break" above the stand-in caveat.

Both amendments are text-only and independent of each other; neither changes the code, which is right.


Seat disposition

Not landing.needs:contract-review stays on both carriers until both amendments are in and this seat verifies them by content. They are being dispatched now. The code is confirmed correct and ⛔ must not change.

Amendment 1 is the find that justifies the tier, and it names a gate hole.content/docs/utilities/data-objectstack.mdx carries a signature fragment ending }): DataSource<T>; under a marker asserting it was "checked against the shipped dist/index.d.ts … with the same type" — and after this merge that assertion is false. It survives because the fragment is declared, so check:doc-snippet-types never compiles the return. A page that claims to be verified against the built types, and is structurally exempt from the check that would verify it, is worse than an unmarked page: this seat, the implementer and the card all read "no swappability note anywhere" and none of us found it. I am recording that as a finding in its own right when the amendment lands.

⚠️ Also worth the record: the reviewer's count of the true delta — 20 members, not 9 — was reached with Assert<Equal<>> plus negative controls at 19, 21 and 9 names, on both the src program and the shipped d.ts. My dispatch asked it to enumerate what else became reachable; it did, and the answer is that the changeset understates the published surface by more than a factor of two. ⛔ That is a description defect, not a design one — the reviewer establishes the 11 extra members were already in the shipped class type, already reachable via new ObjectStackAdapter(), and already the monorepo's working adapter contract (AdapterCtx is typed as the class). So the fix is the changeset's wording, not the return type.


Generated by Claude Code

…idened factory return (objectui#7323)
Two text-only amendments from the in-seat contract review on the pull request.
No code changes: the return-type widening, the type-level pin and the README
rewiring are untouched.
Amendment 1 — content/docs/utilities/data-objectstack.mdx contradicted the
shipped types. The four sites the review named: the Quick Start prose saying the
factory "returns a `DataSource`"; the API Reference blurb "Factory returning a
`DataSource`"; the signature fragment ending `}): DataSource<T>;` together with
its marker's claim to have been checked against the shipped `dist/index.d.ts`;
and the `ObjectStackAdapter` section built on "hold the class type to reach
these", a distinction the factory no longer has. That section is rewritten rather
than deleted, with a "What changed" paragraph so a reader who followed the old
advice can see why constructing the class by hand is no longer needed.
Two further sites in the same file taught the same erased distinction and are
corrected with them: the Mutations paragraph ("hold the adapter at its class type
... before calling them") and Troubleshooting -> Authentication Errors
("Connection introspection lives on the adapter class ... so hold it at the class
type"); both examples now continue from the factory call, matching how the README
limb of this pull request was rewired. `onMutation` was also misfiled under
"Adapter-only" while being an optional member of `DataSource`; it moves in with
`bulk` and `batchTransaction`.
The fragment's marker no longer asserts an unqualified "checked against the
shipped d.ts". Because the block is declared, `check:doc-snippet-types` never
compiles it, so the marker now states that the agreement is hand-checked rather
than gate-enforced -- which is why the stale return type survived there. The gate
and the `declared` marker keyword are unchanged.
Amendment 2 — the changeset understated the published surface by more than 2x.
It presented the card's nine measured TS2339 reads as the delta; the factory's
declared return is now the class itself, so the delta is every public member of
`ObjectStackAdapter` -- 20 beyond `DataSource`, independently re-derived here as
`Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>>` against
the built `dist/index.d.ts` (20 names, matching the review). The README-documents
sentence is corrected: eight of the nine measured reads are on that list, but the
list itself is nine adapter-only members -- `connect()` was documented all along
and simply was not measured. The "Not a breaking change for callers." heading is
reworded to lead with the one shape that does break, so the CHANGELOG line no
longer reads as "no break" above the caveat describing it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
…ui#7323)
Follow-up to the previous commit, on my own prose in the same amendment:
- Drop the "Until v17.7" version claim. The package is at 17.6.0 and the
changeset is a minor, but the released number is decided by the fixed group at
release time, so the page should not assert it.
- Remove a duplicated bullet list. `bulk` and `batchTransaction` were listed once
under the `DataSource` members and again under the optional-members paragraph.
`onMutation` now joins them in the single list, spelled `onMutation?` like its
neighbours, with the correction noted in the paragraph instead.
- Say "six bullets cover nine members" rather than "those nine", since the list
groups related members onto shared lines, and make the parenthetical naming the
other eleven members read as illustrative rather than exhaustive -- it named
four seams out of eleven.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Both amendments verified — needs:contract-review cleared, ready + auto-merge

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance: 「PASS ⇒ 同席剥标、ready、auto-merge」 (维护者 2026-08-31 裁定). The tier review returned PASS WITH REQUIRED AMENDMENTS (5526083981); both are now in.

⚠️The amendment agent was killed mid-run by a transient HTTP 529 and its report is lost. It had already pushed. So everything below is this seat's own verification from the git objects, with no report to lean on — which is the right posture anyway, and it is the reason this comment carries readings rather than a summary.

Verified by content at 5140938cd

Two commits since the reviewed head 922ca54ec:

  • 27d18e179 — docs page + changeset
  • 5140938cd — a follow-up tightening of the rewritten section

Two files across the whole range, and ⭐ not one line of code:

.changeset/7323-adapter-factory-return.md | 53 +++++++++----
content/docs/utilities/data-objectstack.mdx | 99 ++++++++++++++++-------

⇒ The return-type widening, the type-level pin and the README rewiring are byte-identical to what was reviewed. The standing PASS therefore holds and no re-review is owed — the review's own words were that both amendments are "text-only and independent of each other; neither changes the code, which is right."

Amendment 1 — the docs page. Every flagged claim is gone: no returns a DataSource, no Factory returning a DataSource, no }): DataSource<T>; fragment. The "hold the class type to reach these" section was rewritten, not deleted — it now opens "What changed. This section used to tell you to hold the class type to…", which is what a reader who followed the old advice needs.

And the marker is gone entirely. The Checked against the shipped dist/index.d.ts … with the same type assertion was removed, not reworded around it. That matters more than the four corrected sites: that false claim of verification is why the page stayed wrong through three separate readers looking for exactly it.

Amendment 2 — the changeset. Now states the delta is the whole class — "20 members, not nine" — with the tsc-computed enumeration, corrects eight → nine (connect() is adapter-only), and records that the eleven past the documented nine were already in the shipped class type.

Checks — 32/32, read by name by this seat

29 success, 3 skipped by design (dependabot, Test (coverage), Test (coverage shard …)) on 5140938cd. The ones that actually cover this amendment: Doc Snippet Type Check 13:25:45 · Build Docs 13:23:57 · Internal Docs Link Check 13:21:53 · Doc Fence Language Check · README Export Check 13:25:31 · Changeset Declaration / Bump Policy / Fixed Group / Overwrite Report. Long poles: all four Test (shard N/4) 13:34–13:36, Type Check 13:27:31, Lint 13:27:12.

Carriers

needs:contract-review removed from PR #7503 (remaining documentation, data-adapter, tests) and from card #7323 (remaining bug, pm:dispatched, priority:p2, domain:ui). Both label sets were read immediately before writing rather than assumed — the label job had added labels since my last reading of a sibling PR, and writing a stale set would have clobbered them.

⚠️ Both readings are manual. scripts/pm/check-clause2-carriers.mjs exits 2 in this session (it needs its own GitHub token; this seat reaches GitHub only through MCP) and its own output says that is ⛔ not a clearance. A hand label-check covers strictly less than the tool — this shift produced four distinct carrier defects and the fallback is structurally blind to two of their shapes, now filed as objectstack#14965.

The finding this PR produced, filed separately

#7505 — a declared doc fragment is never compiled by check:doc-snippet-types, so a page can carry a "checked against the shipped dist" marker that no gate ever re-checks. The one known instance is fixed here; the class is not, and enumerating the rest is the first task on that card. ⛔ Filed unassigned and unlabelled for triage.


Generated by Claude Code

Merged via the queue into main with commit 6a99bb2Sep 3, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7323-adapter-return-type branch September 3, 2026 13:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data-adapterdocumentationImprovements or additions to documentationtests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns - #7503

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type
Sep 3, 2026
Merged

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns#7503
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#7323

createObjectStackAdapter returned new ObjectStackAdapter(config) while declaring the shared DataSource interface as its return. A wider value is assignable to a narrower annotation, so nothing ever failed to compile — the loss was entirely on the reading side: every adapter-only member was erased from the type the factory hands back, while staying on the object it hands back.

Measured before the change, compiling against the shipped packages/data-objectstack/dist/index.d.ts with the doc-snippet gate's own compiler options (strict, bundler resolution): 9 x TS2339, not the 4 the card lists — getClient, getCacheStats, invalidateCache, clearCache, getConnectionState, isConnected, onConnectionStateChange, onBatchProgress, setSystemCapabilities. Eight of those are exactly the members the README's API Reference — Methods list documents; the ninth is the one the factory's own JSDoc links to ([ADR-0066] See ObjectStackAdapter.setSystemCapabilities). The same probe after this change: 0 diagnostics.

The two questions that decided the shape

The triage set these as a stop-condition and both are answered from the code, on origin/maine17605309.

Q1 — is ObjectStackAdapter exported from the package's public entry? Yes, already.packages/data-objectstack/src/index.ts:2148 reads export class ObjectStackAdapter, and tsup.config.ts has exactly one entry, src/index.ts. The class is in the shipped dist/index.d.ts export list (verified in the built artifact, not inferred). Two pin tests already assert the exported spelling in source — cloud-surface-retired-4152.pin.test.ts:114 and v3-deep-integration-retired-4241.pin.test.ts:135 — and apps/console/src/dataSource.ts:14 re-exports it by name. So this PR exports nothing by implication: the export list in dist/index.d.ts is unchanged, name for name, before and after.

Q2 — is the narrow return deliberate? No, and there are two pieces of evidence against it.

  1. Commit a5d817061 ("Add connection state monitoring, auto-reconnect, and batch progress") added autoReconnect / maxReconnectAttempts / reconnectDelay to the factory's own config bag while leaving the members that observe those features off the factory's declared return. The same change configured a feature it made unobservable through its own entry point. That is an oversight shape, not an encapsulation decision.
  2. The factory's JSDoc, added by 41b7ce3ce, points the reader at ObjectStackAdapter.setSystemCapabilities — a member its declared return hides.

Nothing anywhere pins the return: no comment, no ADR, no test, no Equal assertion, and no swappability note. Searched the export list, package.jsonfiles / exports, ADR-0066, the sibling adapters, and every test that names the factory.

Had either answer come back the other way this would have been a docs-only PR. They did not, so it is option A — and swappability, the property the narrow return was suspected of protecting, is not lost: a wider return is assignable to the narrower annotation, so const ds: DataSource = createObjectStackAdapter(...) still compiles and still gives the narrow surface. There is a control test that says exactly that.

Why A and not B or C

B (add the missing members to DataSource) would make every other DataSource implementation declare caching, connection state and batch progress it does not have — those are this adapter's concerns. C (document a cast) teaches a cast around a declaration that is merely narrower than the value, which is the opposite of declared = enforced. A is one line and makes declared match shipped for every documented member at once.

The pin

packages/data-objectstack/src/adapterFactoryReturn.types.test.ts — the card's TS2339 reproduction, inverted. It reads all nine members through the factory's return, asserts the return's identity with Equal (not mere assignability — two adapter-shaped declarations are mutually assignable, so only identity can tell "returns THE adapter" from "returns something adapter-shaped"), and carries two controls:

  • _NotOnDataSource — the adapter-only members stay ABSENT from the shared DataSource. This is what makes the reads a statement about the factory's return rather than about every data source, and it fires on option B.
  • _StillADataSource — the widened return is still assignable to DataSource.

Both controls are independent of the return annotation, so reverting the source change turns the reads red and leaves the controls green. That split is measured below, not asserted.

Which tree the pin exercises. Two, deliberately. The test is compiled by pnpm --filter @object-ui/data-objectstack type-check, whose program includes the whole src/** (tests included) and resolves ./index from src — proven with --listFiles: 1 hit for the new file, 55 test files, src/index.ts present. @object-ui/types in that same program resolves from packages/types/dist, so the type used for the controls is the shipped one. The README blocks are the dist half: check:doc-snippets compiles them against the built dist/*.d.ts (its own resolution control prints '@object-ui/types' was successfully resolved to packages/types/dist/index.d.ts).

Documentation

The README note naming this card (added by #5174 batch 8) is removed. The four sections built on the adapter-only members — Metadata Caching, Connection State Monitoring, Batch Operation Progress, Troubleshooting → Cache Issues — now continue from Basic Setup's createObjectStackAdapter(...) call instead of hand-declaring the class, so the page teaches one shape and the doc-snippet gate pins the fix against dist. The other README blocks still declare the class where they only need DataSource-level members; that is truthful (the class is public and documented) and rewriting them buys nothing, so they are left alone.

setSystemCapabilities is still absent from the README's Methods list. It was absent before this PR too, it is a separate doc gap, and adding it here would be scope this card did not ask for.

Verification — every number below was observed, at head 922ca54ec

CheckCommandResult
Package type-checkpnpm --filter @object-ui/data-objectstack type-checkexit 0, tsc --noEmit echoed
Package testspnpm exec vitest run --maxWorkers=2 packages/data-objectstack/ (repo root — a package-dir run is refused by objectui#3378's guard)55 files, 737 tests, all passed
check:doc-snippetsnode scripts/check-doc-snippet-types.mjsexit 0 — "Semantic phase: 455 of 455 block(s) judged, 0 failed." / "Every covered documentation snippet compiles against the built types."
check:readme-exportspnpm check:readme-exportsexit 0 — "OK (43 tracked README(s) ...; 414 self-imports judged (414 real, 0 wrong-path, 0 fabricated); 3305 export symbol(s) read from 37 of 40 tracked package(s) (0 unbuilt ...))"
changeset presencenode scripts/check-changeset-presence.mjsexit 0 — "1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)"
check:doc-fencesnode scripts/check-doc-fence-languages.mjsexit 0 — "every TypeScript block in 227 document(s) is fenced ts/tsx/typescript ..."
control bytesnode scripts/check-control-bytes.mjsexit 0 — "OK (scanned 6180 tracked text file(s); skipped 85 binary)"
Package lintpnpm --filter @object-ui/data-objectstack lintexit 0 — 423 problems, 0 errors, 423 warnings, all pre-existing no-explicit-any; the new file contributes 0 findings (eslint --format json on the two changed source files: new test 0/0)

Every gate result above is quoted from the gate's own verdict line; exit codes were captured by redirecting first and reading $? before any pipe.

Dependency closure built first (pnpm --filter '@object-ui/data-objectstack...' build, then the doc-snippet gate's own derived --build-filter: 26 packages, 34 turbo tasks, then --filter='./packages/*' for check:readme-exports, which had reported 2 unbuilt packages — a "could not run", not a defect).

Lint scope, declared: the table's lint row is the changed package only, not the repo. pnpm lint here is turbo run lint (each package's own eslint .), and CI runs it in full regardless. One reading that needs stating: a separate probe with eslint --no-inline-config reports 4 errors in src/index.ts at lines 1369–1389. Those are the documented eslint-disable-next-line no-console sites for the spec Logger binding (objectui#4029), they are ~4300 lines from either of my edits, and they are invisible to the lint CI actually runs because it honours inline config. Not introduced here, and not silently omitted either.

Reverse verification — red/green split, both directions proven on disk

Method: commit first, then mutate packages/data-objectstack/src/index.ts back to the narrow return under trap ... EXIT INT TERM with an absolute restore path, prove the mutation landed by counting the injected and removed text (not by the editor's exit code) and by comparing blob hashes, then restore and prove the restore by state.

Mutation landed: injected-text count 0 → 1, removed-text count 1 → 0; mutated blob 447fffc8 differs from HEAD blob bad31df4.

Leg 1 — the src pin, no rebuild needed (the pin imports ./index from src; the mutation cannot reach packages/types/dist, which is where its DataSource comes from):

  • 13 diagnostics total, every one of them inside the pin file, 0 anywhere else in the package.
  • 10 x TS2339 at lines 75–84: the nine member reads (invalidateCache twice, with and without an argument).
  • TS2344 at 89 and 113: the _HasHiddenMembers and _IsTheAdapter assertions.
  • TS7053 at 157: the runtime member sweep can no longer index the narrowed type.
  • The controls at lines 120 (_NotOnDataSource) and 135 (_StillADataSource) produced ZERO diagnostics — the split the pin was designed for.
  • vitest on the same mutated tree still passes, which is the point: esbuild erases types, so the colour comes from tsc and from nowhere else.

Leg 2 — the dist pin, rebuilt in both directions. Rebuilt from the mutated source, then a dist preflight: the widened marker count in dist/index.d.ts was 0 (the mutation reached dist). The standalone dist probe then reproduced the card's failure exactly — 9 x TS2339 — and check:doc-snippets went to exit 1, "455 of 455 block(s) judged, 4 failed": precisely the four README sections this PR rewired, failing on getCacheStats, invalidateCache x2, clearCache, onConnectionStateChange, getConnectionState, isConnected, onBatchProgress, clearCache, invalidateCache.

Restore, proven by state, both halves. Source: restored blob bad31df4 equals HEAD's blob and git diff HEAD is 0 bytes. dist: rebuilt again, widened marker back to 1 and narrow marker 0, dist probe back to 0 diagnostics, check:doc-snippets back to exit 0, 0 failed. The restore leg is rebuilt on purpose — a mutated marker left in dist would keep acting on every later run in this tree.

Clause ②

Clause-②: yes, declared in the claim comment on #7323 in the fixed machine spelling, and needs:contract-review is on both carriers (this PR and the card). It widens the declared public surface reachable through a published entry point. Q1's answer removes the triage's specific worry — no class is exported by implication — but the declaration limb is judged from content, not from paths or from diff size.

The one compatibility note a reviewer should weigh. The widened return is a class with private members, so it is no longer satisfied by a hand-written structural stand-in: an object literal assigned to the factory's ReturnType will now be rejected where it used to be accepted. Nothing in this repo does that (searched every test and app that names the factory — apps/console/src/dataSource.ts only re-exports it), and the fix for a downstream consumer is to annotate such a fake as DataSource, which is what it was standing in for. It is the only direction in which this change is not purely additive, and it belongs in the contract review rather than in a footnote.

Draft on purpose, not flipped ready, auto-merge not enabled — the seat lands it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…it returns
The factory returned `new ObjectStackAdapter(config)` while declaring
`DataSource<T>`. A wider value is assignable to a narrower annotation, so
nothing ever failed to compile — the loss was entirely on the reading side.
Measured against the shipped `dist/index.d.ts` with the doc-snippet gate's own
compiler options, nine reads through `ReturnType<typeof
createObjectStackAdapter>` failed with TS2339: getClient, getCacheStats,
invalidateCache, clearCache, getConnectionState, isConnected,
onConnectionStateChange, onBatchProgress and setSystemCapabilities. Eight are
exactly the members the package README's API Reference documents; the ninth is
the one the factory's own JSDoc links to. Same probe after this change: 0
diagnostics.
Option A of the card. B (widening `DataSource` itself) would make every other
data source declare caching, connection state and batch progress it does not
have; C (documenting a cast) teaches a cast around a declaration that is merely
narrower than the value. `ObjectStackAdapter` was already exported from the
package's only entry, so nothing is exported by implication, and no comment,
ADR or test pinned the narrow return — the commit that added autoReconnect /
maxReconnectAttempts / reconnectDelay to the factory's config bag left the
members observing those features off its declared return in the same change.
Callers are unaffected: `const ds: DataSource = createObjectStackAdapter(…)`
still compiles and still gives the narrow surface.
The README note saying the page could not yet teach the factory's shape is
removed, and the four sections built on the adapter-only members now continue
from Basic Setup's factory call. `adapterFactoryReturn.types.test.ts` pins the
card's TS2339 reproduction inverted, with a control for option B and a control
for swappability.
Part of #7323
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation data-adapter tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Clause-② in-seat contract review — PASS WITH REQUIRED AMENDMENTS. needs:contract-review stays on.

Commissioned by the domain:ui execution seat (PM session session_01EMrWaQw3XS5DxTHxp4yRyC) per the in-seat review route (维护者 2026-08-31 裁定). This seat runs opus and is not at tier, so it commissioned an isolated reviewer rather than self-reviewing. The reviewer was given only the card, its triage and the PR — ⛔ not the dispatch order and not this seat's conclusions.

Tier verification (维护者 2026-08-27 裁定): every harness-stamped model field in the transcript was read before adopting — 76 stamps, all claude-fable-5-1, no other value, and zero fallback/overload markers. CONTRACT_REVIEW_TIER is claude-fable-5 (objectstack/scripts/pm/dispatch-gates.mjs:8091); claude-fable-5-1 is at or above that floor. ⇒ Verified, so the verdict is adopted verbatim — ⛔ nothing below is rewritten, abridged or polished.


Contract review — objectui PR #7503 (head 922ca54ec, base e17605309)

Everything below was read from the diff, the card, triage 5524408927, and my own runs in a detached worktree at /home/user/objectui-review-7503 (dependency closure built; git diff HEAD 0 bytes at the end). No edits, commits, pushes, labels or GitHub posts.

① Derived judgments

Accept set

  • Config parameter type: byte-identical before/after. No change to what the factory accepts. Right.

Published surface

  • Declared return DataSource<T>ObjectStackAdapter<T>. Ships: built dist/index.d.ts ends the factory }): ObjectStackAdapter<T>;. Export list unchanged (the diff adds no export; the test file is outside tsup's single entry). Right.
  • The true delta is 20 members, not 9. tsc-computed Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>> is exactly: clearCache, connect, getCacheStats, getCached, getClient, getConnectionState, getDiscovery, getItems, invalidateCache, invalidateViewKeys, isConnected, listImportMappings, onBatchProgress, onConnectionStateChange, onSaveAdvisory, onWriteWarning, probeAppAccess, queryDataset, setSystemCapabilities, updateDashboard. Proven with Assert<Equal<Extra, Twenty>> silent, and negative controls red at 19 names, 21 names, and the PR's nine (TS2344 ×3) — on both the src program and the shipped dist/index.d.ts. All 20 are now the factory's promise.
  • Judgment on the other 11: right to publish, but under-described. None is @internal/@deprecated; stripInternal is not set anywhere, so they were already in the shipped class type; and the class type is already the monorepo's working adapter contract — packages/react/src/context/AppShellContext.tsx:8 types AdapterCtx as ObjectStackAdapter | null, and app-shell's AdapterProvider, MetadataProvider, MetadataService all take ObjectStackAdapter. Two are escape-hatch shaped and worth the seat's eye: getCached(key): unknown | undefined (raw cache read) and getDiscovery(): Promise<unknown | null> whose own JSDoc says "accesses an internal property of the ObjectStackClient"; invalidateViewKeys is a cache-key coupling seam. Not blocking — reachable via new ObjectStackAdapter() and every app-shell path today — but the changeset presents "nine" as the delta. → Amendment 2.

Assignability attack — no consumer regressed except the one the PR names

  • Probed at PR head, all compile: const a: DataSource = …; const b: DataSource<User> = createObjectStackAdapter({…}) (T infers through the class); explicit <User>; DataSource[] and DataSource<User>[] mixed arrays; DataSource | null union; take(ds: DataSource<User>) with and without explicit T; function-type assignment (cfg) => DataSource (return covariance); generic→concrete (cfg) => DataSource<User>; satisfies DataSource; callback contravariance withDs((ds: DataSource) => …) against a (ds: FactoryReturn) => void parameter; Record<string, DataSource>; Promise<DataSource>.
  • The only break: a structural stand-in for ReturnType<typeof createObjectStackAdapter> — TS2740, "missing … client, connected, connectPromise, metadataCache, and 64 more". Repo has no typeof createObjectStackAdapter anywhere; the sole vi.mock('@object-ui/data-objectstack') (packages/app-shell/src/chrome/ConditionalAuthWrapper.previewRetired-6654.test.tsx:72) mocks only getSharedDiscovery, untyped. Correctly named by the PR. Not a rejection.
  • Secondary, not new: a class-with-privates return is nominal across duplicate installs; every ObjectStackAdapter-typed seam in @object-ui/react/app-shell already carries that property.

Q1 — class already exported: verified.src/index.ts:2148 export class ObjectStackAdapter; tsup.config.ts single entry src/index.ts; name present in built dist/index.d.ts export block; apps/console/src/dataSource.ts:14 re-exports it. Nothing exported by implication.

Q2 — narrow return not deliberate: verified, with one miss.

  • No Equal/expectTypeOf/satisfies/ReturnType pin on the factory anywhere in packages/apps; no ADR names it. 780a1b993 (introduced ): DataSource) carries no design statement. a5d817061 added autoReconnect/maxReconnectAttempts/reconnectDelay to the factory config (diff +358–360) without touching the return; 41b7ce3ce (feat(data-objectstack,app-shell,i18n): view config is explicitly org-wide — permission-gated write path + console wording #6125) added the [ADR-0066] See {@link ObjectStackAdapter.setSystemCapabilities} JSDoc. Plus the AdapterCtx fact above — the React layer already treats the class as the contract, the opposite of a swappability design.
  • Miss: the PR's "no swappability note anywhere" overlooked content/docs/utilities/data-objectstack.mdx, untouched by the diff: L53 "createObjectStackAdapter returns a DataSource — the same universal interface every ObjectUI renderer consumes"; L109 "Factory returning a DataSource"; the signature fragment at L137 ending }): DataSource<T>; under a marker claiming it was "Checked against the shipped dist/index.d.ts … with the same type"; and L163–200, a whole "Adapter-only (hold the class type to reach these)" section built on the distinction this PR erases. Provenance 92c0b1f40 (docs(data-objectstack): document the real headless surface, not a phantom React API #4129, a docs batch that "verified snippets against the built packages") — it describes the shipped d.ts, it does not prescribe, so Q2's answer stands. But the page is now false about the shipped types and nothing gates it (the fragment is declared, so the doc-snippet gate never compiles the return). → Amendment 1.

README

Unmentioned diff content: the factory JSDoc gains 15 lines including "⛔ Do not narrow this back" — consistent with the change; nothing else hidden. Test file is exactly as described.

② Semver

  • Changeset: '@object-ui/data-objectstack': minor. Policy (scripts/check-changeset-no-major.mjs header): major reserved for the objectstack-synchronised bump; objectui's own breaks ship as minor with the break spelled out. The body spells out the stand-in break. I would assign minor. Matches. Changeset Bump Policy / Fixed Group / Declaration checks all green.
  • Wording nit folded into Amendment 2: the bold "Not a breaking change for callers." heading sits directly over the paragraph describing the break.

③ Boundary flags

  • (a) Q1 yes/already; Q2 not deliberate — both confirmed, with the mdx caveat above.
  • (b) 9 vs 4. The card measured 4 (its snippet) and named 8, saying the other four were "exactly the members" on the same README list; setSystemCapabilities was introduced by the triage, not the card. All nine do fail together — reproduced (mutation B: 10 × TS2339 on the nine reads, invalidateCache twice). So "one tier, not two" is right in substance, and the card was partial rather than wrong. But the implementer's own count is partial too: README-documented adapter-only members are 9 (connect()), and the hidden delta is 20.
  • (c) Lint narrowing hides nothing.eslint.config.js has no parserOptions.project/projectService — rules are syntactic and per-file, so a return-type change cannot alter lint output elsewhere. What the package-only run skipped (//#lint:root, other packages) CI's full Lint ran and passed.

Pin verification (not vacuous)

  • In the program:tsc --listFiles for the package lists src/adapterFactoryReturn.types.test.ts and src/index.ts; DataSource resolves to packages/types/dist/data.d.ts (project-reference redirect), as the implementer said.
  • Mutation B (narrow back to DataSource<T>): 13 diagnostics, all in the pin — lines 75–84 TS2339 ×10, 89 and 113 TS2344, 157 TS7053; 0 elsewhere; controls at 120/135/137: 0 hits. Reproduces the reported split exactly. My stand-in probe starts compiling under B, confirming that is the only direction that moved.
  • Mutation C (return Omit<ObjectStackAdapter<T>,'find'>, i.e. no longer a DataSource): line 135 _StillADataSource TS2344 red, line 137 TS2741 red, line 113 red, line 120 green. _StillADataSource genuinely discriminates; _NotOnDataSource reads keyof DataSource and is independent of the return, so it fires only on option B, as designed.
  • vitest from the repo root on the pin file: 1 file, 4 tests pass.

CI by name at 922ca54ec (32 runs, all completed)

  • success (29): Lint · Type Check · Build & E2E · Test (shard 1/4) · Test (shard 2/4) · Test (shard 3/4) · Test (shard 4/4) · Doc Snippet Type Check · README Export Check · Changeset Bump Policy · Changeset Declaration · Changeset Fixed Group Check · Changeset Overwrite Report · Doc Fence Language Check · Doc Component Type Check · Internal Docs Link Check · Docs Route Eager Closure Check · Build Docs · Bundle Analysis · Control Byte Scan · Shell Escape Residue Scan · Pre-Install Import Graph Check · Governed Surface Queue Guard · Inert vi.mock Specifier Check · Skill Eval Token Check · Skill Example Check · Skill Guide Path Check · Live E2E (informational) · label
  • skipped (3, conditional jobs): Test (coverage) · Test (coverage shard) · dependabot
  • failed: none.

Verdict

PASS WITH REQUIRED AMENDMENTS

  1. Bring content/docs/utilities/data-objectstack.mdx in line with the shipped types — four sites: L53 prose ("returns a DataSource"), L109 ("Factory returning a DataSource"), the fragment's }): DataSource<T>; at L137 together with its marker's "checked against dist … same type" claim, and the L163–200 "hold the class type to reach these" section, which now teaches a distinction the factory no longer has. This PR already carries the docs limb for the same package's README; the docs-site page must not contradict dist/index.d.ts after merge.
  2. Fix the changeset's description of the delta (.changeset/7323-adapter-factory-return.md, mirrored in the PR body and test header if desired): state that the factory's declared return is now the full class — every public member of ObjectStackAdapter, 20 beyond DataSource — rather than presenting the nine measured reads as the delta; correct "Eight of those are exactly the members … the README documents" to nine (connect()); and reword the "Not a breaking change for callers." heading so the CHANGELOG line does not read as "no break" above the stand-in caveat.

Both amendments are text-only and independent of each other; neither changes the code, which is right.


Seat disposition

Not landing.needs:contract-review stays on both carriers until both amendments are in and this seat verifies them by content. They are being dispatched now. The code is confirmed correct and ⛔ must not change.

Amendment 1 is the find that justifies the tier, and it names a gate hole.content/docs/utilities/data-objectstack.mdx carries a signature fragment ending }): DataSource<T>; under a marker asserting it was "checked against the shipped dist/index.d.ts … with the same type" — and after this merge that assertion is false. It survives because the fragment is declared, so check:doc-snippet-types never compiles the return. A page that claims to be verified against the built types, and is structurally exempt from the check that would verify it, is worse than an unmarked page: this seat, the implementer and the card all read "no swappability note anywhere" and none of us found it. I am recording that as a finding in its own right when the amendment lands.

⚠️ Also worth the record: the reviewer's count of the true delta — 20 members, not 9 — was reached with Assert<Equal<>> plus negative controls at 19, 21 and 9 names, on both the src program and the shipped d.ts. My dispatch asked it to enumerate what else became reachable; it did, and the answer is that the changeset understates the published surface by more than a factor of two. ⛔ That is a description defect, not a design one — the reviewer establishes the 11 extra members were already in the shipped class type, already reachable via new ObjectStackAdapter(), and already the monorepo's working adapter contract (AdapterCtx is typed as the class). So the fix is the changeset's wording, not the return type.


Generated by Claude Code

…idened factory return (objectui#7323)
Two text-only amendments from the in-seat contract review on the pull request.
No code changes: the return-type widening, the type-level pin and the README
rewiring are untouched.
Amendment 1 — content/docs/utilities/data-objectstack.mdx contradicted the
shipped types. The four sites the review named: the Quick Start prose saying the
factory "returns a `DataSource`"; the API Reference blurb "Factory returning a
`DataSource`"; the signature fragment ending `}): DataSource<T>;` together with
its marker's claim to have been checked against the shipped `dist/index.d.ts`;
and the `ObjectStackAdapter` section built on "hold the class type to reach
these", a distinction the factory no longer has. That section is rewritten rather
than deleted, with a "What changed" paragraph so a reader who followed the old
advice can see why constructing the class by hand is no longer needed.
Two further sites in the same file taught the same erased distinction and are
corrected with them: the Mutations paragraph ("hold the adapter at its class type
... before calling them") and Troubleshooting -> Authentication Errors
("Connection introspection lives on the adapter class ... so hold it at the class
type"); both examples now continue from the factory call, matching how the README
limb of this pull request was rewired. `onMutation` was also misfiled under
"Adapter-only" while being an optional member of `DataSource`; it moves in with
`bulk` and `batchTransaction`.
The fragment's marker no longer asserts an unqualified "checked against the
shipped d.ts". Because the block is declared, `check:doc-snippet-types` never
compiles it, so the marker now states that the agreement is hand-checked rather
than gate-enforced -- which is why the stale return type survived there. The gate
and the `declared` marker keyword are unchanged.
Amendment 2 — the changeset understated the published surface by more than 2x.
It presented the card's nine measured TS2339 reads as the delta; the factory's
declared return is now the class itself, so the delta is every public member of
`ObjectStackAdapter` -- 20 beyond `DataSource`, independently re-derived here as
`Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>>` against
the built `dist/index.d.ts` (20 names, matching the review). The README-documents
sentence is corrected: eight of the nine measured reads are on that list, but the
list itself is nine adapter-only members -- `connect()` was documented all along
and simply was not measured. The "Not a breaking change for callers." heading is
reworded to lead with the one shape that does break, so the CHANGELOG line no
longer reads as "no break" above the caveat describing it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
…ui#7323)
Follow-up to the previous commit, on my own prose in the same amendment:
- Drop the "Until v17.7" version claim. The package is at 17.6.0 and the
changeset is a minor, but the released number is decided by the fixed group at
release time, so the page should not assert it.
- Remove a duplicated bullet list. `bulk` and `batchTransaction` were listed once
under the `DataSource` members and again under the optional-members paragraph.
`onMutation` now joins them in the single list, spelled `onMutation?` like its
neighbours, with the correction noted in the paragraph instead.
- Say "six bullets cover nine members" rather than "those nine", since the list
groups related members onto shared lines, and make the parenthetical naming the
other eleven members read as illustrative rather than exhaustive -- it named
four seams out of eleven.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Both amendments verified — needs:contract-review cleared, ready + auto-merge

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance: 「PASS ⇒ 同席剥标、ready、auto-merge」 (维护者 2026-08-31 裁定). The tier review returned PASS WITH REQUIRED AMENDMENTS (5526083981); both are now in.

⚠️The amendment agent was killed mid-run by a transient HTTP 529 and its report is lost. It had already pushed. So everything below is this seat's own verification from the git objects, with no report to lean on — which is the right posture anyway, and it is the reason this comment carries readings rather than a summary.

Verified by content at 5140938cd

Two commits since the reviewed head 922ca54ec:

  • 27d18e179 — docs page + changeset
  • 5140938cd — a follow-up tightening of the rewritten section

Two files across the whole range, and ⭐ not one line of code:

.changeset/7323-adapter-factory-return.md | 53 +++++++++----
content/docs/utilities/data-objectstack.mdx | 99 ++++++++++++++++-------

⇒ The return-type widening, the type-level pin and the README rewiring are byte-identical to what was reviewed. The standing PASS therefore holds and no re-review is owed — the review's own words were that both amendments are "text-only and independent of each other; neither changes the code, which is right."

Amendment 1 — the docs page. Every flagged claim is gone: no returns a DataSource, no Factory returning a DataSource, no }): DataSource<T>; fragment. The "hold the class type to reach these" section was rewritten, not deleted — it now opens "What changed. This section used to tell you to hold the class type to…", which is what a reader who followed the old advice needs.

And the marker is gone entirely. The Checked against the shipped dist/index.d.ts … with the same type assertion was removed, not reworded around it. That matters more than the four corrected sites: that false claim of verification is why the page stayed wrong through three separate readers looking for exactly it.

Amendment 2 — the changeset. Now states the delta is the whole class — "20 members, not nine" — with the tsc-computed enumeration, corrects eight → nine (connect() is adapter-only), and records that the eleven past the documented nine were already in the shipped class type.

Checks — 32/32, read by name by this seat

29 success, 3 skipped by design (dependabot, Test (coverage), Test (coverage shard …)) on 5140938cd. The ones that actually cover this amendment: Doc Snippet Type Check 13:25:45 · Build Docs 13:23:57 · Internal Docs Link Check 13:21:53 · Doc Fence Language Check · README Export Check 13:25:31 · Changeset Declaration / Bump Policy / Fixed Group / Overwrite Report. Long poles: all four Test (shard N/4) 13:34–13:36, Type Check 13:27:31, Lint 13:27:12.

Carriers

needs:contract-review removed from PR #7503 (remaining documentation, data-adapter, tests) and from card #7323 (remaining bug, pm:dispatched, priority:p2, domain:ui). Both label sets were read immediately before writing rather than assumed — the label job had added labels since my last reading of a sibling PR, and writing a stale set would have clobbered them.

⚠️ Both readings are manual. scripts/pm/check-clause2-carriers.mjs exits 2 in this session (it needs its own GitHub token; this seat reaches GitHub only through MCP) and its own output says that is ⛔ not a clearance. A hand label-check covers strictly less than the tool — this shift produced four distinct carrier defects and the fallback is structurally blind to two of their shapes, now filed as objectstack#14965.

The finding this PR produced, filed separately

#7505 — a declared doc fragment is never compiled by check:doc-snippet-types, so a page can carry a "checked against the shipped dist" marker that no gate ever re-checks. The one known instance is fixed here; the class is not, and enumerating the rest is the first task on that card. ⛔ Filed unassigned and unlabelled for triage.


Generated by Claude Code

Merged via the queue into main with commit 6a99bb2Sep 3, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7323-adapter-return-type branch September 3, 2026 13:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data-adapterdocumentationImprovements or additions to documentationtests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns - #7503

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type
Sep 3, 2026
Merged

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns#7503
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#7323

createObjectStackAdapter returned new ObjectStackAdapter(config) while declaring the shared DataSource interface as its return. A wider value is assignable to a narrower annotation, so nothing ever failed to compile — the loss was entirely on the reading side: every adapter-only member was erased from the type the factory hands back, while staying on the object it hands back.

Measured before the change, compiling against the shipped packages/data-objectstack/dist/index.d.ts with the doc-snippet gate's own compiler options (strict, bundler resolution): 9 x TS2339, not the 4 the card lists — getClient, getCacheStats, invalidateCache, clearCache, getConnectionState, isConnected, onConnectionStateChange, onBatchProgress, setSystemCapabilities. Eight of those are exactly the members the README's API Reference — Methods list documents; the ninth is the one the factory's own JSDoc links to ([ADR-0066] See ObjectStackAdapter.setSystemCapabilities). The same probe after this change: 0 diagnostics.

The two questions that decided the shape

The triage set these as a stop-condition and both are answered from the code, on origin/maine17605309.

Q1 — is ObjectStackAdapter exported from the package's public entry? Yes, already.packages/data-objectstack/src/index.ts:2148 reads export class ObjectStackAdapter, and tsup.config.ts has exactly one entry, src/index.ts. The class is in the shipped dist/index.d.ts export list (verified in the built artifact, not inferred). Two pin tests already assert the exported spelling in source — cloud-surface-retired-4152.pin.test.ts:114 and v3-deep-integration-retired-4241.pin.test.ts:135 — and apps/console/src/dataSource.ts:14 re-exports it by name. So this PR exports nothing by implication: the export list in dist/index.d.ts is unchanged, name for name, before and after.

Q2 — is the narrow return deliberate? No, and there are two pieces of evidence against it.

  1. Commit a5d817061 ("Add connection state monitoring, auto-reconnect, and batch progress") added autoReconnect / maxReconnectAttempts / reconnectDelay to the factory's own config bag while leaving the members that observe those features off the factory's declared return. The same change configured a feature it made unobservable through its own entry point. That is an oversight shape, not an encapsulation decision.
  2. The factory's JSDoc, added by 41b7ce3ce, points the reader at ObjectStackAdapter.setSystemCapabilities — a member its declared return hides.

Nothing anywhere pins the return: no comment, no ADR, no test, no Equal assertion, and no swappability note. Searched the export list, package.jsonfiles / exports, ADR-0066, the sibling adapters, and every test that names the factory.

Had either answer come back the other way this would have been a docs-only PR. They did not, so it is option A — and swappability, the property the narrow return was suspected of protecting, is not lost: a wider return is assignable to the narrower annotation, so const ds: DataSource = createObjectStackAdapter(...) still compiles and still gives the narrow surface. There is a control test that says exactly that.

Why A and not B or C

B (add the missing members to DataSource) would make every other DataSource implementation declare caching, connection state and batch progress it does not have — those are this adapter's concerns. C (document a cast) teaches a cast around a declaration that is merely narrower than the value, which is the opposite of declared = enforced. A is one line and makes declared match shipped for every documented member at once.

The pin

packages/data-objectstack/src/adapterFactoryReturn.types.test.ts — the card's TS2339 reproduction, inverted. It reads all nine members through the factory's return, asserts the return's identity with Equal (not mere assignability — two adapter-shaped declarations are mutually assignable, so only identity can tell "returns THE adapter" from "returns something adapter-shaped"), and carries two controls:

  • _NotOnDataSource — the adapter-only members stay ABSENT from the shared DataSource. This is what makes the reads a statement about the factory's return rather than about every data source, and it fires on option B.
  • _StillADataSource — the widened return is still assignable to DataSource.

Both controls are independent of the return annotation, so reverting the source change turns the reads red and leaves the controls green. That split is measured below, not asserted.

Which tree the pin exercises. Two, deliberately. The test is compiled by pnpm --filter @object-ui/data-objectstack type-check, whose program includes the whole src/** (tests included) and resolves ./index from src — proven with --listFiles: 1 hit for the new file, 55 test files, src/index.ts present. @object-ui/types in that same program resolves from packages/types/dist, so the type used for the controls is the shipped one. The README blocks are the dist half: check:doc-snippets compiles them against the built dist/*.d.ts (its own resolution control prints '@object-ui/types' was successfully resolved to packages/types/dist/index.d.ts).

Documentation

The README note naming this card (added by #5174 batch 8) is removed. The four sections built on the adapter-only members — Metadata Caching, Connection State Monitoring, Batch Operation Progress, Troubleshooting → Cache Issues — now continue from Basic Setup's createObjectStackAdapter(...) call instead of hand-declaring the class, so the page teaches one shape and the doc-snippet gate pins the fix against dist. The other README blocks still declare the class where they only need DataSource-level members; that is truthful (the class is public and documented) and rewriting them buys nothing, so they are left alone.

setSystemCapabilities is still absent from the README's Methods list. It was absent before this PR too, it is a separate doc gap, and adding it here would be scope this card did not ask for.

Verification — every number below was observed, at head 922ca54ec

CheckCommandResult
Package type-checkpnpm --filter @object-ui/data-objectstack type-checkexit 0, tsc --noEmit echoed
Package testspnpm exec vitest run --maxWorkers=2 packages/data-objectstack/ (repo root — a package-dir run is refused by objectui#3378's guard)55 files, 737 tests, all passed
check:doc-snippetsnode scripts/check-doc-snippet-types.mjsexit 0 — "Semantic phase: 455 of 455 block(s) judged, 0 failed." / "Every covered documentation snippet compiles against the built types."
check:readme-exportspnpm check:readme-exportsexit 0 — "OK (43 tracked README(s) ...; 414 self-imports judged (414 real, 0 wrong-path, 0 fabricated); 3305 export symbol(s) read from 37 of 40 tracked package(s) (0 unbuilt ...))"
changeset presencenode scripts/check-changeset-presence.mjsexit 0 — "1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)"
check:doc-fencesnode scripts/check-doc-fence-languages.mjsexit 0 — "every TypeScript block in 227 document(s) is fenced ts/tsx/typescript ..."
control bytesnode scripts/check-control-bytes.mjsexit 0 — "OK (scanned 6180 tracked text file(s); skipped 85 binary)"
Package lintpnpm --filter @object-ui/data-objectstack lintexit 0 — 423 problems, 0 errors, 423 warnings, all pre-existing no-explicit-any; the new file contributes 0 findings (eslint --format json on the two changed source files: new test 0/0)

Every gate result above is quoted from the gate's own verdict line; exit codes were captured by redirecting first and reading $? before any pipe.

Dependency closure built first (pnpm --filter '@object-ui/data-objectstack...' build, then the doc-snippet gate's own derived --build-filter: 26 packages, 34 turbo tasks, then --filter='./packages/*' for check:readme-exports, which had reported 2 unbuilt packages — a "could not run", not a defect).

Lint scope, declared: the table's lint row is the changed package only, not the repo. pnpm lint here is turbo run lint (each package's own eslint .), and CI runs it in full regardless. One reading that needs stating: a separate probe with eslint --no-inline-config reports 4 errors in src/index.ts at lines 1369–1389. Those are the documented eslint-disable-next-line no-console sites for the spec Logger binding (objectui#4029), they are ~4300 lines from either of my edits, and they are invisible to the lint CI actually runs because it honours inline config. Not introduced here, and not silently omitted either.

Reverse verification — red/green split, both directions proven on disk

Method: commit first, then mutate packages/data-objectstack/src/index.ts back to the narrow return under trap ... EXIT INT TERM with an absolute restore path, prove the mutation landed by counting the injected and removed text (not by the editor's exit code) and by comparing blob hashes, then restore and prove the restore by state.

Mutation landed: injected-text count 0 → 1, removed-text count 1 → 0; mutated blob 447fffc8 differs from HEAD blob bad31df4.

Leg 1 — the src pin, no rebuild needed (the pin imports ./index from src; the mutation cannot reach packages/types/dist, which is where its DataSource comes from):

  • 13 diagnostics total, every one of them inside the pin file, 0 anywhere else in the package.
  • 10 x TS2339 at lines 75–84: the nine member reads (invalidateCache twice, with and without an argument).
  • TS2344 at 89 and 113: the _HasHiddenMembers and _IsTheAdapter assertions.
  • TS7053 at 157: the runtime member sweep can no longer index the narrowed type.
  • The controls at lines 120 (_NotOnDataSource) and 135 (_StillADataSource) produced ZERO diagnostics — the split the pin was designed for.
  • vitest on the same mutated tree still passes, which is the point: esbuild erases types, so the colour comes from tsc and from nowhere else.

Leg 2 — the dist pin, rebuilt in both directions. Rebuilt from the mutated source, then a dist preflight: the widened marker count in dist/index.d.ts was 0 (the mutation reached dist). The standalone dist probe then reproduced the card's failure exactly — 9 x TS2339 — and check:doc-snippets went to exit 1, "455 of 455 block(s) judged, 4 failed": precisely the four README sections this PR rewired, failing on getCacheStats, invalidateCache x2, clearCache, onConnectionStateChange, getConnectionState, isConnected, onBatchProgress, clearCache, invalidateCache.

Restore, proven by state, both halves. Source: restored blob bad31df4 equals HEAD's blob and git diff HEAD is 0 bytes. dist: rebuilt again, widened marker back to 1 and narrow marker 0, dist probe back to 0 diagnostics, check:doc-snippets back to exit 0, 0 failed. The restore leg is rebuilt on purpose — a mutated marker left in dist would keep acting on every later run in this tree.

Clause ②

Clause-②: yes, declared in the claim comment on #7323 in the fixed machine spelling, and needs:contract-review is on both carriers (this PR and the card). It widens the declared public surface reachable through a published entry point. Q1's answer removes the triage's specific worry — no class is exported by implication — but the declaration limb is judged from content, not from paths or from diff size.

The one compatibility note a reviewer should weigh. The widened return is a class with private members, so it is no longer satisfied by a hand-written structural stand-in: an object literal assigned to the factory's ReturnType will now be rejected where it used to be accepted. Nothing in this repo does that (searched every test and app that names the factory — apps/console/src/dataSource.ts only re-exports it), and the fix for a downstream consumer is to annotate such a fake as DataSource, which is what it was standing in for. It is the only direction in which this change is not purely additive, and it belongs in the contract review rather than in a footnote.

Draft on purpose, not flipped ready, auto-merge not enabled — the seat lands it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…it returns
The factory returned `new ObjectStackAdapter(config)` while declaring
`DataSource<T>`. A wider value is assignable to a narrower annotation, so
nothing ever failed to compile — the loss was entirely on the reading side.
Measured against the shipped `dist/index.d.ts` with the doc-snippet gate's own
compiler options, nine reads through `ReturnType<typeof
createObjectStackAdapter>` failed with TS2339: getClient, getCacheStats,
invalidateCache, clearCache, getConnectionState, isConnected,
onConnectionStateChange, onBatchProgress and setSystemCapabilities. Eight are
exactly the members the package README's API Reference documents; the ninth is
the one the factory's own JSDoc links to. Same probe after this change: 0
diagnostics.
Option A of the card. B (widening `DataSource` itself) would make every other
data source declare caching, connection state and batch progress it does not
have; C (documenting a cast) teaches a cast around a declaration that is merely
narrower than the value. `ObjectStackAdapter` was already exported from the
package's only entry, so nothing is exported by implication, and no comment,
ADR or test pinned the narrow return — the commit that added autoReconnect /
maxReconnectAttempts / reconnectDelay to the factory's config bag left the
members observing those features off its declared return in the same change.
Callers are unaffected: `const ds: DataSource = createObjectStackAdapter(…)`
still compiles and still gives the narrow surface.
The README note saying the page could not yet teach the factory's shape is
removed, and the four sections built on the adapter-only members now continue
from Basic Setup's factory call. `adapterFactoryReturn.types.test.ts` pins the
card's TS2339 reproduction inverted, with a control for option B and a control
for swappability.
Part of #7323
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation data-adapter tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Clause-② in-seat contract review — PASS WITH REQUIRED AMENDMENTS. needs:contract-review stays on.

Commissioned by the domain:ui execution seat (PM session session_01EMrWaQw3XS5DxTHxp4yRyC) per the in-seat review route (维护者 2026-08-31 裁定). This seat runs opus and is not at tier, so it commissioned an isolated reviewer rather than self-reviewing. The reviewer was given only the card, its triage and the PR — ⛔ not the dispatch order and not this seat's conclusions.

Tier verification (维护者 2026-08-27 裁定): every harness-stamped model field in the transcript was read before adopting — 76 stamps, all claude-fable-5-1, no other value, and zero fallback/overload markers. CONTRACT_REVIEW_TIER is claude-fable-5 (objectstack/scripts/pm/dispatch-gates.mjs:8091); claude-fable-5-1 is at or above that floor. ⇒ Verified, so the verdict is adopted verbatim — ⛔ nothing below is rewritten, abridged or polished.


Contract review — objectui PR #7503 (head 922ca54ec, base e17605309)

Everything below was read from the diff, the card, triage 5524408927, and my own runs in a detached worktree at /home/user/objectui-review-7503 (dependency closure built; git diff HEAD 0 bytes at the end). No edits, commits, pushes, labels or GitHub posts.

① Derived judgments

Accept set

  • Config parameter type: byte-identical before/after. No change to what the factory accepts. Right.

Published surface

  • Declared return DataSource<T>ObjectStackAdapter<T>. Ships: built dist/index.d.ts ends the factory }): ObjectStackAdapter<T>;. Export list unchanged (the diff adds no export; the test file is outside tsup's single entry). Right.
  • The true delta is 20 members, not 9. tsc-computed Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>> is exactly: clearCache, connect, getCacheStats, getCached, getClient, getConnectionState, getDiscovery, getItems, invalidateCache, invalidateViewKeys, isConnected, listImportMappings, onBatchProgress, onConnectionStateChange, onSaveAdvisory, onWriteWarning, probeAppAccess, queryDataset, setSystemCapabilities, updateDashboard. Proven with Assert<Equal<Extra, Twenty>> silent, and negative controls red at 19 names, 21 names, and the PR's nine (TS2344 ×3) — on both the src program and the shipped dist/index.d.ts. All 20 are now the factory's promise.
  • Judgment on the other 11: right to publish, but under-described. None is @internal/@deprecated; stripInternal is not set anywhere, so they were already in the shipped class type; and the class type is already the monorepo's working adapter contract — packages/react/src/context/AppShellContext.tsx:8 types AdapterCtx as ObjectStackAdapter | null, and app-shell's AdapterProvider, MetadataProvider, MetadataService all take ObjectStackAdapter. Two are escape-hatch shaped and worth the seat's eye: getCached(key): unknown | undefined (raw cache read) and getDiscovery(): Promise<unknown | null> whose own JSDoc says "accesses an internal property of the ObjectStackClient"; invalidateViewKeys is a cache-key coupling seam. Not blocking — reachable via new ObjectStackAdapter() and every app-shell path today — but the changeset presents "nine" as the delta. → Amendment 2.

Assignability attack — no consumer regressed except the one the PR names

  • Probed at PR head, all compile: const a: DataSource = …; const b: DataSource<User> = createObjectStackAdapter({…}) (T infers through the class); explicit <User>; DataSource[] and DataSource<User>[] mixed arrays; DataSource | null union; take(ds: DataSource<User>) with and without explicit T; function-type assignment (cfg) => DataSource (return covariance); generic→concrete (cfg) => DataSource<User>; satisfies DataSource; callback contravariance withDs((ds: DataSource) => …) against a (ds: FactoryReturn) => void parameter; Record<string, DataSource>; Promise<DataSource>.
  • The only break: a structural stand-in for ReturnType<typeof createObjectStackAdapter> — TS2740, "missing … client, connected, connectPromise, metadataCache, and 64 more". Repo has no typeof createObjectStackAdapter anywhere; the sole vi.mock('@object-ui/data-objectstack') (packages/app-shell/src/chrome/ConditionalAuthWrapper.previewRetired-6654.test.tsx:72) mocks only getSharedDiscovery, untyped. Correctly named by the PR. Not a rejection.
  • Secondary, not new: a class-with-privates return is nominal across duplicate installs; every ObjectStackAdapter-typed seam in @object-ui/react/app-shell already carries that property.

Q1 — class already exported: verified.src/index.ts:2148 export class ObjectStackAdapter; tsup.config.ts single entry src/index.ts; name present in built dist/index.d.ts export block; apps/console/src/dataSource.ts:14 re-exports it. Nothing exported by implication.

Q2 — narrow return not deliberate: verified, with one miss.

  • No Equal/expectTypeOf/satisfies/ReturnType pin on the factory anywhere in packages/apps; no ADR names it. 780a1b993 (introduced ): DataSource) carries no design statement. a5d817061 added autoReconnect/maxReconnectAttempts/reconnectDelay to the factory config (diff +358–360) without touching the return; 41b7ce3ce (feat(data-objectstack,app-shell,i18n): view config is explicitly org-wide — permission-gated write path + console wording #6125) added the [ADR-0066] See {@link ObjectStackAdapter.setSystemCapabilities} JSDoc. Plus the AdapterCtx fact above — the React layer already treats the class as the contract, the opposite of a swappability design.
  • Miss: the PR's "no swappability note anywhere" overlooked content/docs/utilities/data-objectstack.mdx, untouched by the diff: L53 "createObjectStackAdapter returns a DataSource — the same universal interface every ObjectUI renderer consumes"; L109 "Factory returning a DataSource"; the signature fragment at L137 ending }): DataSource<T>; under a marker claiming it was "Checked against the shipped dist/index.d.ts … with the same type"; and L163–200, a whole "Adapter-only (hold the class type to reach these)" section built on the distinction this PR erases. Provenance 92c0b1f40 (docs(data-objectstack): document the real headless surface, not a phantom React API #4129, a docs batch that "verified snippets against the built packages") — it describes the shipped d.ts, it does not prescribe, so Q2's answer stands. But the page is now false about the shipped types and nothing gates it (the fragment is declared, so the doc-snippet gate never compiles the return). → Amendment 1.

README

Unmentioned diff content: the factory JSDoc gains 15 lines including "⛔ Do not narrow this back" — consistent with the change; nothing else hidden. Test file is exactly as described.

② Semver

  • Changeset: '@object-ui/data-objectstack': minor. Policy (scripts/check-changeset-no-major.mjs header): major reserved for the objectstack-synchronised bump; objectui's own breaks ship as minor with the break spelled out. The body spells out the stand-in break. I would assign minor. Matches. Changeset Bump Policy / Fixed Group / Declaration checks all green.
  • Wording nit folded into Amendment 2: the bold "Not a breaking change for callers." heading sits directly over the paragraph describing the break.

③ Boundary flags

  • (a) Q1 yes/already; Q2 not deliberate — both confirmed, with the mdx caveat above.
  • (b) 9 vs 4. The card measured 4 (its snippet) and named 8, saying the other four were "exactly the members" on the same README list; setSystemCapabilities was introduced by the triage, not the card. All nine do fail together — reproduced (mutation B: 10 × TS2339 on the nine reads, invalidateCache twice). So "one tier, not two" is right in substance, and the card was partial rather than wrong. But the implementer's own count is partial too: README-documented adapter-only members are 9 (connect()), and the hidden delta is 20.
  • (c) Lint narrowing hides nothing.eslint.config.js has no parserOptions.project/projectService — rules are syntactic and per-file, so a return-type change cannot alter lint output elsewhere. What the package-only run skipped (//#lint:root, other packages) CI's full Lint ran and passed.

Pin verification (not vacuous)

  • In the program:tsc --listFiles for the package lists src/adapterFactoryReturn.types.test.ts and src/index.ts; DataSource resolves to packages/types/dist/data.d.ts (project-reference redirect), as the implementer said.
  • Mutation B (narrow back to DataSource<T>): 13 diagnostics, all in the pin — lines 75–84 TS2339 ×10, 89 and 113 TS2344, 157 TS7053; 0 elsewhere; controls at 120/135/137: 0 hits. Reproduces the reported split exactly. My stand-in probe starts compiling under B, confirming that is the only direction that moved.
  • Mutation C (return Omit<ObjectStackAdapter<T>,'find'>, i.e. no longer a DataSource): line 135 _StillADataSource TS2344 red, line 137 TS2741 red, line 113 red, line 120 green. _StillADataSource genuinely discriminates; _NotOnDataSource reads keyof DataSource and is independent of the return, so it fires only on option B, as designed.
  • vitest from the repo root on the pin file: 1 file, 4 tests pass.

CI by name at 922ca54ec (32 runs, all completed)

  • success (29): Lint · Type Check · Build & E2E · Test (shard 1/4) · Test (shard 2/4) · Test (shard 3/4) · Test (shard 4/4) · Doc Snippet Type Check · README Export Check · Changeset Bump Policy · Changeset Declaration · Changeset Fixed Group Check · Changeset Overwrite Report · Doc Fence Language Check · Doc Component Type Check · Internal Docs Link Check · Docs Route Eager Closure Check · Build Docs · Bundle Analysis · Control Byte Scan · Shell Escape Residue Scan · Pre-Install Import Graph Check · Governed Surface Queue Guard · Inert vi.mock Specifier Check · Skill Eval Token Check · Skill Example Check · Skill Guide Path Check · Live E2E (informational) · label
  • skipped (3, conditional jobs): Test (coverage) · Test (coverage shard) · dependabot
  • failed: none.

Verdict

PASS WITH REQUIRED AMENDMENTS

  1. Bring content/docs/utilities/data-objectstack.mdx in line with the shipped types — four sites: L53 prose ("returns a DataSource"), L109 ("Factory returning a DataSource"), the fragment's }): DataSource<T>; at L137 together with its marker's "checked against dist … same type" claim, and the L163–200 "hold the class type to reach these" section, which now teaches a distinction the factory no longer has. This PR already carries the docs limb for the same package's README; the docs-site page must not contradict dist/index.d.ts after merge.
  2. Fix the changeset's description of the delta (.changeset/7323-adapter-factory-return.md, mirrored in the PR body and test header if desired): state that the factory's declared return is now the full class — every public member of ObjectStackAdapter, 20 beyond DataSource — rather than presenting the nine measured reads as the delta; correct "Eight of those are exactly the members … the README documents" to nine (connect()); and reword the "Not a breaking change for callers." heading so the CHANGELOG line does not read as "no break" above the stand-in caveat.

Both amendments are text-only and independent of each other; neither changes the code, which is right.


Seat disposition

Not landing.needs:contract-review stays on both carriers until both amendments are in and this seat verifies them by content. They are being dispatched now. The code is confirmed correct and ⛔ must not change.

Amendment 1 is the find that justifies the tier, and it names a gate hole.content/docs/utilities/data-objectstack.mdx carries a signature fragment ending }): DataSource<T>; under a marker asserting it was "checked against the shipped dist/index.d.ts … with the same type" — and after this merge that assertion is false. It survives because the fragment is declared, so check:doc-snippet-types never compiles the return. A page that claims to be verified against the built types, and is structurally exempt from the check that would verify it, is worse than an unmarked page: this seat, the implementer and the card all read "no swappability note anywhere" and none of us found it. I am recording that as a finding in its own right when the amendment lands.

⚠️ Also worth the record: the reviewer's count of the true delta — 20 members, not 9 — was reached with Assert<Equal<>> plus negative controls at 19, 21 and 9 names, on both the src program and the shipped d.ts. My dispatch asked it to enumerate what else became reachable; it did, and the answer is that the changeset understates the published surface by more than a factor of two. ⛔ That is a description defect, not a design one — the reviewer establishes the 11 extra members were already in the shipped class type, already reachable via new ObjectStackAdapter(), and already the monorepo's working adapter contract (AdapterCtx is typed as the class). So the fix is the changeset's wording, not the return type.


Generated by Claude Code

…idened factory return (objectui#7323)
Two text-only amendments from the in-seat contract review on the pull request.
No code changes: the return-type widening, the type-level pin and the README
rewiring are untouched.
Amendment 1 — content/docs/utilities/data-objectstack.mdx contradicted the
shipped types. The four sites the review named: the Quick Start prose saying the
factory "returns a `DataSource`"; the API Reference blurb "Factory returning a
`DataSource`"; the signature fragment ending `}): DataSource<T>;` together with
its marker's claim to have been checked against the shipped `dist/index.d.ts`;
and the `ObjectStackAdapter` section built on "hold the class type to reach
these", a distinction the factory no longer has. That section is rewritten rather
than deleted, with a "What changed" paragraph so a reader who followed the old
advice can see why constructing the class by hand is no longer needed.
Two further sites in the same file taught the same erased distinction and are
corrected with them: the Mutations paragraph ("hold the adapter at its class type
... before calling them") and Troubleshooting -> Authentication Errors
("Connection introspection lives on the adapter class ... so hold it at the class
type"); both examples now continue from the factory call, matching how the README
limb of this pull request was rewired. `onMutation` was also misfiled under
"Adapter-only" while being an optional member of `DataSource`; it moves in with
`bulk` and `batchTransaction`.
The fragment's marker no longer asserts an unqualified "checked against the
shipped d.ts". Because the block is declared, `check:doc-snippet-types` never
compiles it, so the marker now states that the agreement is hand-checked rather
than gate-enforced -- which is why the stale return type survived there. The gate
and the `declared` marker keyword are unchanged.
Amendment 2 — the changeset understated the published surface by more than 2x.
It presented the card's nine measured TS2339 reads as the delta; the factory's
declared return is now the class itself, so the delta is every public member of
`ObjectStackAdapter` -- 20 beyond `DataSource`, independently re-derived here as
`Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>>` against
the built `dist/index.d.ts` (20 names, matching the review). The README-documents
sentence is corrected: eight of the nine measured reads are on that list, but the
list itself is nine adapter-only members -- `connect()` was documented all along
and simply was not measured. The "Not a breaking change for callers." heading is
reworded to lead with the one shape that does break, so the CHANGELOG line no
longer reads as "no break" above the caveat describing it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
…ui#7323)
Follow-up to the previous commit, on my own prose in the same amendment:
- Drop the "Until v17.7" version claim. The package is at 17.6.0 and the
changeset is a minor, but the released number is decided by the fixed group at
release time, so the page should not assert it.
- Remove a duplicated bullet list. `bulk` and `batchTransaction` were listed once
under the `DataSource` members and again under the optional-members paragraph.
`onMutation` now joins them in the single list, spelled `onMutation?` like its
neighbours, with the correction noted in the paragraph instead.
- Say "six bullets cover nine members" rather than "those nine", since the list
groups related members onto shared lines, and make the parenthetical naming the
other eleven members read as illustrative rather than exhaustive -- it named
four seams out of eleven.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Both amendments verified — needs:contract-review cleared, ready + auto-merge

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance: 「PASS ⇒ 同席剥标、ready、auto-merge」 (维护者 2026-08-31 裁定). The tier review returned PASS WITH REQUIRED AMENDMENTS (5526083981); both are now in.

⚠️The amendment agent was killed mid-run by a transient HTTP 529 and its report is lost. It had already pushed. So everything below is this seat's own verification from the git objects, with no report to lean on — which is the right posture anyway, and it is the reason this comment carries readings rather than a summary.

Verified by content at 5140938cd

Two commits since the reviewed head 922ca54ec:

  • 27d18e179 — docs page + changeset
  • 5140938cd — a follow-up tightening of the rewritten section

Two files across the whole range, and ⭐ not one line of code:

.changeset/7323-adapter-factory-return.md | 53 +++++++++----
content/docs/utilities/data-objectstack.mdx | 99 ++++++++++++++++-------

⇒ The return-type widening, the type-level pin and the README rewiring are byte-identical to what was reviewed. The standing PASS therefore holds and no re-review is owed — the review's own words were that both amendments are "text-only and independent of each other; neither changes the code, which is right."

Amendment 1 — the docs page. Every flagged claim is gone: no returns a DataSource, no Factory returning a DataSource, no }): DataSource<T>; fragment. The "hold the class type to reach these" section was rewritten, not deleted — it now opens "What changed. This section used to tell you to hold the class type to…", which is what a reader who followed the old advice needs.

And the marker is gone entirely. The Checked against the shipped dist/index.d.ts … with the same type assertion was removed, not reworded around it. That matters more than the four corrected sites: that false claim of verification is why the page stayed wrong through three separate readers looking for exactly it.

Amendment 2 — the changeset. Now states the delta is the whole class — "20 members, not nine" — with the tsc-computed enumeration, corrects eight → nine (connect() is adapter-only), and records that the eleven past the documented nine were already in the shipped class type.

Checks — 32/32, read by name by this seat

29 success, 3 skipped by design (dependabot, Test (coverage), Test (coverage shard …)) on 5140938cd. The ones that actually cover this amendment: Doc Snippet Type Check 13:25:45 · Build Docs 13:23:57 · Internal Docs Link Check 13:21:53 · Doc Fence Language Check · README Export Check 13:25:31 · Changeset Declaration / Bump Policy / Fixed Group / Overwrite Report. Long poles: all four Test (shard N/4) 13:34–13:36, Type Check 13:27:31, Lint 13:27:12.

Carriers

needs:contract-review removed from PR #7503 (remaining documentation, data-adapter, tests) and from card #7323 (remaining bug, pm:dispatched, priority:p2, domain:ui). Both label sets were read immediately before writing rather than assumed — the label job had added labels since my last reading of a sibling PR, and writing a stale set would have clobbered them.

⚠️ Both readings are manual. scripts/pm/check-clause2-carriers.mjs exits 2 in this session (it needs its own GitHub token; this seat reaches GitHub only through MCP) and its own output says that is ⛔ not a clearance. A hand label-check covers strictly less than the tool — this shift produced four distinct carrier defects and the fallback is structurally blind to two of their shapes, now filed as objectstack#14965.

The finding this PR produced, filed separately

#7505 — a declared doc fragment is never compiled by check:doc-snippet-types, so a page can carry a "checked against the shipped dist" marker that no gate ever re-checks. The one known instance is fixed here; the class is not, and enumerating the rest is the first task on that card. ⛔ Filed unassigned and unlabelled for triage.


Generated by Claude Code

Merged via the queue into main with commit 6a99bb2Sep 3, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7323-adapter-return-type branch September 3, 2026 13:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data-adapterdocumentationImprovements or additions to documentationtests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns - #7503

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type
Sep 3, 2026
Merged

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns#7503
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#7323

createObjectStackAdapter returned new ObjectStackAdapter(config) while declaring the shared DataSource interface as its return. A wider value is assignable to a narrower annotation, so nothing ever failed to compile — the loss was entirely on the reading side: every adapter-only member was erased from the type the factory hands back, while staying on the object it hands back.

Measured before the change, compiling against the shipped packages/data-objectstack/dist/index.d.ts with the doc-snippet gate's own compiler options (strict, bundler resolution): 9 x TS2339, not the 4 the card lists — getClient, getCacheStats, invalidateCache, clearCache, getConnectionState, isConnected, onConnectionStateChange, onBatchProgress, setSystemCapabilities. Eight of those are exactly the members the README's API Reference — Methods list documents; the ninth is the one the factory's own JSDoc links to ([ADR-0066] See ObjectStackAdapter.setSystemCapabilities). The same probe after this change: 0 diagnostics.

The two questions that decided the shape

The triage set these as a stop-condition and both are answered from the code, on origin/maine17605309.

Q1 — is ObjectStackAdapter exported from the package's public entry? Yes, already.packages/data-objectstack/src/index.ts:2148 reads export class ObjectStackAdapter, and tsup.config.ts has exactly one entry, src/index.ts. The class is in the shipped dist/index.d.ts export list (verified in the built artifact, not inferred). Two pin tests already assert the exported spelling in source — cloud-surface-retired-4152.pin.test.ts:114 and v3-deep-integration-retired-4241.pin.test.ts:135 — and apps/console/src/dataSource.ts:14 re-exports it by name. So this PR exports nothing by implication: the export list in dist/index.d.ts is unchanged, name for name, before and after.

Q2 — is the narrow return deliberate? No, and there are two pieces of evidence against it.

  1. Commit a5d817061 ("Add connection state monitoring, auto-reconnect, and batch progress") added autoReconnect / maxReconnectAttempts / reconnectDelay to the factory's own config bag while leaving the members that observe those features off the factory's declared return. The same change configured a feature it made unobservable through its own entry point. That is an oversight shape, not an encapsulation decision.
  2. The factory's JSDoc, added by 41b7ce3ce, points the reader at ObjectStackAdapter.setSystemCapabilities — a member its declared return hides.

Nothing anywhere pins the return: no comment, no ADR, no test, no Equal assertion, and no swappability note. Searched the export list, package.jsonfiles / exports, ADR-0066, the sibling adapters, and every test that names the factory.

Had either answer come back the other way this would have been a docs-only PR. They did not, so it is option A — and swappability, the property the narrow return was suspected of protecting, is not lost: a wider return is assignable to the narrower annotation, so const ds: DataSource = createObjectStackAdapter(...) still compiles and still gives the narrow surface. There is a control test that says exactly that.

Why A and not B or C

B (add the missing members to DataSource) would make every other DataSource implementation declare caching, connection state and batch progress it does not have — those are this adapter's concerns. C (document a cast) teaches a cast around a declaration that is merely narrower than the value, which is the opposite of declared = enforced. A is one line and makes declared match shipped for every documented member at once.

The pin

packages/data-objectstack/src/adapterFactoryReturn.types.test.ts — the card's TS2339 reproduction, inverted. It reads all nine members through the factory's return, asserts the return's identity with Equal (not mere assignability — two adapter-shaped declarations are mutually assignable, so only identity can tell "returns THE adapter" from "returns something adapter-shaped"), and carries two controls:

  • _NotOnDataSource — the adapter-only members stay ABSENT from the shared DataSource. This is what makes the reads a statement about the factory's return rather than about every data source, and it fires on option B.
  • _StillADataSource — the widened return is still assignable to DataSource.

Both controls are independent of the return annotation, so reverting the source change turns the reads red and leaves the controls green. That split is measured below, not asserted.

Which tree the pin exercises. Two, deliberately. The test is compiled by pnpm --filter @object-ui/data-objectstack type-check, whose program includes the whole src/** (tests included) and resolves ./index from src — proven with --listFiles: 1 hit for the new file, 55 test files, src/index.ts present. @object-ui/types in that same program resolves from packages/types/dist, so the type used for the controls is the shipped one. The README blocks are the dist half: check:doc-snippets compiles them against the built dist/*.d.ts (its own resolution control prints '@object-ui/types' was successfully resolved to packages/types/dist/index.d.ts).

Documentation

The README note naming this card (added by #5174 batch 8) is removed. The four sections built on the adapter-only members — Metadata Caching, Connection State Monitoring, Batch Operation Progress, Troubleshooting → Cache Issues — now continue from Basic Setup's createObjectStackAdapter(...) call instead of hand-declaring the class, so the page teaches one shape and the doc-snippet gate pins the fix against dist. The other README blocks still declare the class where they only need DataSource-level members; that is truthful (the class is public and documented) and rewriting them buys nothing, so they are left alone.

setSystemCapabilities is still absent from the README's Methods list. It was absent before this PR too, it is a separate doc gap, and adding it here would be scope this card did not ask for.

Verification — every number below was observed, at head 922ca54ec

CheckCommandResult
Package type-checkpnpm --filter @object-ui/data-objectstack type-checkexit 0, tsc --noEmit echoed
Package testspnpm exec vitest run --maxWorkers=2 packages/data-objectstack/ (repo root — a package-dir run is refused by objectui#3378's guard)55 files, 737 tests, all passed
check:doc-snippetsnode scripts/check-doc-snippet-types.mjsexit 0 — "Semantic phase: 455 of 455 block(s) judged, 0 failed." / "Every covered documentation snippet compiles against the built types."
check:readme-exportspnpm check:readme-exportsexit 0 — "OK (43 tracked README(s) ...; 414 self-imports judged (414 real, 0 wrong-path, 0 fabricated); 3305 export symbol(s) read from 37 of 40 tracked package(s) (0 unbuilt ...))"
changeset presencenode scripts/check-changeset-presence.mjsexit 0 — "1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)"
check:doc-fencesnode scripts/check-doc-fence-languages.mjsexit 0 — "every TypeScript block in 227 document(s) is fenced ts/tsx/typescript ..."
control bytesnode scripts/check-control-bytes.mjsexit 0 — "OK (scanned 6180 tracked text file(s); skipped 85 binary)"
Package lintpnpm --filter @object-ui/data-objectstack lintexit 0 — 423 problems, 0 errors, 423 warnings, all pre-existing no-explicit-any; the new file contributes 0 findings (eslint --format json on the two changed source files: new test 0/0)

Every gate result above is quoted from the gate's own verdict line; exit codes were captured by redirecting first and reading $? before any pipe.

Dependency closure built first (pnpm --filter '@object-ui/data-objectstack...' build, then the doc-snippet gate's own derived --build-filter: 26 packages, 34 turbo tasks, then --filter='./packages/*' for check:readme-exports, which had reported 2 unbuilt packages — a "could not run", not a defect).

Lint scope, declared: the table's lint row is the changed package only, not the repo. pnpm lint here is turbo run lint (each package's own eslint .), and CI runs it in full regardless. One reading that needs stating: a separate probe with eslint --no-inline-config reports 4 errors in src/index.ts at lines 1369–1389. Those are the documented eslint-disable-next-line no-console sites for the spec Logger binding (objectui#4029), they are ~4300 lines from either of my edits, and they are invisible to the lint CI actually runs because it honours inline config. Not introduced here, and not silently omitted either.

Reverse verification — red/green split, both directions proven on disk

Method: commit first, then mutate packages/data-objectstack/src/index.ts back to the narrow return under trap ... EXIT INT TERM with an absolute restore path, prove the mutation landed by counting the injected and removed text (not by the editor's exit code) and by comparing blob hashes, then restore and prove the restore by state.

Mutation landed: injected-text count 0 → 1, removed-text count 1 → 0; mutated blob 447fffc8 differs from HEAD blob bad31df4.

Leg 1 — the src pin, no rebuild needed (the pin imports ./index from src; the mutation cannot reach packages/types/dist, which is where its DataSource comes from):

  • 13 diagnostics total, every one of them inside the pin file, 0 anywhere else in the package.
  • 10 x TS2339 at lines 75–84: the nine member reads (invalidateCache twice, with and without an argument).
  • TS2344 at 89 and 113: the _HasHiddenMembers and _IsTheAdapter assertions.
  • TS7053 at 157: the runtime member sweep can no longer index the narrowed type.
  • The controls at lines 120 (_NotOnDataSource) and 135 (_StillADataSource) produced ZERO diagnostics — the split the pin was designed for.
  • vitest on the same mutated tree still passes, which is the point: esbuild erases types, so the colour comes from tsc and from nowhere else.

Leg 2 — the dist pin, rebuilt in both directions. Rebuilt from the mutated source, then a dist preflight: the widened marker count in dist/index.d.ts was 0 (the mutation reached dist). The standalone dist probe then reproduced the card's failure exactly — 9 x TS2339 — and check:doc-snippets went to exit 1, "455 of 455 block(s) judged, 4 failed": precisely the four README sections this PR rewired, failing on getCacheStats, invalidateCache x2, clearCache, onConnectionStateChange, getConnectionState, isConnected, onBatchProgress, clearCache, invalidateCache.

Restore, proven by state, both halves. Source: restored blob bad31df4 equals HEAD's blob and git diff HEAD is 0 bytes. dist: rebuilt again, widened marker back to 1 and narrow marker 0, dist probe back to 0 diagnostics, check:doc-snippets back to exit 0, 0 failed. The restore leg is rebuilt on purpose — a mutated marker left in dist would keep acting on every later run in this tree.

Clause ②

Clause-②: yes, declared in the claim comment on #7323 in the fixed machine spelling, and needs:contract-review is on both carriers (this PR and the card). It widens the declared public surface reachable through a published entry point. Q1's answer removes the triage's specific worry — no class is exported by implication — but the declaration limb is judged from content, not from paths or from diff size.

The one compatibility note a reviewer should weigh. The widened return is a class with private members, so it is no longer satisfied by a hand-written structural stand-in: an object literal assigned to the factory's ReturnType will now be rejected where it used to be accepted. Nothing in this repo does that (searched every test and app that names the factory — apps/console/src/dataSource.ts only re-exports it), and the fix for a downstream consumer is to annotate such a fake as DataSource, which is what it was standing in for. It is the only direction in which this change is not purely additive, and it belongs in the contract review rather than in a footnote.

Draft on purpose, not flipped ready, auto-merge not enabled — the seat lands it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…it returns
The factory returned `new ObjectStackAdapter(config)` while declaring
`DataSource<T>`. A wider value is assignable to a narrower annotation, so
nothing ever failed to compile — the loss was entirely on the reading side.
Measured against the shipped `dist/index.d.ts` with the doc-snippet gate's own
compiler options, nine reads through `ReturnType<typeof
createObjectStackAdapter>` failed with TS2339: getClient, getCacheStats,
invalidateCache, clearCache, getConnectionState, isConnected,
onConnectionStateChange, onBatchProgress and setSystemCapabilities. Eight are
exactly the members the package README's API Reference documents; the ninth is
the one the factory's own JSDoc links to. Same probe after this change: 0
diagnostics.
Option A of the card. B (widening `DataSource` itself) would make every other
data source declare caching, connection state and batch progress it does not
have; C (documenting a cast) teaches a cast around a declaration that is merely
narrower than the value. `ObjectStackAdapter` was already exported from the
package's only entry, so nothing is exported by implication, and no comment,
ADR or test pinned the narrow return — the commit that added autoReconnect /
maxReconnectAttempts / reconnectDelay to the factory's config bag left the
members observing those features off its declared return in the same change.
Callers are unaffected: `const ds: DataSource = createObjectStackAdapter(…)`
still compiles and still gives the narrow surface.
The README note saying the page could not yet teach the factory's shape is
removed, and the four sections built on the adapter-only members now continue
from Basic Setup's factory call. `adapterFactoryReturn.types.test.ts` pins the
card's TS2339 reproduction inverted, with a control for option B and a control
for swappability.
Part of #7323
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation data-adapter tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Clause-② in-seat contract review — PASS WITH REQUIRED AMENDMENTS. needs:contract-review stays on.

Commissioned by the domain:ui execution seat (PM session session_01EMrWaQw3XS5DxTHxp4yRyC) per the in-seat review route (维护者 2026-08-31 裁定). This seat runs opus and is not at tier, so it commissioned an isolated reviewer rather than self-reviewing. The reviewer was given only the card, its triage and the PR — ⛔ not the dispatch order and not this seat's conclusions.

Tier verification (维护者 2026-08-27 裁定): every harness-stamped model field in the transcript was read before adopting — 76 stamps, all claude-fable-5-1, no other value, and zero fallback/overload markers. CONTRACT_REVIEW_TIER is claude-fable-5 (objectstack/scripts/pm/dispatch-gates.mjs:8091); claude-fable-5-1 is at or above that floor. ⇒ Verified, so the verdict is adopted verbatim — ⛔ nothing below is rewritten, abridged or polished.


Contract review — objectui PR #7503 (head 922ca54ec, base e17605309)

Everything below was read from the diff, the card, triage 5524408927, and my own runs in a detached worktree at /home/user/objectui-review-7503 (dependency closure built; git diff HEAD 0 bytes at the end). No edits, commits, pushes, labels or GitHub posts.

① Derived judgments

Accept set

  • Config parameter type: byte-identical before/after. No change to what the factory accepts. Right.

Published surface

  • Declared return DataSource<T>ObjectStackAdapter<T>. Ships: built dist/index.d.ts ends the factory }): ObjectStackAdapter<T>;. Export list unchanged (the diff adds no export; the test file is outside tsup's single entry). Right.
  • The true delta is 20 members, not 9. tsc-computed Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>> is exactly: clearCache, connect, getCacheStats, getCached, getClient, getConnectionState, getDiscovery, getItems, invalidateCache, invalidateViewKeys, isConnected, listImportMappings, onBatchProgress, onConnectionStateChange, onSaveAdvisory, onWriteWarning, probeAppAccess, queryDataset, setSystemCapabilities, updateDashboard. Proven with Assert<Equal<Extra, Twenty>> silent, and negative controls red at 19 names, 21 names, and the PR's nine (TS2344 ×3) — on both the src program and the shipped dist/index.d.ts. All 20 are now the factory's promise.
  • Judgment on the other 11: right to publish, but under-described. None is @internal/@deprecated; stripInternal is not set anywhere, so they were already in the shipped class type; and the class type is already the monorepo's working adapter contract — packages/react/src/context/AppShellContext.tsx:8 types AdapterCtx as ObjectStackAdapter | null, and app-shell's AdapterProvider, MetadataProvider, MetadataService all take ObjectStackAdapter. Two are escape-hatch shaped and worth the seat's eye: getCached(key): unknown | undefined (raw cache read) and getDiscovery(): Promise<unknown | null> whose own JSDoc says "accesses an internal property of the ObjectStackClient"; invalidateViewKeys is a cache-key coupling seam. Not blocking — reachable via new ObjectStackAdapter() and every app-shell path today — but the changeset presents "nine" as the delta. → Amendment 2.

Assignability attack — no consumer regressed except the one the PR names

  • Probed at PR head, all compile: const a: DataSource = …; const b: DataSource<User> = createObjectStackAdapter({…}) (T infers through the class); explicit <User>; DataSource[] and DataSource<User>[] mixed arrays; DataSource | null union; take(ds: DataSource<User>) with and without explicit T; function-type assignment (cfg) => DataSource (return covariance); generic→concrete (cfg) => DataSource<User>; satisfies DataSource; callback contravariance withDs((ds: DataSource) => …) against a (ds: FactoryReturn) => void parameter; Record<string, DataSource>; Promise<DataSource>.
  • The only break: a structural stand-in for ReturnType<typeof createObjectStackAdapter> — TS2740, "missing … client, connected, connectPromise, metadataCache, and 64 more". Repo has no typeof createObjectStackAdapter anywhere; the sole vi.mock('@object-ui/data-objectstack') (packages/app-shell/src/chrome/ConditionalAuthWrapper.previewRetired-6654.test.tsx:72) mocks only getSharedDiscovery, untyped. Correctly named by the PR. Not a rejection.
  • Secondary, not new: a class-with-privates return is nominal across duplicate installs; every ObjectStackAdapter-typed seam in @object-ui/react/app-shell already carries that property.

Q1 — class already exported: verified.src/index.ts:2148 export class ObjectStackAdapter; tsup.config.ts single entry src/index.ts; name present in built dist/index.d.ts export block; apps/console/src/dataSource.ts:14 re-exports it. Nothing exported by implication.

Q2 — narrow return not deliberate: verified, with one miss.

  • No Equal/expectTypeOf/satisfies/ReturnType pin on the factory anywhere in packages/apps; no ADR names it. 780a1b993 (introduced ): DataSource) carries no design statement. a5d817061 added autoReconnect/maxReconnectAttempts/reconnectDelay to the factory config (diff +358–360) without touching the return; 41b7ce3ce (feat(data-objectstack,app-shell,i18n): view config is explicitly org-wide — permission-gated write path + console wording #6125) added the [ADR-0066] See {@link ObjectStackAdapter.setSystemCapabilities} JSDoc. Plus the AdapterCtx fact above — the React layer already treats the class as the contract, the opposite of a swappability design.
  • Miss: the PR's "no swappability note anywhere" overlooked content/docs/utilities/data-objectstack.mdx, untouched by the diff: L53 "createObjectStackAdapter returns a DataSource — the same universal interface every ObjectUI renderer consumes"; L109 "Factory returning a DataSource"; the signature fragment at L137 ending }): DataSource<T>; under a marker claiming it was "Checked against the shipped dist/index.d.ts … with the same type"; and L163–200, a whole "Adapter-only (hold the class type to reach these)" section built on the distinction this PR erases. Provenance 92c0b1f40 (docs(data-objectstack): document the real headless surface, not a phantom React API #4129, a docs batch that "verified snippets against the built packages") — it describes the shipped d.ts, it does not prescribe, so Q2's answer stands. But the page is now false about the shipped types and nothing gates it (the fragment is declared, so the doc-snippet gate never compiles the return). → Amendment 1.

README

Unmentioned diff content: the factory JSDoc gains 15 lines including "⛔ Do not narrow this back" — consistent with the change; nothing else hidden. Test file is exactly as described.

② Semver

  • Changeset: '@object-ui/data-objectstack': minor. Policy (scripts/check-changeset-no-major.mjs header): major reserved for the objectstack-synchronised bump; objectui's own breaks ship as minor with the break spelled out. The body spells out the stand-in break. I would assign minor. Matches. Changeset Bump Policy / Fixed Group / Declaration checks all green.
  • Wording nit folded into Amendment 2: the bold "Not a breaking change for callers." heading sits directly over the paragraph describing the break.

③ Boundary flags

  • (a) Q1 yes/already; Q2 not deliberate — both confirmed, with the mdx caveat above.
  • (b) 9 vs 4. The card measured 4 (its snippet) and named 8, saying the other four were "exactly the members" on the same README list; setSystemCapabilities was introduced by the triage, not the card. All nine do fail together — reproduced (mutation B: 10 × TS2339 on the nine reads, invalidateCache twice). So "one tier, not two" is right in substance, and the card was partial rather than wrong. But the implementer's own count is partial too: README-documented adapter-only members are 9 (connect()), and the hidden delta is 20.
  • (c) Lint narrowing hides nothing.eslint.config.js has no parserOptions.project/projectService — rules are syntactic and per-file, so a return-type change cannot alter lint output elsewhere. What the package-only run skipped (//#lint:root, other packages) CI's full Lint ran and passed.

Pin verification (not vacuous)

  • In the program:tsc --listFiles for the package lists src/adapterFactoryReturn.types.test.ts and src/index.ts; DataSource resolves to packages/types/dist/data.d.ts (project-reference redirect), as the implementer said.
  • Mutation B (narrow back to DataSource<T>): 13 diagnostics, all in the pin — lines 75–84 TS2339 ×10, 89 and 113 TS2344, 157 TS7053; 0 elsewhere; controls at 120/135/137: 0 hits. Reproduces the reported split exactly. My stand-in probe starts compiling under B, confirming that is the only direction that moved.
  • Mutation C (return Omit<ObjectStackAdapter<T>,'find'>, i.e. no longer a DataSource): line 135 _StillADataSource TS2344 red, line 137 TS2741 red, line 113 red, line 120 green. _StillADataSource genuinely discriminates; _NotOnDataSource reads keyof DataSource and is independent of the return, so it fires only on option B, as designed.
  • vitest from the repo root on the pin file: 1 file, 4 tests pass.

CI by name at 922ca54ec (32 runs, all completed)

  • success (29): Lint · Type Check · Build & E2E · Test (shard 1/4) · Test (shard 2/4) · Test (shard 3/4) · Test (shard 4/4) · Doc Snippet Type Check · README Export Check · Changeset Bump Policy · Changeset Declaration · Changeset Fixed Group Check · Changeset Overwrite Report · Doc Fence Language Check · Doc Component Type Check · Internal Docs Link Check · Docs Route Eager Closure Check · Build Docs · Bundle Analysis · Control Byte Scan · Shell Escape Residue Scan · Pre-Install Import Graph Check · Governed Surface Queue Guard · Inert vi.mock Specifier Check · Skill Eval Token Check · Skill Example Check · Skill Guide Path Check · Live E2E (informational) · label
  • skipped (3, conditional jobs): Test (coverage) · Test (coverage shard) · dependabot
  • failed: none.

Verdict

PASS WITH REQUIRED AMENDMENTS

  1. Bring content/docs/utilities/data-objectstack.mdx in line with the shipped types — four sites: L53 prose ("returns a DataSource"), L109 ("Factory returning a DataSource"), the fragment's }): DataSource<T>; at L137 together with its marker's "checked against dist … same type" claim, and the L163–200 "hold the class type to reach these" section, which now teaches a distinction the factory no longer has. This PR already carries the docs limb for the same package's README; the docs-site page must not contradict dist/index.d.ts after merge.
  2. Fix the changeset's description of the delta (.changeset/7323-adapter-factory-return.md, mirrored in the PR body and test header if desired): state that the factory's declared return is now the full class — every public member of ObjectStackAdapter, 20 beyond DataSource — rather than presenting the nine measured reads as the delta; correct "Eight of those are exactly the members … the README documents" to nine (connect()); and reword the "Not a breaking change for callers." heading so the CHANGELOG line does not read as "no break" above the stand-in caveat.

Both amendments are text-only and independent of each other; neither changes the code, which is right.


Seat disposition

Not landing.needs:contract-review stays on both carriers until both amendments are in and this seat verifies them by content. They are being dispatched now. The code is confirmed correct and ⛔ must not change.

Amendment 1 is the find that justifies the tier, and it names a gate hole.content/docs/utilities/data-objectstack.mdx carries a signature fragment ending }): DataSource<T>; under a marker asserting it was "checked against the shipped dist/index.d.ts … with the same type" — and after this merge that assertion is false. It survives because the fragment is declared, so check:doc-snippet-types never compiles the return. A page that claims to be verified against the built types, and is structurally exempt from the check that would verify it, is worse than an unmarked page: this seat, the implementer and the card all read "no swappability note anywhere" and none of us found it. I am recording that as a finding in its own right when the amendment lands.

⚠️ Also worth the record: the reviewer's count of the true delta — 20 members, not 9 — was reached with Assert<Equal<>> plus negative controls at 19, 21 and 9 names, on both the src program and the shipped d.ts. My dispatch asked it to enumerate what else became reachable; it did, and the answer is that the changeset understates the published surface by more than a factor of two. ⛔ That is a description defect, not a design one — the reviewer establishes the 11 extra members were already in the shipped class type, already reachable via new ObjectStackAdapter(), and already the monorepo's working adapter contract (AdapterCtx is typed as the class). So the fix is the changeset's wording, not the return type.


Generated by Claude Code

…idened factory return (objectui#7323)
Two text-only amendments from the in-seat contract review on the pull request.
No code changes: the return-type widening, the type-level pin and the README
rewiring are untouched.
Amendment 1 — content/docs/utilities/data-objectstack.mdx contradicted the
shipped types. The four sites the review named: the Quick Start prose saying the
factory "returns a `DataSource`"; the API Reference blurb "Factory returning a
`DataSource`"; the signature fragment ending `}): DataSource<T>;` together with
its marker's claim to have been checked against the shipped `dist/index.d.ts`;
and the `ObjectStackAdapter` section built on "hold the class type to reach
these", a distinction the factory no longer has. That section is rewritten rather
than deleted, with a "What changed" paragraph so a reader who followed the old
advice can see why constructing the class by hand is no longer needed.
Two further sites in the same file taught the same erased distinction and are
corrected with them: the Mutations paragraph ("hold the adapter at its class type
... before calling them") and Troubleshooting -> Authentication Errors
("Connection introspection lives on the adapter class ... so hold it at the class
type"); both examples now continue from the factory call, matching how the README
limb of this pull request was rewired. `onMutation` was also misfiled under
"Adapter-only" while being an optional member of `DataSource`; it moves in with
`bulk` and `batchTransaction`.
The fragment's marker no longer asserts an unqualified "checked against the
shipped d.ts". Because the block is declared, `check:doc-snippet-types` never
compiles it, so the marker now states that the agreement is hand-checked rather
than gate-enforced -- which is why the stale return type survived there. The gate
and the `declared` marker keyword are unchanged.
Amendment 2 — the changeset understated the published surface by more than 2x.
It presented the card's nine measured TS2339 reads as the delta; the factory's
declared return is now the class itself, so the delta is every public member of
`ObjectStackAdapter` -- 20 beyond `DataSource`, independently re-derived here as
`Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>>` against
the built `dist/index.d.ts` (20 names, matching the review). The README-documents
sentence is corrected: eight of the nine measured reads are on that list, but the
list itself is nine adapter-only members -- `connect()` was documented all along
and simply was not measured. The "Not a breaking change for callers." heading is
reworded to lead with the one shape that does break, so the CHANGELOG line no
longer reads as "no break" above the caveat describing it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
…ui#7323)
Follow-up to the previous commit, on my own prose in the same amendment:
- Drop the "Until v17.7" version claim. The package is at 17.6.0 and the
changeset is a minor, but the released number is decided by the fixed group at
release time, so the page should not assert it.
- Remove a duplicated bullet list. `bulk` and `batchTransaction` were listed once
under the `DataSource` members and again under the optional-members paragraph.
`onMutation` now joins them in the single list, spelled `onMutation?` like its
neighbours, with the correction noted in the paragraph instead.
- Say "six bullets cover nine members" rather than "those nine", since the list
groups related members onto shared lines, and make the parenthetical naming the
other eleven members read as illustrative rather than exhaustive -- it named
four seams out of eleven.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Both amendments verified — needs:contract-review cleared, ready + auto-merge

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance: 「PASS ⇒ 同席剥标、ready、auto-merge」 (维护者 2026-08-31 裁定). The tier review returned PASS WITH REQUIRED AMENDMENTS (5526083981); both are now in.

⚠️The amendment agent was killed mid-run by a transient HTTP 529 and its report is lost. It had already pushed. So everything below is this seat's own verification from the git objects, with no report to lean on — which is the right posture anyway, and it is the reason this comment carries readings rather than a summary.

Verified by content at 5140938cd

Two commits since the reviewed head 922ca54ec:

  • 27d18e179 — docs page + changeset
  • 5140938cd — a follow-up tightening of the rewritten section

Two files across the whole range, and ⭐ not one line of code:

.changeset/7323-adapter-factory-return.md | 53 +++++++++----
content/docs/utilities/data-objectstack.mdx | 99 ++++++++++++++++-------

⇒ The return-type widening, the type-level pin and the README rewiring are byte-identical to what was reviewed. The standing PASS therefore holds and no re-review is owed — the review's own words were that both amendments are "text-only and independent of each other; neither changes the code, which is right."

Amendment 1 — the docs page. Every flagged claim is gone: no returns a DataSource, no Factory returning a DataSource, no }): DataSource<T>; fragment. The "hold the class type to reach these" section was rewritten, not deleted — it now opens "What changed. This section used to tell you to hold the class type to…", which is what a reader who followed the old advice needs.

And the marker is gone entirely. The Checked against the shipped dist/index.d.ts … with the same type assertion was removed, not reworded around it. That matters more than the four corrected sites: that false claim of verification is why the page stayed wrong through three separate readers looking for exactly it.

Amendment 2 — the changeset. Now states the delta is the whole class — "20 members, not nine" — with the tsc-computed enumeration, corrects eight → nine (connect() is adapter-only), and records that the eleven past the documented nine were already in the shipped class type.

Checks — 32/32, read by name by this seat

29 success, 3 skipped by design (dependabot, Test (coverage), Test (coverage shard …)) on 5140938cd. The ones that actually cover this amendment: Doc Snippet Type Check 13:25:45 · Build Docs 13:23:57 · Internal Docs Link Check 13:21:53 · Doc Fence Language Check · README Export Check 13:25:31 · Changeset Declaration / Bump Policy / Fixed Group / Overwrite Report. Long poles: all four Test (shard N/4) 13:34–13:36, Type Check 13:27:31, Lint 13:27:12.

Carriers

needs:contract-review removed from PR #7503 (remaining documentation, data-adapter, tests) and from card #7323 (remaining bug, pm:dispatched, priority:p2, domain:ui). Both label sets were read immediately before writing rather than assumed — the label job had added labels since my last reading of a sibling PR, and writing a stale set would have clobbered them.

⚠️ Both readings are manual. scripts/pm/check-clause2-carriers.mjs exits 2 in this session (it needs its own GitHub token; this seat reaches GitHub only through MCP) and its own output says that is ⛔ not a clearance. A hand label-check covers strictly less than the tool — this shift produced four distinct carrier defects and the fallback is structurally blind to two of their shapes, now filed as objectstack#14965.

The finding this PR produced, filed separately

#7505 — a declared doc fragment is never compiled by check:doc-snippet-types, so a page can carry a "checked against the shipped dist" marker that no gate ever re-checks. The one known instance is fixed here; the class is not, and enumerating the rest is the first task on that card. ⛔ Filed unassigned and unlabelled for triage.


Generated by Claude Code

Merged via the queue into main with commit 6a99bb2Sep 3, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7323-adapter-return-type branch September 3, 2026 13:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data-adapterdocumentationImprovements or additions to documentationtests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns - #7503

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type
Sep 3, 2026
Merged

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns#7503
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#7323

createObjectStackAdapter returned new ObjectStackAdapter(config) while declaring the shared DataSource interface as its return. A wider value is assignable to a narrower annotation, so nothing ever failed to compile — the loss was entirely on the reading side: every adapter-only member was erased from the type the factory hands back, while staying on the object it hands back.

Measured before the change, compiling against the shipped packages/data-objectstack/dist/index.d.ts with the doc-snippet gate's own compiler options (strict, bundler resolution): 9 x TS2339, not the 4 the card lists — getClient, getCacheStats, invalidateCache, clearCache, getConnectionState, isConnected, onConnectionStateChange, onBatchProgress, setSystemCapabilities. Eight of those are exactly the members the README's API Reference — Methods list documents; the ninth is the one the factory's own JSDoc links to ([ADR-0066] See ObjectStackAdapter.setSystemCapabilities). The same probe after this change: 0 diagnostics.

The two questions that decided the shape

The triage set these as a stop-condition and both are answered from the code, on origin/maine17605309.

Q1 — is ObjectStackAdapter exported from the package's public entry? Yes, already.packages/data-objectstack/src/index.ts:2148 reads export class ObjectStackAdapter, and tsup.config.ts has exactly one entry, src/index.ts. The class is in the shipped dist/index.d.ts export list (verified in the built artifact, not inferred). Two pin tests already assert the exported spelling in source — cloud-surface-retired-4152.pin.test.ts:114 and v3-deep-integration-retired-4241.pin.test.ts:135 — and apps/console/src/dataSource.ts:14 re-exports it by name. So this PR exports nothing by implication: the export list in dist/index.d.ts is unchanged, name for name, before and after.

Q2 — is the narrow return deliberate? No, and there are two pieces of evidence against it.

  1. Commit a5d817061 ("Add connection state monitoring, auto-reconnect, and batch progress") added autoReconnect / maxReconnectAttempts / reconnectDelay to the factory's own config bag while leaving the members that observe those features off the factory's declared return. The same change configured a feature it made unobservable through its own entry point. That is an oversight shape, not an encapsulation decision.
  2. The factory's JSDoc, added by 41b7ce3ce, points the reader at ObjectStackAdapter.setSystemCapabilities — a member its declared return hides.

Nothing anywhere pins the return: no comment, no ADR, no test, no Equal assertion, and no swappability note. Searched the export list, package.jsonfiles / exports, ADR-0066, the sibling adapters, and every test that names the factory.

Had either answer come back the other way this would have been a docs-only PR. They did not, so it is option A — and swappability, the property the narrow return was suspected of protecting, is not lost: a wider return is assignable to the narrower annotation, so const ds: DataSource = createObjectStackAdapter(...) still compiles and still gives the narrow surface. There is a control test that says exactly that.

Why A and not B or C

B (add the missing members to DataSource) would make every other DataSource implementation declare caching, connection state and batch progress it does not have — those are this adapter's concerns. C (document a cast) teaches a cast around a declaration that is merely narrower than the value, which is the opposite of declared = enforced. A is one line and makes declared match shipped for every documented member at once.

The pin

packages/data-objectstack/src/adapterFactoryReturn.types.test.ts — the card's TS2339 reproduction, inverted. It reads all nine members through the factory's return, asserts the return's identity with Equal (not mere assignability — two adapter-shaped declarations are mutually assignable, so only identity can tell "returns THE adapter" from "returns something adapter-shaped"), and carries two controls:

  • _NotOnDataSource — the adapter-only members stay ABSENT from the shared DataSource. This is what makes the reads a statement about the factory's return rather than about every data source, and it fires on option B.
  • _StillADataSource — the widened return is still assignable to DataSource.

Both controls are independent of the return annotation, so reverting the source change turns the reads red and leaves the controls green. That split is measured below, not asserted.

Which tree the pin exercises. Two, deliberately. The test is compiled by pnpm --filter @object-ui/data-objectstack type-check, whose program includes the whole src/** (tests included) and resolves ./index from src — proven with --listFiles: 1 hit for the new file, 55 test files, src/index.ts present. @object-ui/types in that same program resolves from packages/types/dist, so the type used for the controls is the shipped one. The README blocks are the dist half: check:doc-snippets compiles them against the built dist/*.d.ts (its own resolution control prints '@object-ui/types' was successfully resolved to packages/types/dist/index.d.ts).

Documentation

The README note naming this card (added by #5174 batch 8) is removed. The four sections built on the adapter-only members — Metadata Caching, Connection State Monitoring, Batch Operation Progress, Troubleshooting → Cache Issues — now continue from Basic Setup's createObjectStackAdapter(...) call instead of hand-declaring the class, so the page teaches one shape and the doc-snippet gate pins the fix against dist. The other README blocks still declare the class where they only need DataSource-level members; that is truthful (the class is public and documented) and rewriting them buys nothing, so they are left alone.

setSystemCapabilities is still absent from the README's Methods list. It was absent before this PR too, it is a separate doc gap, and adding it here would be scope this card did not ask for.

Verification — every number below was observed, at head 922ca54ec

CheckCommandResult
Package type-checkpnpm --filter @object-ui/data-objectstack type-checkexit 0, tsc --noEmit echoed
Package testspnpm exec vitest run --maxWorkers=2 packages/data-objectstack/ (repo root — a package-dir run is refused by objectui#3378's guard)55 files, 737 tests, all passed
check:doc-snippetsnode scripts/check-doc-snippet-types.mjsexit 0 — "Semantic phase: 455 of 455 block(s) judged, 0 failed." / "Every covered documentation snippet compiles against the built types."
check:readme-exportspnpm check:readme-exportsexit 0 — "OK (43 tracked README(s) ...; 414 self-imports judged (414 real, 0 wrong-path, 0 fabricated); 3305 export symbol(s) read from 37 of 40 tracked package(s) (0 unbuilt ...))"
changeset presencenode scripts/check-changeset-presence.mjsexit 0 — "1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)"
check:doc-fencesnode scripts/check-doc-fence-languages.mjsexit 0 — "every TypeScript block in 227 document(s) is fenced ts/tsx/typescript ..."
control bytesnode scripts/check-control-bytes.mjsexit 0 — "OK (scanned 6180 tracked text file(s); skipped 85 binary)"
Package lintpnpm --filter @object-ui/data-objectstack lintexit 0 — 423 problems, 0 errors, 423 warnings, all pre-existing no-explicit-any; the new file contributes 0 findings (eslint --format json on the two changed source files: new test 0/0)

Every gate result above is quoted from the gate's own verdict line; exit codes were captured by redirecting first and reading $? before any pipe.

Dependency closure built first (pnpm --filter '@object-ui/data-objectstack...' build, then the doc-snippet gate's own derived --build-filter: 26 packages, 34 turbo tasks, then --filter='./packages/*' for check:readme-exports, which had reported 2 unbuilt packages — a "could not run", not a defect).

Lint scope, declared: the table's lint row is the changed package only, not the repo. pnpm lint here is turbo run lint (each package's own eslint .), and CI runs it in full regardless. One reading that needs stating: a separate probe with eslint --no-inline-config reports 4 errors in src/index.ts at lines 1369–1389. Those are the documented eslint-disable-next-line no-console sites for the spec Logger binding (objectui#4029), they are ~4300 lines from either of my edits, and they are invisible to the lint CI actually runs because it honours inline config. Not introduced here, and not silently omitted either.

Reverse verification — red/green split, both directions proven on disk

Method: commit first, then mutate packages/data-objectstack/src/index.ts back to the narrow return under trap ... EXIT INT TERM with an absolute restore path, prove the mutation landed by counting the injected and removed text (not by the editor's exit code) and by comparing blob hashes, then restore and prove the restore by state.

Mutation landed: injected-text count 0 → 1, removed-text count 1 → 0; mutated blob 447fffc8 differs from HEAD blob bad31df4.

Leg 1 — the src pin, no rebuild needed (the pin imports ./index from src; the mutation cannot reach packages/types/dist, which is where its DataSource comes from):

  • 13 diagnostics total, every one of them inside the pin file, 0 anywhere else in the package.
  • 10 x TS2339 at lines 75–84: the nine member reads (invalidateCache twice, with and without an argument).
  • TS2344 at 89 and 113: the _HasHiddenMembers and _IsTheAdapter assertions.
  • TS7053 at 157: the runtime member sweep can no longer index the narrowed type.
  • The controls at lines 120 (_NotOnDataSource) and 135 (_StillADataSource) produced ZERO diagnostics — the split the pin was designed for.
  • vitest on the same mutated tree still passes, which is the point: esbuild erases types, so the colour comes from tsc and from nowhere else.

Leg 2 — the dist pin, rebuilt in both directions. Rebuilt from the mutated source, then a dist preflight: the widened marker count in dist/index.d.ts was 0 (the mutation reached dist). The standalone dist probe then reproduced the card's failure exactly — 9 x TS2339 — and check:doc-snippets went to exit 1, "455 of 455 block(s) judged, 4 failed": precisely the four README sections this PR rewired, failing on getCacheStats, invalidateCache x2, clearCache, onConnectionStateChange, getConnectionState, isConnected, onBatchProgress, clearCache, invalidateCache.

Restore, proven by state, both halves. Source: restored blob bad31df4 equals HEAD's blob and git diff HEAD is 0 bytes. dist: rebuilt again, widened marker back to 1 and narrow marker 0, dist probe back to 0 diagnostics, check:doc-snippets back to exit 0, 0 failed. The restore leg is rebuilt on purpose — a mutated marker left in dist would keep acting on every later run in this tree.

Clause ②

Clause-②: yes, declared in the claim comment on #7323 in the fixed machine spelling, and needs:contract-review is on both carriers (this PR and the card). It widens the declared public surface reachable through a published entry point. Q1's answer removes the triage's specific worry — no class is exported by implication — but the declaration limb is judged from content, not from paths or from diff size.

The one compatibility note a reviewer should weigh. The widened return is a class with private members, so it is no longer satisfied by a hand-written structural stand-in: an object literal assigned to the factory's ReturnType will now be rejected where it used to be accepted. Nothing in this repo does that (searched every test and app that names the factory — apps/console/src/dataSource.ts only re-exports it), and the fix for a downstream consumer is to annotate such a fake as DataSource, which is what it was standing in for. It is the only direction in which this change is not purely additive, and it belongs in the contract review rather than in a footnote.

Draft on purpose, not flipped ready, auto-merge not enabled — the seat lands it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…it returns
The factory returned `new ObjectStackAdapter(config)` while declaring
`DataSource<T>`. A wider value is assignable to a narrower annotation, so
nothing ever failed to compile — the loss was entirely on the reading side.
Measured against the shipped `dist/index.d.ts` with the doc-snippet gate's own
compiler options, nine reads through `ReturnType<typeof
createObjectStackAdapter>` failed with TS2339: getClient, getCacheStats,
invalidateCache, clearCache, getConnectionState, isConnected,
onConnectionStateChange, onBatchProgress and setSystemCapabilities. Eight are
exactly the members the package README's API Reference documents; the ninth is
the one the factory's own JSDoc links to. Same probe after this change: 0
diagnostics.
Option A of the card. B (widening `DataSource` itself) would make every other
data source declare caching, connection state and batch progress it does not
have; C (documenting a cast) teaches a cast around a declaration that is merely
narrower than the value. `ObjectStackAdapter` was already exported from the
package's only entry, so nothing is exported by implication, and no comment,
ADR or test pinned the narrow return — the commit that added autoReconnect /
maxReconnectAttempts / reconnectDelay to the factory's config bag left the
members observing those features off its declared return in the same change.
Callers are unaffected: `const ds: DataSource = createObjectStackAdapter(…)`
still compiles and still gives the narrow surface.
The README note saying the page could not yet teach the factory's shape is
removed, and the four sections built on the adapter-only members now continue
from Basic Setup's factory call. `adapterFactoryReturn.types.test.ts` pins the
card's TS2339 reproduction inverted, with a control for option B and a control
for swappability.
Part of #7323
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation data-adapter tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Clause-② in-seat contract review — PASS WITH REQUIRED AMENDMENTS. needs:contract-review stays on.

Commissioned by the domain:ui execution seat (PM session session_01EMrWaQw3XS5DxTHxp4yRyC) per the in-seat review route (维护者 2026-08-31 裁定). This seat runs opus and is not at tier, so it commissioned an isolated reviewer rather than self-reviewing. The reviewer was given only the card, its triage and the PR — ⛔ not the dispatch order and not this seat's conclusions.

Tier verification (维护者 2026-08-27 裁定): every harness-stamped model field in the transcript was read before adopting — 76 stamps, all claude-fable-5-1, no other value, and zero fallback/overload markers. CONTRACT_REVIEW_TIER is claude-fable-5 (objectstack/scripts/pm/dispatch-gates.mjs:8091); claude-fable-5-1 is at or above that floor. ⇒ Verified, so the verdict is adopted verbatim — ⛔ nothing below is rewritten, abridged or polished.


Contract review — objectui PR #7503 (head 922ca54ec, base e17605309)

Everything below was read from the diff, the card, triage 5524408927, and my own runs in a detached worktree at /home/user/objectui-review-7503 (dependency closure built; git diff HEAD 0 bytes at the end). No edits, commits, pushes, labels or GitHub posts.

① Derived judgments

Accept set

  • Config parameter type: byte-identical before/after. No change to what the factory accepts. Right.

Published surface

  • Declared return DataSource<T>ObjectStackAdapter<T>. Ships: built dist/index.d.ts ends the factory }): ObjectStackAdapter<T>;. Export list unchanged (the diff adds no export; the test file is outside tsup's single entry). Right.
  • The true delta is 20 members, not 9. tsc-computed Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>> is exactly: clearCache, connect, getCacheStats, getCached, getClient, getConnectionState, getDiscovery, getItems, invalidateCache, invalidateViewKeys, isConnected, listImportMappings, onBatchProgress, onConnectionStateChange, onSaveAdvisory, onWriteWarning, probeAppAccess, queryDataset, setSystemCapabilities, updateDashboard. Proven with Assert<Equal<Extra, Twenty>> silent, and negative controls red at 19 names, 21 names, and the PR's nine (TS2344 ×3) — on both the src program and the shipped dist/index.d.ts. All 20 are now the factory's promise.
  • Judgment on the other 11: right to publish, but under-described. None is @internal/@deprecated; stripInternal is not set anywhere, so they were already in the shipped class type; and the class type is already the monorepo's working adapter contract — packages/react/src/context/AppShellContext.tsx:8 types AdapterCtx as ObjectStackAdapter | null, and app-shell's AdapterProvider, MetadataProvider, MetadataService all take ObjectStackAdapter. Two are escape-hatch shaped and worth the seat's eye: getCached(key): unknown | undefined (raw cache read) and getDiscovery(): Promise<unknown | null> whose own JSDoc says "accesses an internal property of the ObjectStackClient"; invalidateViewKeys is a cache-key coupling seam. Not blocking — reachable via new ObjectStackAdapter() and every app-shell path today — but the changeset presents "nine" as the delta. → Amendment 2.

Assignability attack — no consumer regressed except the one the PR names

  • Probed at PR head, all compile: const a: DataSource = …; const b: DataSource<User> = createObjectStackAdapter({…}) (T infers through the class); explicit <User>; DataSource[] and DataSource<User>[] mixed arrays; DataSource | null union; take(ds: DataSource<User>) with and without explicit T; function-type assignment (cfg) => DataSource (return covariance); generic→concrete (cfg) => DataSource<User>; satisfies DataSource; callback contravariance withDs((ds: DataSource) => …) against a (ds: FactoryReturn) => void parameter; Record<string, DataSource>; Promise<DataSource>.
  • The only break: a structural stand-in for ReturnType<typeof createObjectStackAdapter> — TS2740, "missing … client, connected, connectPromise, metadataCache, and 64 more". Repo has no typeof createObjectStackAdapter anywhere; the sole vi.mock('@object-ui/data-objectstack') (packages/app-shell/src/chrome/ConditionalAuthWrapper.previewRetired-6654.test.tsx:72) mocks only getSharedDiscovery, untyped. Correctly named by the PR. Not a rejection.
  • Secondary, not new: a class-with-privates return is nominal across duplicate installs; every ObjectStackAdapter-typed seam in @object-ui/react/app-shell already carries that property.

Q1 — class already exported: verified.src/index.ts:2148 export class ObjectStackAdapter; tsup.config.ts single entry src/index.ts; name present in built dist/index.d.ts export block; apps/console/src/dataSource.ts:14 re-exports it. Nothing exported by implication.

Q2 — narrow return not deliberate: verified, with one miss.

  • No Equal/expectTypeOf/satisfies/ReturnType pin on the factory anywhere in packages/apps; no ADR names it. 780a1b993 (introduced ): DataSource) carries no design statement. a5d817061 added autoReconnect/maxReconnectAttempts/reconnectDelay to the factory config (diff +358–360) without touching the return; 41b7ce3ce (feat(data-objectstack,app-shell,i18n): view config is explicitly org-wide — permission-gated write path + console wording #6125) added the [ADR-0066] See {@link ObjectStackAdapter.setSystemCapabilities} JSDoc. Plus the AdapterCtx fact above — the React layer already treats the class as the contract, the opposite of a swappability design.
  • Miss: the PR's "no swappability note anywhere" overlooked content/docs/utilities/data-objectstack.mdx, untouched by the diff: L53 "createObjectStackAdapter returns a DataSource — the same universal interface every ObjectUI renderer consumes"; L109 "Factory returning a DataSource"; the signature fragment at L137 ending }): DataSource<T>; under a marker claiming it was "Checked against the shipped dist/index.d.ts … with the same type"; and L163–200, a whole "Adapter-only (hold the class type to reach these)" section built on the distinction this PR erases. Provenance 92c0b1f40 (docs(data-objectstack): document the real headless surface, not a phantom React API #4129, a docs batch that "verified snippets against the built packages") — it describes the shipped d.ts, it does not prescribe, so Q2's answer stands. But the page is now false about the shipped types and nothing gates it (the fragment is declared, so the doc-snippet gate never compiles the return). → Amendment 1.

README

Unmentioned diff content: the factory JSDoc gains 15 lines including "⛔ Do not narrow this back" — consistent with the change; nothing else hidden. Test file is exactly as described.

② Semver

  • Changeset: '@object-ui/data-objectstack': minor. Policy (scripts/check-changeset-no-major.mjs header): major reserved for the objectstack-synchronised bump; objectui's own breaks ship as minor with the break spelled out. The body spells out the stand-in break. I would assign minor. Matches. Changeset Bump Policy / Fixed Group / Declaration checks all green.
  • Wording nit folded into Amendment 2: the bold "Not a breaking change for callers." heading sits directly over the paragraph describing the break.

③ Boundary flags

  • (a) Q1 yes/already; Q2 not deliberate — both confirmed, with the mdx caveat above.
  • (b) 9 vs 4. The card measured 4 (its snippet) and named 8, saying the other four were "exactly the members" on the same README list; setSystemCapabilities was introduced by the triage, not the card. All nine do fail together — reproduced (mutation B: 10 × TS2339 on the nine reads, invalidateCache twice). So "one tier, not two" is right in substance, and the card was partial rather than wrong. But the implementer's own count is partial too: README-documented adapter-only members are 9 (connect()), and the hidden delta is 20.
  • (c) Lint narrowing hides nothing.eslint.config.js has no parserOptions.project/projectService — rules are syntactic and per-file, so a return-type change cannot alter lint output elsewhere. What the package-only run skipped (//#lint:root, other packages) CI's full Lint ran and passed.

Pin verification (not vacuous)

  • In the program:tsc --listFiles for the package lists src/adapterFactoryReturn.types.test.ts and src/index.ts; DataSource resolves to packages/types/dist/data.d.ts (project-reference redirect), as the implementer said.
  • Mutation B (narrow back to DataSource<T>): 13 diagnostics, all in the pin — lines 75–84 TS2339 ×10, 89 and 113 TS2344, 157 TS7053; 0 elsewhere; controls at 120/135/137: 0 hits. Reproduces the reported split exactly. My stand-in probe starts compiling under B, confirming that is the only direction that moved.
  • Mutation C (return Omit<ObjectStackAdapter<T>,'find'>, i.e. no longer a DataSource): line 135 _StillADataSource TS2344 red, line 137 TS2741 red, line 113 red, line 120 green. _StillADataSource genuinely discriminates; _NotOnDataSource reads keyof DataSource and is independent of the return, so it fires only on option B, as designed.
  • vitest from the repo root on the pin file: 1 file, 4 tests pass.

CI by name at 922ca54ec (32 runs, all completed)

  • success (29): Lint · Type Check · Build & E2E · Test (shard 1/4) · Test (shard 2/4) · Test (shard 3/4) · Test (shard 4/4) · Doc Snippet Type Check · README Export Check · Changeset Bump Policy · Changeset Declaration · Changeset Fixed Group Check · Changeset Overwrite Report · Doc Fence Language Check · Doc Component Type Check · Internal Docs Link Check · Docs Route Eager Closure Check · Build Docs · Bundle Analysis · Control Byte Scan · Shell Escape Residue Scan · Pre-Install Import Graph Check · Governed Surface Queue Guard · Inert vi.mock Specifier Check · Skill Eval Token Check · Skill Example Check · Skill Guide Path Check · Live E2E (informational) · label
  • skipped (3, conditional jobs): Test (coverage) · Test (coverage shard) · dependabot
  • failed: none.

Verdict

PASS WITH REQUIRED AMENDMENTS

  1. Bring content/docs/utilities/data-objectstack.mdx in line with the shipped types — four sites: L53 prose ("returns a DataSource"), L109 ("Factory returning a DataSource"), the fragment's }): DataSource<T>; at L137 together with its marker's "checked against dist … same type" claim, and the L163–200 "hold the class type to reach these" section, which now teaches a distinction the factory no longer has. This PR already carries the docs limb for the same package's README; the docs-site page must not contradict dist/index.d.ts after merge.
  2. Fix the changeset's description of the delta (.changeset/7323-adapter-factory-return.md, mirrored in the PR body and test header if desired): state that the factory's declared return is now the full class — every public member of ObjectStackAdapter, 20 beyond DataSource — rather than presenting the nine measured reads as the delta; correct "Eight of those are exactly the members … the README documents" to nine (connect()); and reword the "Not a breaking change for callers." heading so the CHANGELOG line does not read as "no break" above the stand-in caveat.

Both amendments are text-only and independent of each other; neither changes the code, which is right.


Seat disposition

Not landing.needs:contract-review stays on both carriers until both amendments are in and this seat verifies them by content. They are being dispatched now. The code is confirmed correct and ⛔ must not change.

Amendment 1 is the find that justifies the tier, and it names a gate hole.content/docs/utilities/data-objectstack.mdx carries a signature fragment ending }): DataSource<T>; under a marker asserting it was "checked against the shipped dist/index.d.ts … with the same type" — and after this merge that assertion is false. It survives because the fragment is declared, so check:doc-snippet-types never compiles the return. A page that claims to be verified against the built types, and is structurally exempt from the check that would verify it, is worse than an unmarked page: this seat, the implementer and the card all read "no swappability note anywhere" and none of us found it. I am recording that as a finding in its own right when the amendment lands.

⚠️ Also worth the record: the reviewer's count of the true delta — 20 members, not 9 — was reached with Assert<Equal<>> plus negative controls at 19, 21 and 9 names, on both the src program and the shipped d.ts. My dispatch asked it to enumerate what else became reachable; it did, and the answer is that the changeset understates the published surface by more than a factor of two. ⛔ That is a description defect, not a design one — the reviewer establishes the 11 extra members were already in the shipped class type, already reachable via new ObjectStackAdapter(), and already the monorepo's working adapter contract (AdapterCtx is typed as the class). So the fix is the changeset's wording, not the return type.


Generated by Claude Code

…idened factory return (objectui#7323)
Two text-only amendments from the in-seat contract review on the pull request.
No code changes: the return-type widening, the type-level pin and the README
rewiring are untouched.
Amendment 1 — content/docs/utilities/data-objectstack.mdx contradicted the
shipped types. The four sites the review named: the Quick Start prose saying the
factory "returns a `DataSource`"; the API Reference blurb "Factory returning a
`DataSource`"; the signature fragment ending `}): DataSource<T>;` together with
its marker's claim to have been checked against the shipped `dist/index.d.ts`;
and the `ObjectStackAdapter` section built on "hold the class type to reach
these", a distinction the factory no longer has. That section is rewritten rather
than deleted, with a "What changed" paragraph so a reader who followed the old
advice can see why constructing the class by hand is no longer needed.
Two further sites in the same file taught the same erased distinction and are
corrected with them: the Mutations paragraph ("hold the adapter at its class type
... before calling them") and Troubleshooting -> Authentication Errors
("Connection introspection lives on the adapter class ... so hold it at the class
type"); both examples now continue from the factory call, matching how the README
limb of this pull request was rewired. `onMutation` was also misfiled under
"Adapter-only" while being an optional member of `DataSource`; it moves in with
`bulk` and `batchTransaction`.
The fragment's marker no longer asserts an unqualified "checked against the
shipped d.ts". Because the block is declared, `check:doc-snippet-types` never
compiles it, so the marker now states that the agreement is hand-checked rather
than gate-enforced -- which is why the stale return type survived there. The gate
and the `declared` marker keyword are unchanged.
Amendment 2 — the changeset understated the published surface by more than 2x.
It presented the card's nine measured TS2339 reads as the delta; the factory's
declared return is now the class itself, so the delta is every public member of
`ObjectStackAdapter` -- 20 beyond `DataSource`, independently re-derived here as
`Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>>` against
the built `dist/index.d.ts` (20 names, matching the review). The README-documents
sentence is corrected: eight of the nine measured reads are on that list, but the
list itself is nine adapter-only members -- `connect()` was documented all along
and simply was not measured. The "Not a breaking change for callers." heading is
reworded to lead with the one shape that does break, so the CHANGELOG line no
longer reads as "no break" above the caveat describing it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
…ui#7323)
Follow-up to the previous commit, on my own prose in the same amendment:
- Drop the "Until v17.7" version claim. The package is at 17.6.0 and the
changeset is a minor, but the released number is decided by the fixed group at
release time, so the page should not assert it.
- Remove a duplicated bullet list. `bulk` and `batchTransaction` were listed once
under the `DataSource` members and again under the optional-members paragraph.
`onMutation` now joins them in the single list, spelled `onMutation?` like its
neighbours, with the correction noted in the paragraph instead.
- Say "six bullets cover nine members" rather than "those nine", since the list
groups related members onto shared lines, and make the parenthetical naming the
other eleven members read as illustrative rather than exhaustive -- it named
four seams out of eleven.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Both amendments verified — needs:contract-review cleared, ready + auto-merge

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance: 「PASS ⇒ 同席剥标、ready、auto-merge」 (维护者 2026-08-31 裁定). The tier review returned PASS WITH REQUIRED AMENDMENTS (5526083981); both are now in.

⚠️The amendment agent was killed mid-run by a transient HTTP 529 and its report is lost. It had already pushed. So everything below is this seat's own verification from the git objects, with no report to lean on — which is the right posture anyway, and it is the reason this comment carries readings rather than a summary.

Verified by content at 5140938cd

Two commits since the reviewed head 922ca54ec:

  • 27d18e179 — docs page + changeset
  • 5140938cd — a follow-up tightening of the rewritten section

Two files across the whole range, and ⭐ not one line of code:

.changeset/7323-adapter-factory-return.md | 53 +++++++++----
content/docs/utilities/data-objectstack.mdx | 99 ++++++++++++++++-------

⇒ The return-type widening, the type-level pin and the README rewiring are byte-identical to what was reviewed. The standing PASS therefore holds and no re-review is owed — the review's own words were that both amendments are "text-only and independent of each other; neither changes the code, which is right."

Amendment 1 — the docs page. Every flagged claim is gone: no returns a DataSource, no Factory returning a DataSource, no }): DataSource<T>; fragment. The "hold the class type to reach these" section was rewritten, not deleted — it now opens "What changed. This section used to tell you to hold the class type to…", which is what a reader who followed the old advice needs.

And the marker is gone entirely. The Checked against the shipped dist/index.d.ts … with the same type assertion was removed, not reworded around it. That matters more than the four corrected sites: that false claim of verification is why the page stayed wrong through three separate readers looking for exactly it.

Amendment 2 — the changeset. Now states the delta is the whole class — "20 members, not nine" — with the tsc-computed enumeration, corrects eight → nine (connect() is adapter-only), and records that the eleven past the documented nine were already in the shipped class type.

Checks — 32/32, read by name by this seat

29 success, 3 skipped by design (dependabot, Test (coverage), Test (coverage shard …)) on 5140938cd. The ones that actually cover this amendment: Doc Snippet Type Check 13:25:45 · Build Docs 13:23:57 · Internal Docs Link Check 13:21:53 · Doc Fence Language Check · README Export Check 13:25:31 · Changeset Declaration / Bump Policy / Fixed Group / Overwrite Report. Long poles: all four Test (shard N/4) 13:34–13:36, Type Check 13:27:31, Lint 13:27:12.

Carriers

needs:contract-review removed from PR #7503 (remaining documentation, data-adapter, tests) and from card #7323 (remaining bug, pm:dispatched, priority:p2, domain:ui). Both label sets were read immediately before writing rather than assumed — the label job had added labels since my last reading of a sibling PR, and writing a stale set would have clobbered them.

⚠️ Both readings are manual. scripts/pm/check-clause2-carriers.mjs exits 2 in this session (it needs its own GitHub token; this seat reaches GitHub only through MCP) and its own output says that is ⛔ not a clearance. A hand label-check covers strictly less than the tool — this shift produced four distinct carrier defects and the fallback is structurally blind to two of their shapes, now filed as objectstack#14965.

The finding this PR produced, filed separately

#7505 — a declared doc fragment is never compiled by check:doc-snippet-types, so a page can carry a "checked against the shipped dist" marker that no gate ever re-checks. The one known instance is fixed here; the class is not, and enumerating the rest is the first task on that card. ⛔ Filed unassigned and unlabelled for triage.


Generated by Claude Code

Merged via the queue into main with commit 6a99bb2Sep 3, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7323-adapter-return-type branch September 3, 2026 13:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data-adapterdocumentationImprovements or additions to documentationtests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns - #7503

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type
Sep 3, 2026
Merged

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns#7503
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#7323

createObjectStackAdapter returned new ObjectStackAdapter(config) while declaring the shared DataSource interface as its return. A wider value is assignable to a narrower annotation, so nothing ever failed to compile — the loss was entirely on the reading side: every adapter-only member was erased from the type the factory hands back, while staying on the object it hands back.

Measured before the change, compiling against the shipped packages/data-objectstack/dist/index.d.ts with the doc-snippet gate's own compiler options (strict, bundler resolution): 9 x TS2339, not the 4 the card lists — getClient, getCacheStats, invalidateCache, clearCache, getConnectionState, isConnected, onConnectionStateChange, onBatchProgress, setSystemCapabilities. Eight of those are exactly the members the README's API Reference — Methods list documents; the ninth is the one the factory's own JSDoc links to ([ADR-0066] See ObjectStackAdapter.setSystemCapabilities). The same probe after this change: 0 diagnostics.

The two questions that decided the shape

The triage set these as a stop-condition and both are answered from the code, on origin/maine17605309.

Q1 — is ObjectStackAdapter exported from the package's public entry? Yes, already.packages/data-objectstack/src/index.ts:2148 reads export class ObjectStackAdapter, and tsup.config.ts has exactly one entry, src/index.ts. The class is in the shipped dist/index.d.ts export list (verified in the built artifact, not inferred). Two pin tests already assert the exported spelling in source — cloud-surface-retired-4152.pin.test.ts:114 and v3-deep-integration-retired-4241.pin.test.ts:135 — and apps/console/src/dataSource.ts:14 re-exports it by name. So this PR exports nothing by implication: the export list in dist/index.d.ts is unchanged, name for name, before and after.

Q2 — is the narrow return deliberate? No, and there are two pieces of evidence against it.

  1. Commit a5d817061 ("Add connection state monitoring, auto-reconnect, and batch progress") added autoReconnect / maxReconnectAttempts / reconnectDelay to the factory's own config bag while leaving the members that observe those features off the factory's declared return. The same change configured a feature it made unobservable through its own entry point. That is an oversight shape, not an encapsulation decision.
  2. The factory's JSDoc, added by 41b7ce3ce, points the reader at ObjectStackAdapter.setSystemCapabilities — a member its declared return hides.

Nothing anywhere pins the return: no comment, no ADR, no test, no Equal assertion, and no swappability note. Searched the export list, package.jsonfiles / exports, ADR-0066, the sibling adapters, and every test that names the factory.

Had either answer come back the other way this would have been a docs-only PR. They did not, so it is option A — and swappability, the property the narrow return was suspected of protecting, is not lost: a wider return is assignable to the narrower annotation, so const ds: DataSource = createObjectStackAdapter(...) still compiles and still gives the narrow surface. There is a control test that says exactly that.

Why A and not B or C

B (add the missing members to DataSource) would make every other DataSource implementation declare caching, connection state and batch progress it does not have — those are this adapter's concerns. C (document a cast) teaches a cast around a declaration that is merely narrower than the value, which is the opposite of declared = enforced. A is one line and makes declared match shipped for every documented member at once.

The pin

packages/data-objectstack/src/adapterFactoryReturn.types.test.ts — the card's TS2339 reproduction, inverted. It reads all nine members through the factory's return, asserts the return's identity with Equal (not mere assignability — two adapter-shaped declarations are mutually assignable, so only identity can tell "returns THE adapter" from "returns something adapter-shaped"), and carries two controls:

  • _NotOnDataSource — the adapter-only members stay ABSENT from the shared DataSource. This is what makes the reads a statement about the factory's return rather than about every data source, and it fires on option B.
  • _StillADataSource — the widened return is still assignable to DataSource.

Both controls are independent of the return annotation, so reverting the source change turns the reads red and leaves the controls green. That split is measured below, not asserted.

Which tree the pin exercises. Two, deliberately. The test is compiled by pnpm --filter @object-ui/data-objectstack type-check, whose program includes the whole src/** (tests included) and resolves ./index from src — proven with --listFiles: 1 hit for the new file, 55 test files, src/index.ts present. @object-ui/types in that same program resolves from packages/types/dist, so the type used for the controls is the shipped one. The README blocks are the dist half: check:doc-snippets compiles them against the built dist/*.d.ts (its own resolution control prints '@object-ui/types' was successfully resolved to packages/types/dist/index.d.ts).

Documentation

The README note naming this card (added by #5174 batch 8) is removed. The four sections built on the adapter-only members — Metadata Caching, Connection State Monitoring, Batch Operation Progress, Troubleshooting → Cache Issues — now continue from Basic Setup's createObjectStackAdapter(...) call instead of hand-declaring the class, so the page teaches one shape and the doc-snippet gate pins the fix against dist. The other README blocks still declare the class where they only need DataSource-level members; that is truthful (the class is public and documented) and rewriting them buys nothing, so they are left alone.

setSystemCapabilities is still absent from the README's Methods list. It was absent before this PR too, it is a separate doc gap, and adding it here would be scope this card did not ask for.

Verification — every number below was observed, at head 922ca54ec

CheckCommandResult
Package type-checkpnpm --filter @object-ui/data-objectstack type-checkexit 0, tsc --noEmit echoed
Package testspnpm exec vitest run --maxWorkers=2 packages/data-objectstack/ (repo root — a package-dir run is refused by objectui#3378's guard)55 files, 737 tests, all passed
check:doc-snippetsnode scripts/check-doc-snippet-types.mjsexit 0 — "Semantic phase: 455 of 455 block(s) judged, 0 failed." / "Every covered documentation snippet compiles against the built types."
check:readme-exportspnpm check:readme-exportsexit 0 — "OK (43 tracked README(s) ...; 414 self-imports judged (414 real, 0 wrong-path, 0 fabricated); 3305 export symbol(s) read from 37 of 40 tracked package(s) (0 unbuilt ...))"
changeset presencenode scripts/check-changeset-presence.mjsexit 0 — "1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)"
check:doc-fencesnode scripts/check-doc-fence-languages.mjsexit 0 — "every TypeScript block in 227 document(s) is fenced ts/tsx/typescript ..."
control bytesnode scripts/check-control-bytes.mjsexit 0 — "OK (scanned 6180 tracked text file(s); skipped 85 binary)"
Package lintpnpm --filter @object-ui/data-objectstack lintexit 0 — 423 problems, 0 errors, 423 warnings, all pre-existing no-explicit-any; the new file contributes 0 findings (eslint --format json on the two changed source files: new test 0/0)

Every gate result above is quoted from the gate's own verdict line; exit codes were captured by redirecting first and reading $? before any pipe.

Dependency closure built first (pnpm --filter '@object-ui/data-objectstack...' build, then the doc-snippet gate's own derived --build-filter: 26 packages, 34 turbo tasks, then --filter='./packages/*' for check:readme-exports, which had reported 2 unbuilt packages — a "could not run", not a defect).

Lint scope, declared: the table's lint row is the changed package only, not the repo. pnpm lint here is turbo run lint (each package's own eslint .), and CI runs it in full regardless. One reading that needs stating: a separate probe with eslint --no-inline-config reports 4 errors in src/index.ts at lines 1369–1389. Those are the documented eslint-disable-next-line no-console sites for the spec Logger binding (objectui#4029), they are ~4300 lines from either of my edits, and they are invisible to the lint CI actually runs because it honours inline config. Not introduced here, and not silently omitted either.

Reverse verification — red/green split, both directions proven on disk

Method: commit first, then mutate packages/data-objectstack/src/index.ts back to the narrow return under trap ... EXIT INT TERM with an absolute restore path, prove the mutation landed by counting the injected and removed text (not by the editor's exit code) and by comparing blob hashes, then restore and prove the restore by state.

Mutation landed: injected-text count 0 → 1, removed-text count 1 → 0; mutated blob 447fffc8 differs from HEAD blob bad31df4.

Leg 1 — the src pin, no rebuild needed (the pin imports ./index from src; the mutation cannot reach packages/types/dist, which is where its DataSource comes from):

  • 13 diagnostics total, every one of them inside the pin file, 0 anywhere else in the package.
  • 10 x TS2339 at lines 75–84: the nine member reads (invalidateCache twice, with and without an argument).
  • TS2344 at 89 and 113: the _HasHiddenMembers and _IsTheAdapter assertions.
  • TS7053 at 157: the runtime member sweep can no longer index the narrowed type.
  • The controls at lines 120 (_NotOnDataSource) and 135 (_StillADataSource) produced ZERO diagnostics — the split the pin was designed for.
  • vitest on the same mutated tree still passes, which is the point: esbuild erases types, so the colour comes from tsc and from nowhere else.

Leg 2 — the dist pin, rebuilt in both directions. Rebuilt from the mutated source, then a dist preflight: the widened marker count in dist/index.d.ts was 0 (the mutation reached dist). The standalone dist probe then reproduced the card's failure exactly — 9 x TS2339 — and check:doc-snippets went to exit 1, "455 of 455 block(s) judged, 4 failed": precisely the four README sections this PR rewired, failing on getCacheStats, invalidateCache x2, clearCache, onConnectionStateChange, getConnectionState, isConnected, onBatchProgress, clearCache, invalidateCache.

Restore, proven by state, both halves. Source: restored blob bad31df4 equals HEAD's blob and git diff HEAD is 0 bytes. dist: rebuilt again, widened marker back to 1 and narrow marker 0, dist probe back to 0 diagnostics, check:doc-snippets back to exit 0, 0 failed. The restore leg is rebuilt on purpose — a mutated marker left in dist would keep acting on every later run in this tree.

Clause ②

Clause-②: yes, declared in the claim comment on #7323 in the fixed machine spelling, and needs:contract-review is on both carriers (this PR and the card). It widens the declared public surface reachable through a published entry point. Q1's answer removes the triage's specific worry — no class is exported by implication — but the declaration limb is judged from content, not from paths or from diff size.

The one compatibility note a reviewer should weigh. The widened return is a class with private members, so it is no longer satisfied by a hand-written structural stand-in: an object literal assigned to the factory's ReturnType will now be rejected where it used to be accepted. Nothing in this repo does that (searched every test and app that names the factory — apps/console/src/dataSource.ts only re-exports it), and the fix for a downstream consumer is to annotate such a fake as DataSource, which is what it was standing in for. It is the only direction in which this change is not purely additive, and it belongs in the contract review rather than in a footnote.

Draft on purpose, not flipped ready, auto-merge not enabled — the seat lands it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…it returns
The factory returned `new ObjectStackAdapter(config)` while declaring
`DataSource<T>`. A wider value is assignable to a narrower annotation, so
nothing ever failed to compile — the loss was entirely on the reading side.
Measured against the shipped `dist/index.d.ts` with the doc-snippet gate's own
compiler options, nine reads through `ReturnType<typeof
createObjectStackAdapter>` failed with TS2339: getClient, getCacheStats,
invalidateCache, clearCache, getConnectionState, isConnected,
onConnectionStateChange, onBatchProgress and setSystemCapabilities. Eight are
exactly the members the package README's API Reference documents; the ninth is
the one the factory's own JSDoc links to. Same probe after this change: 0
diagnostics.
Option A of the card. B (widening `DataSource` itself) would make every other
data source declare caching, connection state and batch progress it does not
have; C (documenting a cast) teaches a cast around a declaration that is merely
narrower than the value. `ObjectStackAdapter` was already exported from the
package's only entry, so nothing is exported by implication, and no comment,
ADR or test pinned the narrow return — the commit that added autoReconnect /
maxReconnectAttempts / reconnectDelay to the factory's config bag left the
members observing those features off its declared return in the same change.
Callers are unaffected: `const ds: DataSource = createObjectStackAdapter(…)`
still compiles and still gives the narrow surface.
The README note saying the page could not yet teach the factory's shape is
removed, and the four sections built on the adapter-only members now continue
from Basic Setup's factory call. `adapterFactoryReturn.types.test.ts` pins the
card's TS2339 reproduction inverted, with a control for option B and a control
for swappability.
Part of #7323
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation data-adapter tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Clause-② in-seat contract review — PASS WITH REQUIRED AMENDMENTS. needs:contract-review stays on.

Commissioned by the domain:ui execution seat (PM session session_01EMrWaQw3XS5DxTHxp4yRyC) per the in-seat review route (维护者 2026-08-31 裁定). This seat runs opus and is not at tier, so it commissioned an isolated reviewer rather than self-reviewing. The reviewer was given only the card, its triage and the PR — ⛔ not the dispatch order and not this seat's conclusions.

Tier verification (维护者 2026-08-27 裁定): every harness-stamped model field in the transcript was read before adopting — 76 stamps, all claude-fable-5-1, no other value, and zero fallback/overload markers. CONTRACT_REVIEW_TIER is claude-fable-5 (objectstack/scripts/pm/dispatch-gates.mjs:8091); claude-fable-5-1 is at or above that floor. ⇒ Verified, so the verdict is adopted verbatim — ⛔ nothing below is rewritten, abridged or polished.


Contract review — objectui PR #7503 (head 922ca54ec, base e17605309)

Everything below was read from the diff, the card, triage 5524408927, and my own runs in a detached worktree at /home/user/objectui-review-7503 (dependency closure built; git diff HEAD 0 bytes at the end). No edits, commits, pushes, labels or GitHub posts.

① Derived judgments

Accept set

  • Config parameter type: byte-identical before/after. No change to what the factory accepts. Right.

Published surface

  • Declared return DataSource<T>ObjectStackAdapter<T>. Ships: built dist/index.d.ts ends the factory }): ObjectStackAdapter<T>;. Export list unchanged (the diff adds no export; the test file is outside tsup's single entry). Right.
  • The true delta is 20 members, not 9. tsc-computed Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>> is exactly: clearCache, connect, getCacheStats, getCached, getClient, getConnectionState, getDiscovery, getItems, invalidateCache, invalidateViewKeys, isConnected, listImportMappings, onBatchProgress, onConnectionStateChange, onSaveAdvisory, onWriteWarning, probeAppAccess, queryDataset, setSystemCapabilities, updateDashboard. Proven with Assert<Equal<Extra, Twenty>> silent, and negative controls red at 19 names, 21 names, and the PR's nine (TS2344 ×3) — on both the src program and the shipped dist/index.d.ts. All 20 are now the factory's promise.
  • Judgment on the other 11: right to publish, but under-described. None is @internal/@deprecated; stripInternal is not set anywhere, so they were already in the shipped class type; and the class type is already the monorepo's working adapter contract — packages/react/src/context/AppShellContext.tsx:8 types AdapterCtx as ObjectStackAdapter | null, and app-shell's AdapterProvider, MetadataProvider, MetadataService all take ObjectStackAdapter. Two are escape-hatch shaped and worth the seat's eye: getCached(key): unknown | undefined (raw cache read) and getDiscovery(): Promise<unknown | null> whose own JSDoc says "accesses an internal property of the ObjectStackClient"; invalidateViewKeys is a cache-key coupling seam. Not blocking — reachable via new ObjectStackAdapter() and every app-shell path today — but the changeset presents "nine" as the delta. → Amendment 2.

Assignability attack — no consumer regressed except the one the PR names

  • Probed at PR head, all compile: const a: DataSource = …; const b: DataSource<User> = createObjectStackAdapter({…}) (T infers through the class); explicit <User>; DataSource[] and DataSource<User>[] mixed arrays; DataSource | null union; take(ds: DataSource<User>) with and without explicit T; function-type assignment (cfg) => DataSource (return covariance); generic→concrete (cfg) => DataSource<User>; satisfies DataSource; callback contravariance withDs((ds: DataSource) => …) against a (ds: FactoryReturn) => void parameter; Record<string, DataSource>; Promise<DataSource>.
  • The only break: a structural stand-in for ReturnType<typeof createObjectStackAdapter> — TS2740, "missing … client, connected, connectPromise, metadataCache, and 64 more". Repo has no typeof createObjectStackAdapter anywhere; the sole vi.mock('@object-ui/data-objectstack') (packages/app-shell/src/chrome/ConditionalAuthWrapper.previewRetired-6654.test.tsx:72) mocks only getSharedDiscovery, untyped. Correctly named by the PR. Not a rejection.
  • Secondary, not new: a class-with-privates return is nominal across duplicate installs; every ObjectStackAdapter-typed seam in @object-ui/react/app-shell already carries that property.

Q1 — class already exported: verified.src/index.ts:2148 export class ObjectStackAdapter; tsup.config.ts single entry src/index.ts; name present in built dist/index.d.ts export block; apps/console/src/dataSource.ts:14 re-exports it. Nothing exported by implication.

Q2 — narrow return not deliberate: verified, with one miss.

  • No Equal/expectTypeOf/satisfies/ReturnType pin on the factory anywhere in packages/apps; no ADR names it. 780a1b993 (introduced ): DataSource) carries no design statement. a5d817061 added autoReconnect/maxReconnectAttempts/reconnectDelay to the factory config (diff +358–360) without touching the return; 41b7ce3ce (feat(data-objectstack,app-shell,i18n): view config is explicitly org-wide — permission-gated write path + console wording #6125) added the [ADR-0066] See {@link ObjectStackAdapter.setSystemCapabilities} JSDoc. Plus the AdapterCtx fact above — the React layer already treats the class as the contract, the opposite of a swappability design.
  • Miss: the PR's "no swappability note anywhere" overlooked content/docs/utilities/data-objectstack.mdx, untouched by the diff: L53 "createObjectStackAdapter returns a DataSource — the same universal interface every ObjectUI renderer consumes"; L109 "Factory returning a DataSource"; the signature fragment at L137 ending }): DataSource<T>; under a marker claiming it was "Checked against the shipped dist/index.d.ts … with the same type"; and L163–200, a whole "Adapter-only (hold the class type to reach these)" section built on the distinction this PR erases. Provenance 92c0b1f40 (docs(data-objectstack): document the real headless surface, not a phantom React API #4129, a docs batch that "verified snippets against the built packages") — it describes the shipped d.ts, it does not prescribe, so Q2's answer stands. But the page is now false about the shipped types and nothing gates it (the fragment is declared, so the doc-snippet gate never compiles the return). → Amendment 1.

README

Unmentioned diff content: the factory JSDoc gains 15 lines including "⛔ Do not narrow this back" — consistent with the change; nothing else hidden. Test file is exactly as described.

② Semver

  • Changeset: '@object-ui/data-objectstack': minor. Policy (scripts/check-changeset-no-major.mjs header): major reserved for the objectstack-synchronised bump; objectui's own breaks ship as minor with the break spelled out. The body spells out the stand-in break. I would assign minor. Matches. Changeset Bump Policy / Fixed Group / Declaration checks all green.
  • Wording nit folded into Amendment 2: the bold "Not a breaking change for callers." heading sits directly over the paragraph describing the break.

③ Boundary flags

  • (a) Q1 yes/already; Q2 not deliberate — both confirmed, with the mdx caveat above.
  • (b) 9 vs 4. The card measured 4 (its snippet) and named 8, saying the other four were "exactly the members" on the same README list; setSystemCapabilities was introduced by the triage, not the card. All nine do fail together — reproduced (mutation B: 10 × TS2339 on the nine reads, invalidateCache twice). So "one tier, not two" is right in substance, and the card was partial rather than wrong. But the implementer's own count is partial too: README-documented adapter-only members are 9 (connect()), and the hidden delta is 20.
  • (c) Lint narrowing hides nothing.eslint.config.js has no parserOptions.project/projectService — rules are syntactic and per-file, so a return-type change cannot alter lint output elsewhere. What the package-only run skipped (//#lint:root, other packages) CI's full Lint ran and passed.

Pin verification (not vacuous)

  • In the program:tsc --listFiles for the package lists src/adapterFactoryReturn.types.test.ts and src/index.ts; DataSource resolves to packages/types/dist/data.d.ts (project-reference redirect), as the implementer said.
  • Mutation B (narrow back to DataSource<T>): 13 diagnostics, all in the pin — lines 75–84 TS2339 ×10, 89 and 113 TS2344, 157 TS7053; 0 elsewhere; controls at 120/135/137: 0 hits. Reproduces the reported split exactly. My stand-in probe starts compiling under B, confirming that is the only direction that moved.
  • Mutation C (return Omit<ObjectStackAdapter<T>,'find'>, i.e. no longer a DataSource): line 135 _StillADataSource TS2344 red, line 137 TS2741 red, line 113 red, line 120 green. _StillADataSource genuinely discriminates; _NotOnDataSource reads keyof DataSource and is independent of the return, so it fires only on option B, as designed.
  • vitest from the repo root on the pin file: 1 file, 4 tests pass.

CI by name at 922ca54ec (32 runs, all completed)

  • success (29): Lint · Type Check · Build & E2E · Test (shard 1/4) · Test (shard 2/4) · Test (shard 3/4) · Test (shard 4/4) · Doc Snippet Type Check · README Export Check · Changeset Bump Policy · Changeset Declaration · Changeset Fixed Group Check · Changeset Overwrite Report · Doc Fence Language Check · Doc Component Type Check · Internal Docs Link Check · Docs Route Eager Closure Check · Build Docs · Bundle Analysis · Control Byte Scan · Shell Escape Residue Scan · Pre-Install Import Graph Check · Governed Surface Queue Guard · Inert vi.mock Specifier Check · Skill Eval Token Check · Skill Example Check · Skill Guide Path Check · Live E2E (informational) · label
  • skipped (3, conditional jobs): Test (coverage) · Test (coverage shard) · dependabot
  • failed: none.

Verdict

PASS WITH REQUIRED AMENDMENTS

  1. Bring content/docs/utilities/data-objectstack.mdx in line with the shipped types — four sites: L53 prose ("returns a DataSource"), L109 ("Factory returning a DataSource"), the fragment's }): DataSource<T>; at L137 together with its marker's "checked against dist … same type" claim, and the L163–200 "hold the class type to reach these" section, which now teaches a distinction the factory no longer has. This PR already carries the docs limb for the same package's README; the docs-site page must not contradict dist/index.d.ts after merge.
  2. Fix the changeset's description of the delta (.changeset/7323-adapter-factory-return.md, mirrored in the PR body and test header if desired): state that the factory's declared return is now the full class — every public member of ObjectStackAdapter, 20 beyond DataSource — rather than presenting the nine measured reads as the delta; correct "Eight of those are exactly the members … the README documents" to nine (connect()); and reword the "Not a breaking change for callers." heading so the CHANGELOG line does not read as "no break" above the stand-in caveat.

Both amendments are text-only and independent of each other; neither changes the code, which is right.


Seat disposition

Not landing.needs:contract-review stays on both carriers until both amendments are in and this seat verifies them by content. They are being dispatched now. The code is confirmed correct and ⛔ must not change.

Amendment 1 is the find that justifies the tier, and it names a gate hole.content/docs/utilities/data-objectstack.mdx carries a signature fragment ending }): DataSource<T>; under a marker asserting it was "checked against the shipped dist/index.d.ts … with the same type" — and after this merge that assertion is false. It survives because the fragment is declared, so check:doc-snippet-types never compiles the return. A page that claims to be verified against the built types, and is structurally exempt from the check that would verify it, is worse than an unmarked page: this seat, the implementer and the card all read "no swappability note anywhere" and none of us found it. I am recording that as a finding in its own right when the amendment lands.

⚠️ Also worth the record: the reviewer's count of the true delta — 20 members, not 9 — was reached with Assert<Equal<>> plus negative controls at 19, 21 and 9 names, on both the src program and the shipped d.ts. My dispatch asked it to enumerate what else became reachable; it did, and the answer is that the changeset understates the published surface by more than a factor of two. ⛔ That is a description defect, not a design one — the reviewer establishes the 11 extra members were already in the shipped class type, already reachable via new ObjectStackAdapter(), and already the monorepo's working adapter contract (AdapterCtx is typed as the class). So the fix is the changeset's wording, not the return type.


Generated by Claude Code

…idened factory return (objectui#7323)
Two text-only amendments from the in-seat contract review on the pull request.
No code changes: the return-type widening, the type-level pin and the README
rewiring are untouched.
Amendment 1 — content/docs/utilities/data-objectstack.mdx contradicted the
shipped types. The four sites the review named: the Quick Start prose saying the
factory "returns a `DataSource`"; the API Reference blurb "Factory returning a
`DataSource`"; the signature fragment ending `}): DataSource<T>;` together with
its marker's claim to have been checked against the shipped `dist/index.d.ts`;
and the `ObjectStackAdapter` section built on "hold the class type to reach
these", a distinction the factory no longer has. That section is rewritten rather
than deleted, with a "What changed" paragraph so a reader who followed the old
advice can see why constructing the class by hand is no longer needed.
Two further sites in the same file taught the same erased distinction and are
corrected with them: the Mutations paragraph ("hold the adapter at its class type
... before calling them") and Troubleshooting -> Authentication Errors
("Connection introspection lives on the adapter class ... so hold it at the class
type"); both examples now continue from the factory call, matching how the README
limb of this pull request was rewired. `onMutation` was also misfiled under
"Adapter-only" while being an optional member of `DataSource`; it moves in with
`bulk` and `batchTransaction`.
The fragment's marker no longer asserts an unqualified "checked against the
shipped d.ts". Because the block is declared, `check:doc-snippet-types` never
compiles it, so the marker now states that the agreement is hand-checked rather
than gate-enforced -- which is why the stale return type survived there. The gate
and the `declared` marker keyword are unchanged.
Amendment 2 — the changeset understated the published surface by more than 2x.
It presented the card's nine measured TS2339 reads as the delta; the factory's
declared return is now the class itself, so the delta is every public member of
`ObjectStackAdapter` -- 20 beyond `DataSource`, independently re-derived here as
`Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>>` against
the built `dist/index.d.ts` (20 names, matching the review). The README-documents
sentence is corrected: eight of the nine measured reads are on that list, but the
list itself is nine adapter-only members -- `connect()` was documented all along
and simply was not measured. The "Not a breaking change for callers." heading is
reworded to lead with the one shape that does break, so the CHANGELOG line no
longer reads as "no break" above the caveat describing it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
…ui#7323)
Follow-up to the previous commit, on my own prose in the same amendment:
- Drop the "Until v17.7" version claim. The package is at 17.6.0 and the
changeset is a minor, but the released number is decided by the fixed group at
release time, so the page should not assert it.
- Remove a duplicated bullet list. `bulk` and `batchTransaction` were listed once
under the `DataSource` members and again under the optional-members paragraph.
`onMutation` now joins them in the single list, spelled `onMutation?` like its
neighbours, with the correction noted in the paragraph instead.
- Say "six bullets cover nine members" rather than "those nine", since the list
groups related members onto shared lines, and make the parenthetical naming the
other eleven members read as illustrative rather than exhaustive -- it named
four seams out of eleven.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Both amendments verified — needs:contract-review cleared, ready + auto-merge

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance: 「PASS ⇒ 同席剥标、ready、auto-merge」 (维护者 2026-08-31 裁定). The tier review returned PASS WITH REQUIRED AMENDMENTS (5526083981); both are now in.

⚠️The amendment agent was killed mid-run by a transient HTTP 529 and its report is lost. It had already pushed. So everything below is this seat's own verification from the git objects, with no report to lean on — which is the right posture anyway, and it is the reason this comment carries readings rather than a summary.

Verified by content at 5140938cd

Two commits since the reviewed head 922ca54ec:

  • 27d18e179 — docs page + changeset
  • 5140938cd — a follow-up tightening of the rewritten section

Two files across the whole range, and ⭐ not one line of code:

.changeset/7323-adapter-factory-return.md | 53 +++++++++----
content/docs/utilities/data-objectstack.mdx | 99 ++++++++++++++++-------

⇒ The return-type widening, the type-level pin and the README rewiring are byte-identical to what was reviewed. The standing PASS therefore holds and no re-review is owed — the review's own words were that both amendments are "text-only and independent of each other; neither changes the code, which is right."

Amendment 1 — the docs page. Every flagged claim is gone: no returns a DataSource, no Factory returning a DataSource, no }): DataSource<T>; fragment. The "hold the class type to reach these" section was rewritten, not deleted — it now opens "What changed. This section used to tell you to hold the class type to…", which is what a reader who followed the old advice needs.

And the marker is gone entirely. The Checked against the shipped dist/index.d.ts … with the same type assertion was removed, not reworded around it. That matters more than the four corrected sites: that false claim of verification is why the page stayed wrong through three separate readers looking for exactly it.

Amendment 2 — the changeset. Now states the delta is the whole class — "20 members, not nine" — with the tsc-computed enumeration, corrects eight → nine (connect() is adapter-only), and records that the eleven past the documented nine were already in the shipped class type.

Checks — 32/32, read by name by this seat

29 success, 3 skipped by design (dependabot, Test (coverage), Test (coverage shard …)) on 5140938cd. The ones that actually cover this amendment: Doc Snippet Type Check 13:25:45 · Build Docs 13:23:57 · Internal Docs Link Check 13:21:53 · Doc Fence Language Check · README Export Check 13:25:31 · Changeset Declaration / Bump Policy / Fixed Group / Overwrite Report. Long poles: all four Test (shard N/4) 13:34–13:36, Type Check 13:27:31, Lint 13:27:12.

Carriers

needs:contract-review removed from PR #7503 (remaining documentation, data-adapter, tests) and from card #7323 (remaining bug, pm:dispatched, priority:p2, domain:ui). Both label sets were read immediately before writing rather than assumed — the label job had added labels since my last reading of a sibling PR, and writing a stale set would have clobbered them.

⚠️ Both readings are manual. scripts/pm/check-clause2-carriers.mjs exits 2 in this session (it needs its own GitHub token; this seat reaches GitHub only through MCP) and its own output says that is ⛔ not a clearance. A hand label-check covers strictly less than the tool — this shift produced four distinct carrier defects and the fallback is structurally blind to two of their shapes, now filed as objectstack#14965.

The finding this PR produced, filed separately

#7505 — a declared doc fragment is never compiled by check:doc-snippet-types, so a page can carry a "checked against the shipped dist" marker that no gate ever re-checks. The one known instance is fixed here; the class is not, and enumerating the rest is the first task on that card. ⛔ Filed unassigned and unlabelled for triage.


Generated by Claude Code

Merged via the queue into main with commit 6a99bb2Sep 3, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7323-adapter-return-type branch September 3, 2026 13:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data-adapterdocumentationImprovements or additions to documentationtests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns - #7503

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type
Sep 3, 2026
Merged

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns#7503
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#7323

createObjectStackAdapter returned new ObjectStackAdapter(config) while declaring the shared DataSource interface as its return. A wider value is assignable to a narrower annotation, so nothing ever failed to compile — the loss was entirely on the reading side: every adapter-only member was erased from the type the factory hands back, while staying on the object it hands back.

Measured before the change, compiling against the shipped packages/data-objectstack/dist/index.d.ts with the doc-snippet gate's own compiler options (strict, bundler resolution): 9 x TS2339, not the 4 the card lists — getClient, getCacheStats, invalidateCache, clearCache, getConnectionState, isConnected, onConnectionStateChange, onBatchProgress, setSystemCapabilities. Eight of those are exactly the members the README's API Reference — Methods list documents; the ninth is the one the factory's own JSDoc links to ([ADR-0066] See ObjectStackAdapter.setSystemCapabilities). The same probe after this change: 0 diagnostics.

The two questions that decided the shape

The triage set these as a stop-condition and both are answered from the code, on origin/maine17605309.

Q1 — is ObjectStackAdapter exported from the package's public entry? Yes, already.packages/data-objectstack/src/index.ts:2148 reads export class ObjectStackAdapter, and tsup.config.ts has exactly one entry, src/index.ts. The class is in the shipped dist/index.d.ts export list (verified in the built artifact, not inferred). Two pin tests already assert the exported spelling in source — cloud-surface-retired-4152.pin.test.ts:114 and v3-deep-integration-retired-4241.pin.test.ts:135 — and apps/console/src/dataSource.ts:14 re-exports it by name. So this PR exports nothing by implication: the export list in dist/index.d.ts is unchanged, name for name, before and after.

Q2 — is the narrow return deliberate? No, and there are two pieces of evidence against it.

  1. Commit a5d817061 ("Add connection state monitoring, auto-reconnect, and batch progress") added autoReconnect / maxReconnectAttempts / reconnectDelay to the factory's own config bag while leaving the members that observe those features off the factory's declared return. The same change configured a feature it made unobservable through its own entry point. That is an oversight shape, not an encapsulation decision.
  2. The factory's JSDoc, added by 41b7ce3ce, points the reader at ObjectStackAdapter.setSystemCapabilities — a member its declared return hides.

Nothing anywhere pins the return: no comment, no ADR, no test, no Equal assertion, and no swappability note. Searched the export list, package.jsonfiles / exports, ADR-0066, the sibling adapters, and every test that names the factory.

Had either answer come back the other way this would have been a docs-only PR. They did not, so it is option A — and swappability, the property the narrow return was suspected of protecting, is not lost: a wider return is assignable to the narrower annotation, so const ds: DataSource = createObjectStackAdapter(...) still compiles and still gives the narrow surface. There is a control test that says exactly that.

Why A and not B or C

B (add the missing members to DataSource) would make every other DataSource implementation declare caching, connection state and batch progress it does not have — those are this adapter's concerns. C (document a cast) teaches a cast around a declaration that is merely narrower than the value, which is the opposite of declared = enforced. A is one line and makes declared match shipped for every documented member at once.

The pin

packages/data-objectstack/src/adapterFactoryReturn.types.test.ts — the card's TS2339 reproduction, inverted. It reads all nine members through the factory's return, asserts the return's identity with Equal (not mere assignability — two adapter-shaped declarations are mutually assignable, so only identity can tell "returns THE adapter" from "returns something adapter-shaped"), and carries two controls:

  • _NotOnDataSource — the adapter-only members stay ABSENT from the shared DataSource. This is what makes the reads a statement about the factory's return rather than about every data source, and it fires on option B.
  • _StillADataSource — the widened return is still assignable to DataSource.

Both controls are independent of the return annotation, so reverting the source change turns the reads red and leaves the controls green. That split is measured below, not asserted.

Which tree the pin exercises. Two, deliberately. The test is compiled by pnpm --filter @object-ui/data-objectstack type-check, whose program includes the whole src/** (tests included) and resolves ./index from src — proven with --listFiles: 1 hit for the new file, 55 test files, src/index.ts present. @object-ui/types in that same program resolves from packages/types/dist, so the type used for the controls is the shipped one. The README blocks are the dist half: check:doc-snippets compiles them against the built dist/*.d.ts (its own resolution control prints '@object-ui/types' was successfully resolved to packages/types/dist/index.d.ts).

Documentation

The README note naming this card (added by #5174 batch 8) is removed. The four sections built on the adapter-only members — Metadata Caching, Connection State Monitoring, Batch Operation Progress, Troubleshooting → Cache Issues — now continue from Basic Setup's createObjectStackAdapter(...) call instead of hand-declaring the class, so the page teaches one shape and the doc-snippet gate pins the fix against dist. The other README blocks still declare the class where they only need DataSource-level members; that is truthful (the class is public and documented) and rewriting them buys nothing, so they are left alone.

setSystemCapabilities is still absent from the README's Methods list. It was absent before this PR too, it is a separate doc gap, and adding it here would be scope this card did not ask for.

Verification — every number below was observed, at head 922ca54ec

CheckCommandResult
Package type-checkpnpm --filter @object-ui/data-objectstack type-checkexit 0, tsc --noEmit echoed
Package testspnpm exec vitest run --maxWorkers=2 packages/data-objectstack/ (repo root — a package-dir run is refused by objectui#3378's guard)55 files, 737 tests, all passed
check:doc-snippetsnode scripts/check-doc-snippet-types.mjsexit 0 — "Semantic phase: 455 of 455 block(s) judged, 0 failed." / "Every covered documentation snippet compiles against the built types."
check:readme-exportspnpm check:readme-exportsexit 0 — "OK (43 tracked README(s) ...; 414 self-imports judged (414 real, 0 wrong-path, 0 fabricated); 3305 export symbol(s) read from 37 of 40 tracked package(s) (0 unbuilt ...))"
changeset presencenode scripts/check-changeset-presence.mjsexit 0 — "1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)"
check:doc-fencesnode scripts/check-doc-fence-languages.mjsexit 0 — "every TypeScript block in 227 document(s) is fenced ts/tsx/typescript ..."
control bytesnode scripts/check-control-bytes.mjsexit 0 — "OK (scanned 6180 tracked text file(s); skipped 85 binary)"
Package lintpnpm --filter @object-ui/data-objectstack lintexit 0 — 423 problems, 0 errors, 423 warnings, all pre-existing no-explicit-any; the new file contributes 0 findings (eslint --format json on the two changed source files: new test 0/0)

Every gate result above is quoted from the gate's own verdict line; exit codes were captured by redirecting first and reading $? before any pipe.

Dependency closure built first (pnpm --filter '@object-ui/data-objectstack...' build, then the doc-snippet gate's own derived --build-filter: 26 packages, 34 turbo tasks, then --filter='./packages/*' for check:readme-exports, which had reported 2 unbuilt packages — a "could not run", not a defect).

Lint scope, declared: the table's lint row is the changed package only, not the repo. pnpm lint here is turbo run lint (each package's own eslint .), and CI runs it in full regardless. One reading that needs stating: a separate probe with eslint --no-inline-config reports 4 errors in src/index.ts at lines 1369–1389. Those are the documented eslint-disable-next-line no-console sites for the spec Logger binding (objectui#4029), they are ~4300 lines from either of my edits, and they are invisible to the lint CI actually runs because it honours inline config. Not introduced here, and not silently omitted either.

Reverse verification — red/green split, both directions proven on disk

Method: commit first, then mutate packages/data-objectstack/src/index.ts back to the narrow return under trap ... EXIT INT TERM with an absolute restore path, prove the mutation landed by counting the injected and removed text (not by the editor's exit code) and by comparing blob hashes, then restore and prove the restore by state.

Mutation landed: injected-text count 0 → 1, removed-text count 1 → 0; mutated blob 447fffc8 differs from HEAD blob bad31df4.

Leg 1 — the src pin, no rebuild needed (the pin imports ./index from src; the mutation cannot reach packages/types/dist, which is where its DataSource comes from):

  • 13 diagnostics total, every one of them inside the pin file, 0 anywhere else in the package.
  • 10 x TS2339 at lines 75–84: the nine member reads (invalidateCache twice, with and without an argument).
  • TS2344 at 89 and 113: the _HasHiddenMembers and _IsTheAdapter assertions.
  • TS7053 at 157: the runtime member sweep can no longer index the narrowed type.
  • The controls at lines 120 (_NotOnDataSource) and 135 (_StillADataSource) produced ZERO diagnostics — the split the pin was designed for.
  • vitest on the same mutated tree still passes, which is the point: esbuild erases types, so the colour comes from tsc and from nowhere else.

Leg 2 — the dist pin, rebuilt in both directions. Rebuilt from the mutated source, then a dist preflight: the widened marker count in dist/index.d.ts was 0 (the mutation reached dist). The standalone dist probe then reproduced the card's failure exactly — 9 x TS2339 — and check:doc-snippets went to exit 1, "455 of 455 block(s) judged, 4 failed": precisely the four README sections this PR rewired, failing on getCacheStats, invalidateCache x2, clearCache, onConnectionStateChange, getConnectionState, isConnected, onBatchProgress, clearCache, invalidateCache.

Restore, proven by state, both halves. Source: restored blob bad31df4 equals HEAD's blob and git diff HEAD is 0 bytes. dist: rebuilt again, widened marker back to 1 and narrow marker 0, dist probe back to 0 diagnostics, check:doc-snippets back to exit 0, 0 failed. The restore leg is rebuilt on purpose — a mutated marker left in dist would keep acting on every later run in this tree.

Clause ②

Clause-②: yes, declared in the claim comment on #7323 in the fixed machine spelling, and needs:contract-review is on both carriers (this PR and the card). It widens the declared public surface reachable through a published entry point. Q1's answer removes the triage's specific worry — no class is exported by implication — but the declaration limb is judged from content, not from paths or from diff size.

The one compatibility note a reviewer should weigh. The widened return is a class with private members, so it is no longer satisfied by a hand-written structural stand-in: an object literal assigned to the factory's ReturnType will now be rejected where it used to be accepted. Nothing in this repo does that (searched every test and app that names the factory — apps/console/src/dataSource.ts only re-exports it), and the fix for a downstream consumer is to annotate such a fake as DataSource, which is what it was standing in for. It is the only direction in which this change is not purely additive, and it belongs in the contract review rather than in a footnote.

Draft on purpose, not flipped ready, auto-merge not enabled — the seat lands it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…it returns
The factory returned `new ObjectStackAdapter(config)` while declaring
`DataSource<T>`. A wider value is assignable to a narrower annotation, so
nothing ever failed to compile — the loss was entirely on the reading side.
Measured against the shipped `dist/index.d.ts` with the doc-snippet gate's own
compiler options, nine reads through `ReturnType<typeof
createObjectStackAdapter>` failed with TS2339: getClient, getCacheStats,
invalidateCache, clearCache, getConnectionState, isConnected,
onConnectionStateChange, onBatchProgress and setSystemCapabilities. Eight are
exactly the members the package README's API Reference documents; the ninth is
the one the factory's own JSDoc links to. Same probe after this change: 0
diagnostics.
Option A of the card. B (widening `DataSource` itself) would make every other
data source declare caching, connection state and batch progress it does not
have; C (documenting a cast) teaches a cast around a declaration that is merely
narrower than the value. `ObjectStackAdapter` was already exported from the
package's only entry, so nothing is exported by implication, and no comment,
ADR or test pinned the narrow return — the commit that added autoReconnect /
maxReconnectAttempts / reconnectDelay to the factory's config bag left the
members observing those features off its declared return in the same change.
Callers are unaffected: `const ds: DataSource = createObjectStackAdapter(…)`
still compiles and still gives the narrow surface.
The README note saying the page could not yet teach the factory's shape is
removed, and the four sections built on the adapter-only members now continue
from Basic Setup's factory call. `adapterFactoryReturn.types.test.ts` pins the
card's TS2339 reproduction inverted, with a control for option B and a control
for swappability.
Part of #7323
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation data-adapter tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Clause-② in-seat contract review — PASS WITH REQUIRED AMENDMENTS. needs:contract-review stays on.

Commissioned by the domain:ui execution seat (PM session session_01EMrWaQw3XS5DxTHxp4yRyC) per the in-seat review route (维护者 2026-08-31 裁定). This seat runs opus and is not at tier, so it commissioned an isolated reviewer rather than self-reviewing. The reviewer was given only the card, its triage and the PR — ⛔ not the dispatch order and not this seat's conclusions.

Tier verification (维护者 2026-08-27 裁定): every harness-stamped model field in the transcript was read before adopting — 76 stamps, all claude-fable-5-1, no other value, and zero fallback/overload markers. CONTRACT_REVIEW_TIER is claude-fable-5 (objectstack/scripts/pm/dispatch-gates.mjs:8091); claude-fable-5-1 is at or above that floor. ⇒ Verified, so the verdict is adopted verbatim — ⛔ nothing below is rewritten, abridged or polished.


Contract review — objectui PR #7503 (head 922ca54ec, base e17605309)

Everything below was read from the diff, the card, triage 5524408927, and my own runs in a detached worktree at /home/user/objectui-review-7503 (dependency closure built; git diff HEAD 0 bytes at the end). No edits, commits, pushes, labels or GitHub posts.

① Derived judgments

Accept set

  • Config parameter type: byte-identical before/after. No change to what the factory accepts. Right.

Published surface

  • Declared return DataSource<T>ObjectStackAdapter<T>. Ships: built dist/index.d.ts ends the factory }): ObjectStackAdapter<T>;. Export list unchanged (the diff adds no export; the test file is outside tsup's single entry). Right.
  • The true delta is 20 members, not 9. tsc-computed Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>> is exactly: clearCache, connect, getCacheStats, getCached, getClient, getConnectionState, getDiscovery, getItems, invalidateCache, invalidateViewKeys, isConnected, listImportMappings, onBatchProgress, onConnectionStateChange, onSaveAdvisory, onWriteWarning, probeAppAccess, queryDataset, setSystemCapabilities, updateDashboard. Proven with Assert<Equal<Extra, Twenty>> silent, and negative controls red at 19 names, 21 names, and the PR's nine (TS2344 ×3) — on both the src program and the shipped dist/index.d.ts. All 20 are now the factory's promise.
  • Judgment on the other 11: right to publish, but under-described. None is @internal/@deprecated; stripInternal is not set anywhere, so they were already in the shipped class type; and the class type is already the monorepo's working adapter contract — packages/react/src/context/AppShellContext.tsx:8 types AdapterCtx as ObjectStackAdapter | null, and app-shell's AdapterProvider, MetadataProvider, MetadataService all take ObjectStackAdapter. Two are escape-hatch shaped and worth the seat's eye: getCached(key): unknown | undefined (raw cache read) and getDiscovery(): Promise<unknown | null> whose own JSDoc says "accesses an internal property of the ObjectStackClient"; invalidateViewKeys is a cache-key coupling seam. Not blocking — reachable via new ObjectStackAdapter() and every app-shell path today — but the changeset presents "nine" as the delta. → Amendment 2.

Assignability attack — no consumer regressed except the one the PR names

  • Probed at PR head, all compile: const a: DataSource = …; const b: DataSource<User> = createObjectStackAdapter({…}) (T infers through the class); explicit <User>; DataSource[] and DataSource<User>[] mixed arrays; DataSource | null union; take(ds: DataSource<User>) with and without explicit T; function-type assignment (cfg) => DataSource (return covariance); generic→concrete (cfg) => DataSource<User>; satisfies DataSource; callback contravariance withDs((ds: DataSource) => …) against a (ds: FactoryReturn) => void parameter; Record<string, DataSource>; Promise<DataSource>.
  • The only break: a structural stand-in for ReturnType<typeof createObjectStackAdapter> — TS2740, "missing … client, connected, connectPromise, metadataCache, and 64 more". Repo has no typeof createObjectStackAdapter anywhere; the sole vi.mock('@object-ui/data-objectstack') (packages/app-shell/src/chrome/ConditionalAuthWrapper.previewRetired-6654.test.tsx:72) mocks only getSharedDiscovery, untyped. Correctly named by the PR. Not a rejection.
  • Secondary, not new: a class-with-privates return is nominal across duplicate installs; every ObjectStackAdapter-typed seam in @object-ui/react/app-shell already carries that property.

Q1 — class already exported: verified.src/index.ts:2148 export class ObjectStackAdapter; tsup.config.ts single entry src/index.ts; name present in built dist/index.d.ts export block; apps/console/src/dataSource.ts:14 re-exports it. Nothing exported by implication.

Q2 — narrow return not deliberate: verified, with one miss.

  • No Equal/expectTypeOf/satisfies/ReturnType pin on the factory anywhere in packages/apps; no ADR names it. 780a1b993 (introduced ): DataSource) carries no design statement. a5d817061 added autoReconnect/maxReconnectAttempts/reconnectDelay to the factory config (diff +358–360) without touching the return; 41b7ce3ce (feat(data-objectstack,app-shell,i18n): view config is explicitly org-wide — permission-gated write path + console wording #6125) added the [ADR-0066] See {@link ObjectStackAdapter.setSystemCapabilities} JSDoc. Plus the AdapterCtx fact above — the React layer already treats the class as the contract, the opposite of a swappability design.
  • Miss: the PR's "no swappability note anywhere" overlooked content/docs/utilities/data-objectstack.mdx, untouched by the diff: L53 "createObjectStackAdapter returns a DataSource — the same universal interface every ObjectUI renderer consumes"; L109 "Factory returning a DataSource"; the signature fragment at L137 ending }): DataSource<T>; under a marker claiming it was "Checked against the shipped dist/index.d.ts … with the same type"; and L163–200, a whole "Adapter-only (hold the class type to reach these)" section built on the distinction this PR erases. Provenance 92c0b1f40 (docs(data-objectstack): document the real headless surface, not a phantom React API #4129, a docs batch that "verified snippets against the built packages") — it describes the shipped d.ts, it does not prescribe, so Q2's answer stands. But the page is now false about the shipped types and nothing gates it (the fragment is declared, so the doc-snippet gate never compiles the return). → Amendment 1.

README

Unmentioned diff content: the factory JSDoc gains 15 lines including "⛔ Do not narrow this back" — consistent with the change; nothing else hidden. Test file is exactly as described.

② Semver

  • Changeset: '@object-ui/data-objectstack': minor. Policy (scripts/check-changeset-no-major.mjs header): major reserved for the objectstack-synchronised bump; objectui's own breaks ship as minor with the break spelled out. The body spells out the stand-in break. I would assign minor. Matches. Changeset Bump Policy / Fixed Group / Declaration checks all green.
  • Wording nit folded into Amendment 2: the bold "Not a breaking change for callers." heading sits directly over the paragraph describing the break.

③ Boundary flags

  • (a) Q1 yes/already; Q2 not deliberate — both confirmed, with the mdx caveat above.
  • (b) 9 vs 4. The card measured 4 (its snippet) and named 8, saying the other four were "exactly the members" on the same README list; setSystemCapabilities was introduced by the triage, not the card. All nine do fail together — reproduced (mutation B: 10 × TS2339 on the nine reads, invalidateCache twice). So "one tier, not two" is right in substance, and the card was partial rather than wrong. But the implementer's own count is partial too: README-documented adapter-only members are 9 (connect()), and the hidden delta is 20.
  • (c) Lint narrowing hides nothing.eslint.config.js has no parserOptions.project/projectService — rules are syntactic and per-file, so a return-type change cannot alter lint output elsewhere. What the package-only run skipped (//#lint:root, other packages) CI's full Lint ran and passed.

Pin verification (not vacuous)

  • In the program:tsc --listFiles for the package lists src/adapterFactoryReturn.types.test.ts and src/index.ts; DataSource resolves to packages/types/dist/data.d.ts (project-reference redirect), as the implementer said.
  • Mutation B (narrow back to DataSource<T>): 13 diagnostics, all in the pin — lines 75–84 TS2339 ×10, 89 and 113 TS2344, 157 TS7053; 0 elsewhere; controls at 120/135/137: 0 hits. Reproduces the reported split exactly. My stand-in probe starts compiling under B, confirming that is the only direction that moved.
  • Mutation C (return Omit<ObjectStackAdapter<T>,'find'>, i.e. no longer a DataSource): line 135 _StillADataSource TS2344 red, line 137 TS2741 red, line 113 red, line 120 green. _StillADataSource genuinely discriminates; _NotOnDataSource reads keyof DataSource and is independent of the return, so it fires only on option B, as designed.
  • vitest from the repo root on the pin file: 1 file, 4 tests pass.

CI by name at 922ca54ec (32 runs, all completed)

  • success (29): Lint · Type Check · Build & E2E · Test (shard 1/4) · Test (shard 2/4) · Test (shard 3/4) · Test (shard 4/4) · Doc Snippet Type Check · README Export Check · Changeset Bump Policy · Changeset Declaration · Changeset Fixed Group Check · Changeset Overwrite Report · Doc Fence Language Check · Doc Component Type Check · Internal Docs Link Check · Docs Route Eager Closure Check · Build Docs · Bundle Analysis · Control Byte Scan · Shell Escape Residue Scan · Pre-Install Import Graph Check · Governed Surface Queue Guard · Inert vi.mock Specifier Check · Skill Eval Token Check · Skill Example Check · Skill Guide Path Check · Live E2E (informational) · label
  • skipped (3, conditional jobs): Test (coverage) · Test (coverage shard) · dependabot
  • failed: none.

Verdict

PASS WITH REQUIRED AMENDMENTS

  1. Bring content/docs/utilities/data-objectstack.mdx in line with the shipped types — four sites: L53 prose ("returns a DataSource"), L109 ("Factory returning a DataSource"), the fragment's }): DataSource<T>; at L137 together with its marker's "checked against dist … same type" claim, and the L163–200 "hold the class type to reach these" section, which now teaches a distinction the factory no longer has. This PR already carries the docs limb for the same package's README; the docs-site page must not contradict dist/index.d.ts after merge.
  2. Fix the changeset's description of the delta (.changeset/7323-adapter-factory-return.md, mirrored in the PR body and test header if desired): state that the factory's declared return is now the full class — every public member of ObjectStackAdapter, 20 beyond DataSource — rather than presenting the nine measured reads as the delta; correct "Eight of those are exactly the members … the README documents" to nine (connect()); and reword the "Not a breaking change for callers." heading so the CHANGELOG line does not read as "no break" above the stand-in caveat.

Both amendments are text-only and independent of each other; neither changes the code, which is right.


Seat disposition

Not landing.needs:contract-review stays on both carriers until both amendments are in and this seat verifies them by content. They are being dispatched now. The code is confirmed correct and ⛔ must not change.

Amendment 1 is the find that justifies the tier, and it names a gate hole.content/docs/utilities/data-objectstack.mdx carries a signature fragment ending }): DataSource<T>; under a marker asserting it was "checked against the shipped dist/index.d.ts … with the same type" — and after this merge that assertion is false. It survives because the fragment is declared, so check:doc-snippet-types never compiles the return. A page that claims to be verified against the built types, and is structurally exempt from the check that would verify it, is worse than an unmarked page: this seat, the implementer and the card all read "no swappability note anywhere" and none of us found it. I am recording that as a finding in its own right when the amendment lands.

⚠️ Also worth the record: the reviewer's count of the true delta — 20 members, not 9 — was reached with Assert<Equal<>> plus negative controls at 19, 21 and 9 names, on both the src program and the shipped d.ts. My dispatch asked it to enumerate what else became reachable; it did, and the answer is that the changeset understates the published surface by more than a factor of two. ⛔ That is a description defect, not a design one — the reviewer establishes the 11 extra members were already in the shipped class type, already reachable via new ObjectStackAdapter(), and already the monorepo's working adapter contract (AdapterCtx is typed as the class). So the fix is the changeset's wording, not the return type.


Generated by Claude Code

…idened factory return (objectui#7323)
Two text-only amendments from the in-seat contract review on the pull request.
No code changes: the return-type widening, the type-level pin and the README
rewiring are untouched.
Amendment 1 — content/docs/utilities/data-objectstack.mdx contradicted the
shipped types. The four sites the review named: the Quick Start prose saying the
factory "returns a `DataSource`"; the API Reference blurb "Factory returning a
`DataSource`"; the signature fragment ending `}): DataSource<T>;` together with
its marker's claim to have been checked against the shipped `dist/index.d.ts`;
and the `ObjectStackAdapter` section built on "hold the class type to reach
these", a distinction the factory no longer has. That section is rewritten rather
than deleted, with a "What changed" paragraph so a reader who followed the old
advice can see why constructing the class by hand is no longer needed.
Two further sites in the same file taught the same erased distinction and are
corrected with them: the Mutations paragraph ("hold the adapter at its class type
... before calling them") and Troubleshooting -> Authentication Errors
("Connection introspection lives on the adapter class ... so hold it at the class
type"); both examples now continue from the factory call, matching how the README
limb of this pull request was rewired. `onMutation` was also misfiled under
"Adapter-only" while being an optional member of `DataSource`; it moves in with
`bulk` and `batchTransaction`.
The fragment's marker no longer asserts an unqualified "checked against the
shipped d.ts". Because the block is declared, `check:doc-snippet-types` never
compiles it, so the marker now states that the agreement is hand-checked rather
than gate-enforced -- which is why the stale return type survived there. The gate
and the `declared` marker keyword are unchanged.
Amendment 2 — the changeset understated the published surface by more than 2x.
It presented the card's nine measured TS2339 reads as the delta; the factory's
declared return is now the class itself, so the delta is every public member of
`ObjectStackAdapter` -- 20 beyond `DataSource`, independently re-derived here as
`Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>>` against
the built `dist/index.d.ts` (20 names, matching the review). The README-documents
sentence is corrected: eight of the nine measured reads are on that list, but the
list itself is nine adapter-only members -- `connect()` was documented all along
and simply was not measured. The "Not a breaking change for callers." heading is
reworded to lead with the one shape that does break, so the CHANGELOG line no
longer reads as "no break" above the caveat describing it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
…ui#7323)
Follow-up to the previous commit, on my own prose in the same amendment:
- Drop the "Until v17.7" version claim. The package is at 17.6.0 and the
changeset is a minor, but the released number is decided by the fixed group at
release time, so the page should not assert it.
- Remove a duplicated bullet list. `bulk` and `batchTransaction` were listed once
under the `DataSource` members and again under the optional-members paragraph.
`onMutation` now joins them in the single list, spelled `onMutation?` like its
neighbours, with the correction noted in the paragraph instead.
- Say "six bullets cover nine members" rather than "those nine", since the list
groups related members onto shared lines, and make the parenthetical naming the
other eleven members read as illustrative rather than exhaustive -- it named
four seams out of eleven.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Both amendments verified — needs:contract-review cleared, ready + auto-merge

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance: 「PASS ⇒ 同席剥标、ready、auto-merge」 (维护者 2026-08-31 裁定). The tier review returned PASS WITH REQUIRED AMENDMENTS (5526083981); both are now in.

⚠️The amendment agent was killed mid-run by a transient HTTP 529 and its report is lost. It had already pushed. So everything below is this seat's own verification from the git objects, with no report to lean on — which is the right posture anyway, and it is the reason this comment carries readings rather than a summary.

Verified by content at 5140938cd

Two commits since the reviewed head 922ca54ec:

  • 27d18e179 — docs page + changeset
  • 5140938cd — a follow-up tightening of the rewritten section

Two files across the whole range, and ⭐ not one line of code:

.changeset/7323-adapter-factory-return.md | 53 +++++++++----
content/docs/utilities/data-objectstack.mdx | 99 ++++++++++++++++-------

⇒ The return-type widening, the type-level pin and the README rewiring are byte-identical to what was reviewed. The standing PASS therefore holds and no re-review is owed — the review's own words were that both amendments are "text-only and independent of each other; neither changes the code, which is right."

Amendment 1 — the docs page. Every flagged claim is gone: no returns a DataSource, no Factory returning a DataSource, no }): DataSource<T>; fragment. The "hold the class type to reach these" section was rewritten, not deleted — it now opens "What changed. This section used to tell you to hold the class type to…", which is what a reader who followed the old advice needs.

And the marker is gone entirely. The Checked against the shipped dist/index.d.ts … with the same type assertion was removed, not reworded around it. That matters more than the four corrected sites: that false claim of verification is why the page stayed wrong through three separate readers looking for exactly it.

Amendment 2 — the changeset. Now states the delta is the whole class — "20 members, not nine" — with the tsc-computed enumeration, corrects eight → nine (connect() is adapter-only), and records that the eleven past the documented nine were already in the shipped class type.

Checks — 32/32, read by name by this seat

29 success, 3 skipped by design (dependabot, Test (coverage), Test (coverage shard …)) on 5140938cd. The ones that actually cover this amendment: Doc Snippet Type Check 13:25:45 · Build Docs 13:23:57 · Internal Docs Link Check 13:21:53 · Doc Fence Language Check · README Export Check 13:25:31 · Changeset Declaration / Bump Policy / Fixed Group / Overwrite Report. Long poles: all four Test (shard N/4) 13:34–13:36, Type Check 13:27:31, Lint 13:27:12.

Carriers

needs:contract-review removed from PR #7503 (remaining documentation, data-adapter, tests) and from card #7323 (remaining bug, pm:dispatched, priority:p2, domain:ui). Both label sets were read immediately before writing rather than assumed — the label job had added labels since my last reading of a sibling PR, and writing a stale set would have clobbered them.

⚠️ Both readings are manual. scripts/pm/check-clause2-carriers.mjs exits 2 in this session (it needs its own GitHub token; this seat reaches GitHub only through MCP) and its own output says that is ⛔ not a clearance. A hand label-check covers strictly less than the tool — this shift produced four distinct carrier defects and the fallback is structurally blind to two of their shapes, now filed as objectstack#14965.

The finding this PR produced, filed separately

#7505 — a declared doc fragment is never compiled by check:doc-snippet-types, so a page can carry a "checked against the shipped dist" marker that no gate ever re-checks. The one known instance is fixed here; the class is not, and enumerating the rest is the first task on that card. ⛔ Filed unassigned and unlabelled for triage.


Generated by Claude Code

Merged via the queue into main with commit 6a99bb2Sep 3, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7323-adapter-return-type branch September 3, 2026 13:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data-adapterdocumentationImprovements or additions to documentationtests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns - #7503

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type
Sep 3, 2026
Merged

fix(data-objectstack): createObjectStackAdapter declares the adapter it returns#7503
os-project-manager merged 3 commits into
mainfrom
claude/issue-7323-adapter-return-type

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#7323

createObjectStackAdapter returned new ObjectStackAdapter(config) while declaring the shared DataSource interface as its return. A wider value is assignable to a narrower annotation, so nothing ever failed to compile — the loss was entirely on the reading side: every adapter-only member was erased from the type the factory hands back, while staying on the object it hands back.

Measured before the change, compiling against the shipped packages/data-objectstack/dist/index.d.ts with the doc-snippet gate's own compiler options (strict, bundler resolution): 9 x TS2339, not the 4 the card lists — getClient, getCacheStats, invalidateCache, clearCache, getConnectionState, isConnected, onConnectionStateChange, onBatchProgress, setSystemCapabilities. Eight of those are exactly the members the README's API Reference — Methods list documents; the ninth is the one the factory's own JSDoc links to ([ADR-0066] See ObjectStackAdapter.setSystemCapabilities). The same probe after this change: 0 diagnostics.

The two questions that decided the shape

The triage set these as a stop-condition and both are answered from the code, on origin/maine17605309.

Q1 — is ObjectStackAdapter exported from the package's public entry? Yes, already.packages/data-objectstack/src/index.ts:2148 reads export class ObjectStackAdapter, and tsup.config.ts has exactly one entry, src/index.ts. The class is in the shipped dist/index.d.ts export list (verified in the built artifact, not inferred). Two pin tests already assert the exported spelling in source — cloud-surface-retired-4152.pin.test.ts:114 and v3-deep-integration-retired-4241.pin.test.ts:135 — and apps/console/src/dataSource.ts:14 re-exports it by name. So this PR exports nothing by implication: the export list in dist/index.d.ts is unchanged, name for name, before and after.

Q2 — is the narrow return deliberate? No, and there are two pieces of evidence against it.

  1. Commit a5d817061 ("Add connection state monitoring, auto-reconnect, and batch progress") added autoReconnect / maxReconnectAttempts / reconnectDelay to the factory's own config bag while leaving the members that observe those features off the factory's declared return. The same change configured a feature it made unobservable through its own entry point. That is an oversight shape, not an encapsulation decision.
  2. The factory's JSDoc, added by 41b7ce3ce, points the reader at ObjectStackAdapter.setSystemCapabilities — a member its declared return hides.

Nothing anywhere pins the return: no comment, no ADR, no test, no Equal assertion, and no swappability note. Searched the export list, package.jsonfiles / exports, ADR-0066, the sibling adapters, and every test that names the factory.

Had either answer come back the other way this would have been a docs-only PR. They did not, so it is option A — and swappability, the property the narrow return was suspected of protecting, is not lost: a wider return is assignable to the narrower annotation, so const ds: DataSource = createObjectStackAdapter(...) still compiles and still gives the narrow surface. There is a control test that says exactly that.

Why A and not B or C

B (add the missing members to DataSource) would make every other DataSource implementation declare caching, connection state and batch progress it does not have — those are this adapter's concerns. C (document a cast) teaches a cast around a declaration that is merely narrower than the value, which is the opposite of declared = enforced. A is one line and makes declared match shipped for every documented member at once.

The pin

packages/data-objectstack/src/adapterFactoryReturn.types.test.ts — the card's TS2339 reproduction, inverted. It reads all nine members through the factory's return, asserts the return's identity with Equal (not mere assignability — two adapter-shaped declarations are mutually assignable, so only identity can tell "returns THE adapter" from "returns something adapter-shaped"), and carries two controls:

  • _NotOnDataSource — the adapter-only members stay ABSENT from the shared DataSource. This is what makes the reads a statement about the factory's return rather than about every data source, and it fires on option B.
  • _StillADataSource — the widened return is still assignable to DataSource.

Both controls are independent of the return annotation, so reverting the source change turns the reads red and leaves the controls green. That split is measured below, not asserted.

Which tree the pin exercises. Two, deliberately. The test is compiled by pnpm --filter @object-ui/data-objectstack type-check, whose program includes the whole src/** (tests included) and resolves ./index from src — proven with --listFiles: 1 hit for the new file, 55 test files, src/index.ts present. @object-ui/types in that same program resolves from packages/types/dist, so the type used for the controls is the shipped one. The README blocks are the dist half: check:doc-snippets compiles them against the built dist/*.d.ts (its own resolution control prints '@object-ui/types' was successfully resolved to packages/types/dist/index.d.ts).

Documentation

The README note naming this card (added by #5174 batch 8) is removed. The four sections built on the adapter-only members — Metadata Caching, Connection State Monitoring, Batch Operation Progress, Troubleshooting → Cache Issues — now continue from Basic Setup's createObjectStackAdapter(...) call instead of hand-declaring the class, so the page teaches one shape and the doc-snippet gate pins the fix against dist. The other README blocks still declare the class where they only need DataSource-level members; that is truthful (the class is public and documented) and rewriting them buys nothing, so they are left alone.

setSystemCapabilities is still absent from the README's Methods list. It was absent before this PR too, it is a separate doc gap, and adding it here would be scope this card did not ask for.

Verification — every number below was observed, at head 922ca54ec

CheckCommandResult
Package type-checkpnpm --filter @object-ui/data-objectstack type-checkexit 0, tsc --noEmit echoed
Package testspnpm exec vitest run --maxWorkers=2 packages/data-objectstack/ (repo root — a package-dir run is refused by objectui#3378's guard)55 files, 737 tests, all passed
check:doc-snippetsnode scripts/check-doc-snippet-types.mjsexit 0 — "Semantic phase: 455 of 455 block(s) judged, 0 failed." / "Every covered documentation snippet compiles against the built types."
check:readme-exportspnpm check:readme-exportsexit 0 — "OK (43 tracked README(s) ...; 414 self-imports judged (414 real, 0 wrong-path, 0 fabricated); 3305 export symbol(s) read from 37 of 40 tracked package(s) (0 unbuilt ...))"
changeset presencenode scripts/check-changeset-presence.mjsexit 0 — "1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)"
check:doc-fencesnode scripts/check-doc-fence-languages.mjsexit 0 — "every TypeScript block in 227 document(s) is fenced ts/tsx/typescript ..."
control bytesnode scripts/check-control-bytes.mjsexit 0 — "OK (scanned 6180 tracked text file(s); skipped 85 binary)"
Package lintpnpm --filter @object-ui/data-objectstack lintexit 0 — 423 problems, 0 errors, 423 warnings, all pre-existing no-explicit-any; the new file contributes 0 findings (eslint --format json on the two changed source files: new test 0/0)

Every gate result above is quoted from the gate's own verdict line; exit codes were captured by redirecting first and reading $? before any pipe.

Dependency closure built first (pnpm --filter '@object-ui/data-objectstack...' build, then the doc-snippet gate's own derived --build-filter: 26 packages, 34 turbo tasks, then --filter='./packages/*' for check:readme-exports, which had reported 2 unbuilt packages — a "could not run", not a defect).

Lint scope, declared: the table's lint row is the changed package only, not the repo. pnpm lint here is turbo run lint (each package's own eslint .), and CI runs it in full regardless. One reading that needs stating: a separate probe with eslint --no-inline-config reports 4 errors in src/index.ts at lines 1369–1389. Those are the documented eslint-disable-next-line no-console sites for the spec Logger binding (objectui#4029), they are ~4300 lines from either of my edits, and they are invisible to the lint CI actually runs because it honours inline config. Not introduced here, and not silently omitted either.

Reverse verification — red/green split, both directions proven on disk

Method: commit first, then mutate packages/data-objectstack/src/index.ts back to the narrow return under trap ... EXIT INT TERM with an absolute restore path, prove the mutation landed by counting the injected and removed text (not by the editor's exit code) and by comparing blob hashes, then restore and prove the restore by state.

Mutation landed: injected-text count 0 → 1, removed-text count 1 → 0; mutated blob 447fffc8 differs from HEAD blob bad31df4.

Leg 1 — the src pin, no rebuild needed (the pin imports ./index from src; the mutation cannot reach packages/types/dist, which is where its DataSource comes from):

  • 13 diagnostics total, every one of them inside the pin file, 0 anywhere else in the package.
  • 10 x TS2339 at lines 75–84: the nine member reads (invalidateCache twice, with and without an argument).
  • TS2344 at 89 and 113: the _HasHiddenMembers and _IsTheAdapter assertions.
  • TS7053 at 157: the runtime member sweep can no longer index the narrowed type.
  • The controls at lines 120 (_NotOnDataSource) and 135 (_StillADataSource) produced ZERO diagnostics — the split the pin was designed for.
  • vitest on the same mutated tree still passes, which is the point: esbuild erases types, so the colour comes from tsc and from nowhere else.

Leg 2 — the dist pin, rebuilt in both directions. Rebuilt from the mutated source, then a dist preflight: the widened marker count in dist/index.d.ts was 0 (the mutation reached dist). The standalone dist probe then reproduced the card's failure exactly — 9 x TS2339 — and check:doc-snippets went to exit 1, "455 of 455 block(s) judged, 4 failed": precisely the four README sections this PR rewired, failing on getCacheStats, invalidateCache x2, clearCache, onConnectionStateChange, getConnectionState, isConnected, onBatchProgress, clearCache, invalidateCache.

Restore, proven by state, both halves. Source: restored blob bad31df4 equals HEAD's blob and git diff HEAD is 0 bytes. dist: rebuilt again, widened marker back to 1 and narrow marker 0, dist probe back to 0 diagnostics, check:doc-snippets back to exit 0, 0 failed. The restore leg is rebuilt on purpose — a mutated marker left in dist would keep acting on every later run in this tree.

Clause ②

Clause-②: yes, declared in the claim comment on #7323 in the fixed machine spelling, and needs:contract-review is on both carriers (this PR and the card). It widens the declared public surface reachable through a published entry point. Q1's answer removes the triage's specific worry — no class is exported by implication — but the declaration limb is judged from content, not from paths or from diff size.

The one compatibility note a reviewer should weigh. The widened return is a class with private members, so it is no longer satisfied by a hand-written structural stand-in: an object literal assigned to the factory's ReturnType will now be rejected where it used to be accepted. Nothing in this repo does that (searched every test and app that names the factory — apps/console/src/dataSource.ts only re-exports it), and the fix for a downstream consumer is to annotate such a fake as DataSource, which is what it was standing in for. It is the only direction in which this change is not purely additive, and it belongs in the contract review rather than in a footnote.

Draft on purpose, not flipped ready, auto-merge not enabled — the seat lands it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…it returns
The factory returned `new ObjectStackAdapter(config)` while declaring
`DataSource<T>`. A wider value is assignable to a narrower annotation, so
nothing ever failed to compile — the loss was entirely on the reading side.
Measured against the shipped `dist/index.d.ts` with the doc-snippet gate's own
compiler options, nine reads through `ReturnType<typeof
createObjectStackAdapter>` failed with TS2339: getClient, getCacheStats,
invalidateCache, clearCache, getConnectionState, isConnected,
onConnectionStateChange, onBatchProgress and setSystemCapabilities. Eight are
exactly the members the package README's API Reference documents; the ninth is
the one the factory's own JSDoc links to. Same probe after this change: 0
diagnostics.
Option A of the card. B (widening `DataSource` itself) would make every other
data source declare caching, connection state and batch progress it does not
have; C (documenting a cast) teaches a cast around a declaration that is merely
narrower than the value. `ObjectStackAdapter` was already exported from the
package's only entry, so nothing is exported by implication, and no comment,
ADR or test pinned the narrow return — the commit that added autoReconnect /
maxReconnectAttempts / reconnectDelay to the factory's config bag left the
members observing those features off its declared return in the same change.
Callers are unaffected: `const ds: DataSource = createObjectStackAdapter(…)`
still compiles and still gives the narrow surface.
The README note saying the page could not yet teach the factory's shape is
removed, and the four sections built on the adapter-only members now continue
from Basic Setup's factory call. `adapterFactoryReturn.types.test.ts` pins the
card's TS2339 reproduction inverted, with a control for option B and a control
for swappability.
Part of #7323
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation data-adapter tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Clause-② in-seat contract review — PASS WITH REQUIRED AMENDMENTS. needs:contract-review stays on.

Commissioned by the domain:ui execution seat (PM session session_01EMrWaQw3XS5DxTHxp4yRyC) per the in-seat review route (维护者 2026-08-31 裁定). This seat runs opus and is not at tier, so it commissioned an isolated reviewer rather than self-reviewing. The reviewer was given only the card, its triage and the PR — ⛔ not the dispatch order and not this seat's conclusions.

Tier verification (维护者 2026-08-27 裁定): every harness-stamped model field in the transcript was read before adopting — 76 stamps, all claude-fable-5-1, no other value, and zero fallback/overload markers. CONTRACT_REVIEW_TIER is claude-fable-5 (objectstack/scripts/pm/dispatch-gates.mjs:8091); claude-fable-5-1 is at or above that floor. ⇒ Verified, so the verdict is adopted verbatim — ⛔ nothing below is rewritten, abridged or polished.


Contract review — objectui PR #7503 (head 922ca54ec, base e17605309)

Everything below was read from the diff, the card, triage 5524408927, and my own runs in a detached worktree at /home/user/objectui-review-7503 (dependency closure built; git diff HEAD 0 bytes at the end). No edits, commits, pushes, labels or GitHub posts.

① Derived judgments

Accept set

  • Config parameter type: byte-identical before/after. No change to what the factory accepts. Right.

Published surface

  • Declared return DataSource<T>ObjectStackAdapter<T>. Ships: built dist/index.d.ts ends the factory }): ObjectStackAdapter<T>;. Export list unchanged (the diff adds no export; the test file is outside tsup's single entry). Right.
  • The true delta is 20 members, not 9. tsc-computed Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>> is exactly: clearCache, connect, getCacheStats, getCached, getClient, getConnectionState, getDiscovery, getItems, invalidateCache, invalidateViewKeys, isConnected, listImportMappings, onBatchProgress, onConnectionStateChange, onSaveAdvisory, onWriteWarning, probeAppAccess, queryDataset, setSystemCapabilities, updateDashboard. Proven with Assert<Equal<Extra, Twenty>> silent, and negative controls red at 19 names, 21 names, and the PR's nine (TS2344 ×3) — on both the src program and the shipped dist/index.d.ts. All 20 are now the factory's promise.
  • Judgment on the other 11: right to publish, but under-described. None is @internal/@deprecated; stripInternal is not set anywhere, so they were already in the shipped class type; and the class type is already the monorepo's working adapter contract — packages/react/src/context/AppShellContext.tsx:8 types AdapterCtx as ObjectStackAdapter | null, and app-shell's AdapterProvider, MetadataProvider, MetadataService all take ObjectStackAdapter. Two are escape-hatch shaped and worth the seat's eye: getCached(key): unknown | undefined (raw cache read) and getDiscovery(): Promise<unknown | null> whose own JSDoc says "accesses an internal property of the ObjectStackClient"; invalidateViewKeys is a cache-key coupling seam. Not blocking — reachable via new ObjectStackAdapter() and every app-shell path today — but the changeset presents "nine" as the delta. → Amendment 2.

Assignability attack — no consumer regressed except the one the PR names

  • Probed at PR head, all compile: const a: DataSource = …; const b: DataSource<User> = createObjectStackAdapter({…}) (T infers through the class); explicit <User>; DataSource[] and DataSource<User>[] mixed arrays; DataSource | null union; take(ds: DataSource<User>) with and without explicit T; function-type assignment (cfg) => DataSource (return covariance); generic→concrete (cfg) => DataSource<User>; satisfies DataSource; callback contravariance withDs((ds: DataSource) => …) against a (ds: FactoryReturn) => void parameter; Record<string, DataSource>; Promise<DataSource>.
  • The only break: a structural stand-in for ReturnType<typeof createObjectStackAdapter> — TS2740, "missing … client, connected, connectPromise, metadataCache, and 64 more". Repo has no typeof createObjectStackAdapter anywhere; the sole vi.mock('@object-ui/data-objectstack') (packages/app-shell/src/chrome/ConditionalAuthWrapper.previewRetired-6654.test.tsx:72) mocks only getSharedDiscovery, untyped. Correctly named by the PR. Not a rejection.
  • Secondary, not new: a class-with-privates return is nominal across duplicate installs; every ObjectStackAdapter-typed seam in @object-ui/react/app-shell already carries that property.

Q1 — class already exported: verified.src/index.ts:2148 export class ObjectStackAdapter; tsup.config.ts single entry src/index.ts; name present in built dist/index.d.ts export block; apps/console/src/dataSource.ts:14 re-exports it. Nothing exported by implication.

Q2 — narrow return not deliberate: verified, with one miss.

  • No Equal/expectTypeOf/satisfies/ReturnType pin on the factory anywhere in packages/apps; no ADR names it. 780a1b993 (introduced ): DataSource) carries no design statement. a5d817061 added autoReconnect/maxReconnectAttempts/reconnectDelay to the factory config (diff +358–360) without touching the return; 41b7ce3ce (feat(data-objectstack,app-shell,i18n): view config is explicitly org-wide — permission-gated write path + console wording #6125) added the [ADR-0066] See {@link ObjectStackAdapter.setSystemCapabilities} JSDoc. Plus the AdapterCtx fact above — the React layer already treats the class as the contract, the opposite of a swappability design.
  • Miss: the PR's "no swappability note anywhere" overlooked content/docs/utilities/data-objectstack.mdx, untouched by the diff: L53 "createObjectStackAdapter returns a DataSource — the same universal interface every ObjectUI renderer consumes"; L109 "Factory returning a DataSource"; the signature fragment at L137 ending }): DataSource<T>; under a marker claiming it was "Checked against the shipped dist/index.d.ts … with the same type"; and L163–200, a whole "Adapter-only (hold the class type to reach these)" section built on the distinction this PR erases. Provenance 92c0b1f40 (docs(data-objectstack): document the real headless surface, not a phantom React API #4129, a docs batch that "verified snippets against the built packages") — it describes the shipped d.ts, it does not prescribe, so Q2's answer stands. But the page is now false about the shipped types and nothing gates it (the fragment is declared, so the doc-snippet gate never compiles the return). → Amendment 1.

README

Unmentioned diff content: the factory JSDoc gains 15 lines including "⛔ Do not narrow this back" — consistent with the change; nothing else hidden. Test file is exactly as described.

② Semver

  • Changeset: '@object-ui/data-objectstack': minor. Policy (scripts/check-changeset-no-major.mjs header): major reserved for the objectstack-synchronised bump; objectui's own breaks ship as minor with the break spelled out. The body spells out the stand-in break. I would assign minor. Matches. Changeset Bump Policy / Fixed Group / Declaration checks all green.
  • Wording nit folded into Amendment 2: the bold "Not a breaking change for callers." heading sits directly over the paragraph describing the break.

③ Boundary flags

  • (a) Q1 yes/already; Q2 not deliberate — both confirmed, with the mdx caveat above.
  • (b) 9 vs 4. The card measured 4 (its snippet) and named 8, saying the other four were "exactly the members" on the same README list; setSystemCapabilities was introduced by the triage, not the card. All nine do fail together — reproduced (mutation B: 10 × TS2339 on the nine reads, invalidateCache twice). So "one tier, not two" is right in substance, and the card was partial rather than wrong. But the implementer's own count is partial too: README-documented adapter-only members are 9 (connect()), and the hidden delta is 20.
  • (c) Lint narrowing hides nothing.eslint.config.js has no parserOptions.project/projectService — rules are syntactic and per-file, so a return-type change cannot alter lint output elsewhere. What the package-only run skipped (//#lint:root, other packages) CI's full Lint ran and passed.

Pin verification (not vacuous)

  • In the program:tsc --listFiles for the package lists src/adapterFactoryReturn.types.test.ts and src/index.ts; DataSource resolves to packages/types/dist/data.d.ts (project-reference redirect), as the implementer said.
  • Mutation B (narrow back to DataSource<T>): 13 diagnostics, all in the pin — lines 75–84 TS2339 ×10, 89 and 113 TS2344, 157 TS7053; 0 elsewhere; controls at 120/135/137: 0 hits. Reproduces the reported split exactly. My stand-in probe starts compiling under B, confirming that is the only direction that moved.
  • Mutation C (return Omit<ObjectStackAdapter<T>,'find'>, i.e. no longer a DataSource): line 135 _StillADataSource TS2344 red, line 137 TS2741 red, line 113 red, line 120 green. _StillADataSource genuinely discriminates; _NotOnDataSource reads keyof DataSource and is independent of the return, so it fires only on option B, as designed.
  • vitest from the repo root on the pin file: 1 file, 4 tests pass.

CI by name at 922ca54ec (32 runs, all completed)

  • success (29): Lint · Type Check · Build & E2E · Test (shard 1/4) · Test (shard 2/4) · Test (shard 3/4) · Test (shard 4/4) · Doc Snippet Type Check · README Export Check · Changeset Bump Policy · Changeset Declaration · Changeset Fixed Group Check · Changeset Overwrite Report · Doc Fence Language Check · Doc Component Type Check · Internal Docs Link Check · Docs Route Eager Closure Check · Build Docs · Bundle Analysis · Control Byte Scan · Shell Escape Residue Scan · Pre-Install Import Graph Check · Governed Surface Queue Guard · Inert vi.mock Specifier Check · Skill Eval Token Check · Skill Example Check · Skill Guide Path Check · Live E2E (informational) · label
  • skipped (3, conditional jobs): Test (coverage) · Test (coverage shard) · dependabot
  • failed: none.

Verdict

PASS WITH REQUIRED AMENDMENTS

  1. Bring content/docs/utilities/data-objectstack.mdx in line with the shipped types — four sites: L53 prose ("returns a DataSource"), L109 ("Factory returning a DataSource"), the fragment's }): DataSource<T>; at L137 together with its marker's "checked against dist … same type" claim, and the L163–200 "hold the class type to reach these" section, which now teaches a distinction the factory no longer has. This PR already carries the docs limb for the same package's README; the docs-site page must not contradict dist/index.d.ts after merge.
  2. Fix the changeset's description of the delta (.changeset/7323-adapter-factory-return.md, mirrored in the PR body and test header if desired): state that the factory's declared return is now the full class — every public member of ObjectStackAdapter, 20 beyond DataSource — rather than presenting the nine measured reads as the delta; correct "Eight of those are exactly the members … the README documents" to nine (connect()); and reword the "Not a breaking change for callers." heading so the CHANGELOG line does not read as "no break" above the stand-in caveat.

Both amendments are text-only and independent of each other; neither changes the code, which is right.


Seat disposition

Not landing.needs:contract-review stays on both carriers until both amendments are in and this seat verifies them by content. They are being dispatched now. The code is confirmed correct and ⛔ must not change.

Amendment 1 is the find that justifies the tier, and it names a gate hole.content/docs/utilities/data-objectstack.mdx carries a signature fragment ending }): DataSource<T>; under a marker asserting it was "checked against the shipped dist/index.d.ts … with the same type" — and after this merge that assertion is false. It survives because the fragment is declared, so check:doc-snippet-types never compiles the return. A page that claims to be verified against the built types, and is structurally exempt from the check that would verify it, is worse than an unmarked page: this seat, the implementer and the card all read "no swappability note anywhere" and none of us found it. I am recording that as a finding in its own right when the amendment lands.

⚠️ Also worth the record: the reviewer's count of the true delta — 20 members, not 9 — was reached with Assert<Equal<>> plus negative controls at 19, 21 and 9 names, on both the src program and the shipped d.ts. My dispatch asked it to enumerate what else became reachable; it did, and the answer is that the changeset understates the published surface by more than a factor of two. ⛔ That is a description defect, not a design one — the reviewer establishes the 11 extra members were already in the shipped class type, already reachable via new ObjectStackAdapter(), and already the monorepo's working adapter contract (AdapterCtx is typed as the class). So the fix is the changeset's wording, not the return type.


Generated by Claude Code

…idened factory return (objectui#7323)
Two text-only amendments from the in-seat contract review on the pull request.
No code changes: the return-type widening, the type-level pin and the README
rewiring are untouched.
Amendment 1 — content/docs/utilities/data-objectstack.mdx contradicted the
shipped types. The four sites the review named: the Quick Start prose saying the
factory "returns a `DataSource`"; the API Reference blurb "Factory returning a
`DataSource`"; the signature fragment ending `}): DataSource<T>;` together with
its marker's claim to have been checked against the shipped `dist/index.d.ts`;
and the `ObjectStackAdapter` section built on "hold the class type to reach
these", a distinction the factory no longer has. That section is rewritten rather
than deleted, with a "What changed" paragraph so a reader who followed the old
advice can see why constructing the class by hand is no longer needed.
Two further sites in the same file taught the same erased distinction and are
corrected with them: the Mutations paragraph ("hold the adapter at its class type
... before calling them") and Troubleshooting -> Authentication Errors
("Connection introspection lives on the adapter class ... so hold it at the class
type"); both examples now continue from the factory call, matching how the README
limb of this pull request was rewired. `onMutation` was also misfiled under
"Adapter-only" while being an optional member of `DataSource`; it moves in with
`bulk` and `batchTransaction`.
The fragment's marker no longer asserts an unqualified "checked against the
shipped d.ts". Because the block is declared, `check:doc-snippet-types` never
compiles it, so the marker now states that the agreement is hand-checked rather
than gate-enforced -- which is why the stale return type survived there. The gate
and the `declared` marker keyword are unchanged.
Amendment 2 — the changeset understated the published surface by more than 2x.
It presented the card's nine measured TS2339 reads as the delta; the factory's
declared return is now the class itself, so the delta is every public member of
`ObjectStackAdapter` -- 20 beyond `DataSource`, independently re-derived here as
`Exclude<keyof ObjectStackAdapter<unknown>, keyof DataSource<unknown>>` against
the built `dist/index.d.ts` (20 names, matching the review). The README-documents
sentence is corrected: eight of the nine measured reads are on that list, but the
list itself is nine adapter-only members -- `connect()` was documented all along
and simply was not measured. The "Not a breaking change for callers." heading is
reworded to lead with the one shape that does break, so the CHANGELOG line no
longer reads as "no break" above the caveat describing it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
…ui#7323)
Follow-up to the previous commit, on my own prose in the same amendment:
- Drop the "Until v17.7" version claim. The package is at 17.6.0 and the
changeset is a minor, but the released number is decided by the fixed group at
release time, so the page should not assert it.
- Remove a duplicated bullet list. `bulk` and `batchTransaction` were listed once
under the `DataSource` members and again under the optional-members paragraph.
`onMutation` now joins them in the single list, spelled `onMutation?` like its
neighbours, with the correction noted in the paragraph instead.
- Say "six bullets cover nine members" rather than "those nine", since the list
groups related members onto shared lines, and make the parenthetical naming the
other eleven members read as illustrative rather than exhaustive -- it named
four seams out of eleven.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3181.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRFIrfBN.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.19KB117.80KB
core (index.js)6.12KB2.42KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)242.42KB61.26KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.87KB19.72KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.82KB34.64KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.07KB64.12KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.33KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.84KB8.85KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.72KB21.04KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Both amendments verified — needs:contract-review cleared, ready + auto-merge

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance: 「PASS ⇒ 同席剥标、ready、auto-merge」 (维护者 2026-08-31 裁定). The tier review returned PASS WITH REQUIRED AMENDMENTS (5526083981); both are now in.

⚠️The amendment agent was killed mid-run by a transient HTTP 529 and its report is lost. It had already pushed. So everything below is this seat's own verification from the git objects, with no report to lean on — which is the right posture anyway, and it is the reason this comment carries readings rather than a summary.

Verified by content at 5140938cd

Two commits since the reviewed head 922ca54ec:

  • 27d18e179 — docs page + changeset
  • 5140938cd — a follow-up tightening of the rewritten section

Two files across the whole range, and ⭐ not one line of code:

.changeset/7323-adapter-factory-return.md | 53 +++++++++----
content/docs/utilities/data-objectstack.mdx | 99 ++++++++++++++++-------

⇒ The return-type widening, the type-level pin and the README rewiring are byte-identical to what was reviewed. The standing PASS therefore holds and no re-review is owed — the review's own words were that both amendments are "text-only and independent of each other; neither changes the code, which is right."

Amendment 1 — the docs page. Every flagged claim is gone: no returns a DataSource, no Factory returning a DataSource, no }): DataSource<T>; fragment. The "hold the class type to reach these" section was rewritten, not deleted — it now opens "What changed. This section used to tell you to hold the class type to…", which is what a reader who followed the old advice needs.

And the marker is gone entirely. The Checked against the shipped dist/index.d.ts … with the same type assertion was removed, not reworded around it. That matters more than the four corrected sites: that false claim of verification is why the page stayed wrong through three separate readers looking for exactly it.

Amendment 2 — the changeset. Now states the delta is the whole class — "20 members, not nine" — with the tsc-computed enumeration, corrects eight → nine (connect() is adapter-only), and records that the eleven past the documented nine were already in the shipped class type.

Checks — 32/32, read by name by this seat

29 success, 3 skipped by design (dependabot, Test (coverage), Test (coverage shard …)) on 5140938cd. The ones that actually cover this amendment: Doc Snippet Type Check 13:25:45 · Build Docs 13:23:57 · Internal Docs Link Check 13:21:53 · Doc Fence Language Check · README Export Check 13:25:31 · Changeset Declaration / Bump Policy / Fixed Group / Overwrite Report. Long poles: all four Test (shard N/4) 13:34–13:36, Type Check 13:27:31, Lint 13:27:12.

Carriers

needs:contract-review removed from PR #7503 (remaining documentation, data-adapter, tests) and from card #7323 (remaining bug, pm:dispatched, priority:p2, domain:ui). Both label sets were read immediately before writing rather than assumed — the label job had added labels since my last reading of a sibling PR, and writing a stale set would have clobbered them.

⚠️ Both readings are manual. scripts/pm/check-clause2-carriers.mjs exits 2 in this session (it needs its own GitHub token; this seat reaches GitHub only through MCP) and its own output says that is ⛔ not a clearance. A hand label-check covers strictly less than the tool — this shift produced four distinct carrier defects and the fallback is structurally blind to two of their shapes, now filed as objectstack#14965.

The finding this PR produced, filed separately

#7505 — a declared doc fragment is never compiled by check:doc-snippet-types, so a page can carry a "checked against the shipped dist" marker that no gate ever re-checks. The one known instance is fixed here; the class is not, and enumerating the rest is the first task on that card. ⛔ Filed unassigned and unlabelled for triage.


Generated by Claude Code

Merged via the queue into main with commit 6a99bb2Sep 3, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7323-adapter-return-type branch September 3, 2026 13:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data-adapterdocumentationImprovements or additions to documentationtests

Projects

None yet

2 participants

@os-project-manager@claude