Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions .changeset/plugin-manifest-loading-removed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
"@objectstack/spec": major
---

refactor(spec)!: remove `manifest.loading` — a whole plugin loading block nothing read, including a sandbox that isolated nothing (#4914)

<!-- adr-0087: registered plugin-manifest-loading-retired -->

`manifest.loading` and the entire `PluginLoadingConfig` block behind it are
removed under ADR-0049 enforce-or-remove (maintainer ruling 2026-08-04). The
block declared a complete plugin loading policy — `strategy`, `preload`,
`codeSplitting`, `dynamicImport`, `initialization`, `dependencyResolution`,
`hotReload`, `caching`, `sandboxing`, `monitoring` — and **nothing read any of
it**. A bare-name scan of all three repos (objectstack, cloud, objectui, each
with a control probe proving the scan saw the tree) put every reference inside
`packages/spec` itself: the declaration, its own unit tests, the
`Manifest.loading` embed and the generated artifacts. Authoring it parsed
cleanly, entered the manifest, and configured nothing.

**FROM → TO:** delete the `loading` key from `objectstack.plugin.json`. There is
nothing to re-declare. Plugins are composed at boot — `defineStack` registers
them and the kernel runs `init` then `start` in an order topologically resolved
from each composed plugin's own `dependencies` / `optionalDependencies`
(`resolvePluginOrder`); the set is fixed until the process restarts.

**⚠️ `loading.sandboxing` is the reason this is a major rather than tidying.** It
declared `isolationLevel: 'process' | 'vm' | 'iframe' | 'web-worker'`, IPC
transports and an `allowedServices` ACL — and applied **none** of it. If you
authored it believing plugins were isolated, they were not, and they never were:
no process/vm/iframe/worker boundary was ever created and `allowedServices`
gated no call. An inert security control is worse than an absent one because it
is believed — the ADR-0033 hazard, since an AI author reads a published
vocabulary as proof of the capability. The enforced surfaces are the plugin
trust tier (`manifest.runtime`, ADR-0025 §3.6) and the manifest permission
declarations.

**Hot reload converges on one vocabulary.** `PluginHotReloadSchema` was the dead
one of two: `HotReloadManager` (`packages/core/src/hot-reload.ts`) reads
`HotReloadConfigSchema` in `plugin-lifecycle-advanced.zod.ts`, which is **kept**.
`PLUGIN_STANDARDS.md` §5.1 now points at that surviving side and states its real
status — it has an implementation body but no runtime composes one, so it is a
foundation, not a shipped capability. Enforcing it is a separate future decision
and deliberately not part of this change.

The retirement kit:

- **`retiredKey()` tombstone** on `Manifest.loading`, not a plain deletion:
`ManifestSchema` is not `.strict()`, so deleting the key would let zod strip it
in silence — trading an inert declaration for an invisible one (the #3726 /
#3733 shape, ADR-0104). The tombstone is audible through `tsc` (input type
`never`) and through the parse, which raises the prescription itself.
- **Eleven whole defs unpublished** with the carrier key, registered in
`RETIRED_DEFS_BY_MAJOR[17]`: `PluginLoadingConfig`, `PluginLoadingStrategy`,
`PluginPreloadConfig`, `PluginCodeSplitting`, `PluginDynamicImport`,
`PluginInitialization`, `PluginDependencyResolution`, `PluginHotReload`,
`PluginCaching`, `PluginSandboxing`, `PluginPerformanceMonitoring`, plus every
type alias. `PluginLoadingEvent` / `PluginLoadingState` survive — the module's
observational half, never embedded in the config block.
- **ADR-0087 D3 `SemanticMigration`** (`plugin-manifest-loading-retired`) and the
`kernel/Manifest:loading` entry in `RETIRED_KEYS_BY_MAJOR[17]`. Deliberately
**no D2 conversion**: the chain walks a normalized stack and
`applyConversionsToStoredItem` maps a metadata type onto one of its
collections, but `PLURAL_TO_SINGULAR` has no `packages` / `plugins` entry — a
manifest is not a stack collection member, and a stored manifest row passes
that seam through unchanged. A conversion would be a transform with no seam
that ever runs.
- **No liveness-ledger change**: the ledger walks `BUILTIN_METADATA_TYPE_SCHEMAS`
(authorable metadata types), and the kernel manifest is not one, so
`manifest.loading` never had a row to keep or orphan.
- Docs rewritten (`PLUGIN_STANDARDS.md` §5.1/§5.2/§5.4, `PROTOCOL_MAP.md`);
generated baselines and reference pages regenerated.

**Already-installed packages keep working.** Nothing ever read the block, so
removing it removes no behaviour. A stored manifest that still carries `loading`
degrades to a single `[metadata_spec_invalid]` diagnostic at registration —
`Registry.validate()` is deliberately a diagnostic and not a gate, so bad
metadata is never a data outage — and clears when you delete the key from the
source manifest and reinstall. The enforced channel is `os plugin build`, which
runs `ManifestSchema.safeParse` with the author present and exits non-zero
carrying the prescription.
2 changes: 1 addition & 1 deletion content/docs/protocol/kernel/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ const message = i18n.t('slack.button.send', context.locale);

Kernel is the **control plane** that orchestrates ObjectStack:
- **Lifecycle Management:** Declarative deployment, zero-downtime upgrades, rollback safety
- **Plugin System:** Microkernel architecture with dependency resolution and sandboxing
- **Plugin System:** Microkernel architecture with dependency resolution and trust tiers
- **Configuration:** Unified config with merge strategies and tenant isolation
- **i18n:** Multi-language support built into the platform

Expand Down
10 changes: 5 additions & 5 deletions content/docs/references/index.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Protocol Reference
description: Every schema published by @objectstack/spec — 1586 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1575 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/. */}
Expand All @@ -26,14 +26,14 @@ counts are sums of the rows they head. Regenerate with
| [Data Protocol](/docs/references/data) | 30 | 166 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. |
| [Identity Protocol](/docs/references/identity) | 5 | 28 | Users and accounts, organizations, positions, API keys, 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 | 187 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. |
| [Kernel Protocol](/docs/references/kernel) | 31 | 176 | 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) | 37 | 292 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
| [UI Protocol](/docs/references/ui) | 16 | 147 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
| **Total** | **200** | **1586** | 14 protocol modules |
| **Total** | **200** | **1575** | 14 protocol modules |

---

Expand Down Expand Up @@ -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, 187 schemas**
**Source:** `packages/spec/src/kernel/` · **Import:** `@objectstack/spec/kernel` · **31 pages, 176 schemas**

Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry.

Expand Down Expand Up @@ -243,7 +243,7 @@ Plugin lifecycle and manifests, capabilities and security, metadata loading, ser
| [`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-loading.zod.ts`](/docs/references/kernel/plugin-loading) | `PluginCaching`, `PluginCodeSplitting`, `PluginDependencyResolution`, `PluginDynamicImport`, `PluginHotReload`, `PluginInitialization`, `PluginLoadingConfig`, `PluginLoadingEvent`, `PluginLoadingState`, `PluginLoadingStrategy`, `PluginPerformanceMonitoring`, `PluginPreloadConfig`, `PluginSandboxing` |
| [`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` |
| [`plugin-security-advanced.zod.ts`](/docs/references/kernel/plugin-security-advanced) | `KernelSecurityPolicy`, `KernelSecurityScanResult`, `KernelSecurityVulnerability`, `PermissionAction`, `PermissionScope`, `PluginPermission`, `PluginPermissionSet`, `PluginSecurityManifest`, `PluginTrustLevel`, `ResourceType`, `RuntimeConfig`, `SandboxConfig` |
Expand Down
2 changes: 1 addition & 1 deletion content/docs/references/kernel/manifest.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const result = ManifestSchema.parse(data);
| **capabilities** | `{ implements?: object[]; provides?: object[]; requires?: object[]; extensionPoints?: object[]; … }` | optional | Plugin capability declarations for interoperability |
| **extensions** | `Record<string, any>` | optional | Extension points and contributions |
| **navigationContributions** | `{ app: string; group?: string; priority?: integer; items: (object \| … +8 more)[] }[]` | optional | Navigation items this package contributes into apps owned by other packages |
| **loading** | `{ strategy?: Enum<'eager' \| 'lazy' \| 'parallel' \| 'deferred' \| 'on-demand'>; preload?: object; codeSplitting?: object; dynamicImport?: object; … }` | optional | Plugin loading and runtime behavior configuration |
| **loading** | `never` | optional | [REMOVED] `manifest.loading` was removed in @objectstack/spec 17.0.0 (#4914, ADR-0049 enforce-or-remove) — the entire block (`strategy`, `preload`, `codeSplitting`, `dynamicImport`, `initialization`, `dependencyResolution`, `hotReload`, `caching`, `sandboxing`, `monitoring`) had no runtime reader in any repo, so authoring it configured nothing. Delete the key. Plugins are composed at boot — `defineStack` registers them and the kernel runs `init` then `start` in an order topologically resolved from each composed plugin's own `dependencies` / `optionalDependencies` (`resolvePluginOrder`); the set is fixed until the process restarts. ⚠️ `loading.sandboxing` in particular never isolated anything: it did not run plugins in a process, vm, iframe or web-worker, and `allowedServices` gated no call. If you were relying on it for isolation, you had none — use the plugin trust tier (`manifest.runtime`) and the permission declarations, which are enforced. |
| **engine** | `{ objectstack: string }` | optional | Platform compatibility requirements (legacy; superseded by `engines`) |
| **engines** | `{ platform?: string; protocol?: string }` | optional | Plugin compatibility ranges (ADR-0025 §3.2; supersedes `engine`) |
| **runtime** | `Enum<'node' \| 'sandbox' \| 'worker'>` | optional | Plugin trust tier (ADR-0025 §3.6) |
Expand Down
Loading
Loading