Repository navigation
Provider discovery and routing #7
Description
Activity
- addedwayfinder:grillingDecision requiring discussion with the project ownerDecision requiring discussion with the project owner
on Sep 22, 2026 - added a parent issue
on Sep 22, 2026 Native discovery and routing evidence
Separate local commit
e8673e5addsdocs/research/provider-routing.md. Download the committed source and report through 57f3381. The archive SHA-256 is83a7e3788718bb15b3a1f97f98d341c1da41e800033f3673ba199a0df46818d6. These commits are not pushed.The report maps the accepted provider/capability/target model to actual Devframe, hub, kit, browser-port and optional CDB APIs. It separates source inspection from executed evidence and identifies publication-parity limits. The important Devframe/kit findings were checked in released 1.0.0/0.7.5 artifacts whose runtime bytes match the original packages.
Two findings affect the experiment:
- Devframe checks stored connection state before fetching a supplied
baseURL. Two clients created with different URLs in the same document can therefore resolve the same endpoint. Explicit connection descriptors are the public candidate for isolation, but endpoint and credential independence still need a real two-provider test. - The native service catalog advertises npm package versions and initially reads empty before first synchronization. It cannot directly serve as the SDK's exact numeric capability catalog or distinguish unknown discovery from confirmed absence.
The bounded experiment proposal uses a genuine server provider and an extension background provider with separate counters and execution receipts, then a same-document two-server subcase. It exercises selection, disconnect/navigation races, broadcast outcomes, replacement incarnations and cleanup. The optional CDB profile uses its real connection ownership. No such routing experiment has yet run.
The initial owner questions are now pending in the active discussion:
- Stable host-configured provider ID plus a new incarnation on backend restart, or a new provider identity each time?
- Host-composed shared registry, with explicitly registered discovery sources and clients consuming snapshots, or independent discovery in each client?
The report lists subsequent decisions and their dependencies: catalog readiness, directive/default precedence and ambiguity, pre-dispatch waiting/races, broadcast typing/cancellation, routing-default authoring, and target/trust mapping. It does not reopen settled no-reroute/no-replay-after-dispatch or provider-pinned-binding rules. The old issue pseudocode remains illustrative; the accepted initial realms are
devserverandwebext, and declarations use the core factories rather than the olddefineContributionsketch.The note passes Oxfmt and its local links were checked. Issue 7 stays open until the owner decisions, declarations and required experiment inventory are resolved.
- Devframe checks stored connection state before fetching a supplied
Executed endpoint and authentication isolation evidence
Separate local commit
f029dc8preserves the final browser/server fixture, exact failed and successful attempts, source hashes, validation and the updated routing report. Download the committed workspace and evidence. Archive SHA-256:0030c7abe8151f60bea4b366e6a75de3f1db915bb9731c2e0a5d3bf2fdfee1c1. The branch remains local and unpushed.The bounded two-hub subcase now has run. It uses released Devframe 1.0.0, hub 1.0.0 and kit 0.7.5 runtime bytes, Vite 8.3.0's real preview HTTP server, distinct authentication handlers/storage/counters, and actual browser RPC/state clients. One client uses kit's published wrapper. This does not constitute a genuine DevTools server/UI, an extension provider, or the complete server-plus-extension routing experiment.
Test Observed result Connect A, then request B using a different baseURLSecond client inherits A metadata; its action increments A, while B remains unchanged. Explicit connection descriptors and distinct static tokens Calls and shared state reach their intended A/B endpoints. A exchanges a real freshly requested authorization code B's public connection descriptor changes to A's issued token through the shared auth channel. Recreate B from that descriptor B is unauthorized. The protected action rejects; its Node handler count stays exactly 2 before/after. Recreate B with its original explicit credential B authenticates again. Close A, then call B A rejects subsequent calls; B remains connected and increments to 15. The existing trusted B connection continued working after its descriptor changed. That concealed the reconnect failure, which is why two initially successful clients were insufficient proof. The released options
simpleAuth: falseandotpParam: falsedo not disable the shared authorization channel. Explicit initial descriptors therefore do not satisfy full multi-provider authentication isolation.docs/probes/provider-connection-isolation/README.mdgives the topology, exact source basis, reproduction and limits.baseline/result.jsonrecords browser observations plus independent Node receipts. Itspassed: truemeans the defect and controls reproduced as specified, not that isolation passed. The final run completed at2026-09-23T02:47:09.391Z; both hubs and preview closed, the runner exited 0, and a subsequent endpoint request was refused. The actual in-app browser reported Chrome 152.0.0.0; no current-stable Chromium/Firefox matrix claim is made.The final source passes strict TypeScript 7 with declaration checking, type-aware Oxlint with warnings denied, and Oxfmt. Earlier snapshots preserve an actual fixture guard error and its correction. Generated credentials, one-time codes, storage, dependencies and built assets are excluded. All retained file hashes were verified.
A separate bounded upstream isolation proposal is being tested under issue 6. No upstream PR, SDK dependency patch, routing policy or provider identity choice is adopted by this experiment. Separate origins/processes, revocation, process restart, genuine DevTools/extension hosts and the full routing matrix remain outstanding. The pending owner choices about identity continuity and discovery ownership are unchanged.
Owner-confirmed identity and discovery ownership
The owner agreed to these boundaries in the live review on 2026-09-23. Separate ticket commit
f66311erecords them inARCHITECTURE.md,GLOSSARY.md,docs/planning/007-provider-routing.mdand the routing research note.Concern Accepted behavior Logical provider identity Stable ID configured by the embedding host. Backend lifetime Mandatory opaque incarnation, created once when that backend is created and changed when it is recreated. UI HMR or transport reconnect Retain the incarnation if the same backend survives. Existing binding Remains owned by its original backend; equal logical IDs never redirect it to a successor. Discovery owner The client application's composition root composes supported native connection adapters and supplies the registry to its consumers. Several UI documents An extension background runtime may coordinate metadata through existing ports; document-only native hooks remain local. No global daemon or cross-origin singleton is introduced. Identity is metadata, not authentication. Discovery does not merge provider state or authorize replay. Domain/origin selection remains embedding-application policy.
Local incarnation implementation and native server installers can proceed on these settled boundaries. Three routing choices are now pending in the live review:
- Per-call route replaces contribution default; exact provider before ordered realm preference; equally eligible providers remain ambiguous.
- Before dispatch, only explicitly allowed fallback may reconsider a vanished candidate; ordinary calls fail unavailable rather than wait indefinitely. After dispatch the accepted rule remains no fallback or replay.
- Separate capability/action broadcast methods return per-provider settled outcomes while ordinary invocation keeps returning its value.
Those are recommendations awaiting owner answers, not adopted router behavior. Catalog readiness, routing authoring and target/trust/state integration still need their corresponding contract details. This partial agreement does not close issue 7 or claim a real server-plus-extension router exists.
Provider incarnation and local ownership implemented
Ticket-7 commit cd17d22 makes
ProviderDescriptor.incarnationmandatory. The runtime validates it and freezes identity snapshots before setup and asynchronous call validation. A stable logical ID no longer implies that a recreated backend can inherit an old binding. 51eac35 records the evidence in the routing deliverable.Eight focused tests cover invalid incarnations, identity mutation, contribution reactivation, asynchronous validation and same-ID successors. A binding keeps invoking its original backend; after disposal it rejects instead of switching. Core has 53 tests and 28 negative type fixtures; runtime has 63 tests. The affected packages pass strict TS7, type-aware Oxlint, Oxfmt and builds. Parent verification repeated the identity tests and both existing packed-consumer gates, including strict Bundler/NodeNext declarations and browser dependency boundaries.
@devkit/servernow mints a UUID per installation lifetime. This remains local ownership evidence, not a remote routing implementation or a client reconnect proof.Accepted decisions are recorded in
ARCHITECTURE.md,GLOSSARY.mdanddocs/planning/007-provider-routing.md: host-configured stable IDs, per-backend incarnations, and discovery owned by the client application's composition root. Three selection decisions remain pending with the owner:- Per-call selector replaces the contribution default; exact provider before ordered realm preference; equal candidates are ambiguous.
- Candidate disappearance before dispatch permits only explicitly configured fallback; ordinary calls fail promptly rather than waiting indefinitely.
- Separate capability/action broadcast methods return per-provider settled outcomes while ordinary invoke keeps its value result.
These recommendations are not silently implemented. The accepted rule prohibiting rerouting/replay after dispatch remains unchanged. Registry attachment, catalog/availability semantics, remote transport, target/trust filtering and server-plus-extension proof remain open obligations.
Source is published on main, with the current checkpoint at 0fbaf3e. The history is linear and commits remain separated by ticket. Direct commit references replace source-archive delivery. Full repository CI passed builds, strict lint, type checks, formatting, packed-consumer checks and tests. The complete supported-host conformance matrix remains open.
- added a commit that references this issue
on Sep 24, 2026 Accepted answers and compact API investigation
Source commit: 1cc2d7f.
The owner confirmed ambiguity errors requiring an explicit UI/agent/caller discriminant, availability at dispatch without implicit connection waiting, no reroute/replay after dispatch, separate capability/action broadcast methods with per-provider outcomes, and a compact request API. The previous core review had already settled per-call replacement of lower defaults and no inherited fallback for exact pins. The planning record now corrects its earlier claim that these were open.
The owner's contribution-local
routingproposal remains under review. One property can express an explicit realm, provider, ordered fallback or callback. The proposed object values distinguish the two namespaces without runtime guessing:routing: { realm: 'devserver' } routing: { provider: 'project:frontend' } routing: [{ realm: 'devserver' }, { provider: 'browser:local' }] routing: (context) => selectRoute(context)
These are alternative proposed values, not implemented API. The remaining placement decision is how the client receives a default before choosing a backend. A public action descriptor can carry it; a backend-only handler cannot serialize an executable callback into a new client. See the reviewable declaration sketch.
A strict TS7 request-object probe passes four positive assignments and eight negative assertions for operation/input/output inference, required targets and broadcast result values. Core lint/type checks and the probe type check pass. This is compile-only evidence, not a router implementation or dynamic operation-union proof.
Connection-isolation constraint
Fresh dependency verification confirms the opt-in patch applies with zero fuzz over the locked declaration-repaired Devframe 1.0.0 package. Eight tests, strict TS7 and type-aware Oxlint pass. The current upstream source still has shared connection/token caching and the unconditional authentication channel.
Kit/hub wrappers can forward the flag or accept SDK-owned RPC clients. Published native UI assets contain an inlined client, so changing the installed Devframe package does not rewrite those bundles. Root pnpm patches also do not propagate to downstream SDK consumers. The proposed temporary patch is suitable for SDK-owned connections in this workspace; publication still needs an explicit upstream/fork/consumer-patch solution.
No workspace runtime patch was adopted. The fresh browser replay could not start because the in-app browser was unavailable. Its failed result and completed hub/preview cleanup are preserved; historical browser passes retain their original dates.
Remaining gates
- Owner review of selector syntax and client-visible default placement.
- Owner choice on carrying the narrow isolation patch while upstream integration is pursued.
- Registry/callback/broadcast edge contracts and maintained implementation/conformance tests.
Issue remains open. No upstream PR has been opened.
- added a commit that references this issue
on Sep 24, 2026 The owner accepted public-action routing defaults and proposed a combined selector with a required realm and optional provider. Provider-only selection and symbol portability remain questions. f599053 records this clarification and a strict TS7 single-object declaration probe. Four negative type assertions pass for action input, declared dependencies and service operation input. Core scoped lint/type checks pass. Runtime helpers and both proposed dependency patches remain unchanged.
A provider-only string would require registry-wide uniqueness; a realm/provider pair still needs within-realm collision detection. Symbols cannot directly identify a provider across JSON/structured-clone transports. Descriptor-backed stable IDs or explicitly mapped local aliases need owner review.
Contribution kind-specific helpers and generic tagged helpers are both possible; dedicated plugin arrays do not settle factory naming. The second positional argument is an authoring choice, not a lifecycle requirement. The tested single-object shape places contract/capability beside execution, requirements and handler/setup. Issue remains open.
- added a commit that references this issue
on Sep 24, 2026 40b46e8 records the owner-confirmed selector shape: realm is required and provider is optional.
ID normalization and helper naming remain under review. Named symbols can normalize to strings, but then their descriptions determine identity rather than symbol uniqueness. Numbers introduce the same coercion-equivalence issue. The recommendation is string IDs, caller-owned naming and SDK conflict detection, without another normalization utility.
The revised helper naming proposal is defineActionContract for the existing client-safe descriptor and defineAction for the existing handler definition, parallel to defineCapability/defineService. This adds no runtime entity and has not been implemented or recorded as accepted. The single-object direction is accepted.
The registry/router remains an in-process client SDK component composed by the consuming application. It is not a mandatory dev server or daemon. Existing brokers can be optional adapter inputs. Backend-owned authentication already exists; the isolation proposal addresses client-side cache/channel sharing when one viewer connects directly to independent backends. No runtime patch is approved or installed.
13 remaining items
- added a commit that references this issue
on Sep 28, 2026 Native extension integration checkpoint, 2026-09-28
The owner selected native API extraction. The committed Chromium proof uses the candidate public native shared-state factories, JSON view publisher and existing bundled renderer over actual
runtime.Portconnections. Reproduction and recorded evidence cover two peers, independent caller identity across asynchronous handlers, native state writes, Map/BigInt round trips, function rejection, pending-call disconnect, retained worker state without action replay, denied sender admission and cleanup during failed mounting. The recorded run has zero page errors.The upstream candidate is split into reviewable commits on the personal fork: RPC/state and renderer/view. It reuses native implementations, retains default renderer context types and adds no transport protocol or state policy. Scoped upstream lint, four-package type checks, 43 focused tests and browser artifact checks pass. The proof passes strict SDK Oxlint and TypeScript 7 checks.
The working SDK dependencies remain unchanged. A new draft upstream PR is awaiting owner approval. Next implementation work is a compatible downstream backport followed by the portable WebExtension provider/catalog/router binding. Full browser surfaces, content/page sender bridging, debugger integration, worker suspension, Firefox conformance and extension HMR remain open; this proof does not complete those gates.
SDK main also includes the separate renderer evidence/map update. Full-repository CI passed all gates; no full-workspace command was run locally.
Devframe draft PR #410 is open following owner approval and a pre-publication code review. It remains a draft.
The review corrected the Knip export entry and renderer peer declaration, retained the existing diagnostic export, added coverage through the native Node RPC resolver, and documented reuse of one publishing context for view discovery. The RPC and renderer corrections are separate commits. No new runtime abstraction was added.
Validation passed: scoped lint and Knip, four-package type checks, affected builds, 44 focused tests, 140 API snapshot checks, and a rebuilt Chromium Port proof with all 12 checks and zero page errors. The SDK checkpoint f037ce4 has passing CI.
The first full upstream run passed lint/type checking, Bun/Deno and 1,602 tests. Its only unit failures were two stale diagnostic snapshots, also present in CI for the exact upstream base. They are regenerated in the separate snapshot-only commit, and full CI is rerunning. Vercel preview requires upstream maintainer authorization.
The SDK's dependency baseline and completion gates remain unchanged. A compatible backport and maintained WebExtension provider binding are still required.
- added a commit that references this issue
on Sep 28, 2026 Implemented the first maintained WebExtension transport slice in 3fb91fd.
@devkit/webext.createPortChannel({ port, onDisconnect })returns Devframe's existing native channel type. Chrome/Firefox Port members are accepted structurally, with no global browser lookup or polyfill. The channel uses Devframe's records serializer, attaches native message/disconnect listeners and removes both through nativeoff. The owner supplies sender admission and closes the native RPC connection on disconnect. Native RPC keeps request correlation, errors and pending-call behavior; the adapter adds no protocol, retry, routing or state policy.const connection = createRpcClient(localFunctions, { channel: createPortChannel({ port, onDisconnect: () => connection.$close(), }), });
The extension example now imports the built package instead of a local channel copy. Its live Chromium rerun passed all 12 scenarios with zero page errors: two actual Ports, distinct asynchronous caller identities, renderer actions, native writes, Map/BigInt, unsupported functions, pending-call rejection, unmount, completion without replay, retained state after reconnect, rejected sender/failed mount cleanup and worker-initiated disconnect. Two focused tests also exercise real MessageChannels under JSON and clone delivery, including empty listener sets after native close. Strict TS7, type-aware Oxlint, formatting and the package build pass.
The upstream feature is now split into narrow drafts: RPC/state #410 at
fb8cf6a6, JSON renderer/view #411 atf6c36c33, and independent diagnostic snapshot repair #412. The renderer shim and implementation file moves were removed. The live proof combines the two feature commits.This completes the maintained Port-channel binding, not this issue's full DoD. The working SDK still uses Devframe 1.0.0. A compatible native shared-state/renderer backport and portable provider/catalog/router composition remain next. The current 1.0 RPC registry build also pulls in
node:crypto; the browser-safe upstream build change must be included when backporting that entry. Content/page authority, full surfaces, worker suspension, Firefox browser tests, debugger and HMR remain open.- added a commit that references this issue
on Sep 28, 2026 - added 4 commits that reference this issue
on Sep 29, 2026 Current implementation checkpoint, 2026-09-29
Delivered on linear main in separate commits:
Commit Change d4d2115 Extract existing native provider/catalog integration into browser-safe @devkit/devframe; compose the same contracts over server RPC and actual extension Ports.f217321 Preserve server-owned public declaration names for consumers. fe349b3 One extension page connects to real Devframe and DevTools backends plus its worker; verify native origin/auth denial, realm broadcast, explicit preference and pre-dispatch fallback. d58a0d4 Adopt a selected page's natively published connection with an isolated native client; verify absence, malformed envelope, closed source, scripting denial and independent connection lifetime. The existing server factories delegate to the shared adapter and retain their API. A Port supplies real native call/collector/events members; no full server context is fabricated. Native RPC/auth/codec/state remain upstream-owned. The registry still owns only portable provider metadata, exact contract availability and selection. No credential store, retry, operation replay, new auth callback or discovery daemon was added.
// Shared provider composition; existing server factories supply their real native context. const provider = await createRpcProvider({ context: { rpc, realm, execution, native }, providerId, services, plugins, expose: { actions, capabilities }, }); const connection = await createRpcProviderConnection({ rpc: nativeClient, realm, providerId }); client.providers.attach({ connection });
The application still owns endpoint/domain selection. Explicit configuration and one-shot selected-document handoff are demonstrated. The latter reads the public native descriptor in the selected top-level MAIN world and grants the source page no extension RPC authority. It checks the native envelope only, not a duplicate full metadata schema or endpoint identity. Native server
allowedOriginsand native RPC authentication remain separate. StandardinitHubsupplies no viewer-origin token by default; its explicit native allowlist is used.Validation: 49 server tests, eight channel/provider tests and two shared-adapter tests passed for the extraction. Affected strict TypeScript 7, type-aware Oxlint, Oxfmt and builds pass. The maintained example now passes 24 actual Chromium scenarios with zero page errors, using installed workspace packages and native backend hosts. Commands, API ownership, screenshot and receipt. Full CI at d58a0d4 is green, including the real browser test.
Existing exact-version pnpm patches carry reviewed native isolation/RPC/state/renderer exports while upstream drafts remain open. These slices needed no new upstream patch. The feature drafts remain RPC/state and JSON view/renderer; snapshot repair is independent.
Remaining gates are real Firefox conformance, complete extension surfaces, automatic discovery if an embedding application needs it, privileged page request authority, and JSON-authored cross-provider controls. The last item belongs to Renderer and surface contract: current JSON actions call their owning native backend, while the proven routing controls use HTML. A native handler-hook versus RPC-call-adapter proposal is under owner review. Do not close this issue as full supported-host conformance.
- added a commit that references this issue
on Oct 1, 2026 Resolved: provider discovery ownership and routing contract
The owner has accepted and the maintained packages implement the routing contract. Current main 52bc770 passed full CI, including real mixed native providers, both extension browsers, JSON-authored cross-provider actions and the combined API-evidence gate. The previous open status was carrying validation work from other tickets after the policy was settled.
Accepted contract
const client = createClient({ connections: [devframeConnection, devtoolsConnection, extensionConnection], routing: [{ realm: 'devserver', provider: 'frontend' }, { realm: 'webext' }], }); await client.actions.invoke({ action, input }); await client.actions.broadcast({ action, input, selection: [{ realm: 'devserver' }, { realm: 'webext' }], });
The application owns endpoint discovery and attaches native connections. Explicit origins and one-shot adoption of a selected page's native connection are demonstrated. The client registry/router is in memory and owned by that composition. Each native connection owns its authentication, codec and close behavior. Optional CDB discovery belongs to that capability's integration; it is not a generic routing prerequisite.
Identity is
(realm, provider ID, incarnation), with realm-scoped non-empty string IDs. Selectors require a realm and optionally a provider. Duplicate attachment is rejected. Catalog absence is distinct from unsynchronized discovery. Per-call policy replaces an action default, which replaces a client default. Ordered fallback considers current eligibility before dispatch; ambiguous candidates require a discriminant. Once dispatched, a call is never rerouted or replayed.Callback candidates retain their original attachment owners. Cancellation prevents late dispatch; a replaced selected owner produces
stale-selection. Capability bindings remain pinned. Broadcast has an explicit non-empty recipient union, deduplicates attachments and preflights every selector before any side effect. An unmatched selector rejects the whole broadcast; known unavailable recipients instead yield individual rejected outcomes alongside successful siblings.Client selection chooses realm/provider recipients. Selected action/service implementations inspect schema-defined resource/domain input and return their own result.
not-applicableis a fulfilled result and does not trigger fallback. No resource-specific routing, target registry or generic event bus is added to the transport.Evidence and complete handoff
The client API, canonical architecture and routing record describe the settled policies. The client suite covers defaults, ambiguity, callbacks, stale attachment/binding, cancellation, broadcast preflight and ownership. Real local hub/kit fixtures and actual authenticated native sockets preserve those contracts. Installed public exports compose the same contracts across native WebSockets and real extension Ports.
The maintained extension example verifies explicit preference, realm/all-provider broadcast, native denied origin/credentials, service availability and disconnect outcomes on Chromium and Firefox. Its unchanged native JSON renderer dispatches the existing action contracts; implementation-side applicability and per-provider outcomes are exercised in
test:json-actions. The separate native views remain owned by their backend connections.Privileged page/content authority remains with Permissions and trust. Surface/renderer lifecycle remains with Renderer and surface contract. Examples and API coverage contract retains missing real-browser unmatched-selector/callback race cells and the complete host/mode matrix, while Release and conformance contract owns the final completion gate. Automatic monitoring is an embedding integration, not an unresolved routing policy or promised generic daemon.
The decision is complete; its remaining validation obligations stay visible in those owners and the executable inventory. Closing it removes the stale native dependency on the renderer ticket without claiming complete framework conformance.
Contract resolved, 2026-10-02
The final resolution records the accepted identity/discovery ownership, routing precedence, callback/broadcast/cancellation behavior, implemented native examples and follow-up owners. The policy is settled and implemented across server/extension connections. Remaining authority, surface and coverage work stays in its owning tickets. Earlier dated checkpoints are historical, including then-missing Firefox and JSON action integration.
Native capability broadcast, 2026-10-01
Implementation 45fffe3ff13f230c98fda8dc6f5c8378c4f7139e adds a working capability-broadcast diagnostic to the extension example. It calls the existing client API against the extension, Devframe and DevTools providers already attached to the page. It shares the existing recipient selector and result display; no SDK API, action wrapper, backend RPC method, serializer or transport changed.
Both maintained browser suites read distinct backend values, assert complete provider identities and attachment order, select each realm or an explicit server, disable/reenable the extension service through its JSON controls, and close a server. Known unavailable recipients return rejected outcomes while the selected surviving providers still fulfill. Read calls preserve the existing mutation counts; recovery reads the original values.
Scoped strict lint, TypeScript, formatting, both extension production builds and three example tests pass. Fresh Chromium 153.0.8010.12 passed 47 scenarios with zero page errors; Firefox 157.0 passed 51 scenarios and retains its explicit lack of global page-error capture. The committed receipts and screenshots come from those successful terminal runs. The client unit suite also passed all 31 tests. Unchanged development scenarios were not rerun locally; full CI owns that broader check.
The new control is a host diagnostic. This does not introduce a second JSON action API or complete the broader JSON management interface. Automatic discovery, unmatched-selector browser details, cancellation/lifecycle races and full host/mode conformance remain open. Full CI passed for the final pushed head, including workspace validation, native Chromium/Firefox production tests, concurrent development transitions and the combined API evidence gate.
Reconciled status, 2026-09-30
Portable catalog/router composition over real extension Ports is delivered in d4d2115, mixed native backends in fe349b3, selected-page handoff in d58a0d4, and native JSON action routing in a99cb87. 1ef4f90 records Firefox and HMR regression evidence.
The handler/RPC seam is settled and implemented. Reconnecting to a surviving backend preserves its incarnation; replacing that backend changes it. Selectors are strings, with realm required and provider optional. CDB discovery is optional integration work, not a prerequisite for generic routing.
Native server view-index composition now has Chromium and Firefox acceptance in ef43f0f. Automatic discovery, untrusted-page authority, the remaining view/browser matrix and full capability/host conformance remain open.
Earlier checkpoints below are dated evidence. Their then-pending items are superseded by this status and later accepted resolutions; they do not reopen settled architecture choices.
Latest implementation checkpoint, 2026-09-29
The native integration is now installed in the workspace, with no upstream checkout required. Port binding
3fb91fd, RPC/state backport5ebcb5a, renderer/view backport631db2b, and maintained example86b24b3are separate ticket commits on main.examples/webext consumes built public exports from the pinned, patched 1.0 dependency graph. Its real Chromium test passes 12 scenarios with zero page errors. The same test now runs in full SDK CI, which passes. The old temporary prototype is replaced by this maintained example.
The upstream review is split into draft RPC/state #410, JSON view/renderer #411, and independent baseline snapshot repair #412. The renderer declaration shim and implementation file moves are removed.
createPortChannel({ port, onDisconnect })reuses native framing, serializer and close behavior. Native state tests prove per-peer subscriptions, writes and disconnect isolation. All 48 existing server tests pass. Portable WebExtension provider/catalog/router composition remains the next step. Full surfaces, content/page authority, debugger, worker suspension, Firefox browser conformance and HMR remain open; this issue is not complete.Accepted two-layer selection amendment, 2026-09-27
The owner chose typed command/results with native state observation, and implementation-owned applicability. Contract migration and authenticated native proof are on main. The architecture diagram and API examples supersede the earlier universal target contract and A1/A2/B mapping questions.
Client selectors choose realm/provider recipients. Selected action/service implementations inspect schema-defined domain/resource input and return their own declared result, such as
appliedornot-applicable. There is no SDK target envelope, target resolver,acceptshook or new topic/event bus. Anot-applicableresult is fulfilled; it neither makes the provider unavailable nor triggers fallback/replay. If multiple selected implementations match, all may act.Provider incarnation, contribution activation generation, exact versions, native authorization/serialization and dispatch ownership remain unchanged. Browser/debugger capabilities still validate actual resources and permissions.
Part of Design a portable contribution SDK and WebExtension runtime.
Current status, 2026-09-27
@devkit/clientnow executes local multi-provider routing. The accepted missing-recipient rule is implemented: if any broadcast selector has no known provider, reject the entire broadcast before dispatch, with codeunmatched-selection, all unmatched.selectors, and a message explaining how to correct the request. Known unavailable providers instead receive individual rejected outcomes.Commit: 2c6ef9b. This builds on the local catalog, declarative route defaults, provider identity validation and scoped invocation requests.
Implemented and tested:
(realm, provider ID)identity, duplicate rejection, immutable snapshots and owned subscription/cancellation cleanup. Detaching a connection does not dispose its host.selection, no inherited ordinary routing defaults, complete unmatched-selector preflight, deduplication and individual outcomes.[3, 6]; overlapping selectors increment each once to[4, 7]. Client disposal leaves native commands/state alive.Local validation passes: 75 core tests, 32 client tests, 78 runtime tests, 20 native server tests, both individual server examples and their new simultaneous routing check. Strict TypeScript 7, type-aware Oxlint, scoped formatting/builds and tooling checks pass. The packed consumer installs core/runtime/client tarballs, checks Bundler and NodeNext declarations, executes callback/broadcast behavior and builds a portable browser bundle without host/framework modules. Full repository CI passes on this commit.
The owner has accepted the local routing semantics, including rejecting all dispatch when any requested recipient is missing. The maintained routing record, client API, glossary, architecture and API/example/test matrix record the implementation and its limits.
Authorized remote catalog synchronization and native remote routing are now implemented. Still open: verified endpoint discovery, extension actors and capability resource checks, renderer/extension examples, and the optional CDB path. The workspace has the
connection.isolatedbackport from Devframe draft 401; that solves native SDK-owned connection isolation, not these missing adapter contracts. The local client adds no daemon, transport engine or global state store.Upstream integration boundary, 2026-09-27
Keep the implemented client focused on selection and ownership. Server connections use
devframe/client; extension Port integration first uses the channel seam indevframe/rpc/client. Each native connection owns auth, serialization and request correlation. The contribution catalog supplies exact contract/version/lifecycle metadata that native discovery does not supply; do not duplicate the entire native service registry or add a daemon.See the implementation and map review. This narrows implementation mechanisms without dropping the accepted feature or real-host coverage requirements.
Question
How does a contribution discover compatible providers and route each operation when extension, Devframe, Devtools, and optional CDB providers are available together?
Resolve provider identity, discovery, selection precedence, callback selection, broadcast outcomes, and the meaning of unavailable providers. The answer must let one JSON-authored UI operate against several backends without choosing a backend through renderer-specific code.
Context and current behavior
The agreed destination is a generic framework in the devkit-extension pnpm/Turbo monorepo. Build-time npm contributions may provide UI, actions, transforms, realm implementations, or any composition. Devframe and Devtools hosts coexist with Chromium and Firefox extension hosts. Raw APIs remain local, with separately typed realm, execution context, and UI surface.
The local ownership and selection rules above are implemented. Remote endpoint discovery remains outstanding; authenticated native catalog/invocation adapters are implemented. Devtools and extension providers may reach the same page, while another tab exposes a different target, potentially through the same provider installation. Choosing the first connection could direct a mutation to the wrong target.
The accepted routing modes are broadcast, a caller callback, provider-ID or realm precedence, and a UI choice supplied as callback input. Contributions declare defaults; individual operations may override them. Provider state remains separate, and synchronization is optional.
Requirements and scope
Options and recommendation
One global preferred backend is easy to explain but cannot express operations that need different capabilities. Hidden fallback chains make availability convenient while obscuring which provider performed an action. A central router with explicit selectors gives callers enough control without duplicating transport handling.
Recommend the central router. An operation override replaces the contribution default for that invocation. A selector requires a realm and may pin a provider within that realm; both constraints must match. Provider-only selectors and symbol/number coercion are excluded. A missing explicitly selected provider returns an unavailable result unless the caller declared a fallback. Ordered realm preferences choose the first eligible realm, with an explicit ambiguity result when several equally eligible instances remain.
Callbacks receive immutable candidate descriptions and caller input, including any UI selection. They return provider identities, not raw transports. Revalidate the chosen incarnation before dispatch. Do not silently reroute an interrupted mutation. Broadcast takes a candidate snapshot, dispatches once to each selected instance, and returns all settled outcomes.
Ordinary invocations already use dispatch-time readiness without implicit waiting, and fallback is limited to explicit ordered alternatives. The settled core prohibits rerouting or automatic replay after dispatch for every operation, including reads; a separately requested call makes a new selection. Callback staleness now rejects, and broadcast requires a separate selection list. An unmatched broadcast selector rejects the entire broadcast before dispatch; cancellation of a running handler does not imply that its side effects did not happen.
Proposed API or experiment
The implemented compact local calls and core client contracts are:
CapabilityInvocationRequestpreserves each operation name and its schema-defined input as a correlated union.ActionInvocationRequestinfers input and output from the action descriptor. Bound capability methods retaininput, options; a local provider handle has no routing override.The accepted callback contract rejects a selection whose provider incarnation changed while the picker waited. Broadcast uses
broadcast({ action, input, selection: [...] }), where selection is a union of recipients. These are implemented by the local client. Missing recipients reject the whole request before dispatch and are listed in the error.Build a runnable routing example with two real providers and a selector UI rendered from the shared JSON authoring format. Print each provider identity, availability, dispatch decision, and result. Add a CLI or test consumer for the same route callback so the selection logic is demonstrably independent of a renderer. Connect the real optional CDB adapter in its separately enabled example profile.
Scenarios and acceptance criteria
Record every selected public API and hook in the versioned API-to-example-to-test-to-host matrix owned by Examples and API coverage contract.
Dependencies
Contribution and realm contract blocks this decision because it defines contribution identity, host registration, typed local contexts, and capability ownership. Routing must consume those definitions rather than invent a competing realm hierarchy. State scope and recovery consumes the selected provider identities but owns synchronization and persistence semantics.
Definition of Ready
Identify the actual native connection, close and channel APIs; distinguish native close rejection from the remaining cooperative server cancellation guarantee.
Contribution and realm contract supplies the provider registration boundary and availability vocabulary.
Simultaneous native Devframe/DevTools/extension provider examples are maintained.
Record the optional CDB discovery path within that integration, without gating generic routing.
The human compared and accepted precedence, fallback, callback lifetime and missing-recipient behavior through concrete mutation scenarios.
Definition of Done
Local slice delivered:
Complete issue gate:
The resolution specifies identity, discovery ownership, precedence, ambiguity, cancellation, broadcast, and reconnect behavior.
Runnable routing examples and named tests are required for every selected routing API and supported host.
Unsupported and permission-related paths have explicit expected outcomes.
Remaining implementation work is handed off with no unresolved routing policy.
The human has confirmed the resolution through discussion; the agent has not supplied the human side of the decision.
Resolution record
Local routing, authenticated remote catalogs, mixed server/extension routing and a selected-page native handoff are implemented. Automatic monitoring and complete supported-host conformance remain open. Close this decision with a resolution comment recording the selected contract, rejected alternatives, example/test obligations, and any newly exposed decisions. A written proposal does not establish that the router or adapters already work.
Current implementation checkpoint, 2026-09-29
Delivered on linear main in separate commits:
@devkit/devframe; compose the same contracts over server RPC and actual extension Ports.The existing server factories delegate to the shared adapter and retain their API. A Port supplies real native call/collector/events members; no full server context is fabricated. Native RPC/auth/codec/state remain upstream-owned. The registry still owns only portable provider metadata, exact contract availability and selection. No credential store, retry, operation replay, new auth callback or discovery daemon was added.
The application still owns endpoint/domain selection. Explicit configuration and one-shot selected-document handoff are demonstrated. The latter reads the public native descriptor in the selected top-level MAIN world and grants the source page no extension RPC authority. It checks the native envelope only, not a duplicate full metadata schema or endpoint identity. Native server
allowedOriginsand native RPC authentication remain separate. StandardinitHubsupplies no viewer-origin token by default; its explicit native allowlist is used.Validation: 49 server tests, eight channel/provider tests and two shared-adapter tests passed for the extraction. Affected strict TypeScript 7, type-aware Oxlint, Oxfmt and builds pass. The maintained example now passes 24 actual Chromium scenarios with zero page errors, using installed workspace packages and native backend hosts. Commands, API ownership, screenshot and receipt. Full CI at d58a0d4 is green, including the real browser test.
Existing exact-version pnpm patches carry reviewed native isolation/RPC/state/renderer exports while upstream drafts remain open. These slices needed no new upstream patch. The feature drafts remain RPC/state and JSON view/renderer; snapshot repair is independent.
Remaining gates are real Firefox conformance, complete extension surfaces, automatic discovery if an embedding application needs it, privileged page request authority, and JSON-authored cross-provider controls. The last item belongs to Renderer and surface contract: current JSON actions call their owning native backend, while the proven routing controls use HTML. A native handler-hook versus RPC-call-adapter proposal is under owner review. Do not close this issue as full supported-host conformance.