Scoped Instances and Nested Composition
Up to this point, every embedded plugin instance has assumed a single storage domain.
That works for:
- Simple add-ons
- Single-instance tools
- Linear workflows
But it limits composition.
What happens if:
- You embed the same plugin twice?
- You want two encoders with different settings?
- A host embeds another host?
- You build nested systems?
Static storage resolution breaks down.
We need instance isolation.
Earlier, in Chapter 9, we hinted at a more scalable routing model. We now extend that idea into scoped composition.
The Limitation of Static Storage
In the single-instance model:
shared_key → KEYMAP → Scene.some_pointer.some_attr
All plugin instances share the same storage location.
This prevents:
- Multiple independent instances
- Nested hosts
- Scoped isolation
To unlock composition, storage must become scoped.
Introducing Scope
A scope represents one logical instance of a plugin inside a host.
Instead of resolving shared values globally:
key → value
We now resolve:
(scope, key) → value
Each mounted plugin instance receives its own scope.
Scope is allocated and owned by the host.
Binding a Scope for a Mounted Instance
HostAPI v2 introduces:
host_api.bind(context, instance_hint=None)
During hosted runtime execution for a mounted instance:
- The host/runtime allocates or selects a scope.
- A new HostAPI bound to that scope is returned.
- All shared resolution occurs within that scope.
Plugins do not manage scope.
Hosts do.
What Changes for Plugins?
Nothing in the tool source.
The generator ensures:
- Shared keys remain declarative.
get()anddraw()operate through the bound API.- Plugins remain unaware of storage paths.
The change is architectural, not behavioral.
Scoped KEYMAP
Routing must now consider scope.
Conceptually:
(scope, key) → host-owned property
If no host-owned mapping exists:
- Scoped fallback storage is used.
Each plugin instance is isolated unless the host explicitly routes keys together.
HostAPI v2 enables the scalable routing model we anticipated earlier.
Nested Composition
Scopes make nested composition possible.
Example:
- Host A embeds Host B.
- Host A mounts a root instance.
- Host-owned storage remains the ground.
- Child systems may later mount child instances on top of the same scoped model.
Each layer isolates its instances.
No collisions. No implicit sharing. No accidental overwrites.
Composition becomes predictable.
Storage Backend
Scoped storage must not rely on dynamic RNA property creation.
Recommended fallback backend:
scene["qa_scope:<scope_id>:<fallback_prop>"]
Hosts may still route specific keys to typed PropertyGroups.
Fallback storage, however, must remain dynamic and scope-aware.
Multi-Instance Example
Imagine embedding the same plugin twice:
audiodeck/
├── encoder_A
└── encoder_B
Each registration calls bind().
Each receives a unique scope.
Each has independent values for:
project.audio_pathproject.bitrateproject.output_dir
They do not interfere with one another.
Responsibility Boundaries
Plugins:
- Declare shared keys
- Remain stateless across scopes
- Do not access scene storage directly
Hosts:
- Allocate scopes
- Own scoped storage
- Maintain scope-aware routing
- Decide when scopes intentionally share values
Isolation is a host responsibility.
When Should You Use Scoped Instances?
Scoped instances are necessary when:
- A plugin may appear multiple times
- Nested composition is required
- Isolation is critical
- Complex systems are being built
Simple add-ons may not need it.
QuickAddon supports both models.
Scoped instances transform QuickAddon from:
an add-on generator
into
a composable UI architecture framework.
At this point, you may want to review the formal specification: HostAPI V2 Spec.