packages/plugin-sdk/docs/design/capability-orchestration.md
Define a capability-oriented orchestration model for AIRI plugins and modules where dependency resolution is runtime-driven instead of static metadata-driven. Plugin Host coordinates module lifecycle using a stateful capability registry and readiness gates, enabling multi-runtime deployments (Electron, Web, Pocket) without hardcoding stage-first boot order.
Current plugin lifecycle in PluginHost is phase-based but does not yet enforce dynamic dependency waiting between modules and platform-provided APIs. In practice, APIs like provider listing can be available only after platform runtime, stage runtime, and UI/store initialization complete. This creates race conditions when modules initialize before required capabilities are actually ready.
The AIRI ecosystem is moving toward:
The design must avoid privileged hardcoded ordering such as "stage always loads first", while still giving deterministic and testable startup behavior.
Introduce a host-owned capability registry and make module initialization capability-aware:
announced/preparing phase).waiting-deps until requirements resolve.Readiness and availability are stateful in registry snapshots, with event notifications as incremental updates.
AIRI plugin orchestration should behave like an extensible runtime kernel:
Capability-first lifecycle orchestration with dynamic dependency resolution.
Proposed host-side phases for each module:
loadingloadedauthenticatingauthenticatedannouncedpreparingwaiting-depspreparedconfiguration-neededconfiguredreadydegradedfailedstoppedKey rules:
preparing -> waiting-deps if required capabilities are unresolved.waiting-deps -> prepared when all required capability predicates resolve.ready -> degraded when previously bound capability is withdrawn/degraded.degraded -> ready when dependency set becomes healthy again.Registry properties:
hostId, instanceId, and runtime.Capability record baseline:
interface CapabilityRecord {
capabilityId: string
providerModuleId: string
hostId: string
instanceId?: string
runtime: 'electron' | 'web' | 'pocket' | 'node'
state: 'announced' | 'ready' | 'degraded' | 'withdrawn'
version?: string
health?: 'ok' | 'degraded' | 'unknown'
metadata?: Record<string, unknown>
}
Requirement baseline:
interface CapabilityRequirement {
allOf?: string[]
anyOf?: string[]
predicate?: (record: CapabilityRecord) => boolean
timeoutMs?: number
}
Stage provider catalog dependency chain
Multi-stage instance isolation
instanceId.instanceId scope.Pocket runtime API extension
mobile.sensor capability.Late readiness and replay safety
waiting-deps when required capabilities are missing.instanceId prevents cross-stage binding errors.preparing -> waiting-deps -> prepared.ready to degraded.Proposed.
packages/plugin-sdk/src/plugin-host.waiting-deps and degraded transitions to host state machine and tests.Q: Why not use static plugin dependency metadata? A: Runtime capability dependencies are more flexible for multi-stage and multi-runtime setups where availability depends on live initialization state, not package declarations.
Q: How does this avoid missed readiness events? A: Registry snapshot is authoritative. Events are incremental signals only; late consumers always query snapshot state.
Q: Does this force one core stage ordering? A: No. Ordering emerges from capability availability and requirement predicates, not privileged host rules.