DSH plugin client slots: cardinality, scope and props
DSH plugin Web UI extension is not "export a component and let others import it"; it is a typed composition system where SlotMap declares slots at compile time and feature plugins contribute through ctx.slots.register(), with components never importing each other. Two consequences follow directly: props are derived from declarations, so do not re-copy members, and registration and declaration follow the effect lifecycle, so contributions collapse recursively when the owner unloads. Those two rules prevent the three most common problems: missing session values, duplicate registration errors and leftover UI after unload.
DSH plugin slot declarations and lifecycle
Bottom line: declaring a child does three things at once, and each declaration allows exactly one live owner.
- Three effects: the child key activates, the parent entry is authorized to call
renderSlot/renderSlotChain, and the runtime dispatch spec is recorded. - Single owner: registering into an undeclared slot, or re-declaring a child another entry owns, fails at plugin activation.
rootis special: the only built-in declaration and the only key rendered by the Cordis service itself; all descendants render through props.- Effect lifecycle: destroying an entry removes its contributions and recursively collapses the child slots it declared.
- Cross-package contribution: use
ctx.slots.inject(key, callback); the callback runs per declaration lifecycle and re-runs when the owner remounts.
DSH plugin cardinalities: count and addressing
Bottom line: single picks a winner, list lays out by id, keyed matches by key, chain competes through select.
| cardinality | Behavior | Use |
|---|---|---|
single | One cell rendering the current priority winner | Exactly one implementation |
list | Entries addressed by required id, ordered by order then registration | Multiple equal contributions |
keyed | Owner passes entryKey; the matching cell renders with that key's props | Contributions addressed by entity |
chain | Each entry provides select(owner); first non-null wins | "Who can handle this" decisions |
- Do not treat single as a list: declare a child slot for side-by-side content.
- Priority semantics: shadowing priority for single/list/keyed, election order for chain; smaller runs first.
- Add with new ids: use a new list
idor keyedkey; reusing an existing cell replaces it intentionally. - Chain fallback: the owner's fallback renders when everything declines.
DSH plugin scopes: whether a session binding is required
Bottom line: root has one instance, session requires a definite session, session-maybe tolerates absence.
- root: one root-scoped component and store instance.
- session: requires a resolvable outer Provider binding and receives a definite Session value.
- session-maybe: inherits the outer binding but renders without one, with an optional session value.
- Subtree override: declaring session or session-maybe children provides
SessionProvider; omitting it inherits, while passing a reference or undefined overrides only that subtree. - Remount rules: strict session entries remount when the binding generation changes; blank session-maybe entries do not remount on first acceptance, only on later generation changes or returning to missing.
DSH plugin component props: derive, do not copy
Bottom line: six input classes each have a declarer; components derive props from types while service objects stay in the closure.
| Input | Source | Type |
|---|---|---|
| Owner and scope values | SlotMap entry and scope adapters | PropsRuntime<K> |
| Authorized child renderers | Entry's children keys | PropsRenderSlots<S> |
| Shared view state | Entry's store | PropsStore<H> |
| Private data and observables | Entry's inject factory | InjectFace<I> |
| Localization function | locale namespace | PropsLocale<N> |
| Chain-selected value | select result | ComposedProps.matched |
- No ctx: components never receive the context object.
- Parent values via owner: values known at render time arrive through
renderSlot's owner argument. - Services stay hidden: service and model objects remain in the
applyclosure, projecting callbacks or observable sources. - Localize through the namespace: do not hardcode copy.
DSH plugin slot troubleshooting
Bottom line: separate undeclared, registration conflict and missing values.
- Registration errors — confirm the target slot is declared and the child is not owned elsewhere.
- Duplicate registration — the same
idregistered twice. - Missing session — check whether the scope is session or session-maybe and whether a binding exists.
- Leftover UI after unload — registration is not on an effect, or inject was bypassed.
- Wrong list order — check
order, then registration order. - Repair status, stated honestly: declaration effects, cardinalities, scopes and input classes follow the official Web Client Slots page; confirm slot keys and types against the official package and your version.
Sources: Web Client Slots, dsh-client-ui-slots README, Client resources.
FAQ
DSH plugin registration and declaration follow the Cordis effect lifecycle, so contributions disappear when the owner collapses. slots.inject callbacks run once per declaration lifecycle, so contributions return automatically when the owner remounts.
DSH plugin single renders the current priority winner, list lays out entries addressed by a required id, keyed matches an entryKey passed by the owner, and chain lets entries compete through select. Declare a child slot when you need side-by-side content instead of treating single as a list.
DSH plugin session requires a resolvable outer Provider binding and a definite session value, while session-maybe can render without a binding and treats the session value as optional. Choosing wrong makes components disappear when no session exists.
No: DSH plugin slot components never receive ctx. Values the parent already knows arrive through renderSlot's owner argument, shared view state uses the declared store, and services stay in the apply closure projecting callbacks or observables.
Declaring a DSH plugin child does three things: it activates the child key, authorizes the parent entry to call renderSlot or renderSlotChain, and records the runtime dispatch spec. Each declaration allows exactly one live owner.
Related Terms
- SlotMap
- SlotMap is the compile-time registry of DSH plugin Web UI slots where packages declare keys, cardinality, scope, owner props, keyed props and an optional inject face through declaration merging. Runtime declarations are the entries an owning component supplies in children.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/slots
- cardinality
- Cardinality decides how many contributions a slot holds, taking the values single, list, keyed or chain. It describes count and addressing only, independent of scope.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/slots
- scope
- Scope decides whether a slot component can receive a session binding, taking root, session-maybe or session. Root has one instance, session requires a definite session, and session-maybe tolerates absence.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/slots
- chain cardinality
- Chain is a DSH plugin slot cardinality where each entry provides a pure select(owner) function, the first non-null result by priority wins and receives matched, and the owner fallback renders when all decline.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/slots
Sources
- DeepSeek Harness docs — Web Client Slots (declarations, cardinality, scope, component inputs, extension rules)· deepseek-harness.github.io
- dsh-client-ui-slots package README (registry and type algebra)· deepseek-ai (GitHub)
- DeepSeek Harness docs — Client resources (slot components receive useResource)· deepseek-harness.github.io