From ef5632b7a62b7c067e239def3e80694e30a93310 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 17:46:49 +0000 Subject: [PATCH 1/2] feat(spec): retire the authorable AdvancedPluginLifecycleConfig surface (#11825, ADR-0049 route 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whole-def retirement of the zero-reader authorable lifecycle-config surface: AdvancedPluginLifecycleConfigSchema (container), GracefulDegradationSchema, PluginUpdateStrategySchema — 3 defs, 9 exported names, 17 authorable-surface keys. The host-driven library classes in @objectstack/core and their input vocabularies (PluginHealthCheck/Status/Report, HotReloadConfig, DistributedStateConfig, PluginStateSnapshot) are KEPT per the ruling. Route 3 (no carrier key, no authored document): RETIRED_DEFS_BY_MAJOR[18] + D3 semantic entry advanced-plugin-lifecycle-config-retired ARE the declaration; no tombstone, no D2 conversion. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV --- ...dvanced-plugin-lifecycle-config-retired.md | 64 +++++ .../docs/getting-started/quick-reference.mdx | 2 +- content/docs/references/index.mdx | 10 +- .../kernel/plugin-lifecycle-advanced.mdx | 71 +---- ...07-unknown-key-strictness-ledger.counts.md | 2 +- packages/spec/PLUGIN_STANDARDS.md | 24 +- packages/spec/PROTOCOL_MAP.md | 2 +- packages/spec/api-surface/kernel.json | 9 - packages/spec/authorable-defaults/kernel.json | 3 - packages/spec/authorable-surface/kernel.json | 17 -- packages/spec/export-origins/kernel.json | 9 - .../spec/json-schema.manifest/kernel.json | 3 - ...ugin-lifecycle-advanced-retirement.test.ts | 114 ++++++++ .../kernel/plugin-lifecycle-advanced.test.ts | 167 ----------- .../kernel/plugin-lifecycle-advanced.zod.ts | 271 +++++------------- ...8.kernel__AdvancedPluginLifecycleConfig.ts | 30 ++ .../18.kernel__GracefulDegradation.ts | 18 ++ .../18.kernel__PluginUpdateStrategy.ts | 18 ++ ...dvanced-plugin-lifecycle-config-retired.ts | 70 +++++ packages/spec/src/migrations/registry.ts | 126 ++++++++ 20 files changed, 540 insertions(+), 490 deletions(-) create mode 100644 .changeset/advanced-plugin-lifecycle-config-retired.md create mode 100644 packages/spec/src/kernel/plugin-lifecycle-advanced-retirement.test.ts create mode 100644 packages/spec/src/migrations/entries/retired-defs/18.kernel__AdvancedPluginLifecycleConfig.ts create mode 100644 packages/spec/src/migrations/entries/retired-defs/18.kernel__GracefulDegradation.ts create mode 100644 packages/spec/src/migrations/entries/retired-defs/18.kernel__PluginUpdateStrategy.ts create mode 100644 packages/spec/src/migrations/entries/semantic/18.advanced-plugin-lifecycle-config-retired.ts diff --git a/.changeset/advanced-plugin-lifecycle-config-retired.md b/.changeset/advanced-plugin-lifecycle-config-retired.md new file mode 100644 index 0000000000..a58b7e0388 --- /dev/null +++ b/.changeset/advanced-plugin-lifecycle-config-retired.md @@ -0,0 +1,64 @@ +--- +"@objectstack/spec": minor +--- + +feat(spec): retire the authorable `AdvancedPluginLifecycleConfig` surface; the lifecycle classes stay as a host-driven library (#11825, ADR-0049) + + + +**BREAKING** export removal, landing after the v17.0.0 cut (the lockstep +launch-window convention ships it as `minor`; the prescription is registered +under protocol major 18 — `RETIRED_DEFS_BY_MAJOR[18]` + the D3 semantic entry +`advanced-plugin-lifecycle-config-retired` — where `os migrate meta` users +will look). + +`AdvancedPluginLifecycleConfigSchema` aggregated six lifecycle config groups — +`health`, `hotReload`, `degradation`, `updates`, `resources`, +`observability` — and NO group had a runtime reader, re-measured per group at +the retirement's base commit with positive controls: the kernel never +constructs `PluginHealthMonitor` or `HotReloadManager` (only their own unit +tests and `core/examples/phase2-integration.ts` do, passing config DIRECTLY +to the classes, never through this container); the `degradation` / `updates` +/ `resources` / `observability` keys have no implementation body at all; and +no manifest, stack collection or metadata-type binding ever embedded the +container, so no authored document could carry it. An author declaring +`health: {...}` or `rollback: { automatic: true }` got a clean parse and +NOTHING — the #3950 shape at container scale, sharpened by production-safety +vocabulary (auto-restart, zero-downtime rolling updates, automatic rollback) +an AI author reads as proof the capability exists. + +FROM → TO: + +- `AdvancedPluginLifecycleConfigSchema` / `AdvancedPluginLifecycleConfig` / + `AdvancedPluginLifecycleConfigParsed` → *(removed — no replacement + container)*. The supported lifecycle surface is the host-driven library in + `@objectstack/core`: construct `PluginHealthMonitor` and pass a + `PluginHealthCheck`, construct `HotReloadManager` and pass a + `HotReloadConfig` (the `content/docs/protocol/kernel/lifecycle.mdx` + examples, #11811, are the supported usage). +- `GracefulDegradationSchema` / `GracefulDegradation` / + `GracefulDegradationParsed` and `PluginUpdateStrategySchema` / + `PluginUpdateStrategy` / `PluginUpdateStrategyParsed` → *(removed)* — value + schemas whose only consumer was the retired container; no implementation + body exists for any of their keys. They return only via the ENFORCE route + of ADR-0049 through a new ADR — the executor first, the vocabulary second. + +One-line fix: delete the config object (it configured nothing); if you drive +the library classes yourself, keep passing `PluginHealthCheck` / +`HotReloadConfig` directly — those vocabularies survive unchanged. + +The retirement kit: + +- whole-def deletion (route 3 — no carrier key, no authored document, so no + tombstone and no D2 conversion; the #8715 `identity/ApiKey` shape): + `kernel/AdvancedPluginLifecycleConfig`, `kernel/GracefulDegradation`, + `kernel/PluginUpdateStrategy` in `RETIRED_DEFS_BY_MAJOR[18]`, plus the D3 + semantic entry `advanced-plugin-lifecycle-config-retired` +- pin test (`kernel/plugin-lifecycle-advanced-retirement.test.ts`): zero + holders for all 9 retired names on every public entry, survivors pinned + (`PluginHealthStatus` / `PluginHealthCheck` / `PluginHealthReport`, + `HotReloadConfig` + `DistributedStateConfig`, `PluginStateSnapshot` — the + kept host-driven library vocabularies) +- zero authored occurrences in objectstack or objectui (measured at + dispatch, re-verified per group), so no in-repo source changes ride along; + `@objectstack/core` classes and tests are untouched and stay green diff --git a/content/docs/getting-started/quick-reference.mdx b/content/docs/getting-started/quick-reference.mdx index 44153c3c61..d128cdda1e 100644 --- a/content/docs/getting-started/quick-reference.mdx +++ b/content/docs/getting-started/quick-reference.mdx @@ -73,7 +73,7 @@ Plugin architecture, manifests, and kernel runtime. | **[Context](/docs/references/kernel/context)** | `context.zod.ts` | KernelContext | Runtime execution context | | **[Plugin](/docs/references/kernel/plugin)** | `plugin.zod.ts` | Plugin, PluginLifecycle | Plugin system interface | | **[Plugin Capability](/docs/references/kernel/plugin-capability)** | `plugin-capability.zod.ts` | PluginCapability | Plugin capability declarations | -| **[Plugin Lifecycle Advanced](/docs/references/kernel/plugin-lifecycle-advanced)** | `plugin-lifecycle-advanced.zod.ts` | AdvancedPluginLifecycleConfig, PluginHealthCheck | Advanced lifecycle hooks | +| **[Plugin Lifecycle Advanced](/docs/references/kernel/plugin-lifecycle-advanced)** | `plugin-lifecycle-advanced.zod.ts` | PluginHealthCheck, HotReloadConfig | Host-driven lifecycle library inputs | | **[Plugin Loading](/docs/references/kernel/plugin-loading)** | `plugin-loading.zod.ts` | PluginLoadingConfig | Plugin loading and init | | **[Plugin Registry](/docs/references/kernel/plugin-registry)** | `plugin-registry.zod.ts` | PluginRegistryEntry, PluginVendor | Plugin registry entries and quality metrics | | **[Plugin Security](/docs/references/kernel/plugin-security)** | `plugin-security.zod.ts` | PluginSecurityProtocol, SBOM | Plugin security policies | diff --git a/content/docs/references/index.mdx b/content/docs/references/index.mdx index 49eb4e6576..6cca68ed63 100644 --- a/content/docs/references/index.mdx +++ b/content/docs/references/index.mdx @@ -1,6 +1,6 @@ --- title: Protocol Reference -description: Every schema published by @objectstack/spec — 1588 schemas across 14 protocol modules +description: Every schema published by @objectstack/spec — 1585 schemas across 14 protocol modules --- {/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} @@ -26,14 +26,14 @@ counts are sums of the rows they head. Regenerate with | [Data Protocol](/docs/references/data) | 29 | 166 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. | | [Identity Protocol](/docs/references/identity) | 5 | 27 | Users and accounts, organizations, positions, SCIM provisioning. | | [Integration Protocol](/docs/references/integration) | 1 | 27 | The single connector protocol (ADR-0097) — catalog descriptors and provider-bound instances. | -| [Kernel Protocol](/docs/references/kernel) | 31 | 176 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. | +| [Kernel Protocol](/docs/references/kernel) | 31 | 173 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. | | [QA Protocol](/docs/references/qa) | 1 | 8 | Declarative test suites — scenarios, steps, actions and assertions. | | [Security Protocol](/docs/references/security) | 5 | 27 | Permission sets, row-level security, sharing rules, tenancy posture. | | [Shared Protocol](/docs/references/shared) | 8 | 31 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. | | [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. | | [System Protocol](/docs/references/system) | 36 | 288 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. | | [UI Protocol](/docs/references/ui) | 16 | 152 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. | -| **Total** | **199** | **1588** | 14 protocol modules | +| **Total** | **199** | **1585** | 14 protocol modules | --- @@ -215,7 +215,7 @@ The single connector protocol (ADR-0097) — catalog descriptors and provider-bo ## Kernel Protocol -**Source:** `packages/spec/src/kernel/` · **Import:** `@objectstack/spec/kernel` · **31 pages, 176 schemas** +**Source:** `packages/spec/src/kernel/` · **Import:** `@objectstack/spec/kernel` · **31 pages, 173 schemas** Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. @@ -242,7 +242,7 @@ Plugin lifecycle and manifests, capabilities and security, metadata loading, ser | [`package-upgrade.zod.ts`](/docs/references/kernel/package-upgrade) | `MetadataChangeType`, `MetadataDiffItem`, `RollbackPackageRequest`, `RollbackPackageResponse`, `UpgradeImpactLevel`, `UpgradePackageRequest`, `UpgradePackageResponse`, `UpgradePhase`, `UpgradePlan`, `UpgradeSnapshot` | | [`plugin.zod.ts`](/docs/references/kernel/plugin) | `Plugin` | | [`plugin-capability.zod.ts`](/docs/references/kernel/plugin-capability) | `CapabilityConformanceLevel`, `ExtensionPoint`, `PluginCapability`, `PluginCapabilityManifest`, `PluginDependency`, `PluginInterface`, `ProtocolFeature`, `ProtocolReference`, `ProtocolVersion` | -| [`plugin-lifecycle-advanced.zod.ts`](/docs/references/kernel/plugin-lifecycle-advanced) | `AdvancedPluginLifecycleConfig`, `DistributedStateConfig`, `GracefulDegradation`, `HotReloadConfig`, `PluginHealthCheck`, `PluginHealthReport`, `PluginHealthStatus`, `PluginStateSnapshot`, `PluginUpdateStrategy` | +| [`plugin-lifecycle-advanced.zod.ts`](/docs/references/kernel/plugin-lifecycle-advanced) | `DistributedStateConfig`, `HotReloadConfig`, `PluginHealthCheck`, `PluginHealthReport`, `PluginHealthStatus`, `PluginStateSnapshot` | | [`plugin-loading.zod.ts`](/docs/references/kernel/plugin-loading) | `PluginLoadingEvent`, `PluginLoadingState` | | [`plugin-registry.zod.ts`](/docs/references/kernel/plugin-registry) | `PluginInstallConfig`, `PluginQualityMetrics`, `PluginRegistryEntry`, `PluginSearchFilters`, `PluginStatistics`, `PluginVendor` | | [`plugin-security.zod.ts`](/docs/references/kernel/plugin-security) | `DependencyGraph`, `DependencyGraphNode`, `PackageDependencyConflict`, `PackageDependencyResolutionResult`, `PluginProvenance`, `PluginTrustScore`, `ResolvedPackageDependency`, `SBOM`, `SBOMEntry`, `SecurityPolicy`, `SecurityScanResult`, `SecurityVulnerability`, `VulnerabilitySeverity` | diff --git a/content/docs/references/kernel/plugin-lifecycle-advanced.mdx b/content/docs/references/kernel/plugin-lifecycle-advanced.mdx index ab01704421..b7148f8d9f 100644 --- a/content/docs/references/kernel/plugin-lifecycle-advanced.mdx +++ b/content/docs/references/kernel/plugin-lifecycle-advanced.mdx @@ -5,16 +5,18 @@ description: Plugin Lifecycle Advanced protocol schemas {/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} -# Advanced Plugin Lifecycle Protocol +# Advanced Plugin Lifecycle — host-driven library vocabularies -Defines advanced lifecycle management capabilities including: -- Hot reload and live updates -- Graceful degradation and fallback mechanisms -- Health monitoring and auto-recovery -- State preservation during updates +Declares the INPUT contracts of the host-driven lifecycle classes exported +by `@objectstack/core` — `PluginHealthMonitor` (reads `PluginHealthCheck`, +emits `PluginHealthStatus` / `PluginHealthReport`) and `HotReloadManager` +(reads `HotReloadConfig`, snapshots via `PluginStateSnapshot`). The kernel +does not construct either class: a HOST composes them and passes these +shapes directly (`content/docs/protocol/kernel/lifecycle.mdx`, the #11811 +examples, is the supported usage). -This protocol extends the basic plugin lifecycle with enterprise-grade -features for production environments. +This module deliberately declares NO authorable configuration surface — see +the #11825 retirement record below. **Source:** `packages/spec/src/kernel/plugin-lifecycle-advanced.zod.ts` @@ -23,29 +25,13 @@ features for production environments. ## TypeScript Usage ```typescript -import { AdvancedPluginLifecycleConfigSchema, DistributedStateConfigSchema, GracefulDegradationSchema, HotReloadConfigSchema, PluginHealthCheckSchema, PluginHealthReportSchema, PluginHealthStatusSchema, PluginStateSnapshotSchema, PluginUpdateStrategySchema } from '@objectstack/spec/kernel'; -import type { AdvancedPluginLifecycleConfig, DistributedStateConfig, GracefulDegradation, HotReloadConfig, PluginHealthCheck, PluginHealthReport, PluginHealthStatus, PluginStateSnapshot, PluginUpdateStrategy } from '@objectstack/spec/kernel'; +import { DistributedStateConfigSchema, HotReloadConfigSchema, PluginHealthCheckSchema, PluginHealthReportSchema, PluginHealthStatusSchema, PluginStateSnapshotSchema } from '@objectstack/spec/kernel'; +import type { DistributedStateConfig, HotReloadConfig, PluginHealthCheck, PluginHealthReport, PluginHealthStatus, PluginStateSnapshot } from '@objectstack/spec/kernel'; // Validate data -const result = AdvancedPluginLifecycleConfigSchema.parse(data); +const result = DistributedStateConfigSchema.parse(data); ``` ---- - -## AdvancedPluginLifecycleConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **health** | `{ interval: integer; timeout: integer; failureThreshold: integer; successThreshold: integer; … }` | optional | | -| **hotReload** | `{ enabled: boolean; watchPatterns?: string[]; debounceDelay: integer; preserveState: boolean; … }` | optional | | -| **degradation** | `{ enabled: boolean; fallbackMode: Enum<'minimal' \| 'cached' \| 'readonly' \| 'offline' \| 'disabled'>; criticalDependencies?: string[]; optionalDependencies?: string[]; … }` | optional | | -| **updates** | `{ mode: Enum<'manual' \| 'automatic' \| 'scheduled' \| 'rolling'>; autoUpdateConstraints?: object; schedule?: object; rollback?: object; … }` | optional | | -| **resources** | `{ maxMemory?: integer; maxCpu?: number; maxConnections?: integer; timeout?: integer }` | optional | | -| **observability** | `{ enableMetrics: boolean; enableTracing: boolean; enableProfiling: boolean; metricsInterval: integer }` | optional | | - - --- ## DistributedStateConfig @@ -63,22 +49,6 @@ const result = AdvancedPluginLifecycleConfigSchema.parse(data); | **customConfig** | `Record` | optional | Provider-specific configuration | ---- - -## GracefulDegradation - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **enabled** | `boolean` | optional (default: `true`) | | -| **fallbackMode** | `Enum<'minimal' \| 'cached' \| 'readonly' \| 'offline' \| 'disabled'>` | optional (default: `"minimal"`) | | -| **criticalDependencies** | `string[]` | optional | Plugin IDs that are required for operation | -| **optionalDependencies** | `string[]` | optional | Plugin IDs that are nice to have but not required | -| **degradedFeatures** | `{ feature: string; enabled: boolean; reason?: string }[]` | optional | | -| **autoRecovery** | `{ enabled: boolean; retryInterval: integer; maxAttempts: integer }` | optional | | - - --- ## HotReloadConfig @@ -165,18 +135,3 @@ Current health status of the plugin --- -## PluginUpdateStrategy - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **mode** | `Enum<'manual' \| 'automatic' \| 'scheduled' \| 'rolling'>` | optional (default: `"manual"`) | | -| **autoUpdateConstraints** | `{ major: boolean; minor: boolean; patch: boolean }` | optional | | -| **schedule** | `{ cron?: string; timezone: string; maintenanceWindow: integer }` | optional | | -| **rollback** | `{ enabled: boolean; automatic: boolean; keepVersions: integer; timeout: integer }` | optional | | -| **validation** | `{ checkCompatibility: boolean; runTests: boolean; testSuite?: string }` | optional | | - - ---- - diff --git a/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md b/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md index d5dc8b5e06..2bc687f003 100644 --- a/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md +++ b/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md @@ -261,7 +261,7 @@ directory rather than per file. | `cloud/` | 83 | | `identity/` | 32 | | `integration/` | 10 | -| `kernel/` | 288 | +| `kernel/` | 277 | | `qa/` | 6 | | `shared/` | 20 | | `system/` | 361 | diff --git a/packages/spec/PLUGIN_STANDARDS.md b/packages/spec/PLUGIN_STANDARDS.md index 92a4044752..d03093ca24 100644 --- a/packages/spec/PLUGIN_STANDARDS.md +++ b/packages/spec/PLUGIN_STANDARDS.md @@ -133,19 +133,23 @@ validation, auto-rollback, connection draining and `maxConcurrentReloads` — an none of it was read by anything. **The surviving vocabulary** is `plugin-lifecycle-advanced.zod.ts` → -`HotReloadConfigSchema`, carried on `AdvancedPluginLifecycleConfig.hotReload`. -That is the one with an implementation body behind it: `HotReloadManager` in -`packages/core/src/hot-reload.ts` reads it. +`HotReloadConfigSchema`. That is the one with an implementation body behind it: +`HotReloadManager` in `packages/core/src/hot-reload.ts` reads it — a HOST +constructs the manager and passes the config directly. + +(The `AdvancedPluginLifecycleConfig` container that used to carry it on its +`hotReload` key was itself retired in #11825, ADR-0049 route 2 — the container +had zero runtime readers and nothing ever embedded it, so the schema survives +as the class's standalone input type, not as an authorable config surface.) ⚠️ **Status, stated honestly: a foundation, not a shipped capability.** `HotReloadManager` exists and is unit-tested, but **no runtime composes one** — the only constructions are its own test and -`packages/core/examples/phase2-integration.ts`. So configuring -`AdvancedPluginLifecycleConfig.hotReload` today does not give a running system -hot reload either. It is kept, unenforced, as the starting point if hot reload is -ever built for real; making it enforced is a separate decision (ADR-0049's -enforce leg) and deliberately **not** part of the #4914 retirement. Treat this -section as "one honest pointer", not as a feature you can turn on. +`packages/core/examples/phase2-integration.ts`. It is kept, unenforced, as the +starting point if hot reload is ever built for real; making it enforced is a +separate decision (ADR-0049's enforce leg) and deliberately **not** part of +the #4914 or #11825 retirements. Treat this section as "one honest pointer", +not as a feature you can turn on. ### 5.2 Plugin Isolation — ~~`plugin-loading.zod.ts` → `PluginSandboxingSchema`~~ REMOVED in v17 @@ -204,7 +208,7 @@ through a new ADR — loader first, vocabulary second. | Service Registry | ✅ | `service-registry.zod.ts` — 17 services across 13 plugins via `ctx.registerService()` | | Event Bus | ✅ | `events.zod.ts` — Pub/sub with pattern matching | | Dependency Resolution | ✅ | `manifest.zod.ts` (`dependencies`) + `packages/core/src/plugin-order.ts` — `resolvePluginOrder` topologically orders plugins from each composed plugin's `dependencies` / `optionalDependencies`, erroring on a cycle or a missing hard dependency. (The `PluginDependencyResolution` *config* schema this row used to cite was inert and went with the `loading` block in #4914 — the capability is real, the configuration surface was not) | -| Health Checks | ✅ | `plugin-lifecycle-advanced.zod.ts` — Per-plugin health + system aggregation | +| Health Checks | ✅ | `plugin.zod.ts` `healthCheck()` + kernel `checkPluginHealth()` (kernel-wired); `plugin-lifecycle-advanced.zod.ts` carries the vocabularies of the host-driven `PluginHealthMonitor` library (`@objectstack/core`), which no runtime composes — a host constructs it and passes `PluginHealthCheck` directly. (The `AdvancedPluginLifecycleConfig` container this row used to imply was retired in #11825 — zero readers) | | Hot Reload | ⚠️ | **Foundation only, not enforced.** `plugin-lifecycle-advanced.zod.ts` → `HotReloadConfigSchema` is the surviving vocabulary and `HotReloadManager` (`packages/core/src/hot-reload.ts`) reads it — but no runtime composes one, so configuring it changes nothing today. The rival `PluginHotReloadSchema` was removed in v17 (#4914, ADR-0049): it had no reader at all. See §5.1 | | Plugin Isolation | ❌ | **Not built.** The `PluginSandboxingSchema` vocabulary that declared it (scope, `process`/`vm`/`iframe`/`web-worker` isolation, IPC, `allowedServices` ACL) was removed in v17 (#4914, ADR-0049) — it had no runtime reader, so it isolated nothing while appearing to. Trust tiers (`manifest.runtime`) and permission declarations are the real surfaces. See §5.2 | | Dynamic Loading | ❌ | **Not built.** The `plugin-runtime.zod.ts` vocabulary that declared it was removed in v17 (#4834, ADR-0049) — it had no runtime reader in any repo. Plugins are composed at boot; the set is fixed until restart | diff --git a/packages/spec/PROTOCOL_MAP.md b/packages/spec/PROTOCOL_MAP.md index b7e14ba4a3..3e7a28813a 100644 --- a/packages/spec/PROTOCOL_MAP.md +++ b/packages/spec/PROTOCOL_MAP.md @@ -189,6 +189,6 @@ This document serves as the **Grand Map** of the ObjectStack specification. It l | [`plugin-structure.zod.ts`](src/kernel/plugin-structure.zod.ts) | | **Structure**. Zod rules for folder layout and file naming. | | [`plugin-capability.zod.ts`](src/kernel/plugin-capability.zod.ts) | | **Capabilities**. What a plugin can do. | | [`plugin-lifecycle-events.ts`](src/contracts/plugin-lifecycle-events.ts) | | **Lifecycle Events**. Registry of every kernel-bus event that actually fires, with payload tuples; feeds `hook`/`trigger` autocomplete. | -| [`plugin-lifecycle-advanced.zod.ts`](src/kernel/plugin-lifecycle-advanced.zod.ts) | | **Advanced Lifecycle**. Health monitoring, hot reload state management, graceful degradation, and update strategies. | +| [`plugin-lifecycle-advanced.zod.ts`](src/kernel/plugin-lifecycle-advanced.zod.ts) | | **Advanced Lifecycle**. Input vocabularies of the host-driven `@objectstack/core` lifecycle classes: health monitoring (`PluginHealthMonitor`) and hot-reload state management (`HotReloadManager`). The authorable config container and the graceful-degradation / update-strategy vocabularies were retired in #11825 (ADR-0049 — zero readers). | | [`plugin-security-advanced.zod.ts`](src/kernel/plugin-security-advanced.zod.ts) | | **Advanced Security**. Permission system, sandbox configuration (V8/WASM/container/process), security scanning, and trust levels. | | [`startup-orchestrator.zod.ts`](src/kernel/startup-orchestrator.zod.ts) | | **Startup**. Boot sequence orchestration. | diff --git a/packages/spec/api-surface/kernel.json b/packages/spec/api-surface/kernel.json index 9a10cd2309..f30c26f54d 100644 --- a/packages/spec/api-surface/kernel.json +++ b/packages/spec/api-surface/kernel.json @@ -2,9 +2,6 @@ "description": "Every exported `name (kind)` of one published entry point of @objectstack/spec — the breadth half of the ADR-0059 backward-compatibility gate. Sharded by entry point (#5837) so two PRs touching different entry points never share a file. Reads the BUILT dist/*.d.ts: regenerate with `pnpm --filter @objectstack/spec gen:api-surface` after a real build.", "entry": "./kernel", "exports": [ - "AdvancedPluginLifecycleConfig (type)", - "AdvancedPluginLifecycleConfigParsed (type)", - "AdvancedPluginLifecycleConfigSchema (const)", "ArtifactChecksum (type)", "ArtifactChecksumParsed (type)", "ArtifactChecksumSchema (const)", @@ -138,9 +135,6 @@ "GetPackageResponse (type)", "GetPackageResponseParsed (type)", "GetPackageResponseSchema (const)", - "GracefulDegradation (type)", - "GracefulDegradationParsed (type)", - "GracefulDegradationSchema (const)", "HealthStatus (type)", "HealthStatusSchema (const)", "HotReloadConfig (type)", @@ -361,9 +355,6 @@ "PluginTrustScore (type)", "PluginTrustScoreParsed (type)", "PluginTrustScoreSchema (const)", - "PluginUpdateStrategy (type)", - "PluginUpdateStrategyParsed (type)", - "PluginUpdateStrategySchema (const)", "PluginVendor (type)", "PluginVendorParsed (type)", "PluginVendorSchema (const)", diff --git a/packages/spec/authorable-defaults/kernel.json b/packages/spec/authorable-defaults/kernel.json index b94eb66aef..738765cd55 100644 --- a/packages/spec/authorable-defaults/kernel.json +++ b/packages/spec/authorable-defaults/kernel.json @@ -44,8 +44,6 @@ "kernel/ExecutionContext:permissions = []", "kernel/ExecutionContext:positions = []", "kernel/ExtensionPoint:cardinality = \"multiple\"", - "kernel/GracefulDegradation:enabled = true", - "kernel/GracefulDegradation:fallbackMode = \"minimal\"", "kernel/HotReloadConfig:debounceDelay = 1000", "kernel/HotReloadConfig:enabled = false", "kernel/HotReloadConfig:preserveState = true", @@ -122,7 +120,6 @@ "kernel/PluginStatistics:downloads = 0", "kernel/PluginStatistics:downloadsLastMonth = 0", "kernel/PluginTrustScore:badges = []", - "kernel/PluginUpdateStrategy:mode = \"manual\"", "kernel/PluginVendor:trustLevel = \"unverified\"", "kernel/PluginVendor:verified = false", "kernel/PreviewModeConfig:autoLogin = true", diff --git a/packages/spec/authorable-surface/kernel.json b/packages/spec/authorable-surface/kernel.json index 688f225863..ca1ad66b0c 100644 --- a/packages/spec/authorable-surface/kernel.json +++ b/packages/spec/authorable-surface/kernel.json @@ -2,12 +2,6 @@ "description": "Ratchet of every AUTHORABLE key in one category of the spec — what a metadata author may write, which for this platform IS the third-party API. Sharded by category (#5837) so two PRs touching different categories never share a file; the gate reads the whole authorable-surface/ directory as ONE set, so deleting a shard deletes its keys exactly as deleting lines did. Auto-updated on additions (commit the change). A key that disappears without a tombstone fails gen:schema, because these schemas are not .strict() and Zod would silently strip it. \"[RETIRED]\" marks a tombstoned key that still rejects with an upgrade prescription. See #3855, ADR-0059 §5.", "category": "kernel", "keys": [ - "kernel/AdvancedPluginLifecycleConfig:degradation", - "kernel/AdvancedPluginLifecycleConfig:health", - "kernel/AdvancedPluginLifecycleConfig:hotReload", - "kernel/AdvancedPluginLifecycleConfig:observability", - "kernel/AdvancedPluginLifecycleConfig:resources", - "kernel/AdvancedPluginLifecycleConfig:updates", "kernel/ArtifactChecksum:algorithm", "kernel/ArtifactChecksum:files", "kernel/ArtifactFileEntry:category", @@ -230,12 +224,6 @@ "kernel/FieldChange:path", "kernel/GetPackageRequest:id", "kernel/GetPackageResponse:package", - "kernel/GracefulDegradation:autoRecovery", - "kernel/GracefulDegradation:criticalDependencies", - "kernel/GracefulDegradation:degradedFeatures", - "kernel/GracefulDegradation:enabled", - "kernel/GracefulDegradation:fallbackMode", - "kernel/GracefulDegradation:optionalDependencies", "kernel/HealthStatus:details", "kernel/HealthStatus:healthy", "kernel/HealthStatus:message", @@ -660,11 +648,6 @@ "kernel/PluginTrustScore:pluginId", "kernel/PluginTrustScore:score", "kernel/PluginTrustScore:updatedAt", - "kernel/PluginUpdateStrategy:autoUpdateConstraints", - "kernel/PluginUpdateStrategy:mode", - "kernel/PluginUpdateStrategy:rollback", - "kernel/PluginUpdateStrategy:schedule", - "kernel/PluginUpdateStrategy:validation", "kernel/PluginVendor:email", "kernel/PluginVendor:id", "kernel/PluginVendor:name", diff --git a/packages/spec/export-origins/kernel.json b/packages/spec/export-origins/kernel.json index e9e5675d12..63b03f322d 100644 --- a/packages/spec/export-origins/kernel.json +++ b/packages/spec/export-origins/kernel.json @@ -2,9 +2,6 @@ "description": "Which SOURCE DECLARATION each name exported by one public entry point of @objectstack/spec resolves to, after its alias chain is unwound: `# ()`. Two exports share an origin string iff they are the same declaration — so equal origins across two entries are a harmless re-export, and different origins under one name are the #4411 dual-source trap. Generated from src/ (no build needed) and read by the export-surface pin tests, which compare against it instead of each building their own ts.createProgram — that was ~55s of compilation per CI lap and a non-deterministic timeout that ejected unrelated PRs from the merge queue (#4796). Sharded by entry point (#5837) so two retirement PRs never share a file. Carries NO line numbers: the pins asserted the line as `\\d+`, and recording it would rewrite this artifact on every edit that shifts a line in any .zod.ts. Regenerate with `pnpm --filter @objectstack/spec gen:export-origins` and read the diff.", "entry": "./kernel", "exports": { - "AdvancedPluginLifecycleConfig": "src/kernel/plugin-lifecycle-advanced.zod.ts#AdvancedPluginLifecycleConfig (type)", - "AdvancedPluginLifecycleConfigParsed": "src/kernel/plugin-lifecycle-advanced.zod.ts#AdvancedPluginLifecycleConfigParsed (type)", - "AdvancedPluginLifecycleConfigSchema": "src/kernel/plugin-lifecycle-advanced.zod.ts#AdvancedPluginLifecycleConfigSchema (const)", "ArtifactChecksum": "src/kernel/package-artifact.zod.ts#ArtifactChecksum (type)", "ArtifactChecksumParsed": "src/kernel/package-artifact.zod.ts#ArtifactChecksumParsed (type)", "ArtifactChecksumSchema": "src/kernel/package-artifact.zod.ts#ArtifactChecksumSchema (const)", @@ -138,9 +135,6 @@ "GetPackageResponse": "src/kernel/package-registry.zod.ts#GetPackageResponse (type)", "GetPackageResponseParsed": "src/kernel/package-registry.zod.ts#GetPackageResponseParsed (type)", "GetPackageResponseSchema": "src/kernel/package-registry.zod.ts#GetPackageResponseSchema (const)", - "GracefulDegradation": "src/kernel/plugin-lifecycle-advanced.zod.ts#GracefulDegradation (type)", - "GracefulDegradationParsed": "src/kernel/plugin-lifecycle-advanced.zod.ts#GracefulDegradationParsed (type)", - "GracefulDegradationSchema": "src/kernel/plugin-lifecycle-advanced.zod.ts#GracefulDegradationSchema (const)", "HealthStatus": "src/kernel/startup-orchestrator.zod.ts#HealthStatus (type)", "HealthStatusSchema": "src/kernel/startup-orchestrator.zod.ts#HealthStatusSchema (const)", "HotReloadConfig": "src/kernel/plugin-lifecycle-advanced.zod.ts#HotReloadConfig (type)", @@ -361,9 +355,6 @@ "PluginTrustScore": "src/kernel/plugin-security.zod.ts#PluginTrustScore (type)", "PluginTrustScoreParsed": "src/kernel/plugin-security.zod.ts#PluginTrustScoreParsed (type)", "PluginTrustScoreSchema": "src/kernel/plugin-security.zod.ts#PluginTrustScoreSchema (const)", - "PluginUpdateStrategy": "src/kernel/plugin-lifecycle-advanced.zod.ts#PluginUpdateStrategy (type)", - "PluginUpdateStrategyParsed": "src/kernel/plugin-lifecycle-advanced.zod.ts#PluginUpdateStrategyParsed (type)", - "PluginUpdateStrategySchema": "src/kernel/plugin-lifecycle-advanced.zod.ts#PluginUpdateStrategySchema (const)", "PluginVendor": "src/kernel/plugin-registry.zod.ts#PluginVendor (type)", "PluginVendorParsed": "src/kernel/plugin-registry.zod.ts#PluginVendorParsed (type)", "PluginVendorSchema": "src/kernel/plugin-registry.zod.ts#PluginVendorSchema (const)", diff --git a/packages/spec/json-schema.manifest/kernel.json b/packages/spec/json-schema.manifest/kernel.json index f41f858424..90ef2d9cf3 100644 --- a/packages/spec/json-schema.manifest/kernel.json +++ b/packages/spec/json-schema.manifest/kernel.json @@ -2,7 +2,6 @@ "description": "Ratchet manifest of every JSON Schema emitted by scripts/build-schemas.ts for one category. Sharded by category (#5837); the gate reads the whole json-schema.manifest/ directory as ONE set. Auto-appended when new schemas are added (commit the change). A listed schema that a build no longer emits fails gen:schema. DELETING a key is gated too (#4725): the removal is measured against this directory at the merge base with origin/main — which the commit under test cannot rewrite — and every def that leaves the published set must be declared in RETIRED_DEFS_BY_MAJOR (src/migrations/registry.ts), or in RENAMED_DEFS (scripts/lib/renamed-defs.ts) when it is a rename rather than a removal. See #2978, #4725.", "category": "kernel", "schemas": [ - "kernel/AdvancedPluginLifecycleConfig", "kernel/ArtifactChecksum", "kernel/ArtifactFileEntry", "kernel/ArtifactSignature", @@ -50,7 +49,6 @@ "kernel/FieldChange", "kernel/GetPackageRequest", "kernel/GetPackageResponse", - "kernel/GracefulDegradation", "kernel/HealthStatus", "kernel/HotReloadConfig", "kernel/InstallPackageRequest", @@ -130,7 +128,6 @@ "kernel/PluginStatistics", "kernel/PluginTrustLevel", "kernel/PluginTrustScore", - "kernel/PluginUpdateStrategy", "kernel/PluginVendor", "kernel/PluginVersionMetadata", "kernel/PreviewModeConfig", diff --git a/packages/spec/src/kernel/plugin-lifecycle-advanced-retirement.test.ts b/packages/spec/src/kernel/plugin-lifecycle-advanced-retirement.test.ts new file mode 100644 index 0000000000..73d485ecf9 --- /dev/null +++ b/packages/spec/src/kernel/plugin-lifecycle-advanced-retirement.test.ts @@ -0,0 +1,114 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import { describe, it, expect } from 'vitest'; + +import { + EXPORT_ENTRY_POINTS, + exportNamesOf, + holdersOf, +} from '../../scripts/lib/export-origins-testkit'; + +// ─── [#11825] The authorable lifecycle-config surface is RETIRED ──────────── +// +// ADR-0049 enforce-or-remove; maintainer ruling 2026-08-25 (route 2). +// `kernel/plugin-lifecycle-advanced.zod.ts` no longer declares +// `AdvancedPluginLifecycleConfigSchema` (the aggregating +// `{ health, hotReload, degradation, updates, resources, observability }` +// config container), `GracefulDegradationSchema` or +// `PluginUpdateStrategySchema` — 3 emitted defs, 9 exported names, 17 +// authorable-surface keys, the reference page's sections with them. +// +// The measurement that decided it (re-run per group at this retirement's base +// commit, 8cdd696, with positive controls — the full record lives in the +// retirement block inside the zod module): +// +// 1. WIRING — no runtime constructs `PluginHealthMonitor` or +// `HotReloadManager`; the only constructions are their own unit tests +// and `core/examples/phase2-integration.ts`, and every one passes the +// config DIRECTLY to the class, never through this container. +// 2. STATIC — zero readers of any `degradation` / `updates` / `resources` / +// `observability` key in objectstack or objectui outside `packages/spec` +// itself (controls: `checkMethod` resolves to +// `core/src/health-monitor.ts`, `debounceDelay` to +// `core/src/hot-reload.ts` — the scan sees real readers). +// 3. DOORS — no metadata-type binding, no stack collection, no manifest +// embed: no authored document could ever carry the container. +// +// Route 3: with no carrier key there is nothing to tombstone and no seam for +// a D2 conversion — `RETIRED_DEFS_BY_MAJOR[18]` plus the D3 semantic entry +// `advanced-plugin-lifecycle-config-retired` ARE the declaration. +// +// Form follows #8715 / #4988 / #5055: resolved symbol identity over every +// public entry via the build-time `export-origins/` artifact. +describe('[#11825] kernel/ AdvancedPluginLifecycleConfig retirement', () => { + /** The 9 names the retired defs exported (3 schema consts + 6 types). */ + const RETIRED_NAMES = [ + 'AdvancedPluginLifecycleConfigSchema', + 'AdvancedPluginLifecycleConfig', + 'AdvancedPluginLifecycleConfigParsed', + 'GracefulDegradationSchema', + 'GracefulDegradation', + 'GracefulDegradationParsed', + 'PluginUpdateStrategySchema', + 'PluginUpdateStrategy', + 'PluginUpdateStrategyParsed', + ] as const; + + /** + * Names that must SURVIVE on `./kernel`: the input vocabularies of the + * host-driven library classes the ruling keeps (`PluginHealthMonitor` / + * `HotReloadManager` in `@objectstack/core` — the #11811 lifecycle.mdx + * examples are the supported usage). Exactly what a too-wide "tidy the + * lifecycle module" sweep would take. + */ + const MUST_SURVIVE_KERNEL = [ + 'PluginHealthStatusSchema', + 'PluginHealthCheckSchema', + 'PluginHealthReportSchema', + 'HotReloadConfigSchema', + 'DistributedStateConfigSchema', + 'PluginStateSnapshotSchema', + 'PluginHealthCheckParsed', + 'HotReloadConfigParsed', + 'PluginStateSnapshot', + ] as const; + + it('every retired name has ZERO holders on any public entry; the survivors still stand', () => { + // Anti-vacuity: the baseline must cover the real surface. + for (const needed of ['.', './kernel']) { + expect(EXPORT_ENTRY_POINTS, `exports map must include ${needed}`).toContain(needed); + } + expect( + exportNamesOf('./kernel').length, + './kernel must export a non-trivial surface' + ).toBeGreaterThan(50); + + // ── ABSENCE (every entry, not just ./kernel) ────────────────────────── + for (const name of RETIRED_NAMES) { + expect(holdersOf(name), `${name} must have zero holders after #11825`).toEqual([]); + } + + // ── SURVIVAL ────────────────────────────────────────────────────────── + const kernelNames = exportNamesOf('./kernel'); + for (const name of MUST_SURVIVE_KERNEL) { + expect(kernelNames, `${name} must SURVIVE this retirement`).toContain(name); + } + }); + + it('the runtime barrel resolves without the retired names and keeps the survivors', async () => { + const kernel = await import('./index'); + for (const name of [ + 'AdvancedPluginLifecycleConfigSchema', + 'GracefulDegradationSchema', + 'PluginUpdateStrategySchema', + ]) { + expect(kernel, `${name} must not be exported after #11825`).not.toHaveProperty(name); + } + // Anti-vacuity: the barrel really resolved and still exports the kept + // library vocabularies (the #4914 §2 keep, restated by this ruling). + expect(kernel).toHaveProperty('PluginHealthCheckSchema'); + expect(kernel).toHaveProperty('HotReloadConfigSchema'); + expect(kernel).toHaveProperty('PluginStateSnapshotSchema'); + expect(kernel).toHaveProperty('ManifestSchema'); + }); +}); diff --git a/packages/spec/src/kernel/plugin-lifecycle-advanced.test.ts b/packages/spec/src/kernel/plugin-lifecycle-advanced.test.ts index 182278cd90..d7cbb331d2 100644 --- a/packages/spec/src/kernel/plugin-lifecycle-advanced.test.ts +++ b/packages/spec/src/kernel/plugin-lifecycle-advanced.test.ts @@ -5,10 +5,7 @@ import { PluginHealthReportSchema, DistributedStateConfigSchema, HotReloadConfigSchema, - GracefulDegradationSchema, - PluginUpdateStrategySchema, PluginStateSnapshotSchema, - AdvancedPluginLifecycleConfigSchema, } from './plugin-lifecycle-advanced.zod'; describe('Plugin Lifecycle Advanced Schemas', () => { @@ -203,84 +200,6 @@ describe('Plugin Lifecycle Advanced Schemas', () => { }); }); - describe('GracefulDegradationSchema', () => { - it('should validate graceful degradation with defaults', () => { - const config = GracefulDegradationSchema.parse({}); - expect(config.enabled).toBe(true); - expect(config.fallbackMode).toBe('minimal'); - }); - - it('should validate complete degradation configuration', () => { - const config = { - enabled: true, - fallbackMode: 'readonly' as const, - criticalDependencies: ['com.objectstack.driver.postgres'], - optionalDependencies: ['com.acme.analytics'], - degradedFeatures: [ - { - feature: 'advanced-search', - enabled: false, - reason: 'Search engine unavailable', - }, - ], - autoRecovery: { - enabled: true, - retryInterval: 120000, - maxAttempts: 10, - }, - }; - const result = GracefulDegradationSchema.parse(config); - expect(result.fallbackMode).toBe('readonly'); - expect(result.criticalDependencies).toHaveLength(1); - }); - }); - - describe('PluginUpdateStrategySchema', () => { - it('should validate update strategy with defaults', () => { - const strategy = PluginUpdateStrategySchema.parse({}); - expect(strategy.mode).toBe('manual'); - }); - - it('should validate automatic update strategy', () => { - const strategy = { - mode: 'automatic' as const, - autoUpdateConstraints: { - major: false, - minor: true, - patch: true, - }, - rollback: { - enabled: true, - automatic: true, - keepVersions: 5, - timeout: 60000, - }, - validation: { - checkCompatibility: true, - runTests: true, - testSuite: 'integration', - }, - }; - const result = PluginUpdateStrategySchema.parse(strategy); - expect(result.mode).toBe('automatic'); - expect(result.autoUpdateConstraints?.patch).toBe(true); - }); - - it('should validate scheduled update strategy', () => { - const strategy = { - mode: 'scheduled' as const, - schedule: { - cron: '0 2 * * 0', - timezone: 'America/New_York', - maintenanceWindow: 120, - }, - }; - const result = PluginUpdateStrategySchema.parse(strategy); - expect(result.mode).toBe('scheduled'); - expect(result.schedule?.timezone).toBe('America/New_York'); - }); - }); - describe('PluginStateSnapshotSchema', () => { it('should validate state snapshot', () => { const snapshot = { @@ -305,90 +224,4 @@ describe('Plugin Lifecycle Advanced Schemas', () => { }); }); - describe('AdvancedPluginLifecycleConfigSchema', () => { - it('should validate empty config', () => { - const config = AdvancedPluginLifecycleConfigSchema.parse({}); - expect(config).toBeDefined(); - }); - - it('should validate complete lifecycle configuration', () => { - const config = { - health: { - interval: 60000, - timeout: 10000, - failureThreshold: 3, - autoRestart: true, - }, - hotReload: { - enabled: true, - watchPatterns: ['src/**/*.ts'], - preserveState: true, - }, - degradation: { - enabled: true, - fallbackMode: 'minimal' as const, - }, - updates: { - mode: 'automatic' as const, - autoUpdateConstraints: { - patch: true, - }, - }, - resources: { - maxMemory: 536870912, - maxCpu: 80, - maxConnections: 100, - timeout: 30000, - }, - observability: { - enableMetrics: true, - enableTracing: true, - enableProfiling: false, - metricsInterval: 60000, - }, - }; - const result = AdvancedPluginLifecycleConfigSchema.parse(config); - expect(result.health?.interval).toBe(60000); - expect(result.hotReload?.enabled).toBe(true); - expect(result.resources?.maxMemory).toBe(536870912); - expect(result.observability?.enableMetrics).toBe(true); - }); - }); - - describe('Integration scenarios', () => { - it('should support plugin with health monitoring and hot reload', () => { - const config = AdvancedPluginLifecycleConfigSchema.parse({ - health: { - interval: 30000, - autoRestart: true, - maxRestartAttempts: 3, - }, - hotReload: { - enabled: true, - preserveState: true, - stateStrategy: 'memory' as const, - }, - }); - expect(config.health?.autoRestart).toBe(true); - expect(config.hotReload?.enabled).toBe(true); - }); - - it('should support plugin with graceful degradation', () => { - const config = AdvancedPluginLifecycleConfigSchema.parse({ - degradation: { - enabled: true, - fallbackMode: 'readonly' as const, - criticalDependencies: ['database'], - optionalDependencies: ['cache', 'analytics'], - autoRecovery: { - enabled: true, - retryInterval: 60000, - maxAttempts: 5, - }, - }, - }); - expect(config.degradation?.fallbackMode).toBe('readonly'); - expect(config.degradation?.criticalDependencies).toHaveLength(1); - }); - }); }); diff --git a/packages/spec/src/kernel/plugin-lifecycle-advanced.zod.ts b/packages/spec/src/kernel/plugin-lifecycle-advanced.zod.ts index 62ead6b20c..160c0cf030 100644 --- a/packages/spec/src/kernel/plugin-lifecycle-advanced.zod.ts +++ b/packages/spec/src/kernel/plugin-lifecycle-advanced.zod.ts @@ -3,16 +3,18 @@ import { z } from 'zod'; /** - * # Advanced Plugin Lifecycle Protocol - * - * Defines advanced lifecycle management capabilities including: - * - Hot reload and live updates - * - Graceful degradation and fallback mechanisms - * - Health monitoring and auto-recovery - * - State preservation during updates - * - * This protocol extends the basic plugin lifecycle with enterprise-grade - * features for production environments. + * # Advanced Plugin Lifecycle — host-driven library vocabularies + * + * Declares the INPUT contracts of the host-driven lifecycle classes exported + * by `@objectstack/core` — `PluginHealthMonitor` (reads `PluginHealthCheck`, + * emits `PluginHealthStatus` / `PluginHealthReport`) and `HotReloadManager` + * (reads `HotReloadConfig`, snapshots via `PluginStateSnapshot`). The kernel + * does not construct either class: a HOST composes them and passes these + * shapes directly (`content/docs/protocol/kernel/lifecycle.mdx`, the #11811 + * examples, is the supported usage). + * + * This module deliberately declares NO authorable configuration surface — see + * the #11825 retirement record below. */ /** @@ -248,146 +250,59 @@ export const HotReloadConfigSchema = lazySchema(() => z.object({ .describe('Hook names to call after reload'), })); -/** - * Graceful Degradation Configuration - * Defines how plugin degrades when dependencies fail - */ -export const GracefulDegradationSchema = lazySchema(() => z.object({ - /** - * Enable graceful degradation - */ - enabled: z.boolean().default(true), - - /** - * Fallback mode when dependencies fail - */ - fallbackMode: z.enum([ - 'minimal', // Provide minimal functionality - 'cached', // Use cached data - 'readonly', // Allow read-only operations - 'offline', // Offline mode with local data - 'disabled', // Disable plugin functionality - ]).default('minimal'), - - /** - * Critical dependencies that must be available - */ - criticalDependencies: z.array(z.string()).optional() - .describe('Plugin IDs that are required for operation'), - - /** - * Optional dependencies that can fail - */ - optionalDependencies: z.array(z.string()).optional() - .describe('Plugin IDs that are nice to have but not required'), - - /** - * Feature flags for degraded mode - */ - degradedFeatures: z.array(z.object({ - feature: z.string().describe('Feature name'), - enabled: z.boolean().describe('Whether feature is available in degraded mode'), - reason: z.string().optional(), - })).optional(), - - /** - * Automatic recovery attempts - */ - autoRecovery: z.object({ - enabled: z.boolean().default(true), - retryInterval: z.number().int().min(1000).default(60000) - .describe('Interval between recovery attempts (ms)'), - maxAttempts: z.number().int().min(0).default(5) - .describe('Maximum recovery attempts before giving up'), - }).optional(), -})); - -/** - * Plugin Update Strategy - * Defines how plugin handles version updates - */ -export const PluginUpdateStrategySchema = lazySchema(() => z.object({ - /** - * Update mode - */ - mode: z.enum([ - 'manual', // Manual updates only - 'automatic', // Automatic updates - 'scheduled', // Scheduled update windows - 'rolling', // Rolling updates with zero downtime - ]).default('manual'), - - /** - * Version constraints for automatic updates - */ - autoUpdateConstraints: z.object({ - major: z.boolean().default(false).describe('Allow major version updates'), - minor: z.boolean().default(true).describe('Allow minor version updates'), - patch: z.boolean().default(true).describe('Allow patch version updates'), - }).optional(), - - /** - * Update schedule (for scheduled mode) - */ - schedule: z.object({ - /** - * Cron expression for update window - */ - cron: z.string().optional(), - - /** - * Timezone for schedule - */ - timezone: z.string().default('UTC'), - - /** - * Maintenance window duration in minutes - */ - maintenanceWindow: z.number().int().min(1).default(60), - }).optional(), - - /** - * Rollback configuration - */ - rollback: z.object({ - enabled: z.boolean().default(true), - - /** - * Automatic rollback on failure - */ - automatic: z.boolean().default(true), - - /** - * Keep N previous versions for rollback - */ - keepVersions: z.number().int().min(1).default(3), - - /** - * Rollback timeout in milliseconds - */ - timeout: z.number().int().min(1000).default(30000), - }).optional(), - - /** - * Pre-update validation - */ - validation: z.object({ - /** - * Run compatibility checks before update - */ - checkCompatibility: z.boolean().default(true), - - /** - * Run tests before applying update - */ - runTests: z.boolean().default(false), - - /** - * Test suite to run - */ - testSuite: z.string().optional(), - }).optional(), -})); +// ─── [#11825] The authorable lifecycle-config surface is RETIRED ──────────── +// +// ADR-0049 enforce-or-remove; maintainer ruling 2026-08-25 (route 2). This +// module used to end in `AdvancedPluginLifecycleConfigSchema` — an aggregating +// `{ health, hotReload, degradation, updates, resources, observability }` +// config container — plus the `GracefulDegradationSchema` and +// `PluginUpdateStrategySchema` value schemas two of its keys carried. All +// three defs are REMOVED. +// +// Why: none of it had a runtime reader, re-measured per group at the +// retirement's base commit with positive controls. The kernel never parses, +// stores or forwards the container; no manifest, stack collection or +// metadata-type binding ever embedded it (so no authored document could carry +// it); and a scan of objectstack + objectui put every reference inside +// `packages/spec` itself — the declaration, its own unit test and the +// generated artifacts. Group by group: +// +// - `health` — `PluginHealthMonitor` reads `PluginHealthCheck` (control: +// `checkMethod`, `core/src/health-monitor.ts`), but no runtime constructs +// the monitor; only its unit test and `core/examples/phase2-integration.ts` +// do, and both pass the config DIRECTLY to the class, never through this +// container. +// - `hotReload` — same shape: `HotReloadManager` reads `HotReloadConfig` +// (control: `debounceDelay` / `stateStrategy`, `core/src/hot-reload.ts`), +// and nothing composes the manager at runtime either. +// - `degradation` / `updates` — zero readers of any key, anywhere: no +// implementation body even exists for `fallbackMode`, `criticalDependencies`, +// `autoUpdateConstraints`, `rollback` et al. An author declaring a rollback +// policy or a degraded-mode contract got a clean parse and NOTHING — the +// #3950 shape (an exported schema with no consumer reads as a capability), +// sharpened by the vocabulary promising production-safety behaviour. +// - `resources` / `observability` — inline sub-objects of the container with +// zero readers (`maxMemory` / `maxCpu` here were never the +// plugin-security-advanced `resourceLimits` that `sandbox-runtime.ts` DOES +// read); they leave with it. +// +// What SURVIVES, deliberately: the input vocabularies of the host-driven +// library classes the ruling keeps — `PluginHealthStatus` / `PluginHealthCheck` +// / `PluginHealthReport`, `HotReloadConfig` (with its embedded +// `DistributedStateConfig`) and `PluginStateSnapshot`. They are library +// parameter types, not an authorable config surface: a host constructs the +// classes and passes these shapes in TypeScript, which is exactly what the +// #4914 ruling kept `HotReloadConfigSchema` for. +// +// Route 3 (no tombstone, no conversion): with no carrier key and no authored +// document there is nothing to tombstone and no seam for a D2 conversion — +// `RETIRED_DEFS_BY_MAJOR[18]` (`kernel/AdvancedPluginLifecycleConfig`, +// `kernel/GracefulDegradation`, `kernel/PluginUpdateStrategy`) plus the D3 +// semantic entry `advanced-plugin-lifecycle-config-retired` ARE the +// declaration. Degradation / update-strategy vocabularies return only via the +// ENFORCE route of ADR-0049 through a new ADR: the implementation first, then +// a declaration of exactly what it honours. +// ──────────────────────────────────────────────────────────────────────────── /** * Plugin State Snapshot @@ -424,53 +339,6 @@ export const PluginStateSnapshotSchema = lazySchema(() => z.object({ }).optional(), })); -/** - * Advanced Plugin Lifecycle Configuration - * Complete configuration for advanced lifecycle management - */ -export const AdvancedPluginLifecycleConfigSchema = lazySchema(() => z.object({ - /** - * Health monitoring configuration - */ - health: PluginHealthCheckSchema.optional(), - - /** - * Hot reload configuration - */ - hotReload: HotReloadConfigSchema.optional(), - - /** - * Graceful degradation configuration - */ - degradation: GracefulDegradationSchema.optional(), - - /** - * Update strategy - */ - updates: PluginUpdateStrategySchema.optional(), - - /** - * Resource limits - */ - resources: z.object({ - maxMemory: z.number().int().optional().describe('Maximum memory in bytes'), - maxCpu: z.number().min(0).max(100).optional().describe('Maximum CPU percentage'), - maxConnections: z.number().int().optional().describe('Maximum concurrent connections'), - timeout: z.number().int().optional().describe('Operation timeout in milliseconds'), - }).optional(), - - /** - * Monitoring and observability - */ - observability: z.object({ - enableMetrics: z.boolean().default(true), - enableTracing: z.boolean().default(true), - enableProfiling: z.boolean().default(false), - metricsInterval: z.number().int().min(1000).default(60000) - .describe('Metrics collection interval in ms'), - }).optional(), -})); - // Export types export type PluginHealthStatus = z.input; export type PluginHealthCheck = z.input; @@ -483,15 +351,6 @@ export type DistributedStateConfigParsed = z.infer; /** Post-parse shape of {@link HotReloadConfig} — defaults applied, transforms run (ADR-0122). */ export type HotReloadConfigParsed = z.infer; -export type GracefulDegradation = z.input; -/** Post-parse shape of {@link GracefulDegradation} — defaults applied, transforms run (ADR-0122). */ -export type GracefulDegradationParsed = z.infer; -export type PluginUpdateStrategy = z.input; -/** Post-parse shape of {@link PluginUpdateStrategy} — defaults applied, transforms run (ADR-0122). */ -export type PluginUpdateStrategyParsed = z.infer; export type PluginStateSnapshot = z.input; /** Post-parse shape of {@link PluginStateSnapshot} — defaults applied, transforms run (ADR-0122). */ export type PluginStateSnapshotParsed = z.infer; -export type AdvancedPluginLifecycleConfig = z.input; -/** Post-parse shape of {@link AdvancedPluginLifecycleConfig} — defaults applied, transforms run (ADR-0122). */ -export type AdvancedPluginLifecycleConfigParsed = z.infer; diff --git a/packages/spec/src/migrations/entries/retired-defs/18.kernel__AdvancedPluginLifecycleConfig.ts b/packages/spec/src/migrations/entries/retired-defs/18.kernel__AdvancedPluginLifecycleConfig.ts new file mode 100644 index 0000000000..3918192e21 --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-defs/18.kernel__AdvancedPluginLifecycleConfig.ts @@ -0,0 +1,30 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #11825 — kernel/plugin-lifecycle-advanced.zod.ts +// `AdvancedPluginLifecycleConfigSchema`, retired whole (ADR-0049 +// enforce-or-remove; maintainer ruling 2026-08-25, route 2). The aggregating +// `{ health, hotReload, degradation, updates, resources, observability }` +// lifecycle-config container had ZERO runtime readers, re-measured per group +// at the retirement's base commit with positive controls: the kernel never +// parses, stores or forwards it; no manifest, stack collection or +// metadata-type binding embeds it (no authored document could carry it); and +// a scan of objectstack + objectui put every reference inside `packages/spec` +// itself. The classes with implementation bodies — `PluginHealthMonitor` and +// `HotReloadManager` in `@objectstack/core` — are KEPT as host-driven library +// (the #11811 lifecycle.mdx examples are the supported usage), and their +// input vocabularies (`PluginHealthCheck`, `PluginHealthStatus`, +// `PluginHealthReport`, `HotReloadConfig`, `DistributedStateConfig`, +// `PluginStateSnapshot`) survive in the same module; but neither class is +// composed by any runtime, and both take their config DIRECTLY — never +// through this container. An author declaring `health: {...}` or a rollback +// policy here got a clean parse and NOTHING — the #3950 shape at container +// scale. Route 3: no carrier key, no authored document for a D2 conversion to +// rewrite, so no tombstone and no conversion — this table plus the D3 +// semantic entry `advanced-plugin-lifecycle-config-retired` ARE the +// declaration. +// +// Registered under 18, not 17: v17.0.0 was cut before this landed, so the +// removal ships on the 17.x line (launch-window convention: accept-set +// narrowings ride minor releases) and the prescription lives at the major +// boundary where `migrate meta` users look (the #8586 / PR #8702 precedent). +export const entry = 'kernel/AdvancedPluginLifecycleConfig'; diff --git a/packages/spec/src/migrations/entries/retired-defs/18.kernel__GracefulDegradation.ts b/packages/spec/src/migrations/entries/retired-defs/18.kernel__GracefulDegradation.ts new file mode 100644 index 0000000000..34993b0511 --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-defs/18.kernel__GracefulDegradation.ts @@ -0,0 +1,18 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #11825 — `kernel/GracefulDegradation` left with +// `kernel/AdvancedPluginLifecycleConfig`: its ONLY consumer was the retired +// container's `degradation` key (the #3950 rule — an exported value schema +// with no consumer reads as a capability). Unlike the `health` / `hotReload` +// vocabularies, which survive as the input types of the kept host-driven +// classes, NO implementation body exists for any of its keys — `fallbackMode`, +// `criticalDependencies`, `optionalDependencies`, `degradedFeatures`, +// `autoRecovery` were never read by anything in objectstack or objectui +// (measured with positive controls; the bare-name collisions — +// plugin-ordering's `optionalDependencies`, auth-manager's private +// `degradedFeatures` map — are different surfaces, verified structurally). +// An author declaring a degraded-mode contract got a clean parse and no +// degradation behaviour of any kind. The vocabulary returns only via the +// ENFORCE route of ADR-0049 through a new ADR — the implementation first. +// See `18.kernel__AdvancedPluginLifecycleConfig.ts` for the family record. +export const entry = 'kernel/GracefulDegradation'; diff --git a/packages/spec/src/migrations/entries/retired-defs/18.kernel__PluginUpdateStrategy.ts b/packages/spec/src/migrations/entries/retired-defs/18.kernel__PluginUpdateStrategy.ts new file mode 100644 index 0000000000..88b55954c1 --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-defs/18.kernel__PluginUpdateStrategy.ts @@ -0,0 +1,18 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #11825 — `kernel/PluginUpdateStrategy` left with +// `kernel/AdvancedPluginLifecycleConfig`: its ONLY consumer was the retired +// container's `updates` key (the #3950 rule — an exported value schema with +// no consumer reads as a capability). NO implementation body exists for any +// of its keys — `mode`, `autoUpdateConstraints`, `schedule`, `rollback`, +// `validation` were never read by anything in objectstack or objectui +// (measured with positive controls; `checkCompatibility` on +// `AppLifecycleService` is a different surface, verified structurally). The +// sharpest face: `rollback: { automatic: true, keepVersions: 3 }` promised +// zero-downtime rolling updates and automatic rollback-on-failure that +// nothing implements — production-safety vocabulary an author (very often an +// AI, ADR-0033) reads as proof the capability exists. The vocabulary returns +// only via the ENFORCE route of ADR-0049 through a new ADR — the +// implementation first. See `18.kernel__AdvancedPluginLifecycleConfig.ts` +// for the family record. +export const entry = 'kernel/PluginUpdateStrategy'; diff --git a/packages/spec/src/migrations/entries/semantic/18.advanced-plugin-lifecycle-config-retired.ts b/packages/spec/src/migrations/entries/semantic/18.advanced-plugin-lifecycle-config-retired.ts new file mode 100644 index 0000000000..02b9394811 --- /dev/null +++ b/packages/spec/src/migrations/entries/semantic/18.advanced-plugin-lifecycle-config-retired.ts @@ -0,0 +1,70 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import type { SemanticMigration } from '../../types.js'; + +export const entry: SemanticMigration = { + id: 'advanced-plugin-lifecycle-config-retired', + surface: + 'kernel.advancedPluginLifecycle (the authorable config surface of ' + + '`plugin-lifecycle-advanced.zod.ts` — 3 defs, 9 exported names: ' + + '`AdvancedPluginLifecycleConfigSchema` / `AdvancedPluginLifecycleConfig` ' + + '/ `AdvancedPluginLifecycleConfigParsed`, `GracefulDegradationSchema` / ' + + '`GracefulDegradation` / `GracefulDegradationParsed`, ' + + '`PluginUpdateStrategySchema` / `PluginUpdateStrategy` / ' + + '`PluginUpdateStrategyParsed`)', + replacement: + '(removed — there is no declarative replacement, because nothing ever read ' + + 'the declaration. The supported lifecycle surface is the HOST-DRIVEN ' + + 'library in `@objectstack/core`: construct `PluginHealthMonitor` and ' + + 'pass a `PluginHealthCheck` per plugin, construct `HotReloadManager` and ' + + 'pass a `HotReloadConfig` — the `content/docs/protocol/kernel/' + + 'lifecycle.mdx` examples (#11811) are the supported usage, and those ' + + 'input vocabularies (`PluginHealthStatus` / `PluginHealthCheck` / ' + + '`PluginHealthReport`, `HotReloadConfig` with its embedded ' + + '`DistributedStateConfig`, `PluginStateSnapshot`) SURVIVE in the same ' + + 'module as library parameter types. Degradation and update-strategy ' + + 'vocabularies return only via the ENFORCE route of ADR-0049 through a ' + + 'new ADR — the executor first, the vocabulary second)', + reason: + 'ADR-0049 enforce-or-remove; maintainer ruling 2026-08-25 on #11825 ' + + '(route 2). The container aggregated six config groups — `health`, ' + + '`hotReload`, `degradation`, `updates`, `resources`, `observability` — ' + + 'and NO group had a runtime reader, re-measured per group at the ' + + 'retirement\'s base commit (8cdd696) with positive controls: the kernel ' + + 'never constructs `PluginHealthMonitor` or `HotReloadManager` (only ' + + 'their own unit tests and `core/examples/phase2-integration.ts` do, ' + + 'passing config DIRECTLY to the classes, never through this container); ' + + '`degradation` / `updates` / `resources` / `observability` keys have no ' + + 'implementation body at all (controls: `checkMethod` resolves to ' + + '`core/src/health-monitor.ts` and `debounceDelay` to ' + + '`core/src/hot-reload.ts`, proving the scan sees real readers; the ' + + 'bare-name collisions — plugin-ordering\'s `optionalDependencies`, ' + + 'auth-manager\'s private `degradedFeatures`, plugin-security-advanced\'s ' + + '`resourceLimits.maxCpu` read by `sandbox-runtime.ts` — are different ' + + 'surfaces, verified structurally). No manifest, stack collection or ' + + 'metadata-type binding ever embedded the container, so no authored ' + + 'document could carry it: an author declaring `health: {...}` or ' + + '`rollback: { automatic: true }` got a clean parse and NOTHING — the ' + + '#3950 shape at container scale, sharpened by production-safety ' + + 'vocabulary (auto-restart, zero-downtime rolling updates, automatic ' + + 'rollback) an AI author (ADR-0033) reads as proof the capability ' + + 'exists. With no carrier key and no authored document there is nothing ' + + 'to tombstone and no seam for a D2 conversion: route 3, the #4834 / ' + + '#8715 shape — RETIRED_DEFS_BY_MAJOR plus this entry ARE the ' + + 'declaration.', + acceptanceCriteria: + 'No code imports any of the 9 retired names from `@objectstack/spec` or ' + + '`@objectstack/spec/kernel` — every one is TS2305 after upgrade, on ' + + 'every public entry (pinned by resolved symbol identity in ' + + '`kernel/plugin-lifecycle-advanced-retirement.test.ts`). No metadata ' + + 'document needs editing: the container was reachable from no ' + + 'metadata-type binding, stack collection or manifest embed, so no ' + + 'document could ever carry it. The host-driven library vocabularies ' + + 'survive unchanged on `./kernel` (`PluginHealthStatusSchema`, ' + + '`PluginHealthCheckSchema`, `PluginHealthReportSchema`, ' + + '`HotReloadConfigSchema`, `DistributedStateConfigSchema`, ' + + '`PluginStateSnapshotSchema` — same pin), and `PluginHealthMonitor` / ' + + '`HotReloadManager` stay exported from `@objectstack/core` with their ' + + 'tests green. ⚠️ Runtime behaviour is deliberately UNCHANGED: nothing ' + + 'ever read the container, so removing it removes no behaviour.', +}; diff --git a/packages/spec/src/migrations/registry.ts b/packages/spec/src/migrations/registry.ts index ffec98a792..c8b1f8c1f7 100644 --- a/packages/spec/src/migrations/registry.ts +++ b/packages/spec/src/migrations/registry.ts @@ -5279,6 +5279,72 @@ const step18: MigrationStep = { + 'exportable by nobody, which is the point of the change; confirm that is what you want ' + 'before granting it back.', }, + { + id: 'advanced-plugin-lifecycle-config-retired', + surface: + 'kernel.advancedPluginLifecycle (the authorable config surface of ' + + '`plugin-lifecycle-advanced.zod.ts` — 3 defs, 9 exported names: ' + + '`AdvancedPluginLifecycleConfigSchema` / `AdvancedPluginLifecycleConfig` ' + + '/ `AdvancedPluginLifecycleConfigParsed`, `GracefulDegradationSchema` / ' + + '`GracefulDegradation` / `GracefulDegradationParsed`, ' + + '`PluginUpdateStrategySchema` / `PluginUpdateStrategy` / ' + + '`PluginUpdateStrategyParsed`)', + replacement: + '(removed — there is no declarative replacement, because nothing ever read ' + + 'the declaration. The supported lifecycle surface is the HOST-DRIVEN ' + + 'library in `@objectstack/core`: construct `PluginHealthMonitor` and ' + + 'pass a `PluginHealthCheck` per plugin, construct `HotReloadManager` and ' + + 'pass a `HotReloadConfig` — the `content/docs/protocol/kernel/' + + 'lifecycle.mdx` examples (#11811) are the supported usage, and those ' + + 'input vocabularies (`PluginHealthStatus` / `PluginHealthCheck` / ' + + '`PluginHealthReport`, `HotReloadConfig` with its embedded ' + + '`DistributedStateConfig`, `PluginStateSnapshot`) SURVIVE in the same ' + + 'module as library parameter types. Degradation and update-strategy ' + + 'vocabularies return only via the ENFORCE route of ADR-0049 through a ' + + 'new ADR — the executor first, the vocabulary second)', + reason: + 'ADR-0049 enforce-or-remove; maintainer ruling 2026-08-25 on #11825 ' + + '(route 2). The container aggregated six config groups — `health`, ' + + '`hotReload`, `degradation`, `updates`, `resources`, `observability` — ' + + 'and NO group had a runtime reader, re-measured per group at the ' + + 'retirement\'s base commit (8cdd696) with positive controls: the kernel ' + + 'never constructs `PluginHealthMonitor` or `HotReloadManager` (only ' + + 'their own unit tests and `core/examples/phase2-integration.ts` do, ' + + 'passing config DIRECTLY to the classes, never through this container); ' + + '`degradation` / `updates` / `resources` / `observability` keys have no ' + + 'implementation body at all (controls: `checkMethod` resolves to ' + + '`core/src/health-monitor.ts` and `debounceDelay` to ' + + '`core/src/hot-reload.ts`, proving the scan sees real readers; the ' + + 'bare-name collisions — plugin-ordering\'s `optionalDependencies`, ' + + 'auth-manager\'s private `degradedFeatures`, plugin-security-advanced\'s ' + + '`resourceLimits.maxCpu` read by `sandbox-runtime.ts` — are different ' + + 'surfaces, verified structurally). No manifest, stack collection or ' + + 'metadata-type binding ever embedded the container, so no authored ' + + 'document could carry it: an author declaring `health: {...}` or ' + + '`rollback: { automatic: true }` got a clean parse and NOTHING — the ' + + '#3950 shape at container scale, sharpened by production-safety ' + + 'vocabulary (auto-restart, zero-downtime rolling updates, automatic ' + + 'rollback) an AI author (ADR-0033) reads as proof the capability ' + + 'exists. With no carrier key and no authored document there is nothing ' + + 'to tombstone and no seam for a D2 conversion: route 3, the #4834 / ' + + '#8715 shape — RETIRED_DEFS_BY_MAJOR plus this entry ARE the ' + + 'declaration.', + acceptanceCriteria: + 'No code imports any of the 9 retired names from `@objectstack/spec` or ' + + '`@objectstack/spec/kernel` — every one is TS2305 after upgrade, on ' + + 'every public entry (pinned by resolved symbol identity in ' + + '`kernel/plugin-lifecycle-advanced-retirement.test.ts`). No metadata ' + + 'document needs editing: the container was reachable from no ' + + 'metadata-type binding, stack collection or manifest embed, so no ' + + 'document could ever carry it. The host-driven library vocabularies ' + + 'survive unchanged on `./kernel` (`PluginHealthStatusSchema`, ' + + '`PluginHealthCheckSchema`, `PluginHealthReportSchema`, ' + + '`HotReloadConfigSchema`, `DistributedStateConfigSchema`, ' + + '`PluginStateSnapshotSchema` — same pin), and `PluginHealthMonitor` / ' + + '`HotReloadManager` stay exported from `@objectstack/core` with their ' + + 'tests green. ⚠️ Runtime behaviour is deliberately UNCHANGED: nothing ' + + 'ever read the container, so removing it removes no behaviour.', + }, { id: 'analytics-authorable-unknown-keys-refused', // Same-major bookkeeping (#10414): batch D also closed the nested @@ -7819,6 +7885,66 @@ export const RETIRED_DEFS_BY_MAJOR: Readonly> // narrowings ride minor releases) and the prescription lives at the major // boundary where `migrate meta` users look (the #8586 / PR #8702 precedent). 'identity/ApiKey', + // #11825 — kernel/plugin-lifecycle-advanced.zod.ts + // `AdvancedPluginLifecycleConfigSchema`, retired whole (ADR-0049 + // enforce-or-remove; maintainer ruling 2026-08-25, route 2). The aggregating + // `{ health, hotReload, degradation, updates, resources, observability }` + // lifecycle-config container had ZERO runtime readers, re-measured per group + // at the retirement's base commit with positive controls: the kernel never + // parses, stores or forwards it; no manifest, stack collection or + // metadata-type binding embeds it (no authored document could carry it); and + // a scan of objectstack + objectui put every reference inside `packages/spec` + // itself. The classes with implementation bodies — `PluginHealthMonitor` and + // `HotReloadManager` in `@objectstack/core` — are KEPT as host-driven library + // (the #11811 lifecycle.mdx examples are the supported usage), and their + // input vocabularies (`PluginHealthCheck`, `PluginHealthStatus`, + // `PluginHealthReport`, `HotReloadConfig`, `DistributedStateConfig`, + // `PluginStateSnapshot`) survive in the same module; but neither class is + // composed by any runtime, and both take their config DIRECTLY — never + // through this container. An author declaring `health: {...}` or a rollback + // policy here got a clean parse and NOTHING — the #3950 shape at container + // scale. Route 3: no carrier key, no authored document for a D2 conversion to + // rewrite, so no tombstone and no conversion — this table plus the D3 + // semantic entry `advanced-plugin-lifecycle-config-retired` ARE the + // declaration. + // + // Registered under 18, not 17: v17.0.0 was cut before this landed, so the + // removal ships on the 17.x line (launch-window convention: accept-set + // narrowings ride minor releases) and the prescription lives at the major + // boundary where `migrate meta` users look (the #8586 / PR #8702 precedent). + 'kernel/AdvancedPluginLifecycleConfig', + // #11825 — `kernel/GracefulDegradation` left with + // `kernel/AdvancedPluginLifecycleConfig`: its ONLY consumer was the retired + // container's `degradation` key (the #3950 rule — an exported value schema + // with no consumer reads as a capability). Unlike the `health` / `hotReload` + // vocabularies, which survive as the input types of the kept host-driven + // classes, NO implementation body exists for any of its keys — `fallbackMode`, + // `criticalDependencies`, `optionalDependencies`, `degradedFeatures`, + // `autoRecovery` were never read by anything in objectstack or objectui + // (measured with positive controls; the bare-name collisions — + // plugin-ordering's `optionalDependencies`, auth-manager's private + // `degradedFeatures` map — are different surfaces, verified structurally). + // An author declaring a degraded-mode contract got a clean parse and no + // degradation behaviour of any kind. The vocabulary returns only via the + // ENFORCE route of ADR-0049 through a new ADR — the implementation first. + // See `18.kernel__AdvancedPluginLifecycleConfig.ts` for the family record. + 'kernel/GracefulDegradation', + // #11825 — `kernel/PluginUpdateStrategy` left with + // `kernel/AdvancedPluginLifecycleConfig`: its ONLY consumer was the retired + // container's `updates` key (the #3950 rule — an exported value schema with + // no consumer reads as a capability). NO implementation body exists for any + // of its keys — `mode`, `autoUpdateConstraints`, `schedule`, `rollback`, + // `validation` were never read by anything in objectstack or objectui + // (measured with positive controls; `checkCompatibility` on + // `AppLifecycleService` is a different surface, verified structurally). The + // sharpest face: `rollback: { automatic: true, keepVersions: 3 }` promised + // zero-downtime rolling updates and automatic rollback-on-failure that + // nothing implements — production-safety vocabulary an author (very often an + // AI, ADR-0033) reads as proof the capability exists. The vocabulary returns + // only via the ENFORCE route of ADR-0049 through a new ADR — the + // implementation first. See `18.kernel__AdvancedPluginLifecycleConfig.ts` + // for the family record. + 'kernel/PluginUpdateStrategy', // #10485 — `ui/BorderRadius` (the border-radius scale sub-block) left with `ui/Theme`: // its ONLY consumer was the retired `ThemeSchema` (the #3950 rule — an // exported value schema with no consumer reads as a capability). See From 30ba941de190e57e6e6a1cabde91b6cb1b7a4109 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 23:38:03 +0000 Subject: [PATCH 2/2] chore(spec): regenerate reference docs and strictness ledger after merging origin/main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The merge took origin/main's side of every os-regen artifact (per scripts/pm/os-regen-merge.sh); this commit re-derives them from the merged tree — gen:schema + gen:docs + gen:strictness-ledger — so main's nested-shape reference tables (#12309) and this branch's retirement both survive. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV --- content/docs/references/index.mdx | 10 ++-- .../kernel/plugin-lifecycle-advanced.mdx | 48 +++++++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/content/docs/references/index.mdx b/content/docs/references/index.mdx index 6cca68ed63..fd47cc44d2 100644 --- a/content/docs/references/index.mdx +++ b/content/docs/references/index.mdx @@ -1,6 +1,6 @@ --- title: Protocol Reference -description: Every schema published by @objectstack/spec — 1585 schemas across 14 protocol modules +description: Every schema published by @objectstack/spec — 1586 schemas across 14 protocol modules --- {/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} @@ -29,11 +29,11 @@ counts are sums of the rows they head. Regenerate with | [Kernel Protocol](/docs/references/kernel) | 31 | 173 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. | | [QA Protocol](/docs/references/qa) | 1 | 8 | Declarative test suites — scenarios, steps, actions and assertions. | | [Security Protocol](/docs/references/security) | 5 | 27 | Permission sets, row-level security, sharing rules, tenancy posture. | -| [Shared Protocol](/docs/references/shared) | 8 | 31 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. | +| [Shared Protocol](/docs/references/shared) | 8 | 32 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. | | [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. | | [System Protocol](/docs/references/system) | 36 | 288 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. | | [UI Protocol](/docs/references/ui) | 16 | 152 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. | -| **Total** | **199** | **1585** | 14 protocol modules | +| **Total** | **199** | **1586** | 14 protocol modules | --- @@ -285,7 +285,7 @@ Permission sets, row-level security, sharing rules, tenancy posture. ## Shared Protocol -**Source:** `packages/spec/src/shared/` · **Import:** `@objectstack/spec/shared` · **8 pages, 31 schemas** +**Source:** `packages/spec/src/shared/` · **Import:** `@objectstack/spec/shared` · **8 pages, 32 schemas** Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. @@ -295,7 +295,7 @@ Primitives used across every protocol — identifiers, HTTP, expressions, error | [`enums.zod.ts`](/docs/references/shared/enums) | `IsolationLevelEnum`, `MutationEventEnum`, `SortDirectionEnum`, `SortItem` | | [`expression.zod.ts`](/docs/references/shared/expression) | `CronExpressionInput`, `Expression`, `ExpressionDialect`, `ExpressionInput`, `ExpressionMeta`, `Predicate`, `PredicateInput`, `TemplateExpressionInput` | | [`http.zod.ts`](/docs/references/shared/http) | `CorsConfig`, `HttpMethod`, `HttpMethodSubset`, `HttpRequest`, `RateLimitConfig`, `StaticMount` | -| [`identifiers.zod.ts`](/docs/references/shared/identifiers) | `EventName`, `SnakeCaseIdentifier`, `SystemIdentifier` | +| [`identifiers.zod.ts`](/docs/references/shared/identifiers) | `EventName`, `MetadataItemName`, `SnakeCaseIdentifier`, `SystemIdentifier` | | [`mapping.zod.ts`](/docs/references/shared/mapping) | `FieldMapping` | | [`metadata-types.zod.ts`](/docs/references/shared/metadata-types) | `BaseMetadataRecord`, `MetadataFormat` | | [`protection.zod.ts`](/docs/references/shared/protection) | `Protection` | diff --git a/content/docs/references/kernel/plugin-lifecycle-advanced.mdx b/content/docs/references/kernel/plugin-lifecycle-advanced.mdx index b7148f8d9f..10dd5d156a 100644 --- a/content/docs/references/kernel/plugin-lifecycle-advanced.mdx +++ b/content/docs/references/kernel/plugin-lifecycle-advanced.mdx @@ -67,6 +67,18 @@ const result = DistributedStateConfigSchema.parse(data); | **beforeReload** | `string[]` | optional | Hook names to call before reload | | **afterReload** | `string[]` | optional | Hook names to call after reload | +### Nested Shape: `HotReloadConfig.distributedConfig` + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **provider** | `Enum<'redis' \| 'etcd' \| 'custom'>` | ✅ | Distributed state backend provider | +| **endpoints** | `string[]` | optional | Backend connection endpoints | +| **keyPrefix** | `string` | optional | Prefix for all keys (e.g., "plugin:my-plugin:") | +| **ttl** | `integer` | optional | State expiration time in seconds | +| **auth** | `{ username?: string; password?: string; token?: string; certificate?: string }` | optional | | +| **replication** | `{ enabled: boolean; minReplicas: integer }` | optional | | +| **customConfig** | `Record` | optional | Provider-specific configuration | + --- @@ -101,6 +113,34 @@ const result = DistributedStateConfigSchema.parse(data); | **checks** | `{ name: string; status: Enum<'passed' \| 'failed' \| 'warning'>; message?: string; data?: Record }[]` | optional | | | **dependencies** | `{ pluginId: string; status: Enum<'healthy' \| 'degraded' \| 'unhealthy' \| 'failed' \| 'recovering' \| 'unknown'>; message?: string }[]` | optional | | +### Nested Shape: `PluginHealthReport.metrics` + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **uptime** | `number` | optional | Plugin uptime in milliseconds | +| **memoryUsage** | `number` | optional | Memory usage in bytes | +| **cpuUsage** | `number` | optional | CPU usage percentage | +| **activeConnections** | `number` | optional | Number of active connections | +| **errorRate** | `number` | optional | Error rate (errors per minute) | +| **responseTime** | `number` | optional | Average response time in ms | + +### Nested Shape: `PluginHealthReport.checks[number]` + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | ✅ | Check name | +| **status** | `Enum<'passed' \| 'failed' \| 'warning'>` | ✅ | | +| **message** | `string` | optional | | +| **data** | `Record` | optional | | + +### Nested Shape: `PluginHealthReport.dependencies[number]` + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **pluginId** | `string` | ✅ | | +| **status** | `Enum<'healthy' \| 'degraded' \| 'unhealthy' \| 'failed' \| 'recovering' \| 'unknown'>` | ✅ | Current health status of the plugin | +| **message** | `string` | optional | | + --- @@ -132,6 +172,14 @@ Current health status of the plugin | **state** | `Record` | ✅ | | | **metadata** | `{ checksum?: string; compressed: boolean; encryption?: string }` | optional | | +### Nested Shape: `PluginStateSnapshot.metadata` + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **checksum** | `string` | optional | State checksum for verification | +| **compressed** | `boolean` | optional (default: `false`) | | +| **encryption** | `string` | optional | Encryption algorithm if encrypted | + ---