2.1 KiB
Keyed load-once hooks have durable host receipts
Plugin Load callbacks are deliberately repeatable: startup, enable, update,
and provisioning-consent replay can all invoke them. Some initialization is
different. A plugin may add a suggested menu link on its first load, but must
not restore that link after an administrator later removes or renames it.
Decision: PluginRegistration.LoadOnce is an ordered slice of callbacks with
stable keys. DESCRIBE publishes those keys in PluginManifest.load_once_keys.
The host checks a durable (plugin, key) receipt before dispatching
HOOK_LOAD_ONCE; it records the receipt only after success. A completed key is
not removed when the plugin is disabled, updated, or uninstalled. A later
release that intentionally needs another one-time action declares a new key.
Load-once runs after the ordinary Load hook so repeatable provisioning can
create resources that a one-time callback references. Keys are lowercase,
MCP-style stable identifiers of at most 128 bytes and must be unique within a
registration. Failed callbacks may be retried because no completion receipt
exists. As with database migrations, callbacks should still make their own
external mutation idempotent to cover a process failure between the mutation
and receipt commit.
Encoding first-install booleans in plugin settings was rejected because it mixes host lifecycle state with customer-visible configuration. Making all provisioner operations one-shot was rejected because pages, settings defaults, and other declarations intentionally reconcile on every load. Version-based execution was rejected because an unrelated release must not repeat the side effect.
Consequences:
- Native and WASM plugins share one keyed lifecycle abstraction.
- The host, not guest memory, owns durable completion state.
- Updating or re-enabling a plugin cannot recreate an administrator-removed resource under an already completed key.
- Changing a key is an explicit request to run a new callback once.
Keywords: PluginRegistration.LoadOnce, LoadOnceHook, load_once_keys, HOOK_LOAD_ONCE, durable receipt, first load, plugin update, idempotency, one-time provisioning, lifecycle