Repository navigation
Contribution and realm contract #5
Description
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 applied or not-applicable. There is no SDK target envelope, target resolver, accepts hook or new topic/event bus. A not-applicable result 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.
The accepted resolution and canonical contract files supersede the historical proposals below. This issue resolves the design contract, not runtime implementation.
Question
What is the smallest complete contribution and provider contract that lets one framework-neutral feature run across devframe, Vite DevTools and browser extensions while exposing typed local native contexts and accurately declaring capabilities?
Context and current behavior
The project will ship generic packages in a pnpm/Turbo monorepo under dvcol/devkit-extension. Consumers assemble contributions at build time. A contribution can contain UI, actions, state, transforms, native integrations, shared behavior, or any combination; it does not need a panel to be useful.
At devframe commit a3f967755af99a0a0d8dd82b7b5d4f3dd4aec0bc, DevframeNodeContext contains RPC and server-oriented services. At DevTools commit 121656dfc5866b2f8de5baa8e0b6166ec28599c8, KitNodeContext extends the same hub context with Vite primitives. These inspected branches show layered contexts, not mutually exclusive devframe/devtools runtimes. Their release availability must follow Upstream reuse audit.
The common contract must distinguish a provider instance, its declared realm, an execution context and a UI surface. A browser-extension provider can execute across a worker, content script, page script and extension documents, while its UI appears in several places. Raw native objects stay in the process or document that owns them.
Requirements and scope
- Specify contribution identity, build-time registration, entry-point discovery, dependencies, duplicate handling, activation and disposal.
- Make realms and capabilities extensible for downstream adapters. The initial supported hosts are devframe, Vite DevTools, Chromium and Firefox; any optional CDB adapter requires independent proof before claiming support.
- Define common behavior separately from provider-native behavior. Common availability distinguishes implementation/execution readiness and missing permissions. Request-specific domain/resource applicability belongs to the implementation and its input/result schemas.
- Type native context access through a discriminant, while preserving the layered relationship of server contexts. Remote calls carry serializable identifiers and data, never raw context objects or callbacks.
- Keep provider selection, separate provider state and per-provider outcomes compatible with their dedicated decisions. This ticket defines their integration points rather than inventing competing routing or state rules.
- Require a runnable example for every package role and complete behavioral coverage of every public SDK API, hook and feature across supported hosts.
Options and recommendation
A single large context can expose every possible native API as optional fields. It is approachable initially but makes invalid combinations and partial support difficult to explain. Separate provider-specific SDKs have clearer native typing but fragment shared features.
Resolve with the user a shared contribution definition, capability contracts and extensible provider descriptors with typed local native access. Adapters implement those contracts and retain native APIs. Decide between explicit contract objects and declaration merging, recording consequences for dependency order and downstream authors.
Proposed API or experiment
Illustrative pseudocode only; names and signatures remain decision outputs:
const inspectionContribution = defineContribution({
id: 'example.inspection',
entries: {
provider: './provider.ts',
page: './page.ts',
},
capabilities: ['state', 'actions', 'json-view'],
});
export function setupProvider(context: ContributionContext) {
const registration = context.actions.register('read-title', readTitle);
context.lifecycle.onDispose(registration.dispose);
if (context.native.realm !== 'webext') return;
if (context.native.execution !== 'background') return;
const installedEvent = context.native.browser.runtime.onInstalled;
installedEvent.addListener(onInstalled);
context.lifecycle.onDispose(() => installedEvent.removeListener(onInstalled));
}Use this sketch to settle entry locations, targets, registration timing and cleanup. Replace capability strings with the chosen typed model. The illustrative execution discriminant narrows privileged background access separately from the realm; page and content contexts must not acquire that privilege through realm narrowing alone.
Walk through one contribution with UI and actions, one transform-only contribution, one page-only integration and one provider-native integration. Publish a contract inventory linking every exported type and hook to observable behavior. The example layout must contain runnable contribution, renderer, devframe-host, devtools-host, Chromium-host and Firefox-host applications. Shared fixtures may reduce duplication, but each host must launch and exercise its integration.
Scenarios and acceptance criteria
- A UI-free contribution activates and disposes without a renderer or open panel.
- The same shared contribution executes under two providers without merging their native contexts or state.
- Adding a downstream realm and capability requires no modification to a closed central union; incompatible capability versions fail explicitly.
- A worker restart, page navigation, panel close and contribution replacement each have defined ownership and cleanup outcomes. Repeated activation leaves no duplicate listener or registration.
- Missing permission and unsupported capability produce distinguishable outcomes; domain-specific resource failures use their capability contract, including per-provider results when routing fans out.
- Every public API and hook has a named runnable example and real-host assertion covering success where supported, unsupported behavior where not, failure and relevant lifecycle transitions. Compile checks and browser mocks supplement this proof; they cannot replace it.
Dependencies
Blocked by Upstream reuse audit and Browser capability audit using native issue-tracker blocking relationships. The first establishes reusable contracts; the second establishes actual browser and execution-context limits. This ticket unblocks Server adapter contract and other provider implementations. Its parent-map relationship is organizational, not its dependency representation.
Definition of Ready
- Both blocking audit tickets are resolved with accessible evidence and an explicit supported-host baseline.
- Contribution scope, build-time assembly, extensibility and local native context requirements are accepted.
- A concrete shared feature and a native-only feature are available as examples for the live design discussion.
Definition of Done
-
A user-reviewed resolution fixes vocabulary, public interfaces, extension mechanisms, registration lifecycle, capability availability semantics and native-context boundaries.
-
The resolution includes complete representative pseudocode with no unexplained lifecycle owner or execution location.
-
Public exports and hooks are enumerated with exact example and test obligations for every supported host, including failure and unsupported outcomes.
-
Routing and state integration points are explicit; unresolved behavior becomes a linked decision dependency rather than an implementer choice.
-
Record rejected alternatives and compatibility consequences, close the issue, and add its named decision pointer to the map. Completion settles the contract, not implementation delivery.
-
The human has confirmed the resolution through discussion; the agent has not supplied the human side of the decision.
Resolution record
Post the agreed contract and terminology, native-context typing examples, lifecycle table, capability behavior, example/test inventory, alternatives considered and follow-on decisions in a resolution comment. The map retains only a named link and a short gist.
Activity
dvcolomban commentedon Sep 22, 2026
Upstream reuse audit resolution supports reusing published RPC/state/JSON contracts through local adapters. The inspected kit context layers Vite fields onto hub and Devframe contexts; these are not mutually exclusive provider identities. The common contract must distinguish provider identity, execution context, UI surface and layered native primitives, with raw handles remaining local.
Acceptance: show valid UI-only, behavior-only and mixed contribution declarations; define minimal action/state/view/lifecycle interfaces and extensible capability outcomes without requiring a complete Node context from extension providers. Renderer action forwarding does not establish routing or authorization policy. Kit context source.
dvcolomban commentedon Sep 22, 2026
Research handoff and acceptance gates
Sources: Browser capability audit.
Accountable owner:
dvcol, until reassigned. These observations inform this open contract; they do not resolve it.The browser audit resolves the semantic availability baseline: unsupported implementation, wrong execution context, missing permission, restricted target, disconnected backend, conflicting owner and stale target are distinct outcomes. Public names and serialization remain this contract's decision.
Acceptance additions:
dvcolomban commentedon Sep 22, 2026
Contribution and realm contract: review packet
Status: proposed, awaiting the project owner's architectural choices. This is preparation for Contribution and realm contract, not an accepted public API or SDK implementation. The canonical decision will live in the issue's eventual resolution comment.
Grounding and settled constraints
The map already fixes build-time executable contributions, framework-neutral authoring, extensible capabilities and realms, separate provider state, and native resources local to their owner. A contribution can contain UI alone, behavior alone, or both. These requirements do not need another approval round.
Upstream reuse audit establishes reusable released RPC/state/JSON contracts and layered Devframe/hub/DevTools contexts. A Vite DevTools provider may expose several native server contexts at once. It is not three competing providers solely because three library contexts are present.
Browser capability audit establishes local execution boundaries and distinct unavailable outcomes. MAIN scripts have no extension messaging, isolated scripts cannot access the debugger, and Firefox lacks the Chromium native extension debugger. An unavailable capability must not silently disable unrelated contribution behavior.
Toolchain and reload audit proves a portable TS7/Vite/Oxc pipeline. Its WXT declaration/reload gates remain owned by later contracts. This ticket does not choose a packager or equate source edits with successful runtime replacement.
The glossary records only the settled distinctions. The
Featuregrouping below is a proposal and therefore is not added to that glossary yet.First decision: how packages extend the API
Recommend explicit imported contract descriptors. A downstream package exports a descriptor carrying its stable identity/version and TypeScript operation types. Providers register implementations against it. Consumers import that same contract to obtain a typed binding. No central realm union or global TypeScript augmentation is required.
Illustrative shape only:
TargetReference,defineCapability,connect, and the result shape are proposed core exports.showUnavailableis example UI behavior, not an SDK hook. Thereadoperation executes in the selected provider's eligible execution context; its consumer receives a serializable result. TypeScript operation types alone are not runtime validation. Wire validation and authorization must be specified with Permissions and trust before implementation admission.The alternative is declaration merging: downstream packages augment a global capability map, and consumers use typed string keys such as
context.capabilities.connect('example.page-title'). This is convenient for a fluent context API, but type visibility then depends on installed augmentations. Both choices still need runtime registration and capability negotiation; declaration merging does not provide those by itself.Proposed identity rule: compare the declared contract identity/version at runtime, never JavaScript object identity across separately bundled copies. Reject ambiguous duplicate implementations within the same provider registration scope. Version compatibility and schema consistency still require an explicit policy after the authoring model is chosen.
Second decision: who owns optional feature lifetimes
Recommend independently managed features within a contribution. A feature is the smallest group of registrations with shared execution requirements and a shared cleanup lifetime. A simple contribution has one feature. A mixed contribution can have a view feature and a debugger feature.
The host activates a feature only when its required capabilities and execution context are available. Failure or loss of a required capability ends that feature's registrations. Other independently eligible features remain active. A JSON view can still explain why its debugger controls are unavailable.
Illustrative build-time declaration, not a finalized manifest:
The entry locations are resolved at build time and contain packaged executable code.
presentationContextandproviderContextdescribe roles satisfied by eligible host executions; they do not magically move a callback to another process. The adapter maps roles to concrete entry bundles. The debugger implementation can live in Chromium background code or an eligible server provider; the presentation consumes its transported operations. No debugger declaration makes Firefox's missing native API available.The alternative is one setup function per contribution execution. Authors check optional capabilities, register available pieces and manage their availability subscriptions and cleanup themselves. This has fewer manifest concepts but puts partial-failure and reconnection rules in every contribution author's code.
This recommendation does not settle automatic retry or reactivation when capability availability returns. Those choices depend on the activation unit and are the next lifecycle questions, together with async teardown and dependency failure propagation.
Shared and native examples for the discussion
Shared title inspector
One contribution describes a JSON title view and a read-title action. Devframe, Vite DevTools and WebExtension providers implement the title capability. The common view invokes the same typed operation; it never imports Node, Chrome or Firefox APIs.
Two provider instances may return different titles and retain different state. A provider binding records its provider identity and target generation. Selection, broadcast and per-provider outcomes use the integration point owned by Provider discovery and routing. Persistence and synchronization use the integration point owned by State scope and recovery. This sketch chooses neither policy.
Native background integration
Only an eligible local background entry may access the native extension event below. Acquiring a realm descriptor alone does not prove background access.
FeatureContext,native.find, thewebExtensionBackgrounddescriptor andscope.onDisposeare proposed core/adapter APIs. This example logs an installation event locally. The callback and native event stay in the background execution. If the single-setup alternative is chosen, the corresponding context/scope belongs to that contribution execution instead.A server-native equivalent can expose Devframe, hub, DevTools kit and Vite resources together, where actually present. A downstream realm provides its own local descriptor and native type without modifying the core.
Transform-only and page-only contributions
A transform-only feature registers an HTTP transform and cleanup with the eligible provider. It has no view and requires the corresponding transform capability. Chromium debugger Fetch and Firefox filtering satisfy different implementations with explicitly declared semantic limits. CDB integration remains conditional on its managed Fetch configuration gate.
A page-only feature runs a packaged entry against a specific document generation. It can access page-owned resources and the validated page bridge but receives no background native namespace. Document replacement ends its activation. Parser timing, CSP and frame coverage remain owned by Injection and transform contract.
Context exposed by every capability binding
Propose a context with separate provider, realm, execution, target and presentation descriptors. It also discriminates local implementation access from transported access.
A provider realm such as
webextnever grants background APIs to its MAIN or content entries. A remote provider describing itself aswebextnever gives the caller a local browser namespace. Third-party realm registration extends typed access without requiring a central exhaustive union.Unavailable semantics already resolved by research remain distinct: unsupported implementation, wrong execution context, missing permission, restricted target, disconnected backend, conflicting owner and stale target. Snapshot availability can change before an operation executes, so invocation failures must carry the same distinctions. Public names and data shapes are still candidate contract decisions.
Proposed ownership and failure table
The host must reject cycles and missing required contribution dependencies before activation. Duplicate contribution identity must produce an explicit diagnostic, not a last-registration-wins overwrite. These are proposed defaults for the final resolution, not completed runtime behavior.
Candidate public inventory and acceptance obligations
This is the complete candidate inventory for this ticket's contribution/context/lifecycle layer. Action, state, routing, rendering and transform protocols remain owned by their linked decisions. Exact signatures depend on the two choices above.
defineContribution, contribution identity and entry/dependency declarationsdefineCapability, contract identity/version and operation typesdescribe,watch,connectand invocation binding contextonDispose, cancellation signal and disposable registrationEach applicable row must execute on standalone Devframe, Vite DevTools, Chromium and Firefox, using public package exports. Add a downstream realm example and a replaceable renderer example. Unsupported combinations must assert unavailability while unrelated functionality continues. Mocks, type checks and parser validation alone cannot satisfy real-host claims.
These are obligations for Examples and API coverage contract, not claims that those examples exist. Its independent inventory/registry checker must reject missing links or unsupported cells mislabeled as supported.
Decisions needed now
After those answers, settle concrete signatures, version compatibility, dependency failure propagation, async disposal and reactivation rules. Final user review still precedes closing the contract. No dependent contract is silently resolved by this packet.
Review artifact: local, unpushed branch
dvcol/wayfinder/contribution-realm, commit01d9c0a, filedocs/planning/005-contribution-realm.md. The same commit contains the glossary derived from already agreed map requirements. This comment is a proposed design round, not the contract resolution.dvcolomban commentedon Sep 22, 2026
Contribution activation: two authoring styles
Part of Contribution and realm contract. The owner selected explicit capability and realm descriptors. Activation style remains open. All API names below are illustrative, not implemented exports.
Compare the same inspector: it publishes a JSON view and adds debugger actions when a debugger provider is available. Both snippets run in a provider entry. The renderer mounts the published JSON in its own UI entry. Content, page and native implementation entries remain separately packaged in either design.
Shared code in both versions
jsonViews,actionRegistryanddebuggerCommandsare imported explicit descriptors. Both versions use exactly the sameinstallInspectorandinstallDebuggerToolsfunctions.captureSnapshotis the example's imported action descriptor. The action registry returns a disposable registration, adopted byscope.use. The scope's cancellation signal covers that activation's calls.captureSnapshoton the debugger API is an illustrative operation to make the example concrete; this does not settle the debugger contract's command model.installInspectorsimilarly publishes the common JSON view and adopts its registrations into its supplied scope.Both versions use these same proposed runtime guarantees:
These are comparison assumptions for both designs. Async disposal ordering, retry policy and permission behavior still require contract decisions. Abrupt worker termination cannot guarantee JavaScript cleanup.
A. Host-managed features
The author declares each feature's requirements. The host connects availability to activation and disposal.
The host resolves every required descriptor before calling
activate, supplies typed capability bindings, and creates the feature's scope.whenAvailableAgainmakes this example's chosen behavior explicit; the API name and default remain open.If debugger availability disappears, the host disposes only
debuggerTools. Its actions are unregistered and its operation signals are cancelled. The inspector stays active. When a valid binding returns, the host starts a fresh debugger-tools scope under the explicit reactivation policy. Re-registering an action does not invoke it or request permission.Feature requirements and activation outcomes are inspectable by the host.
activateis still ordinary code: authors can branch, register native listeners and manage resources inside their feature scope.B. One setup function per provider execution
The contribution owns the availability-to-lifetime connection. It still receives the same typed descriptors and scope helpers; it does not need to reinvent resource disposal.
reportActivationOutcomeis the example's reporting helper, not an implicit SDK feature registry.startChildparents the child activation to the entry scope, so closing the entry disposes both its observer and any active child. The observer's sequence implements the same reactivation behavior as A. Production types would name the activation handle; the sketch omits type boilerplate to focus on ownership.If debugger availability disappears, the author's callback disposes the debugger child while leaving the inspector active. If availability returns, the callback creates a fresh child. The host knows the child resources, but the semantic label, requirements and relationship to debugger availability live in the author's code unless separately declared.
As optional capabilities grow, the author adds observers and combines their readiness. Grouping them into one activation and propagating dependent failures also belongs to this setup code. The host still enforces authorization and stale-target rejection at invocation time in either design.
Behavior and tradeoffs
I recommend A as the normal authoring model, while retaining scopes and imperative APIs inside
activate. The host owns repetitive availability/lifecycle wiring and diagnostics; contributions keep control of their behavior. B is reasonable if minimizing declarative concepts matters more than uniform lifecycle behavior across third-party contributions.Open choice: A or B as the primary authoring model. Selecting A does not yet choose automatic reactivation as the default; both sketches explicitly demonstrate reactivation only to make their behavior comparable.
Recorded owner decision: explicit imported capability and realm descriptors. Global TypeScript declaration merging is not the primary extension mechanism. The owner requested these activation sketches before choosing the second model, so the contract remains open.
Local review commit:
6ef6de2ondvcol/wayfinder/contribution-realm, scoped to this ticket. Markdown structure, local links, snippet syntax and the diff were checked. These are design sketches, not implemented or type-validated SDK APIs.dvcolomban commentedon Sep 22, 2026
Plugin definitions, contributions and host runtime
Proposal for Contribution and realm contract. The owner selected explicit imported descriptors and requires definition modules to stay separate from the core runtime. Host-managed lifecycle is their preference, pending a concrete contract. The names and signatures below remain proposed.
Separate the things we are naming
The earlier
defineContributionsketch described an entire installable tool, its entry modules and its activation. Calling all of that a contribution obscures what the tool adds to its host. Recommend the following vocabulary:A plugin can contribute behavior alone, UI alone, or both. A package may export several plugins or contribution definitions. A plugin is not necessarily an npm package or a separate browser extension. The browser extension itself can host many plugins.
Reserve handler for an actual callback, such as an action handler. Remove the earlier generic
features/handlerslayer. A contribution can have dependencies and an owned registration lifetime without every author inventing another feature grouping.Integrationis a reasonable alternative for the installable unit, especially in host documentation, but it is also used for host adapters and external connections.Moduleclashes with JavaScript entry modules.Capabilityis already needed for the common service contract. RecommendPluginfor the installable unit andContributionfor what it adds.The existing glossary still records the earlier whole-feature meaning of contribution. This packet explicitly proposes revising that meaning; it does not silently treat the rename as accepted.
Architectural basis
The inspected public Devframe definition helper returns a typed definition. Host installation separately resolves assets, queues services and invokes setup. Vite DevTools adds a host adapter around that definition. These are the useful boundaries to retain:
These links identify the inspected checkout revisions, not a new claim about the latest released API. Release reuse constraints remain in Upstream reuse audit.
The owner also asked us to follow the composition pattern of their existing tooling. The generic lesson is that feature authors supply typed definitions or contribution results, and a separate composition root installs them, projects their metadata and owns disposal. This proposal carries that pattern forward without importing downstream application code or domain contracts. Some existing definition factories already allocate resources, so inert definition helpers are a deliberate strengthening of that boundary. Complete unloading is also a new obligation; existing subscription cleanup alone does not prove that actions, views and every native registration can be removed.
Package and import boundaries
Illustrative package names, not a naming decision:
definitionsruntimeA definition helper constructs a typed descriptor. Importing it does not start a host, open a connection, install a listener or import the core runtime. Executable callbacks are packaged code; a definition containing callbacks is not a JSON wire payload. Only serializable metadata and validated operation arguments/results cross contexts.
Keeping definitions and runtime separate is a dependency boundary. A
setupfunction can be declared in a definition and invoked later by the runtime without the definition package depending on that runtime.Concrete authoring sketch
Define a capability contract
TargetReferenceis a common contract type. Real operation declarations also require runtime schemas and the agreed unavailable-outcome shape. TypeScript types alone cannot validate a message. This example isolates the capability identity from any implementation; it does not settle its wire schema.Define what the plugin contributes
readTitleInputandreadTitleOutputare imported portable runtime schemas.requiresmaps local names to explicit capability descriptors. It supplies the typedserviceskeys to this action's callback. It does not request browser permission, select a provider or target, attach a debugger or invoke the action.A separate JSON view definition can reference this action's identity and bind its input/result using the renderer contract. It does not import the action implementation's platform backend. The renderer catalog and exact view schema remain in Renderer and surface contract.
The entry returns contribution definitions. The host validates and installs them in their respective registries. An action callback runs when invoked; returning its definition does not execute it. A view contribution publishes the JSON view; rendering belongs to a UI execution with its own lifetime.
For context-dependent definitions,
setup(context)receives the entry's local execution context and resource scope. It can construct contributions from configuration and register cleanup for resources it owns. Returning static definitions is the simplest case. A staticcontributionsshorthand can be added later if it reduces real repetition; it is not necessary to establish this contract.Package the entries as one plugin
The build integration resolves the entry relative to its defining module and packages it for eligible hosts. The WebExtension adapter maps this role to background execution; a server adapter maps it to its provider process. Content, page and presentation executions use separate declared entry modules where needed. Executable entry discovery, module resolution and host-role compatibility must be checked by the packaging contract. Runtime installation only uses packaged entries.
Install through the runtime
Factories install their supported standard services and contribution registries. The custom
example.pagecontract still needs the explicit implementation registration shown in each composition root. The two imported implementations illustrate the adapter boundary; their platform operations are not implemented by this planning packet. A composition root can retain that registration for explicit disposal:pageImplementationcontains the API and its eligible local context. It is implemented in the adapter, separately frompageCapability. Registration returns a disposable owned by the host. Removing or replacing it invalidates existing bindings. Missing implementations are represented explicitly; no adapter provides a fake successful implementation for an unsupported browser API.installvalidates/adopts the definition and returns a handle even if some contributions are unavailable. Invalid manifests or duplicate plugin identities reject installation. Contribution setup failures and missing implementations appear as diagnostics in the handle. Exact dependency/version rules still require review.What host-managed means
It means the runtime manages the contributions returned by an entry. The author defines the action/view/transform once and supplies any executable callback. The host owns validation, capability binding, registration, cancellation and teardown.
Use two distinct layers of status:
For an action, execution only happens on invocation. For a transform, its active interception registration can exist between requests. These contribution kinds share dependency and ownership rules, while their registries implement the actual registration mechanics.
Remove
whenAvailableAgain. Restoration is a documented runtime lifecycle rule, while a failed setup requires explicit retry. Ordinary service restoration does not rerun the plugin entry's setup just to restore an action. A fresh execution or code generation does run its entry setup again.Setup establishes definitions and reversible owned resources. Side-effecting user commands belong in action callbacks. Completed external effects cannot be undone by disposal. Target permission loss can invalidate a target-scoped binding when that binding itself depends on the permission, but it does not automatically destroy a provider-wide service or plugin.
Context and runtime controls
Every required service binding exposes
.apiand.context. The binding context identifies provider, realm, owning execution, optional target generation and whether access is local or transported. The callback also has its own execution context. Those contexts can differ.An entry setup context contains local provider/execution identity, target only for target-scoped execution, and a scope with
signal,use(disposable)andonDispose(cleanup).native.get(importedDescriptor)returns typed native resources only when that local context owns them. A server context can expose Devframe, hub and Vite descriptors together; a WebExtension context exposes only its actual browser execution's primitives. Native objects and functions never cross RPC.The handle is a local control object. Remote clients receive serializable snapshots and transported control operations. Snapshots contain entry lifecycle status and separate per-contribution availability, identity, generation and diagnostics. Subscription must deliver current state and changes without a read/subscribe race.
retryretries failed entry setup. Enabling alone does not clear failure into a retry loop. It never retries a user action. Async teardown ordering, transition completion deadlines, snapshot wire types and failure isolation for a malformed returned contribution remain open contract details.Extensibility and core API inventory
The contribution result must be extensible through imported descriptors just like capability contracts. Built-in helpers such as
defineActionand the JSON view helpers produce definitions tagged with their contribution-type descriptor. The runtime dispatches them through registered contribution-type implementations. Adding a downstream kind must not require editing a central TypeScript union, a global augmentation or the core runtime's switch statement.A contribution-type implementation owns validation and installation for that kind and returns scoped cleanup. Runtime registration of those implementations belongs in host composition. The public authoring API never imports that registry. The exact descriptor/installer generic signatures need review before claiming a complete public contract.
The proposed core inventory is:
provide, contribution-type registration,installanddisposeoperations.The full action/state/renderer/transform/debugger operation contracts are still owned by their dedicated map tickets. This sketch makes the core authoring/runtime boundary concrete without presenting those operation schemas as already resolved.
Acceptance obligations for the final contract
These are required evidence for later implementation, not claims that this proposal already runs:
Decision frontier
Definition/runtime separation and explicit imported descriptors are requirements. The next owner decision is the naming and composition model: plugin for the installable unit, contribution for each thing it adds, capability for each service contract it consumes or provides. The revised host-managed model operates on these contributions, without a generic feature/handler grouping.
If accepted, update the glossary and settle exact contribution-type registration, dependency binding, setup failure and disposal signatures. Keep this decision ticket open until its full definition of done is satisfied.
Review artifact and validation
Local ticket-scoped commit:
89e7591ondvcol/wayfinder/contribution-realm. The branch is not pushed; the complete proposal is reproduced in this comment for review.Documentation checks passed for Markdown fence balance, local document links, whitespace and the TypeScript syntax of all nine new code blocks. The sketch uses proposed packages and interfaces; it is not SDK type-check or runtime evidence. No production source changed, so application lint/type checks are not applicable to this documentation update. The issue remains open, with its definition of done unchanged.
dvcolomban commentedon Sep 22, 2026
Architecture, glossary and declaration contract
Working proposal for Contribution and realm contract. The owner requires a clear glossary, dependency/interaction diagrams, type safety and declaration API before runtime implementation. Explicit imported descriptors and separation of definition modules from core runtime are settled requirements. Names, signatures and the lifecycle policies below remain under review.
This packet supersedes the earlier two-step
definePluginEntry/definePluginauthoring sketch. There is one plugin definition. Executable script references remain explicit where a real execution boundary requires separate packaging.Correct the relationships first
A capability is a service contract. A provider instance offers an implementation of that contract. A realm describes the environment family; it does not execute or implement anything. A provider only advertises capabilities it can actually implement in eligible contexts.
A contribution is something a plugin adds, including views, actions, state declarations, transforms and packaged scripts. Its implementation can require zero or more capabilities. A view may also reference an action contribution through its public action descriptor. Keep those references distinct from service dependencies.
A plugin groups contributions under one identity and installation lifetime. Its package can also export shared capability contracts and optional implementation definitions. Exporting or listing a contract does not supply an implementation. Several plugins can share the same contract without owning one another.
An action describes a use case, such as capture a snapshot. A capability describes a service that can support that use case, such as debugger operations. An action handler may orchestrate several capabilities; a capability may support several actions or transforms.
Proposed glossary
This is the complete proposed replacement vocabulary for the current discussion. Copy agreed terms into the root glossary after owner review; the earlier glossary is not silently renamed by this packet.
apiandcontextA process may contain both client and provider roles when a host deliberately composes them. The default extension popup is a client; that does not erase the browser APIs that its own document legitimately owns.
Axes that must remain separate
extension:local,server:project-awebext,devserverRecommend one development-server realm for standalone Devframe and Vite DevTools. They use different adapters and can expose different native context descriptors. A Vite DevTools server may expose Devframe, hub and Vite resources together. Those libraries do not create three independent provider identities. Realm IDs remain extensible imported descriptors; the example names are not a closed core union.
An options document can have no selected target. A popup's selected target is not automatically a background service's lifetime. Closing a panel ends that client document's subscriptions and presentation, while independent provider work continues.
Definition dependencies
Arrows in this diagram mean imports or typed references, not runtime messages.
Definitions do not import either runtime. Shared action/view/capability contracts do not import their executable implementations. A client can import an action descriptor without importing the plugin's provider handlers or native server code. Adapter modules and renderer implementations may depend on native APIs or a UI framework within their own eligible bundles.
A definition may contain a packaged function. That is executable source known at build time, not JSON sent to a browser. The compiler/packager resolves the declared execution assignments and script modules; the wire protocol publishes only validated metadata, state and operation messages.
Runtime interactions
Arrows in this diagram mean runtime interactions. One client may connect to several providers; selecting and broadcasting across them is owned by Provider discovery and routing.
flowchart LR subgraph Presentation["Client execution: popup, options or panel"] Renderer["JSON renderer"] Client["Client runtime + client adapter"] Renderer -->|"action request"| Client Client -->|"view, state and availability"| Renderer end Client <-->|"validated messages"| Transport["Transport"] Transport <-->|"calls, results and subscriptions"| Provider["Provider runtime + provider adapter"] Provider -->|"invokes packaged handler"| Action["Action contribution"] Action -->|"typed capability binding"| Service["Capability implementation"] Service -->|"local eligible operation"| Native["Browser or server APIs"] Service <-->|"validated target-scoped requests"| Agent["Content or page execution agent"] Agent -->|"local operation"| Target["Inspected document"] Provider -->|"owns declarations and registrations"| Registry["Views, actions, state and transforms"]The provider runtime remains the authority for its owned operations. A MAIN script does not receive background permissions or become a trusted provider simply because it can send a message. An execution agent reports into a provider under explicit target/session validation. If a future host advertises an agent as an independent provider, it must give it its own identity and authority explicitly.
Placement by host
The server provider can also rely on a browser execution agent for page-side operations. The shared capability contract exposes the operation and its actual availability; it does not require every platform to perform it through the same native API. Detailed injection timing, browser permissions and transport/recovery behavior remain the linked map contracts' work.
An adapter is broader than a transport. It may provide lifecycle hooks, target discovery, capability implementations, entry packaging and local context access. A transport alone does not implement page inspection, debugger operations or rendering.
One plugin declaration
All names below are proposed exports. The snippets define an authoring contract, not implemented SDK packages.
Shared contracts
Schema-derived types keep TypeScript inference and wire validation tied to the same declarations. The imported schemas below are portable runtime schemas for the target and result; they are application examples, not magic SDK globals.
Capability operations are reusable services. The action is the plugin's invocable use case. Their schemas happen to match in this small example; a more complex action can combine several operations and return a different result.
Contribution implementation and plugin definition
defineActioncreates the public contract.defineActionContributionattaches a handler, service dependencies and execution assignment to that contract. Both are inert definition helpers. This is an actual action callback, not a new generic feature/handler grouping.inspectorViewis a separately defined JSON view contribution that referencesreadTitleActionthrough its public descriptor. Its JSON catalog and component bindings belong to the renderer contract. The client imports public contracts and views, never this provider implementation module.There is no
definePluginEntry, no mandatory setup wrapper returning an unchanged array, and no second plugin identity. A contribution kind that owns startup resources supplies a typed activation hook and cleanup scope through that kind's definition contract. It shares the runtime ownership protocol rather than forcing all plugins into an additional feature container.Platform implementations and provider composition
The custom page capability has separate implementation modules, for example
page.webext.tsandpage.devframe.ts. They both implement the operation schemas above, receive eligible local contexts and obey the same outcome contract. A common contribution uses the descriptor without importing either module.The Devframe composition substitutes its provider adapter and page implementation. The Vite DevTools adapter can build on the Devframe adapter while exposing the additional eligible Vite context. These are factories for adapters around the same provider-runtime contract; they do not redefine plugins.
providevalidates and registers an implementation definition for later eligible activation.installstarts that plugin's eligible registrations and returns a handle. The adapter's environment bootstrap owns channel and native listener initialization, including any synchronous registration requirements. The exact start/readiness API and worker recovery sequence must be fixed before implementation, not inferred from this sketch.A plugin package may export optional native implementations, but host composition selects the eligible ones. Contract-only exports do not make those native implementations appear automatically. The first proof can use explicit composition; automatic implementation selection would need its own reviewed build-time declaration contract.
Client composition
popupSurfaceis an imported surface descriptor.inspectorViewReferenceis a typed public reference to the published view.container,selectedProviderandselectedTargetcome from the client application. The client knows its own local execution and selected remote provider separately. Mount/subscription handles belong to the client document and are disposed with it. Exact view mounting and renderer integration signatures remain part of Renderer and surface contract.The Devframe/DevTools client compositions substitute their mount/connection adapters. The view and action contract stay the same. A renderer can also handle a local presentation interaction without creating a provider action; the renderer contract must distinguish those local interactions from authoritative backend commands.
Scripts retain real execution boundaries
Some contributions genuinely need a separate packaged module:
This definition identifies a script contribution. It is not another plugin declaration. Its activation target, match rules, permissions, timing and bridge contract must be specified by Injection and transform contract. The script runs in its declared execution. It cannot inherit a provider's raw native context by importing a common type.
Dependency edges have different meanings
requirescapabilityDefault recommendation: no arbitrary plugin-to-plugin or contribution-to-contribution activation dependencies. Share behavior through capability and action contracts, with explicit references for views/state. If a real ordering dependency remains, name and specify it before adding a general dependency graph API. This revises earlier speculative cycle handling in the first review packet; that older sketch is not an accepted promise of a dependency graph.
An action using several services resolves them under its selected provider by default. Cross-provider orchestration must be explicit and preserve per-provider outcomes. Binding resolution must not silently combine state from one provider with target operations from another.
Type-safety contract
requiresinferenceservices.page.apiinferred; undeclared services rejectedProposed operation outcome shape:
UnavailableReasonretains distinct unsupported implementation, wrong execution, missing permission, restricted target, disconnected provider, conflicting owner, stale target and incompatible contract reasons.OperationErroris serializable and contains no native exception object. Capability bindings and action calls use the same envelope; the declared output schema validates the successfulvalue. The concrete reason/error fields and authorization policy still need review.An implementation is typed against the descriptor's inferred operations rather than a separately hand-maintained API interface. Optionality is explicit in schema and contract declarations. Avoid
any, assertions that pretend a browser Port is a Devframe transport, andskipLibCheckas evidence of compatibility.Recommend exact declared contract-version matching for the first proof. Semver-range negotiation and schema compatibility inference can be added only with an explicit policy. Exporting a changed schema under the same version is a contract violation; version equality alone is not proof that the author preserved the schema. Runtime schema validation remains mandatory.
Context and ownership contract
Every bound API exposes
apiandcontext. Its context describes the service owner. The action callback separately receives its own execution identity and cancellation scope.ProviderDescriptorcarries provider identity and the serializable realm descriptor.LocalNativeContextis a descriptor-indexed accessor restricted to the owning execution, not a union of every host namespace. A local Vite server can expose several native descriptors; a remote popup sees only their declared metadata. Permission and trust remain runtime checks even after TypeScript narrows a local context.A plugin installation owns provider contributions and their activation generations. A client owns its renderer mounts and subscriptions. An agent owns document-scoped resources. A capability implementation owns its native service resources within its eligible execution. The runtime records those owners and unwinds resources on failure, replacement, disable and disposal. Abrupt worker termination requires durable recovery rather than assuming cleanup callbacks ran.
Missing required services keep contribution metadata observable while executable behavior is unavailable. Losing a binding cancels dependent work and active registrations. Compatible restoration recreates registrations and never replays a user action. Permission/target failure on one operation does not automatically dispose a provider-wide plugin.
Public inventory and decision boundaries
defineCapability,defineOperation,defineAction,defineActionContribution,definePlugin; execution/realm/native-context/contribution-kind descriptorscreateProviderRuntime,provide, contribution-kind registration,install, runtime disposalThis packet supplies the architecture and proposed declaration API. It does not claim every domain signature is finished. Before implementation admission, each public method and hook must have an exact type, execution owner, failure behavior and example/test obligation. The map must make remaining decisions explicit rather than leaving them to an implementer.
Required contract evidence before implementation is considered complete
These are future implementation obligations. This planning change adds no SDK runtime. Documentation syntax checks are not evidence of any of these guarantees.
Owner review frontier
definePlugin({ contributions }), separate shared action contracts from handlers, and keep native script modules as explicit contribution packaging references.webextanddevserver, with standalone Devframe versus Vite DevTools represented by adapters and native-context descriptors.After this review, record agreed vocabulary and settle the remaining exact core descriptor, contribution-kind installer, availability, retry/disposal and context signatures. The ticket remains open until the user-reviewed definition of done is satisfied.
Review artifact and validation
Local ticket-scoped commit:
35aa498ondvcol/wayfinder/contribution-realm. The branch is not pushed; this comment contains the complete proposal for review.Documentation checks passed for Markdown fence balance, local links, whitespace and TypeScript syntax in all seven new code blocks. The two Mermaid diagrams were inspected as source; no rendered-diagram or SDK runtime/type-conformance claim is made. No application source changed, so application lint/type checks are not applicable. All final contract acceptance items remain open.
dvcolomban commentedon Sep 22, 2026
Dedicated declarations, service installation and upstream alignment
Proposal for Contribution and realm contract, responding to the owner's three challenges about declaration grouping, declarative versus imperative service installation, and the amount of abstraction above existing tooling. This is a revision for review, not a resolved contract or SDK implementation.
This packet supersedes the heterogeneous
contributionsarray as the recommended built-in authoring shape. It also supersedes imperative-only capability installation and the assumption that every host must expose a new generic provider runtime constructor. The earlier architecture diagrams remain useful descriptions of ownership and execution roles.Recommendations
actions,views,transforms,scriptsandservicespropertiesThese recommendations preserve the accepted requirements for explicit imported descriptors, separate definition modules, framework-neutral authoring and browser-specific implementations.
Released API grounding
A bounded follow-up to Upstream reuse audit rechecked the relevant public declarations and selected implementation paths in the SHA-512-verified
devframe@1.0.0,@devframes/hub@1.0.0and@vitejs/devtools-kit@0.7.5artifacts. These are verified pinned contracts, not a claim that they are the latest available releases or that every source file matches a release.JSON views, command handles and renderer registrations have removal mechanisms; installed services and the server dock host lack equivalent public uninstall methods. Service revocation alone does not undo wire-service installation. The released hub also starts declared service installations without awaiting each returned promise, then awaits a memoized readiness barrier. After startup that barrier may already be fulfilled, so late setup can race asynchronous service construction. This last point is a source-supported risk, not a reproduced runtime failure. It requires a targeted proof before promising startup/dynamic equivalence. Hub installation, service host.
1. Contributions describe a relationship, not a mandatory container
A plugin contributes actions, views, transforms and services. The word contribution is useful for documentation, ownership, diagnostics and common typing. It does not require all of those declarations to live in the same array or expose identical lifecycle hooks.
Each item is an imported definition of the appropriate kind. Omitted properties mean no declarations of that kind. A service-only plugin can instead declare
services: [portableService], provided the implementation definition is eligible for that plugin's installed execution. A host can supply shared services independently of any plugin.All kinds can share identity, ownership, dependency diagnostics and disposal conventions where those concepts apply. Their specific contracts remain distinct:
Do not force a view's action reference to behave like a transform's required execution binding. Do not use array position as identity or implicitly derive cross-kind execution order from it.
contributions: [action, view, transform]actions,views,transforms, etc.Recommend only the dedicated built-in shape. Internal normalization is an implementation choice; it must preserve each kind's schema and semantics. It should not become an extra public execution language.
Downstream extension without weakening the built-ins
Capability and realm extensibility remain based on imported descriptors. A new service capability normally fits
servicesplus existing action/view/transform declarations; it does not require a new contribution kind.For a genuinely new contribution kind, reserve an explicit descriptor-tagged extension collection rather than accepting arbitrary top-level keys:
diagnosticReportDefinitioncarries its imported kind descriptor and validated typed payload. Host composition supplies that kind's installer explicitly. This is a proposed extension path, not a requirement to reimplement every built-in through a universal registry. Its exact helper and installer types must be proved with a concrete downstream example before the contract closes. Unknown top-level fields must remain errors so a typo such asactonscannot silently become an extension.Definition helpers should earn their place
A
define*helper constructs and types an inert definition. It can infer schema types, preserve literal descriptor identity and validate malformed declarations. It does not register listeners or start a service. Plain objects usingsatisfiesremain reasonable where they retain the required types and invariants; do not introduce a factory solely to return the same object under a new name.The separation between an action's client-safe contract and its executable implementation remains necessary. Dedicated properties do not authorize importing provider handlers into a popup. The helper count should follow actual inference and execution requirements, not the number of glossary nouns.
2. One service definition, two installation entry points
The earlier
provideexample belonged to the provider side, not the UI client. It changed a live service registry. That is different from a definition helper constructing a recipe.Use the following distinction:
serviceslistservices.install(definition)A service implementation definition is the existing capability-implementation concept made explicit as a recipe; it is not another provider identity. Its setup receives the eligible native context and resource scope. The exact helper name below is proposed.
createPageApiis local implementation code satisfying the operation schemas ofpageCapability. Resources it creates belong tocontext.scope. The definition helper does not invoke it. The module belongs to the eligible extension provider bundle; a server host supplies a different implementation definition for the same capability.Startup composition:
Runtime composition, in a separate host where this service is not already installed:
createWebExtensionHostis an illustrative host bootstrap, not a new finalized export. A Devframe host uses its native initialization and adapter's startup service declarations. The guarantee is the shared definition and installation semantics; constructor names need not be identical across hosts.Naming and policy
Prefer
services.install(definition)for constructing a service from its definition. Upstream Devframe usesprovide(id, object)for a different operation: publishing an already-created in-process object. Do not make these names aliases and silently inherit different transport or ownership semantics.A raw-object
provideescape hatch can remain available through an eligible native Devframe context. Add it to the portable API only if a concrete use case establishes how its resources, validation, remote publication and lifetime are owned. The normal authoring interface needs the startup list and runtime installation, not three overlapping methods.Both installation paths must share:
Proposed first-contract defaults: an implementation is installed once in its provider/registration scope; an ambiguous duplicate fails explicitly. Shared host services are installed by host composition and consumed by plugins through requirements. A plugin-declared service belongs to that plugin's installation and is disposed with it. Do not silently merge ownership or configuration when two plugins claim the same implementation slot. If shared installation leases are needed later, specify them explicitly before allowing duplicates.
A failed dynamic installation must leave the previous valid registry state intact and unwind its own partial resources. Required startup failure must report which service prevented readiness; optional unsupported services must report availability without fabricating success. Those policies must be fixed in the exact lifecycle contract, with the same service-level behavior in both paths.
The runtime method supports installation from already-packaged code, including user-enabled integrations and replacement during development. It does not permit fetching arbitrary executable plugin code into a packaged extension.
Upstream semantics are precedent, not automatically our policy
Devframe's existing service APIs distinguish startup and later option handling. Startup definitions participate in a readiness barrier and option merging; late installation of an existing package can return its existing API and ignore new options. Its wire-service
installreturns an API, while in-processprovidereturns a revoke function. A proposed disposable installation handle is therefore an additional ownership contract, not an existing upstream guarantee.Our adapter must specify which semantics it preserves and how additional ownership requirements are implemented. Renaming the calls does not establish equivalence.
3. Align the implementation with Devframe and the declarations with typed contribution composition
The earlier proposals were accumulating an abstract service registry, action registry, state layer, view layer, transport layer and host factory above a stack that already implements many of those mechanisms. That would create competing representations and more places to enforce the same rules.
Recommend a small portable authoring contract and direct adapters into existing host contexts. The server should continue using Devframe/Vite bootstrapping. The WebExtension host should use its own browser packaging and lifecycle integration. Shared UI and contribution code use the same contracts across them.
This diagram expresses a reuse target, not proof that the whole Devframe Node context runs in a browser. The previous audits still control which released components are actually reusable.
Do not confuse upstream
ctx.views, which hosts static assets, with portable JSON view declarations. Mapping must follow actual semantics rather than reusing the wordviewsfor unrelated operations.Avoid a mandatory action around every service method
Capability operations already describe callable behavior. A separate action is useful when it adds an invocable use case, orchestration, command/UI metadata or a distinct input/output contract. It need not be a second wrapper around every RPC function. Reuse the original schemas and metadata rather than hand-maintaining an equivalent descriptor at each layer. Client invocation must still use validated, authorized bindings whether it invokes a service operation or a composed action. Only explicitly published operation contracts are remotely callable; registering an in-process service does not expose its raw methods to clients.
The earlier read-title action demonstrated dependency injection, but it is too small to justify a universal action-over-operation requirement. The complete API must allow a deliberate direct service operation path where no extra action semantics are needed.
Upstream adoption
Technical alignment can make upstream review easier; maintainer interest is unknown until discussed. A large generic plugin framework is a much larger adoption request than improvements useful to existing Devframe users.
Potential upstream changes should each solve a concrete missing seam, such as a narrower renderer-host interface, portable service resolution, complete disposable registration ownership, or an optional explicit-descriptor API. The combined portable declaration package can remain a companion integration while those pieces are evaluated. Vite-specific integration belongs in the Vite adapter; browser packaging and permissions belong in the WebExtension adapter.
Do not couple the generic SDK to application-specific contribution or renderer types. The reusable lesson from downstream tooling is typed dedicated contribution properties, direct use of compatible upstream types and centralized registration/teardown. The generic package must remain independent of that tooling's business context.
Exact decision and validation obligations
Before implementation admission, this ticket must settle:
Acceptance evidence must cover positive and negative declaration typing; wrong-kind property entries; misspelled properties; startup/dynamic equivalence; duplicate/conflict behavior; partial installation failure; plugin-owned versus host-owned disposal; actual emitted bundle dependencies; and real host success/unsupported/lifecycle behavior. Preserve the API/example/test/host coverage obligation already recorded by the map.
The owner has not yet accepted these recommendations. The contract remains open. This document is a planning deliverable and contains no runtime implementation.
Review artifact and validation
Local ticket-scoped commit:
1600ea2ondvcol/wayfinder/contribution-realm. The branch is not pushed; this comment contains the full proposal for review.Documentation checks passed for whitespace, fence balance, local document links and TypeScript syntax in all five new code blocks. Two Mermaid diagrams were inspected as source. The pinned released-API review is described above; the late-install ordering concern is not a reproduced runtime failure. No SDK source changed, and no application lint/type or runtime-conformance claim is made. The contract remains open with final acceptance pending owner review.
dvcolomban commentedon Sep 22, 2026
Contribution contract decision ledger
Accepted decisions from the live review of Contribution and realm contract. This ledger distinguishes owner decisions from earlier proposal packets. It does not close the contract or claim an implemented SDK.
Previously settled requirements
Interview round 1: owner decisions
1Aservices,actions,views,transformsandscripts. Contribution is shared terminology/typing; a mixed built-in collection is not the canonical declaration shape.2Awebextanddevserverenvironment families. Devframe/Vite integration differences belong in adapters/native contexts. Adding or changing realm families must remain straightforward through descriptors and adapters.The owner explicitly requires the agreed architecture to be recorded in
GLOSSARY.mdandARCHITECTURE.md, with detailed definitions, tables, schemas and diagrams, after the architecture/declaration interview settles it. The current root glossary records agreed vocabulary in the meantime; the canonical documents must not present remaining proposals as accepted.Correction to the earlier implementation-selection question
The previous question conflated two decisions:
Both are required. A host may have a WebExtension provider and one or more development-server providers available simultaneously. A shared UI can route a development target to its development server and a deployed staging/production target to the extension provider. The same host may also apply capability-specific preferences, caller overrides, callbacks and broadcast.
The earlier recommendation for explicit host installation did not and must not narrow the original runtime routing scope. Provider installation declaration details remain subject to the packaging/lifecycle contract; dynamic provider routing is an accepted requirement, not a new optional feature.
Proposed identity and routing relationship
The following diagram makes the accepted multi-realm host requirement concrete. The model uses separately identifiable providers with their own realm metadata; exact connection and dispatch rules remain under review.
URL-sensitive policy refers to the inspected target, not the popup's own extension URL. The generic SDK must not contain company domains, hard-coded staging/production classifications or assumptions that every development origin is localhost. The host supplies target policies and provider metadata.
Adding a realm requires an exported descriptor and an adapter/integration that advertises eligible implementations. It must not require modifying an exhaustive core realm union or teaching the JSON renderer a new backend. A new realm can initially support only some capabilities, with explicit unsupported outcomes for the remainder.
Still open at the core/routing boundary
Provider discovery and routing already owns detailed selection, ambiguity, cancellation, broadcast and reconnect behavior. The current contribution contract must expose compatible definition/context integration points rather than settle that ticket implicitly.
The next interview must clarify:
Later declaration questions include schema representation, descriptor/version compatibility, local/remote context narrowing, activation failure and async teardown. Accepted answers must be consolidated into the canonical architecture documents once the core review is complete.
Status
The contract remains open. No SDK runtime, canonical architecture declaration or routing implementation is delivered by this decision ledger. This commit records the owner's accepted choices and corrects the previous installation/routing conflation.
Review artifact
Local ticket-scoped commit:
98f1fe3ondvcol/wayfinder/contribution-realm. The branch is not pushed; this comment contains the complete accepted-decision ledger. Documentation whitespace, fence and local-link checks passed. No SDK source changed; application lint/type checks are not applicable. The remaining core and routing questions are still open.dvcolomban commentedon Sep 22, 2026
Accepted routing and duplicate decisions
Continuation of the live Contribution and realm contract review. These answers refine the contract; they do not complete its Definition of Done.
extensionscollection. Built-ins retain dedicated properties; arbitrary top-level plugin keys remain invalid.The Q8 clarification is stricter than the offered recommendation, which also allowed fallback after confirmation that execution never started. The accepted rule removes that exception. A separately requested invocation starts a new routing decision; the SDK must not silently create one to replay the dispatched call. The detailed definition of dispatch, connection races and outcomes remains in Provider discovery and routing, subject to this boundary.
The Q10 clarification supersedes unconditional rejection, but does not yet select reference counting, shared disposal handles or ownership transfer.
Two different identity rules
Different providers can implement the same capability. The duplicate check is local to the provider's owning registry, not a global prohibition across the host. A serialized remote description cannot prove local pointer equality.
Portable installation already accepts service definitions. The next decision is whether the duplicate exception compares that definition object before setup, or the running API object produced by setup. Comparing the definition is recommended because a registry can reject a different recipe before it creates resources. This recommendation is not yet accepted.
Allowing two owners to install the same definition also requires explicit disposal semantics. A recommended proposal is one constructed service with a separate ownership lease per installation; disposing one lease leaves the other installation active. A stricter alternative permits repeated references only within one owner. Neither policy follows automatically from reference equality. HMR creates a new object and therefore needs an explicit replacement lifecycle; structural equality must not bypass duplicate checks.
Schema evidence for the next decision
A read-only follow-up inspected SHA-512-verified
devframe@1.0.0and@devframes/json-render@1.0.0artifacts. There were no new runtime experiments.Devframe already accepts Standard Schema validators, supports asynchronous RPC validation, and infers schema-backed arguments and returns from the schema input type. Its validation discards transformed values. JSON view custom validators must be synchronous, and
patchState()does not rerun the view schema. Public JSON Schema export is a separate operation, with a permissive fallback when conversion is unavailable. Our contract must not mistake that fallback for faithful metadata.Sources: RPC definitions, RPC validation, view validation, schema metadata conversion.
Next review round, recommendations only
Q11: Which service object does the identity exception compare?
Recommend A. The portable API already accepts definitions. B requires executing a second recipe before discovering whether it conflicts, and must account for any resources that recipe created.
Illustrative interpretation under A, pending approval:
Q12: May different plugins own the same accepted service instance?
Recommend A. This permits the same shared definition in two plugin service lists without allowing either plugin's disposal to break the other. Returning one shared disposable handle would not provide that guarantee.
Q13: Which runtime-schema interface should declarations accept?
Recommend A for upstream alignment. The following round must still decide normalization and metadata export; choosing Standard Schema does not silently settle either.
Q14: How should capability contract versions match?
Recommend A initially. Contract versions are separate from package release versions. Compatibility adapters can explicitly satisfy another contract version.
Q15: After declaration validation succeeds, how far should a setup failure propagate?
Recommend B. A debugger setup error should leave an independent status view usable. Malformed declarations and duplicate conflicts remain registration errors; this question concerns setup failure after admission.
Deliverable status
Local ticket-scoped commit:
d6b0cd0, not pushed. Updateddocs/planning/005-decisions.mdand the schema evidence indocs/planning/005-declaration-alignment.md. Markdown structure, local links and whitespace checks pass. Application lint and type checks do not cover this Markdown-only change.The issue remains open. After the declaration review settles, consolidate the agreed vocabulary and full API into
GLOSSARY.mdandARCHITECTURE.md, including the requested tables, diagrams and example/test obligations. No SDK implementation is delivered by this update.dvcolomban commentedon Sep 22, 2026
Standard Schema, numeric contracts and observable setup failures
Continuation of the live Contribution and realm contract review. The owner accepted the schema interface, numeric exact versions and isolated setup failure behavior. The owner also proposed replacing the pointer-identity exception with simpler duplicate handling controlled by a shared strictness toggle.
Proposed common strictness policy
Use a host option such as
strict: true, enabled by default, for duplicate registration and contract mismatch diagnostics. This table is a recommendation awaiting owner confirmation, not permission to silently redefine relaxed behavior.Apply duplicate checks during startup registration as well as runtime installation, before the incoming setup callback creates resources. The proposed rule rejects the same definition object too; it needs no pointer comparison. One provider's duplicate rule does not prevent a different provider from implementing the same capability.
The existing registration wins only within a known registration order. Concurrent startup declarations require deterministic admission rather than choosing whichever asynchronous setup finishes first. The exact startup conflict grouping and failure result belong in the complete registration API review.
Only inspect a version mismatch when resolving or installing the relevant contract; advertising an unrelated version must not fail the entire host. A host may select another compatible provider before dispatch when the existing routing policy permits it. An exact provider pin and the prohibition on routing after dispatch still apply. Relaxed mode changes error handling, not the exact-match rule.
This proposal introduces no implicit service sharing, ownership transfer, replacement or use of an incompatible contract. Actual setup failures retain Q15's failure behavior in either mode. HMR replacement and async cleanup remain explicit lifecycle decisions.
Failure reporting into logs and UI
The accepted requirement is observable failure without removing independent contributions. The proposed integration records the failed contribution's status and a serializable diagnostic identifying its provider, plugin, contribution and lifecycle phase. Runtime logs report the failure; an attached client can render the same diagnostic. A later UI mount reads current failure status, so an error before panel creation is not lost merely because no UI listener existed.
Keep the original native exception in its owning execution. Pass a serializable diagnostic through the existing status/transport integration, rather than requiring a separate diagnostics backend or a renderer to make setup work. The exact status, error and subscription signatures remain part of the complete API sketch. Lifecycle failure reporting does not itself settle how individual operation calls return or throw errors.
Independent contributions keep running. A status view that itself requires the failed service is a dependent contribution and cannot be used as the only failure display. The host's generic failure presentation must remain independent of that service.
Next review round, recommendations only
Q16: Accept one shared strictness policy with strict mode enabled by default?
The table above proposes
strict: trueby default. Duplicate definitions always lose the incoming registration, including repeated references to the same object. Strict mode reports an error; relaxed mode warns and returns a skipped result. A version mismatch remains unavailable in either mode. The toggle changes severity/handling, not compatibility or ownership. Recommend this policy. It removes pointer checks and shared leases while preserving numeric exact matching.Q17: Should schemas only validate, or also transform values?
Recommend A for alignment with the audited Devframe RPC path, which discards transformed output. The tradeoff must be explicit: schema transforms do not run as application normalization under A, and handler/result types must not pretend they do. B requires consistent parsed-value handling and input/output inference across adapters.
Q18: Must every public definition export JSON Schema metadata?
Recommend B. Standard Schema validation remains mandatory for public wire operations. Basic identity/version discovery does not require exporting a validator as JSON Schema. An integration requiring export must report missing or unsupported export explicitly; it must not invent a permissive schema. Metadata is a structural description and does not replace local validation or authorization.
Q19: When a required capability returns, should the host reactivate the dependent contribution automatically?
Recommend A. This makes the host responsible for capability availability changes while keeping programming failures visible instead of retrying them repeatedly. The exact async cleanup and replacement sequence follows this decision.
Evidence and deliverable status
The schema recommendation uses previously inspected, SHA-512-verified
devframe@1.0.0release code. This update adds no new runtime experiment. RPC validation, schema inference, Standard Schema, Standard JSON Schema.Local ticket-scoped commit:
a573d4f, not pushed. Updated the decision ledger, declaration-alignment evidence and the glossary's contract-version term. Markdown structure, local links and whitespace checks pass. Application lint/type checks do not cover this Markdown-only change.The issue and its Definition of Done remain open. Registration/skip results, operation error signatures, schema typing, diagnostics, local/remote contexts and lifecycle replacement still need a complete API review. Consolidate the settled contract into
GLOSSARY.mdandARCHITECTURE.md, with the requested definitions, tables, diagrams and example/test obligations, after that review. No SDK implementation is included.dvcolomban commentedon Sep 22, 2026
Accepted strictness, schema and restoration decisions
Continuation of the live Contribution and realm contract review. The owner accepted Q16 through Q19. These answers replace the earlier pointer-identity exception and settle schema behavior.
The Q16 answer supersedes the historical Q10 pointer-identity exception. The latest policy compares registration identities for conflicts and does not compare definition or running-service pointers to permit duplicates.
Q17 also fixes a typing constraint: public handler inputs and successful return types must describe the values actually used. A guard-only adapter cannot advertise
InferOutputas proof of a schema transformation it discards. The complete declaration sketch must derive those types fromStandardSchemaV1.InferInputfor both the input and return schema. Explicit normalization inside an implementation must still produce a return value accepted by the declared wire return schema.Capability restoration and setup retry remain separate triggers. Normal readiness loss can end an activation and later recreate it; a thrown setup error must not enter an automatic retry loop merely because its dependencies remain available. A permission or target error from one operation does not by itself remove a provider-wide service registration.
Consolidated API direction
Dedicated plugin properties, definition-only installation, multiple providers/realms, explicit descriptors and native host bootstrapping remain the core structure. The new review packet connects registration ownership, activation generations, schema-derived methods, local/remote native contexts and the existing domain tickets. It does not introduce a new generic host runtime or settle the pending alternatives silently.
Standard Schema checks original values. Its successful parsed output does not replace a handler argument or result. Consumer implementations may normalize explicitly, provided their returned wire values satisfy the declared return schema.
The following candidate illustrates type derivation for ordinary value-returning calls, pending Q22. These are proposed types, not published exports.
targetplacement remains Q23; it is intentionally absent from this minimal derivation.Definition helpers must preserve each supplied schema's precise type instead of widening it to the base
OperationDefinition. Input and output names refer to operation direction;InferInputon the return schema describes the original return value that this contract validates. AdvertisingInferOutputwould promise transformations the runtime does not apply.JSON Schema export is required by integrations that need schema inspection, rather than every public definition. An integration must report missing or unsupported conversion. Exported metadata never replaces the validator or permission checks.
Local and remote context sketch
Use an explicit access discriminant, alongside provider/realm/execution metadata:
nativefield after narrowing to remote accessThe proposed accessor is
context.native.get(importedDescriptor), returning its native type orundefined. A realm label alone must not imply privileged background APIs or a running Vite server. A local Vite adapter may expose Devframe, hub and kit contexts together. The contract must not turn those layered contexts into mutually exclusive realms.The complete signatures must prove literal descriptor inference, custom realm/native descriptors, remote exclusion and absence handling. Native resources and executable setup functions never travel in a serialized descriptor.
Lifecycle and diagnostics
Each service registration has one owner. Consumers receive bindings, not ownership leases. Strict duplicates fail the incoming registration; relaxed duplicates warn and skip it before setup. An installation handle is proposed to expose current status, subscriptions, enable/disable, explicit setup retry and async disposal. A plugin handle observes child contribution status, including partial readiness.
Required capability loss ends the affected activation; restoration recreates it after cleanup. Setup failure cleans up that contribution and invalidates dependents while independent contributions remain active. Current status and serializable diagnostics support logging and UI display, including a panel opened after the failure. The exact admission and cleanup failure boundaries are still questions below.
Remaining core review questions
Q20: Strict errors during batch admission
Q15 fixed isolation after setup begins; it did not settle invalid startup declarations discovered before setup. Consider two service definitions claiming the same slot in one startup batch with strict mode enabled.
Recommend A. It separates invalid composition from runtime setup failure and gives strict mode a predictable admission boundary. Relaxed duplicates still warn/skip. This does not change Q15's isolation for setup failures in an admitted batch.
Q21: Service versions in one provider
Should one provider be able to offer both version 1 and version 2 of the same capability ID at the same time?
Recommend A. It supports explicit compatibility implementations while preserving exact matching and no negotiation. Registering the same ID/version twice still conflicts, including repeated object references. Different provider instances remain independent under either option.
Q22: Individual call results
Promise<Value>and reject on failure. Publish stable error codes and a runtime narrowing helper. Availability and lifecycle remain observable separately.Recommend A to preserve upstream call/return-schema semantics. TypeScript does not encode a rejection type in
Promise<Value>, so this is a runtime-classified error contract, not statically typed exceptions. The already-required broadcast routing API still needs separate per-provider outcomes; that does not require wrapping every service method.The earlier architecture packet proposed B. This revision makes the compatibility tradeoff explicit instead of adopting that wrapper by default.
Q23: Target placement
Recommend A. A target-bearing call must not carry two independently editable target identities in routing options and business input. The operation descriptor must specify whether a target is required or absent; the exact typing follows this choice. Browser target data remains a validated transport value, and targetless calls such as provider configuration remain possible. A business operation can still reference other subjects in its payload; those do not silently change its execution target.
Q24: Cleanup failure before replacement
If cleanup rejects or never completes during reactivation or HMR replacement:
Recommend A. This preserves the accepted requirement to end the previous activation before starting another. Async teardown should be awaited and repeated disposal should refer to the same teardown attempt. An abort signal requests cooperative cancellation; it cannot prove that old native work stopped or undo external effects.
The concrete reset/reload choice, timeout and recovery from browser worker termination belong to the reload and state tickets. The core contract must state what qualifies as the old activation ending.
Released API evidence
A read-only follow-up checked SHA-512-verified
devframe@1.0.0and@vitejs/devtools-kit@0.7.5artifacts. No new runtime experiment or latest-release claim is made here.kindvalues for connection, authentication and timeout. Status/events and named RPC diagnostics are separate observability mechanisms. Preservation of native exception prototypes/codes through every future adapter is not established. Connection errors, RPC diagnostics.Validation and deliverable status
Local ticket-scoped commit:
c7ad39e, not pushed. Addeddocs/planning/005-core-api-review.mdand updated the accepted decision ledger and schema-alignment notes.The exact method-type snippet above passed isolated TypeScript 7.0.2 checks with
@standard-schema/spec@1.1.0and Zod 4.6.5 fixtures. Strict mode, unchecked-index checks, exact optional properties and library checking were enabled. Positive fixtures preserve original string input/return types for schemas declaring string-to-number transforms. Four negative fixtures reject transformed input/result types, undeclared methods and missing required input. The same fixture passed Oxlint correctness, suspicious and pedantic categories with warnings denied. Markdown structure, local links and whitespace checks also pass.This proves the small type derivation, not a running SDK, adapter, helper-inference API or error-transport implementation. No full-repository validation was run.
The issue remains open. After the remaining core choices and complete signature review, consolidate the agreed glossary and architecture into
GLOSSARY.mdandARCHITECTURE.md. The new packet links all existing domain tickets, including the exact example/API-coverage obligations, so their unresolved signatures remain explicit planning dependencies. Do not mark the Definition of Done complete or begin SDK implementation on the basis of this partial type proof.dvcolomban commentedon Sep 23, 2026
Resolution: core architecture and declaration contract
The owner accepted the final core decisions, Q20–24, after the earlier four review rounds. This resolves Contribution and realm contract as a design decision. Runtime packages and full host conformance remain implementation work.
Download the canonical contract and review history.
The archive contains
GLOSSARY.md,ARCHITECTURE.md,CONTEXT.md, the complete declaration specification indocs/contracts/core.d.ts, compile-only positive/negative fixtures, reproduction instructions, the API/example/test matrix and the historical review packets. Its source is local ticket commit195e066, followingc7ad39e, ondvcol/wayfinder/contribution-realm. The branch is not pushed. Archive SHA-256:dc7e8fc69929cac5d371383952902fe446695462875b92850eb1b132851f120f.Final owner decisions
Promise<Value>and reject on failure, with portable diagnostics and runtime narrowing. Broadcast outcomes belong to the routing contract.Vocabulary and declaration boundaries
A capability describes a versioned backend service contract; a service definition supplies its implementation recipe. A contribution is an owned addition such as a service, action, view, transform or script. A plugin groups dedicated typed lists of contributions under one installation. A plugin package may also export contracts; exporting a contract does not implement it.
A host composes providers, clients, adapters and policies. A provider is a backend identity. Its realm describes an extensible environment family, initially
webextordevserver. An execution identifies the actual worker, process or document; a surface identifies presentation. These are separate axes. Local Devframe, hub, kit and Vite contexts can coexist within one server provider.Use explicit imported descriptors and one
definePlugin. Built-ins have dedicated properties; custom kinds use validated descriptor-taggedextensionsand an explicitly installed kind handler. Definitions remain inert and separate from runtime, native implementations and rendering. Startup and dynamic installation accept the same service recipe. No generic plugin-entry wrapper, implicit raw-object provider, global augmentation, pointer-identity duplicate exemption or shared ownership lease is required.Representative authoring
The full types and compile-checked fixture are in the archive. This abbreviated example uses imported schemas and an execution descriptor:
The action descriptor is client-safe; its packaged handler executes in the provider. A JSON view references the action contract without importing the handler. A UI-free plugin needs no renderer. A server or browser recipe implements
pagewithdefineService, obtains its actual native objects throughcontext.native.get(importedDescriptor), and registers owned cleanup withcontext.scope.onDispose.A local binding exposes
apiandcontextwithaccess: 'local'and typed native lookup. A remote binding hasaccess: 'remote'and no native member. Native resources never cross messages. A bound API remains pinned to its provider; a freshcapabilities.invoke,actions.invokeor resolution can select another provider before dispatch. Once dispatched, the call never reroutes or replays automatically.Standard Schema validates original wire inputs and results. Both types derive from the respective schema's
InferInput; transformations belong in consumer handlers. Schema export is required only by integrations that need it, with explicit unsupported export instead of permissive fabricated metadata.Ownership and lifecycle
retry.snapshot,subscribe,enable,disable,retryand asyncdispose. Admission is distinct from readiness.The architecture includes dependency, execution, lifecycle and invocation sequence diagrams, installation-state definitions, admission boundaries, replacement rules and error requirements. Raw native APIs remain available in their eligible context. Adapters reuse upstream host RPC/state/JSON contracts; they do not replace those systems with a second generic host engine.
Validation and complete coverage obligations
All 61 public exports appear in
CORE-API-MATRIX.md, mapped to named example/fixture obligations for standalone Devframe, Vite DevTools, Chromium and Firefox. It includes custom realms/native contexts/kinds, UI-free contributions, version coexistence, target errors, duplicate admission, partial failure, subscriptions, cleanup, replacement and client bundle boundaries. Domain-specific APIs must extend that inventory before implementation admission. Planned runtime cells are clearly marked as obligations, not completed tests.The full declaration and fixture compile with TypeScript 7.0.2, Standard Schema 1.1.0 and Zod 4.6.5 under strict mode, unchecked-index checks, exact optional properties and
skipLibCheck: false. 25 expected-error fixtures check missing contracts/targets/methods, invalid return types, guard-only schema inference, undeclared requirements, remote native access, incorrect plugin fields/kinds and extension payloads. Oxlint 1.85.0 correctness, suspicious and pedantic categories pass with warnings denied. Document links, fences and whitespace checks pass. No full-repository validation or SDK runtime-conformance claim is made.Follow-on ownership
Exact native host interfaces belong to Server adapter contract. Dynamic selection, cancellation and broadcast remain in Provider discovery and routing; persistence/recovery in State scope and recovery; authorization in Permissions and trust. Debugger and CDB contract, Injection and transform contract, Renderer and surface contract and Live preview and reload contract own their domain operations. Complete execution evidence belongs to Examples and API coverage contract, Portable contribution proof and Release and conformance contract.
The owner explicitly authorized continuing the map and implementation after the architecture settles until another material choice needs input, with commits separated by ticket. This overrides Wayfinder's default one-ticket-per-session stopping rule; it does not let the agent answer unresolved owner decisions.
dvcolomban commentedon Sep 23, 2026
Core implementation progress
Local commit
5c3469bimplements the reviewed declarations and the first owned runtime components ondvcol/implementation/foundation. This does not reopen or change the resolved core decisions. The branch remains local and no upstream PR has been opened.Reviewable source archive through
76b09e4, SHA-256aaaec5d63ce427ed4c85b57eeb062c4b29d4f72053b52cfdf42fba9a26ac3d4a.Implemented
packages/coreexposes all 61 agreed exports, including the 12 executable factories/error guard. Declarations are inert and validate IDs, numeric contract versions, schema protocols, explicit properties and contribution kinds. Factories snapshot and freeze their owned declaration records and collections without freezing application schemas, payloads or functions. This prevents mutable aliases from changing a capability's identity or requirements after registration. Shared contract imports contain no backend or renderer dependency.packages/runtimeis a private adapter implementation package. It owns:Executed checks
76b09e4adds real tarball installation outside the workspace, Bundler and NodeNext strict declaration consumers, runtime execution and a browser bundle dependency audit. It is a separate issue-16 check, not real extension-host evidence.Remaining work
Installation handles, dependency reconciliation, provider-local bindings and action execution are being composed next, together with a maintained runnable contribution example. Native server/extension adapters, dynamic routing, state, permissions, debugger, transforms, renderer surfaces and HMR still belong to their domain contracts and full host tests. The API proof matrix records these remaining obligations; unit tests and declarations do not mark its four-host cells complete.
dvcolomban commentedon Sep 23, 2026
Local provider lifecycle implemented
Separate commit
8304803implements the accepted core ownership contract in the private@devkit/runtimepackage. The earlier declaration/activation commit is5c3469b. Both are local and unpushed. Committed source checkpoint through 57f3381 includes the complete implementation, canonical architecture/glossary, tests and example.Archive SHA-256:
83a7e3788718bb15b3a1f97f98d341c1da41e800033f3673ba199a0df46818d6.The private adapter entry point now composes real installation handles, dependency activation, status and local calls:
Strict startup preflights the complete batch before setup. Relaxed duplicates return explicit skipped results. Waiting contributions activate when dependencies return, explicit disable survives restoration, and setup failures require explicit retry. Independent failures remain isolated. Status subscriptions deliver the current snapshot and isolate failing listeners.
Replacement validates the successor before retiring the current owner. Old and successor identities remain reserved during cleanup. Unrelated installation and calls continue while cleanup waits; dependency cycles are checked against both possible graphs. Replacements themselves serialize per provider. Failed cleanup retains ownership and blocks a successor. A relaxed skipped service replacement leaves the old service active.
Regression tests cover synchronous abort-listener reentry, dependency teardown when a nested call omits signal forwarding, late setup completion, cleanup order/failure, invalid replacement preserving the old owner, conflicting replacement claims, provider-owned schema validation and prototype-shaped operation names. Native causes reach the local diagnostic sink separately and never appear in portable status records.
Validation passed for this package: 55 runtime tests, strict TypeScript 7, type-aware Oxlint with warnings denied, Oxfmt, Vite build and declaration emit. The final core/runtime tarballs also pass clean Bundler/NodeNext consumer checks and a portable browser bundle check. Core's separate existing suite has 53 passing tests and 25 negative type fixtures.
This completes another local implementation slice of the already-settled decision. It does not complete provider routing, transport, shared state, renderer, scripts/transforms, real server/extension adapters or their host matrix.
packages/runtime/README.mdanddocs/contracts/CORE-API-MATRIX.mdrecord the exact local evidence and remaining obligations. The maintained contribution example is reported under issue 14.refactor(core): keep resource applicability in operation inputs (#5)
test(core): bind negative fixtures to formatted properties (#5)
dvcolomban commentedon Sep 27, 2026
Implemented the owner-approved amendment in c374198: schema-defined operation inputs now own resource references; client selectors retain realm/provider routing. Removed universal
TargetReference/TargetRequirement, target modes, common target context/options and catalog target metadata. Runtime, client, server exposure, examples and packed-consumer fixtures are migrated together.The canonical architecture includes the multi-client/backend diagram, ownership table, code sketch and result/state distinction. Glossary and maintained package documentation agree; earlier target-mapping proposals are explicitly superseded.
Validation: 75 core, 79 runtime and 31 client tests; affected builds, TypeScript 7 and strict Oxlint/Oxfmt; isolated Bundler/NodeNext consumers and browser bundles. CI initially caught formatter-moved
@ts-expect-errordirectives; adc48d7 puts them on the exact invalid properties. Full repository CI passes.The native two-client/two-backend applicability proof is a separate ticket #6 commit. No generic acceptance hook, topic bus, new authentication layer or codec was added. This is an amendment to the closed design contract, not a claim of complete host conformance.