Skip to content

refactor(react): AdapterCtx and useAdapter are typed by DataSource; react drops its data-objectstack dependency - #26292

Merged
objectstack-fleet[bot] merged 13 commits into
mainfrom
claude/project-thread-4u44e4
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 13 commits into
mainfrom
claude/project-thread-4u44e4

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread
Fixes #25917

@object-ui/react no longer depends on @object-ui/data-objectstack. AdapterCtx and useAdapter() are typed by the protocol-level DataSource from @object-ui/types, and a host that installs a concrete adapter declares it once on a new extension point, AdapterContextRegister (module augmentation, the same shape as TanStack Router's Register). The enable-block denial predicate moves to @object-ui/core, and the adapter re-exports it unchanged.

Clause-②: yes (narrowing)
Metadata-protocol: none

Premise, measured on origin/main 4f7e041

  • The card counted 28 components files reaching the concrete adapter. That count is stale: today there are 62 non-test readers of useAdapter / AdapterCtx (app-shell 41, console 14, components 4, plugin-designer 3).
  • 18 of them read ObjectStackAdapter members that DataSource does not declare: getClient (13 files), probeAppAccess, probeMetaItemAccess, getConnectionState / onConnectionStateChange, clearCache, queryDataset. They all sit in app-shell, console and plugin-designer, which already depend on the adapter.
  • isApiAccessDeniedError is also called inside data-objectstack, so core is its only single-source home: both react and data-objectstack depend on core.
  • The seat chose the route (register augmentation over 18 call-site guards) and amended the claim surface to cover core, data-objectstack and the host declarations.

What changed

  • @object-ui/react: AdapterCtx / useAdapter() carry ContextAdapter, which resolves to the adapter a host declares on AdapterContextRegister and otherwise to DataSource. @object-ui/data-objectstack moves to devDependencies, because one boundary test (error-message.normalisation-boundary.test.ts) still reads it. error-message.ts imports the predicate from @object-ui/core. The README documents the extension point.
  • @object-ui/core: new utils/api-access-denied.ts holds API_ACCESS_DENIED_CODES and isApiAccessDeniedError, moved verbatim with their docblock.
  • @object-ui/data-objectstack: re-exports both names from @object-ui/core, so its published face is unchanged, and its internal caller imports the predicate from core.
  • @object-ui/app-shell: adapter-context-register.ts declares ObjectStackAdapter, and the package entry imports it, so the declaration ships in dist/index.d.ts.
  • @object-ui/plugin-designer: the same declaration, imported by the two pages that call getClient, so the test type-check program sees it too.
  • sdui: core and react re-recorded. data-objectstack.json is removed by the generator ("no longer reached"), because the registry walk reached it only through react.

No call site of the 18 changed. Runtime behaviour is unchanged: the context holds the same object.

Verification (merged main 2409c9c, head 29b4eed, all heavy runs under os-verify-lock)

  • Build: turbo run build --filter=@object-ui/console, 71/71 tasks, which includes the Console build.
  • type-check: core, react, data-objectstack, components, plugin-designer, app-shell and console all exit 0. Console resolves @object-ui/react and @object-ui/app-shell from dist, and its seven files that call getClient through app-shell's useAdapter compile, so the published app-shell types carry the declaration.
  • Published-types probe (one-off, deleted afterwards): a file under ui/apps/console that imports type-only from @object-ui/app-shell and reads useAdapter()?.getClient() exits 0. The same file without the app-shell import exits 2 with TS2339, Property 'getClient' does not exist on type 'DataSource' (the compiler prints the type with its any argument).
  • New pin AppShellContext.adapterType.pin.test.ts (13 cases, same tsc harness as SchemaRendererContext.dataSourceType.pin.test.ts). Without a declaration, getClient and probeAppAccess are refused and any DataSource may be provided. With a declaration, the host adapter's own member is readable and a bare DataSource provider value is refused. CONTROL rows guard against a harness that has degraded to any.
  • Mutations through scripts/ablation-replace.mjs, each restored to the HEAD blob with an empty git diff HEAD:
    • Typing the context as any turns 2 cases red: the provider check and a CONTROL row.
    • Widening the DataSource fallback with getClient / probeAppAccess turns 3 cases red: both NARROWED rows and the provider row.
  • vitest over packages/react/, packages/core/, packages/data-objectstack/ and packages/plugin-designer/: 480 files and 7238 tests passed, 27 skipped. app-shell's suite is left to CI Test Core / Test UI because its runtime is untouched (type declaration only).
  • Gates: the 64 families dispatch-gates --commands derived were run and reconciled with --ran. 63 exit 0. check:dual-build-cjs-loads exits 3 (PREREQUISITE NOT MET: it reads the whole repo's built output), so it is NOT MEASURED locally and left to CI. check-adr-0087-registration verifies not-required (runtime-interface-only ...#ContextAdapter). check:new-line-citations: 0 new citations. eslint on the changed files: 0 errors.
  • Dogfood: owed once on the final head (Clause-② narrowing). It runs after the next main merge, and the os-dev report carries its reading.

Acceptance notes

  • The changeset carries a FROM → TO fix block, as the seat asked: declare the adapter, or narrow at the call site. runtime-interface-only inherits the prescription refusal, and the detector does not read this block as a prescription. The disposition stands on the compiler channel: a reader that needs the declaration gets TS2339 at its own call site, and no metadata surface moves.
  • plugin-designer needed the declaration imported by its pages, not only placed under src/. Its tsconfig.test.json compiles the pages through the test files, and that program never includes a file that nothing imports.

🤖 Generated with Claude Code

https://claude.ai/code/session_017sGTFTS9edirCTVBgQysK7


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 29b4eeda2f8e7645a4cf65fd6f97b6c062dbf3d9
Local-runs: none

① Derived judgments

  • @object-ui/react: AdapterCtx / useAdapter() go from ObjectStackAdapter | null to ContextAdapter | null. ContextAdapter resolves to DataSource, unless the new AdapterContextRegister interface is augmented with adapter. For a reader without the augmentation this is a narrowing, since 7+ ObjectStackAdapter-only members disappear. For a provider it is a widening, since any DataSource is accepted. Declared yes (narrowing), which is correct per clause2-line.mjs ("widens one surface and narrows another").
  • In-repo readers are type-identical. app-shell ships the augmentation in its published types (adapter-context-register.ts, imported from src/index.ts), and plugin-designer declares its own. The console probe compiles getClient with an app-shell import and gets TS2339 without one, which proves both sides. The new react pin has 13 cases, and two ablations each turned it red.
  • isApiAccessDeniedError and API_ACCESS_DENIED_CODES move unchanged to @object-ui/core. @object-ui/data-objectstack re-exports both, so its published face is byte-for-byte the same in names.
  • react's dependencies no longer carry @object-ui/data-objectstack (it moves to devDependencies for one boundary test), which is the card's purpose.

② Contract declarations

  • Clause-②: yes (narrowing) with a BREAKING banner, and FROM → TO in the changeset: Before, Now, and Fix (declare the augmentation, or narrow at the call site). The level is patch, as AGENTS §3 prescribes for a non-protocol breaking change.
  • ADR-0087 disposition: not-required (runtime-interface-only …AppShellContext.tsx#ContextAdapter). It is a TypeScript type of a React context, with no Zod schema, spec row or stored metadata behind it, so objectstack migrate meta has nothing to rewrite. The gate verified it.
  • Metadata-protocol: none. Correct, because no authored metadata changes.

③ Evidence and serialisation

  • CI at the head: 62 check runs, all success or skipped, including Check Changeset. Locally: 7 type-checks, 480 files / 7238 tests, and dogfood 297/300 files / 2527 tests, all exit 0. 63 of 64 gates exit 0; dual-build-cjs-loads was not measured locally and CI ran it.
  • sdui: core.json and react.json are re-recorded. data-objectstack.json is removed because the generator no longer reaches it, and sdui.manifest.json does not change, so the per-package rule applies. No other open PR shares the core, react or data-objectstack records.
  • Lock: chore(ui): declare vitest in every ui package whose test script calls it #26290 (vitest devDependencies, in the merge queue) also edits pnpm-lock.yaml and react/package.json. This PR merges main and re-locks after chore(ui): declare vitest in every ui package whose test script calls it #26290 lands, then the seat arms it.

Implemented-by: card thread session_017sGTFTS9edirCTVBgQysK7
Reviewed-by: session_017iXjUm1rujwDk7f3KT1Pkk (seat domain:ui#1)

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · seat domain:ui#1 · 2026-10-26T21:22Z

Head fd6301c8 (read from the PR object). It is a clean merge of main 6ea429df (with #26290) onto the head the seat's contract review passed (6426332356, on 29b4eeda). The PR's own diff is unchanged apart from the lock.

  • Shape. Draft on main, Fixes #25917. 18 files, +478/-61.
    • AdapterCtx and useAdapter are typed by DataSource, and @object-ui/react drops its @object-ui/data-objectstack dependency.
    • isApiAccessDeniedError moves to core, and the adapter context is registered through AdapterContextRegister augmentation.
    • Root changeset. Clause-②: yes (narrowing) (BREAKING, contract review PASS), Metadata-protocol: none.
  • Checks pinned to the head. All check runs completed: 0 failing, 0 pending. The seat's landing script checked this before posting.
  • Governed surface. check-governed-merges.mjs --pr 26292: not governed.
  • Evidence (dev, on fd6301c8, all under the lock).
    • pnpm install --frozen-lockfile passes on the merged lock with no re-lock: react/package.json keeps vitest from chore(ui): declare vitest in every ui package whose test script calls it #26290 and lists data-objectstack only under devDependencies.
    • Builds: Console 71/71.
    • Type-check: react, core, plugin-designer, app-shell and console all exit 0.
    • vitest over react, core, data-objectstack and plugin-designer: 480 files, 7241 tests.
    • dispatch-gates: 64/64 exit 0, 0 NOT-MEASURED.
    • Final-head dogfood: closure 67/67, then 297 files passed and 3 skipped; 2527 tests passed and 18 skipped. VERDICT command-exit 0.
  • sdui. core.json and react.json are re-recorded, and data-objectstack.json is removed (the package left the closure with the dependency). Regenerating on this head moves 0 of 26 records, and sdui.manifest.json is unchanged.
  • Serial constraints. Two open drafts share records with this PR, and neither is queued: feat(types,react,sdui-parser)!: retire the node-level visibleOn / hiddenOn / disabledOn spellings #26274 (domain:spec#2, core.json and react.json) and feat(plugin-map): a map view with no style draws the deployment's default map style before demotiles #26326 (seat 2, react.json). They merge main and re-record after this lands. No other open PR touches pnpm-lock.yaml. The ⛔ paths are untouched.

Landing: ready + auto-merge through the relay.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 26, 2026 21:23
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 26, 2026 21:23
…-4u44e4

# Conflicts:
#	scripts/sdui-manifest.sources/core.json
#	scripts/sdui-manifest.sources/react.json
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit ef025fe Oct 26, 2026
62 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-4u44e4 branch October 26, 2026 22:54
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · re-ACCEPT · seat domain:ui#1 · 2026-10-26T22:55Z

Head 56b7aa20 (read from the PR object). The ACCEPT on fd6301c8 (6427643854) stands for the PR's own diff. This head only merges main (b672cf45), which brings in #26282's move of the core and react records. The records were regenerated under the lock in their own commit, 56b7aa20, not hand-merged.

Landing: auto-merge re-armed through the relay.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants