Files
moeka-project/packages/plugin-sdk/docs/design/capability-orchestration.md
T

212 lines
7.8 KiB
Markdown

# Capability-Oriented Module Orchestration
- [Summary](#summary)
- [Background](#background)
- [Context](#context)
- [Goals](#goals)
- [Non-goals](#non-goals)
- [Proposal](#proposal)
- [Vision](#vision)
- [Design Details](#design-details)
- [Lifecycle Model](#lifecycle-model)
- [Capability Registry Model](#capability-registry-model)
- [Use Cases](#use-cases)
- [Verify & Test](#verify--test)
- [Criteria](#criteria)
- [Test & QA](#test--qa)
- [Progress](#progress)
- [Status](#status)
- [Next Steps](#next-steps)
- [Reviews](#reviews)
- [Q&A](#qa)
- [Related Documentations](#related-documentations)
## Summary
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.
## Background
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.
## Context
The AIRI ecosystem is moving toward:
- Multiple plugin hosts (Electron now, Pocket and Web later).
- Multiple stage instances per host/runtime.
- Platform-specific extension APIs that should be discoverable through one control-plane model.
- Plugin modules that may provide capabilities required by other modules and by stage/configurator flows.
The design must avoid privileged hardcoded ordering such as "stage always loads first", while still giving deterministic and testable startup behavior.
## Goals
- Support runtime capability discovery and readiness gating without static dependency metadata.
- Keep lifecycle orchestration host-driven and runtime-neutral.
- Allow module dependencies to target capabilities, not implementation modules.
- Support multiple stage instances and platform-specific APIs in one model.
- Prevent missed readiness signals by relying on stateful registry snapshots, not one-shot events.
## Non-goals
- Defining the final production policy engine (priority, quotas, fairness) in this iteration.
- Designing UI/UX details of configurator pages.
- Finalizing transport federation protocol between all runtimes in this document.
## Proposal
Introduce a host-owned capability registry and make module initialization capability-aware:
1. Modules announce possible capabilities early (`announced`/`preparing` phase).
2. Modules mark capabilities as ready once invokable.
3. Modules declare runtime requirements as capability predicates.
4. Host transitions modules into `waiting-deps` until requirements resolve.
5. Host resumes module preparation/setup when required capabilities become ready.
Readiness and availability are stateful in registry snapshots, with event notifications as incremental updates.
## Vision
AIRI plugin orchestration should behave like an extensible runtime kernel:
- Hosts are runtime adapters (Electron, Pocket, Web), not policy exceptions.
- Stages are normal modules that publish and consume capabilities.
- Modules compose through capability contracts, not hardcoded start order.
- Adding new platform APIs means registering new capabilities, not rewriting lifecycle code.
## Design Details
Capability-first lifecycle orchestration with dynamic dependency resolution.
### Lifecycle Model
Proposed host-side phases for each module:
- `loading`
- `loaded`
- `authenticating`
- `authenticated`
- `announced`
- `preparing`
- `waiting-deps`
- `prepared`
- `configuration-needed`
- `configured`
- `ready`
- `degraded`
- `failed`
- `stopped`
Key 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.
### Capability Registry Model
Registry properties:
- Stateful snapshot store (authoritative readiness state).
- Capability records scoped by `hostId`, `instanceId`, and runtime.
- Support both direct key lookup and predicate matching.
- Version and health metadata per capability record.
Capability record baseline:
```ts
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:
```ts
interface CapabilityRequirement {
allOf?: string[]
anyOf?: string[]
predicate?: (record: CapabilityRecord) => boolean
timeoutMs?: number
}
```
### Use Cases
1. Stage provider catalog dependency chain
- Provider-definition plugin announces capability.
- Stage/configurator module announces provider catalog capability when store is mounted.
- Consumer plugin module waits for both capabilities, then configures provider-backed features.
2. Multi-stage instance isolation
- Two stage instances in one host register same capability class under different `instanceId`.
- Module requests capability in same `instanceId` scope.
- Host binds consumer to correct stage-local capability.
3. Pocket runtime API extension
- Pocket runtime module registers `mobile.sensor` capability.
- Plugins targeting mobile predicates can activate on pocket host without Electron-specific assumptions.
4. Late readiness and replay safety
- Capability became ready before module started waiting.
- Module receives snapshot-based resolution immediately and skips unnecessary wait.
## Verify & Test
### Criteria
- Module lifecycle correctly enters `waiting-deps` when required capabilities are missing.
- Module resumes deterministically when capabilities become ready.
- Snapshot-based registry prevents missed-ready race conditions.
- Same module capability requirements work across Electron and at least one non-Electron runtime adapter.
- Capability scoping by `instanceId` prevents cross-stage binding errors.
### Test & QA
- Unit test: registry stores and replays announced/ready/degraded states.
- Unit test: module transitions `preparing -> waiting-deps -> prepared`.
- Unit test: pre-existing ready capability resolves wait immediately.
- Unit test: degraded dependency transitions module from `ready` to `degraded`.
- Integration test: stage-side capability registration unblocks plugin module initialization.
## Progress
### Status
Proposed.
### Next Steps
1. Add host registry abstraction and lifecycle integration in `packages/plugin-sdk/src/plugin-host`.
2. Introduce `waiting-deps` and `degraded` transitions to host state machine and tests.
3. Define minimal protocol events for capability announce/ready/degraded/snapshot.
4. Wire stage-side capability publication in Electron host integration first.
## Reviews
### Q&A
- 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.
### Related Documentations
- [AIRI Plugin Platform](./architecture.md)
- [Multi-Transport Plugin Contexts](./multi-transport.md)