原生插件开发
从 manifest 到 engineering validation 的完整公开开发路径。
# Manifest
A manifest binds package identity, type, entrypoint, declared capabilities, dependencies, resources, owned data, migration metadata and uninstall policy.
{
"manifest_version": "1.0",
"id": "example.greeting",
"version": "1.0.0",
"package_type": "business-module",
"entrypoint": "Example\\Greeting\\GreetingRegistrar",
"capabilities": ["event.listen"],
"requires": [],
"optional_requires": [],
"events": [{"id": "example.greeting.render.v1"}],
"data_ownership": [],
"uninstall_policy": "PRESERVE"
}# PluginRegistrar
PluginRegistrar has one method: register(RegistrationContext $context): void. It declares handlers, resources, configuration and presentation contributions. It must not execute product work during registration.
# RegistrationContext
The current declaration surface includes onEvent(), onLifecycle(), template(), asset(), configSchema(), themeProvider(), presentationContribution() and presentationDataProvider().
# Lifecycle
Upgrade temporarily enters UPGRADING; uninstall enters UNINSTALLING and ends at UNINSTALLED. Invalid transitions fail closed.
# Capabilities
Declaration is necessary but not sufficient. Package type policy can allow, conditionally approve or deny a capability. Runtime services re-check grants. A capability never grants another package’s private data namespace.
# Dependencies
Dependencies target a package, provider or contract with an explicit version range. Required dependencies must exist. Optional dependencies require a deterministic fallback. Dependency cycles fail closed.
# Typed Events
The host defines event mode, payload validation, contribution validation and failure policy. Packages register only manifest-declared handlers. Dispatch ordering is deterministic, and disabled packages do not receive events.
# RuntimeContext
RuntimeContext exposes immutable package identity and grants plus scoped data, configuration, cache, templates, assets, logging and normalized request input. Package code never supplies an arbitrary package id to these handles.
# Configuration
Register a strict package schema with configSchema(). Reads and writes remain package-scoped and invalid values fail closed.
# Cache
Use the scoped cache handle. Keys are namespaced by package identity and write operations remain capability-gated.
# Logging
Use the scoped logger. It adds package identity and preserves structured diagnostics without exposing host internals.
# Engineering validation
Test manifest failures, capability denials, dependency order, exact lifecycle transitions, migration replay, ownership isolation, event ordering, configuration failure, resources, stable diagnostics and deterministic reruns. The host—not package code—assigns ENGINEERING_VALIDATED.