Initial Commit
This commit is contained in:
74
openspec/archive/2026-05-25-api-lifecycle/proposal.md
Normal file
74
openspec/archive/2026-05-25-api-lifecycle/proposal.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# `@suaveplan/api-lifecycle` — plugin lifecycle FSM definition
|
||||
|
||||
**Package**: `@suaveplan/api-lifecycle`
|
||||
**Status**: PROPOSED
|
||||
**Type**: new-package
|
||||
**Stage**: 1 (Contracts) — see umbrella `openspec/changes/api-plugin-foundation/`
|
||||
|
||||
## Why
|
||||
|
||||
Design §4 specifies the plugin lifecycle as a finite-state machine with 13 states (`uninstalled`, `installing`, `installed`, `starting`, `started`, `rolled_back`, `crashed`, `stopping`, `stopped`, `uninstalling`, `install_failed`, `start_failed`, `uninstall_failed`) and a documented set of transitions. The host driver, the admin API status renderer, the hot-reload primitive, and operator tooling all need to agree on which transitions are legal — and a graph that lives in only one place. This package owns the FSM definition: the states (as a discriminated union), the transition function, the side-effect descriptors per transition (so the host can wire them), and the per-plugin mutex contract that guarantees at-most-one transition at a time. Reuses `@suaveplan/fsm` for the state-machine primitive — this package is the FSM *definition*, not a new FSM runtime.
|
||||
|
||||
Per design §4: "The FSM has a per-plugin mutex — at most one transition at a time. Concurrent triggers queue."
|
||||
|
||||
## What changes
|
||||
|
||||
Net-new package. Pure definition: states + transitions + side-effect descriptors + retry policies. No concrete plugin code; that lives in `api-host` (Stage 2).
|
||||
|
||||
- ADDED: `./states` — `PluginState` discriminated union (re-imported from `@suaveplan/api-types` for runtime introspection); state-shape descriptors.
|
||||
- ADDED: `./transitions` — `PluginTransitions`: the legal `(from, to, trigger)` set as a frozen table. Trigger names: `install`, `installCompleted`, `installFailed`, `start`, `startCompleted`, `startFailed`, `rollbackTriggered`, `stop`, `stopCompleted`, `uninstall`, `uninstallCompleted`, `uninstallFailed`, `crashDetected`, `crashRestart`, `clearRollback`, `retryInstall`, `retryStart`, `forceUninstall`, `repair`.
|
||||
- ADDED: `./fsm-factory` — `createPluginFsm({ initial, sideEffects, mutex })` returns an FSM bound to the genesis `@suaveplan/fsm` runtime, initialised with `PluginTransitions` and the side-effect bus from design §4's transition table.
|
||||
- ADDED: `./side-effects` — descriptors for each transition's side effects (per §4 table): `installing → installed` emits `plugin.installed`, completes OTel span; `stopping → stopped` force-cancels stragglers, disposes capabilities reverse-registration order; etc. Descriptors are data; the host runtime executes them.
|
||||
- ADDED: `./retry-policies` — `start_failed` auto-retry × 3 with `@suaveplan/retry` exponential backoff; `crashed` auto-restart × `crashRestartMax` (default 3); restart budget reset after `crashRestartCooldownMs` of healthy operation. Default values exported.
|
||||
- ADDED: `./mutex-contract` — `PluginMutex` interface: at most one transition per plugin in flight; concurrent triggers queue (FIFO).
|
||||
- ADDED: `./reinstall-criteria` — `requiresReinstall(prev, next): boolean` returning true when the new manifest bumps `database.schemaVersion`, declares new `scopes`/`events`/`jobs`/`secrets`, or changes `dependsOn` (per §4 reload semantics).
|
||||
- ADDED: `./error-codes` — frozen `LIFECYCLE_CODES`: `ILLEGAL_TRANSITION`, `MUTEX_HELD`, `RETRY_BUDGET_EXHAUSTED`, `REINSTALL_REQUIRED_VERSION_DOWNGRADE`.
|
||||
- ADDED: `./testing` — `runLifecycleConformance(factory)` reusable harness.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- The host's lifecycle DRIVER (the code that actually CALLS `install`/`start`/`stop` hooks and reads side-effect descriptors) — lives in `api-host` (Stage 2).
|
||||
- Plugin ledger persistence — `api-host`.
|
||||
- Worker-tier `crashed` state restart implementation — `api-host` + `api-worker-host` (Stage 3).
|
||||
|
||||
## Source mapping
|
||||
|
||||
Genesis-original. Source of design intent: `docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md`.
|
||||
|
||||
| Requirement | Design doc section |
|
||||
|---|---|
|
||||
| State machine (13 states) | §4 (FSM ASCII diagram, lines 409-473) |
|
||||
| Transition side-effects table | §4 (table, lines 475-484) |
|
||||
| Reinstall criteria (`schemaVersion` bumped, new `scopes`/`events`/`jobs`/`secrets`, `dependsOn` changed) | §4 (line 486) |
|
||||
| `start_failed` auto-retry × 3 + `rolled_back` path | §4 (failure recovery section, lines 491-493) |
|
||||
| `crashed` auto-restart × `crashRestartMax`, cooldown | §4 (line 493) |
|
||||
| Per-plugin mutex; concurrent triggers queue | §4 (final paragraph) |
|
||||
| `VERSION_DOWNGRADE` failure | §4 (line 486) |
|
||||
|
||||
### Requirements with no source line
|
||||
|
||||
| Requirement | Justification |
|
||||
|---|---|
|
||||
| Trigger naming (`installCompleted` etc.) | Mechanically derived from the §4 table; pinned here so the host and admin renderers agree. |
|
||||
| `runLifecycleConformance` harness | Stage-5 deliverable (§13.4). |
|
||||
|
||||
## Verification approach
|
||||
|
||||
- `src/states/states.test.ts` — `PluginState` union matches design §4 exactly (no missing / extra states).
|
||||
- `src/transitions/transitions.test.ts` — exhaustive transition table: every documented `(from, to)` pair is legal; an undocumented pair throws `LIFECYCLE.ILLEGAL_TRANSITION`.
|
||||
- `src/fsm-factory/fsm-factory.test.ts` — `createPluginFsm` wires `@suaveplan/fsm` correctly; transition emits the right side-effect descriptors.
|
||||
- `src/side-effects/side-effects.test.ts` — every documented side effect is in the table for the right transition (`installed → starting` mounts routes, etc.).
|
||||
- `src/retry-policies/retry-policies.test.ts` — `start_failed` retried up to 3 times then parked; `crashed` restarted up to `crashRestartMax`; budget reset after cooldown.
|
||||
- `src/mutex-contract/mutex-contract.test.ts` — concurrent triggers queue FIFO; second trigger waits until first releases mutex.
|
||||
- `src/reinstall-criteria/reinstall-criteria.test.ts` — every documented trigger (`schemaVersion`, new scope/event/job/secret, `dependsOn` change) yields `true`; identical manifest yields `false`; version downgrade yields `REINSTALL_REQUIRED_VERSION_DOWNGRADE`.
|
||||
- `tests/dep-graph.test.ts` — deps: `@suaveplan/api-types`, `@suaveplan/fsm`, `@suaveplan/retry`, `@suaveplan/error`, `@suaveplan/types`.
|
||||
|
||||
## Migration impact
|
||||
|
||||
None — additive only.
|
||||
|
||||
## Impact
|
||||
|
||||
### Affected dependents
|
||||
|
||||
- (none) — [x] no action needed (reason: net-new; first consumer is `api-host` in Stage 2)
|
||||
Reference in New Issue
Block a user