-
Notifications
You must be signed in to change notification settings - Fork 0
Runtime Lifecycle
Jim Daley edited this page May 1, 2026
·
3 revisions
The runtime lifecycle has five phases: construct, boot, discover, activate, and reconcile. Shutdown reverses activation without erasing persisted activation state unless the caller explicitly changes state before shutdown.
flowchart TD
A["Create ForsettiRuntime"] --> B["Build CompatibilityChecker"]
B --> C["Build ForsettiContext"]
C --> D["Build ModuleManager"]
D --> E["boot(bundle, subdirectory)"]
E --> F["ManifestLoader.loadManifests"]
F --> G["Refresh entitlements"]
G --> H["Start entitlement observation"]
H --> I{"restoreActivationState?"}
I -- yes --> J["Reconcile persisted desired activation"]
I -- no --> K["Return discovered manifests"]
J --> K
flowchart TD
A["activateModule(moduleID)"] --> B{"Manifest discovered?"}
B -- no --> X["moduleNotDiscovered"]
B -- yes --> C["Evaluate compatibility"]
C --> D{"Any error severity issue?"}
D -- yes --> Y["incompatible(report)"]
D -- no --> E["Check entitlement provider"]
E --> F{"Unlocked?"}
F -- no --> Z["moduleLocked"]
F -- yes --> G{"moduleType"}
G -- service --> H["Resolve module factory"]
G -- ui/app --> I["Resolve module factory and require ForsettiUIModule"]
H --> V["Validate descriptor and manifest identity"]
I --> V
V --> J["start(scoped context)"]
I --> K{"Conforms to UI protocol?"}
K -- no --> Q["notUIModule"]
K -- yes --> U["Validate UI contribution capabilities"]
U --> J
J --> L{"start throws?"}
L -- yes --> R["Report module error and rollback loaded module"]
L -- no --> M["Record active state"]
M --> N{"UI or app module?"}
N -- yes --> O["Apply sanitized UI contributions"]
N -- no --> P["Persist activation state"]
O --> P
| State | Meaning | Stored In |
|---|---|---|
| Discovered | Manifest was loaded and indexed by moduleID. |
manifestsByID |
| Loaded | Module instance was created from the registry. | loadedModules |
| Enabled service | Service module is active and started. | enabledServiceModuleIDs |
| Enabled UI | Compatibility state for the active UI/app module. The default model keeps this empty or one ID. | enabledUIModuleIDs |
| Active UI | UI/app module currently active for presentation. Activating another UI/app module deactivates the previous one. |
activeUIModuleID / selectedUIModuleID
|
Persisted activation state is desired state. Restore activates persisted IDs through the same compatibility, entitlement, identity, capability, lifecycle, and UI contribution path used for explicit activation. Failed restores do not leave false active state behind.
sequenceDiagram
participant Provider as EntitlementProvider
participant Runtime as ForsettiRuntime
participant Manager as ModuleManager
participant Module as Active Module
participant Store as ActivationStore
Provider-->>Runtime: entitlementsDidChangeStream event
Runtime->>Manager: inspect enabled service and UI modules
loop each active module
Runtime->>Provider: isUnlocked(moduleID, productID)
alt no longer unlocked
Runtime->>Manager: deactivateModule(moduleID)
Manager->>Module: stop(context)
Manager->>Store: save updated activation state
else still unlocked
Runtime-->>Runtime: leave active
end
end
flowchart LR
A["runtime.shutdown()"] --> B["Cancel entitlement observation"]
B --> C["deactivateAllModules(persistState: false)"]
C --> D["Call stop(context) on loaded modules"]
D --> E["Clear loaded and enabled runtime state"]
E --> F["Leave persisted activation state unchanged"]
Shutdown is suitable for app lifecycle teardown. If a product decision should disable a module permanently, call deactivateModule(moduleID:) before shutdown so the activation store is updated.
| Failure | Result |
|---|---|
| Missing manifest directory | Boot throws manifestsDirectoryNotFound. |
| Invalid manifest JSON | Discovery throws validationFailed. |
Duplicate moduleID
|
Discovery throws duplicateModuleID. |
| Denied capability | Activation throws incompatible(report:). |
| Locked module | Activation throws moduleLocked. |
| Factory returns mismatched descriptor or manifest identity | Activation throws moduleIdentityMismatch. |
Factory returns a non-UI module for ui or app manifest after identity validation |
Activation throws notUIModule. |
| UI contribution lacks the required capability | Activation throws missingCapability. |
start(context:) throws |
Module instance is removed from loadedModules, the error is logged, and activation fails. |