Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

  1. The host/runtime allocates or selects a scope.
  2. A new HostAPI bound to that scope is returned.
  3. 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() and draw() 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_path
  • project.bitrate
  • project.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.