pluginsdk/docs/adr/0002-keyed-load-once-hooks-have-durable-host-receipts.md
2026-08-10 23:04:47 +08:00

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