diff --git a/.changeset/25608-retire-package-upgrade.md b/.changeset/25608-retire-package-upgrade.md new file mode 100644 index 00000000000..505fbcceb2c --- /dev/null +++ b/.changeset/25608-retire-package-upgrade.md @@ -0,0 +1,35 @@ +--- +'@objectstack/spec': minor +--- + +**BREAKING** — the package upgrade model (`kernel/package-upgrade.zod.ts`: upgrade plan, metadata diff, impact level, pre-upgrade snapshot, upgrade and rollback request/response, phase vocabulary), the four `IPackageService` upgrade members and the `plan` member of `PackageUpgradeResponse` leave the public surface (#25608). + +A `major`-class change, recorded as `minor` under the launch-window convention. Maintainer ruling A (`6259332195`, item 3, 「其他同意」): "**Retired:** `packages/spec/src/kernel/package-upgrade.zod.ts` (plan, diff, conflict, snapshot, rollback; zero runtime consumers), with its ADR-0087 conversion." Done directly in 18.x, with no retirement stage; ADR-0049 enforce-or-remove. + +Clause-②: no (narrowing) +Metadata-protocol: narrowed +Ruling-ref: 6259332195 + +**Why.** `@objectstack/spec` declared a complete package upgrade lifecycle — plan the upgrade against a metadata diff, grade its impact, take a pre-upgrade snapshot, three-way merge the customer's customizations, roll back to the snapshot — and nothing on the platform implemented any of it. No provider implements `IPackageService`, so `planUpgrade`, `upgrade`, `rollback` and `getSnapshot` had no body anywhere; no route answers `UpgradePackageRequest` or `RollbackPackageRequest`; and no package, example, app or skill in this repository, in objectui or in hotcrm read any of the names. `UpgradePackageRequest.createSnapshot: true` and `mergeStrategy: 'three-way-merge'` read as protections the platform gave an operator's customizations, and it never did. + +### FROM → TO + +| removed | from | what to write instead | +| --- | --- | --- | +| `UpgradePlanSchema`, `MetadataDiffItemSchema`, `MetadataChangeTypeSchema`, `UpgradeImpactLevelSchema` (with their types and `…Parsed` aliases) | `@objectstack/spec/kernel` | nothing — no code ever generated an upgrade plan or diffed two package versions. | +| `UpgradeSnapshotSchema`, `RollbackPackageRequestSchema`, `RollbackPackageResponseSchema` (with their types and `…Parsed` aliases) | `@objectstack/spec/kernel` | nothing — no snapshot was ever taken. The package rollback the platform serves is the commit rollback, `POST /api/v1/packages/:id/rollback` with a `commitId`, answered by `RollbackToPackageCommitResponseSchema` (`@objectstack/spec/api`). | +| `UpgradePackageRequestSchema`, `UpgradePackageResponseSchema`, `UpgradePhaseSchema` (with their types and `…Parsed` aliases) | `@objectstack/spec/kernel` | nothing — no upgrade ever ran. Installing a new version over an installed package is a re-install: `POST /api/v1/packages` with `overwrite: true` (the SDK's `packages.install(manifest, { overwrite: true })`). | +| `IPackageService.planUpgrade`, `.upgrade`, `.rollback`, `.getSnapshot`, and their inputs `PlanUpgradeInput`, `ExecuteUpgradeInput`, `RollbackInput` | `@objectstack/spec/contracts` | delete the members from your implementation and the imports of the three inputs — the contract no longer carries them. | +| `PackageUpgradeResponse.data.plan` | `@objectstack/spec/api` | nothing — it carried the retired `UpgradePlan`. `PackageUpgradeRequestSchema` and `PackageUpgradeResponseSchema` themselves stay published, bound to no route. | + +**The one-line fix: delete every import of the names above and every read of `PackageUpgradeResponse.data.plan`.** The compiler finds them (`TS2305: Module '"@objectstack/spec/kernel"' has no exported member …`, and `TS2339` for `plan`). No behaviour is lost — nothing ever read or produced these shapes. + +**What stays.** The package model beside it — `ManifestSchema`, `InstalledPackageSchema`, `PackageArtifactSchema`, the install, uninstall, enable/disable and dependency-resolution members of `IPackageService` — is unchanged. The `UpgradePlan.estimatedDuration` tombstone leaves with its def's source; its `RETIRED_KEYS_BY_MAJOR[18]` entry and the D3 entry `kernel-package-lifecycle-durations-unit-in-key` stay as history. + +⚠️ Runtime behaviour is deliberately **unchanged**: nothing ever parsed a retired shape or called a retired member, so every request answers exactly as before. The removal retracts a false claim, not a capability. **No deprecation window** (startup-stage posture: retirements take effect immediately). + +⚠️ **The out-of-repo consumer population beyond objectui and hotcrm is NOT MEASURED.** Inside this repository the names occurred only in their declarations, their own tests, generated artifacts and prose; objectui (main and the pinned `.objectui-sha`) and hotcrm name none of them; `@objectstack/spec` is published, so readers elsewhere were not measured. + +The ADR-0087 D3 semantic entry `kernel-package-upgrade-family-retired` carries the judgement, and the ten defs are registered in `RETIRED_DEFS_BY_MAJOR[18]`: none of these shapes is a stack collection or a metadata type, so there is no source for a D2 conversion to rewrite and no carrier key for a tombstone. + + diff --git a/content/docs/getting-started/quick-reference.mdx b/content/docs/getting-started/quick-reference.mdx index 1ea2b087d35..40a06440ab4 100644 --- a/content/docs/getting-started/quick-reference.mdx +++ b/content/docs/getting-started/quick-reference.mdx @@ -61,7 +61,7 @@ Presentation layer - views, forms, dashboards, and app branding. | **[Chart](/docs/references/ui/chart)** | `chart.zod.ts` | Chart, ChartType | Chart definitions | | **[Widget Contract](/docs/protocol/objectui/widget-contract)** ↗ | `widget.zod.ts` | FieldWidgetProps | Props a custom field widget receives — the contract is documented with ObjectUI, outside `references/ui/` | -## Kernel Protocol (17 of 30 schemas) +## Kernel Protocol (17 of 29 schemas) Plugin architecture, manifests, and kernel runtime. diff --git a/content/docs/references/api/package-api.mdx b/content/docs/references/api/package-api.mdx index 518793d23d7..3c4db9ead16 100644 --- a/content/docs/references/api/package-api.mdx +++ b/content/docs/references/api/package-api.mdx @@ -585,7 +585,7 @@ Upgrade package response | **success** | `boolean` | ✅ | Operation success status | | **error** | `{ code: Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| …>; declaredCode?: string; message: string; userMessage?: string; … }` | optional | Error details if success is false | | **meta** | `{ timestamp: string; duration?: integer; requestId?: string; traceId?: string }` | optional | Response metadata | -| **data** | `{ success: boolean; phase: string; plan?: object; snapshotId?: string; … }` | ✅ | | +| **data** | `{ success: boolean; phase: string; snapshotId?: string; conflicts?: object[]; … }` | ✅ | | ### Nested Shape: `PackageUpgradeResponse.error` @@ -616,7 +616,6 @@ Upgrade package response | :--- | :--- | :--- | :--- | | **success** | `boolean` | ✅ | Whether the upgrade succeeded | | **phase** | `string` | ✅ | Current upgrade phase | -| **plan** | `{ packageId: string; fromVersion: string; toVersion: string; impactLevel: Enum<'none' \| 'low' \| 'medium' \| 'high' \| 'critical'>; … }` | optional | Upgrade plan that was executed | | **snapshotId** | `string` | optional | Snapshot ID for rollback | | **conflicts** | `{ path: string; baseValue: any; incomingValue: any; customValue: any }[]` | optional | Unresolved merge conflicts | | **errorMessage** | `string` | optional | Error message if failed | diff --git a/content/docs/references/index.mdx b/content/docs/references/index.mdx index fb20c56a6a1..d98bce42a1d 100644 --- a/content/docs/references/index.mdx +++ b/content/docs/references/index.mdx @@ -1,7 +1,7 @@ --- title: Protocol reference — every schema by module navTitle: Protocol Reference -description: Every schema published by @objectstack/spec — 1549 schemas across 14 protocol modules +description: Every schema published by @objectstack/spec — 1539 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,7 +26,7 @@ counts are sums of the rows they head. Regenerate with | [Data Protocol](/docs/references/data) | 29 | 178 | 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 | 12 | The single connector protocol (ADR-0097) — catalog descriptors and provider-bound instances. | -| [Kernel Protocol](/docs/references/kernel) | 30 | 161 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. | +| [Kernel Protocol](/docs/references/kernel) | 29 | 151 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. | | [Marketplace Protocol](/docs/references/marketplace) | 4 | 30 | The package & marketplace format — package identity and versions, listing, publish, review, search, install, template manifests. | | [QA Protocol](/docs/references/qa) | 1 | 8 | Declarative test suites — scenarios, steps, actions and assertions. | | [Security Protocol](/docs/references/security) | 5 | 34 | Permission sets, row-level security, sharing rules, tenancy posture. | @@ -34,7 +34,7 @@ counts are sums of the rows they head. Regenerate with | [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. | | [System Protocol](/docs/references/system) | 36 | 285 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. | | [UI Protocol](/docs/references/ui) | 16 | 169 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. | -| **Total** | **198** | **1549** | 14 protocol modules | +| **Total** | **197** | **1539** | 14 protocol modules | --- @@ -199,7 +199,7 @@ The single connector protocol (ADR-0097) — catalog descriptors and provider-bo ## Kernel Protocol -**Source:** `packages/spec/src/kernel/` · **Import:** `@objectstack/spec/kernel` · **30 pages, 161 schemas** +**Source:** `packages/spec/src/kernel/` · **Import:** `@objectstack/spec/kernel` · **29 pages, 151 schemas** Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. @@ -222,7 +222,6 @@ Plugin lifecycle and manifests, capabilities and security, metadata loading, ser | [`metadata-protection.zod.ts`](/docs/references/kernel/metadata-protection) | `MetadataLock`, `MetadataLockSource`, `MetadataProvenance` | | [`package-artifact.zod.ts`](/docs/references/kernel/package-artifact) | `ArtifactChecksum`, `ArtifactFileEntry`, `ArtifactSignature`, `MetadataCategoryEnum`, `PackageArtifact` | | [`package-registry.zod.ts`](/docs/references/kernel/package-registry) | `DisablePackageRequest`, `DisablePackageResponse`, `EnablePackageRequest`, `EnablePackageResponse`, `GetPackageRequest`, `GetPackageResponse`, `InstallPackageRequest`, `InstallPackageResponse`, `InstalledPackage`, `InstalledPackageSource`, `ListPackagesRequest`, `ListPackagesResponse`, `NamespaceConflictError`, `NamespaceRegistryEntry`, `PackageStatusEnum`, `UninstallPackageRequest`, `UninstallPackageResponse` | -| [`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) | `HotReloadConfig`, `PluginHealthCheck`, `PluginHealthReport`, `PluginHealthStatus`, `PluginStateSnapshot` | diff --git a/content/docs/references/kernel/index.mdx b/content/docs/references/kernel/index.mdx index 7f8b012e72b..4c1c3e77c5f 100644 --- a/content/docs/references/kernel/index.mdx +++ b/content/docs/references/kernel/index.mdx @@ -1,7 +1,7 @@ --- title: Kernel Protocol — complete schema reference navTitle: Kernel Protocol -description: "The ObjectStack Kernel Protocol in 30 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example." +description: "The ObjectStack Kernel Protocol in 29 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example." --- {/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} @@ -26,7 +26,6 @@ This section contains all protocol schemas for the kernel layer of ObjectStack. - diff --git a/content/docs/references/kernel/meta.json b/content/docs/references/kernel/meta.json index 048eb7e7e6d..5574501de14 100644 --- a/content/docs/references/kernel/meta.json +++ b/content/docs/references/kernel/meta.json @@ -18,7 +18,6 @@ "---Packages---", "package-artifact", "package-registry", - "package-upgrade", "---Metadata & Runtime---", "cli-extension", "cluster", diff --git a/content/docs/references/kernel/package-upgrade.mdx b/content/docs/references/kernel/package-upgrade.mdx deleted file mode 100644 index 7ba57ff6eff..00000000000 --- a/content/docs/references/kernel/package-upgrade.mdx +++ /dev/null @@ -1,319 +0,0 @@ ---- -title: Package Upgrade — Kernel Protocol reference -navTitle: Package Upgrade -description: "Defines the complete lifecycle for upgrading installed packages, including pre-upgrade analysis, snapshot/backup, execution, validation." ---- - -{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} - -## Package Upgrade Protocol - -Defines the complete lifecycle for upgrading installed packages, -including pre-upgrade analysis, snapshot/backup, execution, validation, -and rollback capabilities. - -### Architecture Alignment -- **Salesforce**: Managed Package upgrade with push upgrades and subscriber control -- **ServiceNow**: Update Sets with preview, commit, and back-out support -- **Helm**: Helm upgrade with rollback to previous release -- **Kubernetes**: Rolling update with readiness probes and automatic rollback - -### Upgrade Flow -``` -1. PreCheck → Validate compatibility, check dependencies -2. Plan → Generate upgrade plan with metadata diff -3. Snapshot → Backup current state (metadata + customizations) -4. Execute → Apply new package metadata with 3-way merge -5. Validate → Run post-upgrade health checks -6. Commit → Finalize upgrade (or Rollback on failure) -``` - - -**Source:** `packages/spec/src/kernel/package-upgrade.zod.ts` - - -## TypeScript Usage - -```typescript -import { MetadataChangeTypeSchema, MetadataDiffItemSchema, RollbackPackageRequestSchema, RollbackPackageResponseSchema, UpgradeImpactLevelSchema, UpgradePackageRequestSchema, UpgradePackageResponseSchema, UpgradePhaseSchema, UpgradePlanSchema, UpgradeSnapshotSchema } from '@objectstack/spec/kernel'; -import type { MetadataChangeType, MetadataDiffItem, RollbackPackageRequest, RollbackPackageResponse, UpgradeImpactLevel, UpgradePackageRequest, UpgradePackageResponse, UpgradePhase, UpgradePlan, UpgradeSnapshot } from '@objectstack/spec/kernel'; - -// Validate data -const result = MetadataChangeTypeSchema.parse(data); -``` - ---- - -## MetadataChangeType - -Type of metadata change between package versions - -### Allowed Values - -* `added` -* `modified` -* `removed` -* `renamed` - - ---- - -## MetadataDiffItem - -Single metadata change between package versions - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **type** | `string` | ✅ | Metadata type | -| **name** | `string` | ✅ | Metadata name | -| **changeType** | `Enum<'added' \| 'modified' \| 'removed' \| 'renamed'>` | ✅ | Category of metadata modification (added, modified, removed, or renamed) | -| **hasConflict** | `boolean` | optional (default: `false`) | Whether this change may conflict with customizations | -| **summary** | `string` | optional | Human-readable change summary | -| **previousName** | `string` | optional | Previous name if renamed | - - ---- - -## RollbackPackageRequest - -Rollback package request - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **packageId** | `string` | ✅ | Package ID to rollback | -| **snapshotId** | `string` | ✅ | Snapshot ID to restore from | -| **rollbackCustomizations** | `boolean` | optional (default: `true`) | Whether to restore pre-upgrade customizations | - - ---- - -## RollbackPackageResponse - -Rollback package response - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **success** | `boolean` | ✅ | Whether the rollback succeeded | -| **restoredVersion** | `string` | optional | Version restored to | -| **message** | `string` | optional | Rollback status message | - - ---- - -## UpgradeImpactLevel - -Severity of upgrade impact - -### Allowed Values - -* `none` -* `low` -* `medium` -* `high` -* `critical` - - ---- - -## UpgradePackageRequest - -Upgrade package request - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **packageId** | `string` | ✅ | Package ID to upgrade | -| **targetVersion** | `string` | optional | Target version (defaults to latest) | -| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` | optional | New manifest (if installing from local) | -| **createSnapshot** | `boolean` | optional (default: `true`) | Whether to create a pre-upgrade backup snapshot | -| **mergeStrategy** | `Enum<'keep-custom' \| 'accept-incoming' \| 'three-way-merge'>` | optional (default: `"three-way-merge"`) | How to handle customer customizations | -| **dryRun** | `boolean` | optional (default: `false`) | Preview upgrade without making changes | -| **skipValidation** | `boolean` | optional (default: `false`) | Skip pre-upgrade compatibility checks | - -### Nested Shape: `UpgradePackageRequest.manifest` - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **id** | `string` | ✅ | Unique package identifier — must match reverse-domain notation (e.g. com.acme.crm) | -| **namespace** | `string` | optional | Short namespace identifier; also the mandatory prefix of every object name (e.g. "todo" → object names "todo_task", "todo_project") | -| **defaultDatasource** | `string` | optional (default: `"default"`) | Default datasource for all objects in this package | -| **version** | `string` | ✅ | Package version (SemVer 2.0.0 — e.g. 1.2.3, 2.0.0-beta.1, 1.0.0+20230101) | -| **type** | `Enum<'plugin' \| 'ui' \| 'driver' \| 'server' \| 'app' \| 'theme' \| 'agent' \| 'objectql' \| …>` | ✅ | Type of package | -| **scope** | `Enum<'cloud' \| 'system' \| 'project'>` | optional (default: `"project"`) | Deployment scope: cloud \| system \| project | -| **installModes** | `Enum<'managed' \| 'unmanaged'>[]` | optional | Install modes this package permits (ADR-0131 D6): managed \| unmanaged. Absent means ['managed']. | -| **name** | `string` | ✅ | Human-readable package name | -| **description** | `string` | optional | Package description | -| **permissions** | `{ services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }` | optional | Required permissions at the AUTHORING stage: the structured plugin block `{ services, hooks, network, fs }` (ADR-0025 §3.2) — the legacy flat string list is retired — and at the assembled stage the same key is the ADR-0090 `PermissionSet[]` collection instead (`AssembledPackageBodySchema`) | -| **objects** | `string[]` | optional | Glob patterns for ObjectQL schemas files | -| **datasources** | `string[]` | optional | Glob patterns for Datasource definitions | -| **dependencies** | `Record` | optional | Package dependencies | -| **configuration** | `never` | optional | [REMOVED] `manifest.configuration` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read the block: no settings UI rendered it and no loader resolved a setting from it, so authoring it configured nothing. Worse, `properties.*.secret` promised "value is encrypted/masked (e.g. API Keys)" while nothing encrypted, masked or even parsed the flag — a false assurance about credential handling. Delete the key. A plugin is configured by the host that composes it: pass options to its constructor in `defineStack({ plugins: [new MyPlugin({ … })] })`, which is the enforced channel. A declarative settings surface must be designed with an enforcing reader first, not revived here. | -| **contributes** | `{ kinds?: object[] }` | optional | Platform contributions | -| **data** | `{ object: string; externalId?: string \| string[]; mode?: Enum<'insert' \| 'update' \| 'upsert' \| 'replace' \| 'ignore'>; env?: Enum<'prod' \| 'dev' \| 'test'>[]; … }[]` | optional | Initial seed data (prefer top-level data field) | -| **capabilities** | `never` | optional | [REMOVED] `manifest.capabilities` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — no discovery path ever consulted the block: nothing read `implements`, `provides`, `requires`, `extensionPoints` or `extensions`, so the declared "interoperability and automatic discovery" never happened. Delete the key. Real dependency resolution runs off top-level `manifest.dependencies`, which stays. Capability-based discovery must be designed with an enforcing reader first, not revived here. | -| **extensions** | `never` | optional | [REMOVED] `manifest.extensions` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — an untyped map with zero readers: whatever was parked here was stored and never consulted. Delete the key. Extend the platform through the enforced channels instead: `contributes.kinds` registers metadata kinds, `navigationContributions` injects navigation into other packages' apps, and code-level extension happens in the plugin itself (`init`/`start`). | -| **navigationContributions** | `{ app: string; group?: string; priority?: integer; items: (object \| … +9 more)[] }[]` | optional | Navigation items this package contributes into apps owned by other packages | -| **loading** | `never` | optional | [REMOVED] `manifest.loading` was removed in @objectstack/spec 17.0.0 (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 — and the plugin trust tier (`manifest.runtime`) does not give it back: that tier is enforced at the cloud marketplace PUBLISH gate only (an unverified publisher requesting the `node` tier is rejected with HTTP 422 and forced to manual review), while load-side enforcement is NOT implemented, so a locally installed plugin is not isolated by the tier it declares. ⛔ Nor do the permission declarations give it back: the install-time granted set is REGISTERED on the PluginPermissionEnforcer at load and queried by nothing, so it refuses no operation. Neither surface confines a plugin today — do not author either one expecting isolation. | -| **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 the plugin declares (ADR-0025 §3.6) — enforced at the cloud marketplace publish gate (unverified publisher requesting `node` → HTTP 422 + manual review); load-side enforcement is NOT implemented, so a locally installed plugin is not isolated by the tier it declares | -| **packaging** | `Enum<'bundled' \| 'manifest-deps'>` | optional | Dependency packaging strategy (ADR-0025 §3.3) | -| **main** | `string` | optional | Entry module of a code-bearing plugin, relative to the plugin root; `os plugin build` bundles it and writes `dist/index.mjs` here in the compiled manifest (ADR-0025 §3.4) | -| **integrity** | `Record` | optional | Per-file content digests of the plugin artifact (ADR-0025 §3.2) | - - ---- - -## UpgradePackageResponse - -Upgrade package response - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **success** | `boolean` | ✅ | Whether the upgrade succeeded | -| **phase** | `Enum<'pending' \| 'analyzing' \| 'snapshot' \| 'executing' \| 'migrating' \| 'validating' \| 'completed' \| 'failed' \| 'rolling-back' \| 'rolled-back'>` | ✅ | Current upgrade phase | -| **plan** | `{ packageId: string; fromVersion: string; toVersion: string; impactLevel: Enum<'none' \| 'low' \| 'medium' \| 'high' \| 'critical'>; … }` | optional | Upgrade plan | -| **snapshotId** | `string` | optional | Snapshot ID for rollback | -| **conflicts** | `{ path: string; baseValue: any; incomingValue: any; customValue: any }[]` | optional | Unresolved merge conflicts | -| **errorMessage** | `string` | optional | Error message if upgrade failed | -| **message** | `string` | optional | Human-readable status message | - -### Nested Shape: `UpgradePackageResponse.plan` - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **packageId** | `string` | ✅ | Package identifier | -| **fromVersion** | `string` | ✅ | Currently installed version | -| **toVersion** | `string` | ✅ | Target upgrade version | -| **impactLevel** | `Enum<'none' \| 'low' \| 'medium' \| 'high' \| 'critical'>` | ✅ | Severity assessment from none (seamless) to critical (breaking changes) | -| **changes** | `{ type: string; name: string; changeType: Enum<'added' \| 'modified' \| 'removed' \| 'renamed'>; hasConflict?: boolean; … }[]` | ✅ | All metadata changes | -| **affectedCustomizations** | `integer` | optional (default: `0`) | Count of customizations that may be affected | -| **requiresMigration** | `boolean` | optional (default: `false`) | Whether data migration scripts are needed | -| **migrationScripts** | `string[]` | optional | Paths to migration scripts | -| **dependencyUpgrades** | `{ packageId: string; fromVersion: string; toVersion: string }[]` | optional | Dependent packages that also need upgrading | -| **estimatedDurationSeconds** | `integer` | optional | Estimated upgrade duration in seconds | -| **estimatedDuration** | `never` | optional | [REMOVED] `UpgradePlan.estimatedDuration` was renamed to `estimatedDurationSeconds` in @objectstack/spec 17 — the unit of a duration-shaped number lives in the key name, not only in the describe prose. Rename the key to `estimatedDurationSeconds`; the value (seconds) is unchanged. | -| **summary** | `string` | optional | Human-readable upgrade summary | - - ---- - -## UpgradePhase - -Current phase of the upgrade process - -### Allowed Values - -* `pending` -* `analyzing` -* `snapshot` -* `executing` -* `migrating` -* `validating` -* `completed` -* `failed` -* `rolling-back` -* `rolled-back` - - ---- - -## UpgradePlan - -Upgrade analysis plan generated before execution - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **packageId** | `string` | ✅ | Package identifier | -| **fromVersion** | `string` | ✅ | Currently installed version | -| **toVersion** | `string` | ✅ | Target upgrade version | -| **impactLevel** | `Enum<'none' \| 'low' \| 'medium' \| 'high' \| 'critical'>` | ✅ | Severity assessment from none (seamless) to critical (breaking changes) | -| **changes** | `{ type: string; name: string; changeType: Enum<'added' \| 'modified' \| 'removed' \| 'renamed'>; hasConflict?: boolean; … }[]` | ✅ | All metadata changes | -| **affectedCustomizations** | `integer` | optional (default: `0`) | Count of customizations that may be affected | -| **requiresMigration** | `boolean` | optional (default: `false`) | Whether data migration scripts are needed | -| **migrationScripts** | `string[]` | optional | Paths to migration scripts | -| **dependencyUpgrades** | `{ packageId: string; fromVersion: string; toVersion: string }[]` | optional | Dependent packages that also need upgrading | -| **estimatedDurationSeconds** | `integer` | optional | Estimated upgrade duration in seconds | -| **estimatedDuration** | `never` | optional | [REMOVED] `UpgradePlan.estimatedDuration` was renamed to `estimatedDurationSeconds` in @objectstack/spec 17 — the unit of a duration-shaped number lives in the key name, not only in the describe prose. Rename the key to `estimatedDurationSeconds`; the value (seconds) is unchanged. | -| **summary** | `string` | optional | Human-readable upgrade summary | - -### Nested Shape: `UpgradePlan.changes[number]` - -Single metadata change between package versions - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **type** | `string` | ✅ | Metadata type | -| **name** | `string` | ✅ | Metadata name | -| **changeType** | `Enum<'added' \| 'modified' \| 'removed' \| 'renamed'>` | ✅ | Category of metadata modification (added, modified, removed, or renamed) | -| **hasConflict** | `boolean` | optional (default: `false`) | Whether this change may conflict with customizations | -| **summary** | `string` | optional | Human-readable change summary | -| **previousName** | `string` | optional | Previous name if renamed | - - ---- - -## UpgradeSnapshot - -Pre-upgrade state snapshot for rollback capability - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **id** | `string` | ✅ | Snapshot identifier | -| **packageId** | `string` | ✅ | Package identifier | -| **fromVersion** | `string` | ✅ | Version before upgrade | -| **toVersion** | `string` | ✅ | Target upgrade version | -| **tenantId** | `string` | optional | Tenant identifier | -| **previousManifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` | ✅ | Complete manifest of the previous package version | -| **metadataSnapshot** | `{ type: string; name: string; metadata: Record }[]` | ✅ | Snapshot of all package metadata | -| **customizationSnapshot** | `Record[]` | optional | Snapshot of customer customizations | -| **createdAt** | `string` | ✅ | Snapshot creation timestamp | -| **expiresAt** | `string` | optional | Snapshot expiry timestamp | - -### Nested Shape: `UpgradeSnapshot.previousManifest` - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **id** | `string` | ✅ | Unique package identifier — must match reverse-domain notation (e.g. com.acme.crm) | -| **namespace** | `string` | optional | Short namespace identifier; also the mandatory prefix of every object name (e.g. "todo" → object names "todo_task", "todo_project") | -| **defaultDatasource** | `string` | optional (default: `"default"`) | Default datasource for all objects in this package | -| **version** | `string` | ✅ | Package version (SemVer 2.0.0 — e.g. 1.2.3, 2.0.0-beta.1, 1.0.0+20230101) | -| **type** | `Enum<'plugin' \| 'ui' \| 'driver' \| 'server' \| 'app' \| 'theme' \| 'agent' \| 'objectql' \| …>` | ✅ | Type of package | -| **scope** | `Enum<'cloud' \| 'system' \| 'project'>` | optional (default: `"project"`) | Deployment scope: cloud \| system \| project | -| **installModes** | `Enum<'managed' \| 'unmanaged'>[]` | optional | Install modes this package permits (ADR-0131 D6): managed \| unmanaged. Absent means ['managed']. | -| **name** | `string` | ✅ | Human-readable package name | -| **description** | `string` | optional | Package description | -| **permissions** | `{ services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }` | optional | Required permissions at the AUTHORING stage: the structured plugin block `{ services, hooks, network, fs }` (ADR-0025 §3.2) — the legacy flat string list is retired — and at the assembled stage the same key is the ADR-0090 `PermissionSet[]` collection instead (`AssembledPackageBodySchema`) | -| **objects** | `string[]` | optional | Glob patterns for ObjectQL schemas files | -| **datasources** | `string[]` | optional | Glob patterns for Datasource definitions | -| **dependencies** | `Record` | optional | Package dependencies | -| **configuration** | `never` | optional | [REMOVED] `manifest.configuration` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read the block: no settings UI rendered it and no loader resolved a setting from it, so authoring it configured nothing. Worse, `properties.*.secret` promised "value is encrypted/masked (e.g. API Keys)" while nothing encrypted, masked or even parsed the flag — a false assurance about credential handling. Delete the key. A plugin is configured by the host that composes it: pass options to its constructor in `defineStack({ plugins: [new MyPlugin({ … })] })`, which is the enforced channel. A declarative settings surface must be designed with an enforcing reader first, not revived here. | -| **contributes** | `{ kinds?: object[] }` | optional | Platform contributions | -| **data** | `{ object: string; externalId?: string \| string[]; mode?: Enum<'insert' \| 'update' \| 'upsert' \| 'replace' \| 'ignore'>; env?: Enum<'prod' \| 'dev' \| 'test'>[]; … }[]` | optional | Initial seed data (prefer top-level data field) | -| **capabilities** | `never` | optional | [REMOVED] `manifest.capabilities` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — no discovery path ever consulted the block: nothing read `implements`, `provides`, `requires`, `extensionPoints` or `extensions`, so the declared "interoperability and automatic discovery" never happened. Delete the key. Real dependency resolution runs off top-level `manifest.dependencies`, which stays. Capability-based discovery must be designed with an enforcing reader first, not revived here. | -| **extensions** | `never` | optional | [REMOVED] `manifest.extensions` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — an untyped map with zero readers: whatever was parked here was stored and never consulted. Delete the key. Extend the platform through the enforced channels instead: `contributes.kinds` registers metadata kinds, `navigationContributions` injects navigation into other packages' apps, and code-level extension happens in the plugin itself (`init`/`start`). | -| **navigationContributions** | `{ app: string; group?: string; priority?: integer; items: (object \| … +9 more)[] }[]` | optional | Navigation items this package contributes into apps owned by other packages | -| **loading** | `never` | optional | [REMOVED] `manifest.loading` was removed in @objectstack/spec 17.0.0 (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 — and the plugin trust tier (`manifest.runtime`) does not give it back: that tier is enforced at the cloud marketplace PUBLISH gate only (an unverified publisher requesting the `node` tier is rejected with HTTP 422 and forced to manual review), while load-side enforcement is NOT implemented, so a locally installed plugin is not isolated by the tier it declares. ⛔ Nor do the permission declarations give it back: the install-time granted set is REGISTERED on the PluginPermissionEnforcer at load and queried by nothing, so it refuses no operation. Neither surface confines a plugin today — do not author either one expecting isolation. | -| **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 the plugin declares (ADR-0025 §3.6) — enforced at the cloud marketplace publish gate (unverified publisher requesting `node` → HTTP 422 + manual review); load-side enforcement is NOT implemented, so a locally installed plugin is not isolated by the tier it declares | -| **packaging** | `Enum<'bundled' \| 'manifest-deps'>` | optional | Dependency packaging strategy (ADR-0025 §3.3) | -| **main** | `string` | optional | Entry module of a code-bearing plugin, relative to the plugin root; `os plugin build` bundles it and writes `dist/index.mjs` here in the compiled manifest (ADR-0025 §3.4) | -| **integrity** | `Record` | optional | Per-file content digests of the plugin artifact (ADR-0025 §3.2) | - - ---- - diff --git a/content/docs/upgrading.mdx b/content/docs/upgrading.mdx index 1c8a8ec8618..ba3a61a145e 100644 --- a/content/docs/upgrading.mdx +++ b/content/docs/upgrading.mdx @@ -610,8 +610,10 @@ and the deprecation window a property must sit through before it can be removed ## Not this page **Upgrading an installed package** — a template app or a third-party package -already installed into a deployment — is a different operation with its own -lifecycle (pre-check, plan, snapshot, execute, validate, commit or roll back). -It is neither of the two halves above: it moves *someone else's* metadata inside -your deployment. See the [package upgrade -protocol](/docs/references/kernel/package-upgrade). +already installed into a deployment — is a different operation. It is neither of +the two halves above: it moves *someone else's* metadata inside your deployment. +The platform has no separate upgrade engine (no upgrade plan, pre-upgrade +snapshot or three-way merge): installing the new version over the old one is a +re-install with `overwrite: true`, and the package rollback it serves restores a +package commit. See [Package Management](/docs/api/metadata-api#package-management) +in the Metadata API. diff --git a/docs/audits/2026-07-unknown-key-strictness-ledger.counts/kernel.md b/docs/audits/2026-07-unknown-key-strictness-ledger.counts/kernel.md index b1fe0315f7a..3625c957d44 100644 --- a/docs/audits/2026-07-unknown-key-strictness-ledger.counts/kernel.md +++ b/docs/audits/2026-07-unknown-key-strictness-ledger.counts/kernel.md @@ -19,4 +19,4 @@ hand-patch a number here** — fix the code or the verdict and regenerate. | Dir | Sites | |---|---| -| `kernel/` | 247 | +| `kernel/` | 237 | diff --git a/packages/spec/api-surface/contracts.json b/packages/spec/api-surface/contracts.json index b6107fcb1a9..7e5114fba47 100644 --- a/packages/spec/api-surface/contracts.json +++ b/packages/spec/api-surface/contracts.json @@ -97,7 +97,6 @@ "EngineSchemaRegistryView (interface)", "EngineTransactionInfo (interface)", "EngineTransactionOptions (interface)", - "ExecuteUpgradeInput (interface)", "ExplainAccessRequest (interface)", "FILE_ATTACHMENT_SERVICE (const)", "FLOW_UNBOUND_CAUSES (const)", @@ -229,7 +228,6 @@ "PRINCIPAL_SESSION_MAX_TTL_SECONDS (const)", "PendingActionRow (interface)", "PendingActionStatus (type)", - "PlanUpgradeInput (interface)", "Plugin (interface)", "PluginStartupResult (type)", "PositionGrantSummary (interface)", @@ -266,7 +264,6 @@ "ResumeFailureReport (interface)", "ResumeSignal (interface)", "RlsMembershipContext (interface)", - "RollbackInput (interface)", "RouteHandler (type)", "RunListResult (interface)", "SEED_SETTLEMENT_SERVICE (const)", diff --git a/packages/spec/api-surface/kernel.json b/packages/spec/api-surface/kernel.json index dde89b4d2b2..fd84a0f13fd 100644 --- a/packages/spec/api-surface/kernel.json +++ b/packages/spec/api-surface/kernel.json @@ -169,13 +169,8 @@ "MetadataBulkResultSchema (const)", "MetadataCategory (type)", "MetadataCategoryEnum (const)", - "MetadataChangeType (type)", - "MetadataChangeTypeSchema (const)", "MetadataDependency (type)", "MetadataDependencySchema (const)", - "MetadataDiffItem (type)", - "MetadataDiffItemParsed (type)", - "MetadataDiffItemSchema (const)", "MetadataFallbackStrategy (type)", "MetadataFallbackStrategySchema (const)", "MetadataLock (type)", @@ -366,11 +361,6 @@ "ResolvedPackageDependencySchema (const)", "ResourceType (type)", "ResourceTypeSchema (const)", - "RollbackPackageRequest (type)", - "RollbackPackageRequestParsed (type)", - "RollbackPackageRequestSchema (const)", - "RollbackPackageResponse (type)", - "RollbackPackageResponseSchema (const)", "RuntimeConfig (type)", "RuntimeConfigParsed (type)", "RuntimeConfigSchema (const)", @@ -435,22 +425,6 @@ "UninstallPackageResponseSchema (const)", "UnknownAuthoringKeyFinding (interface)", "UnmanagedInstallKeyDisposition (type)", - "UpgradeImpactLevel (type)", - "UpgradeImpactLevelSchema (const)", - "UpgradePackageRequest (type)", - "UpgradePackageRequestParsed (type)", - "UpgradePackageRequestSchema (const)", - "UpgradePackageResponse (type)", - "UpgradePackageResponseParsed (type)", - "UpgradePackageResponseSchema (const)", - "UpgradePhase (type)", - "UpgradePhaseSchema (const)", - "UpgradePlan (type)", - "UpgradePlanParsed (type)", - "UpgradePlanSchema (const)", - "UpgradeSnapshot (type)", - "UpgradeSnapshotParsed (type)", - "UpgradeSnapshotSchema (const)", "VIEW_LAYOUT_WITHOUT_BINDING (const)", "VIEW_ROW_COLOR_UNRESOLVABLE_VALUE (const)", "VIEW_ROW_COLOR_WITHOUT_COLORS (const)", diff --git a/packages/spec/authorable-defaults/kernel.json b/packages/spec/authorable-defaults/kernel.json index be3bbedbb4b..f486a2b2d20 100644 --- a/packages/spec/authorable-defaults/kernel.json +++ b/packages/spec/authorable-defaults/kernel.json @@ -52,7 +52,6 @@ "kernel/KernelContext:mode = \"production\"", "kernel/Manifest:defaultDatasource = \"default\"", "kernel/Manifest:scope = \"project\"", - "kernel/MetadataDiffItem:hasConflict = false", "kernel/MetadataManagerConfig:fallback = \"none\"", "kernel/MetadataManagerConfig:formats = [\"typescript\",\"json\",\"yaml\"]", "kernel/MetadataManagerConfig:tableName = \"sys_metadata\"", @@ -116,7 +115,6 @@ "kernel/RealTimeNotificationConfig:tenantFilter = true", "kernel/RealTimeNotificationConfig:userFilter = true", "kernel/ResolvedPackageDependency:type = \"required\"", - "kernel/RollbackPackageRequest:rollbackCustomizations = true", "kernel/RuntimeConfig:engine = \"v8-isolate\"", "kernel/SBOM:format = \"cyclonedx\"", "kernel/SBOMEntry:externalRefs = []", @@ -136,12 +134,6 @@ "kernel/ServiceRegistryConfig:enableLogging = false", "kernel/ServiceRegistryConfig:strictMode = true", "kernel/TenantRuntimeContext:features = {}", - "kernel/TenantRuntimeContext:mode = \"production\"", - "kernel/UpgradePackageRequest:createSnapshot = true", - "kernel/UpgradePackageRequest:dryRun = false", - "kernel/UpgradePackageRequest:mergeStrategy = \"three-way-merge\"", - "kernel/UpgradePackageRequest:skipValidation = false", - "kernel/UpgradePlan:affectedCustomizations = 0", - "kernel/UpgradePlan:requiresMigration = false" + "kernel/TenantRuntimeContext:mode = \"production\"" ] } diff --git a/packages/spec/authorable-surface/kernel.json b/packages/spec/authorable-surface/kernel.json index 7b150841a34..a7df5d9aa3d 100644 --- a/packages/spec/authorable-surface/kernel.json +++ b/packages/spec/authorable-surface/kernel.json @@ -297,12 +297,6 @@ "kernel/MetadataDependency:sourceType", "kernel/MetadataDependency:targetName", "kernel/MetadataDependency:targetType", - "kernel/MetadataDiffItem:changeType", - "kernel/MetadataDiffItem:hasConflict", - "kernel/MetadataDiffItem:name", - "kernel/MetadataDiffItem:previousName", - "kernel/MetadataDiffItem:summary", - "kernel/MetadataDiffItem:type", "kernel/MetadataManagerConfig:cache", "kernel/MetadataManagerConfig:datasource", "kernel/MetadataManagerConfig:fallback", @@ -638,12 +632,6 @@ "kernel/ResolvedPackageDependency:resolvedVersion", "kernel/ResolvedPackageDependency:type", "kernel/ResolvedPackageDependency:versionConstraint", - "kernel/RollbackPackageRequest:packageId", - "kernel/RollbackPackageRequest:rollbackCustomizations", - "kernel/RollbackPackageRequest:snapshotId", - "kernel/RollbackPackageResponse:message", - "kernel/RollbackPackageResponse:restoredVersion", - "kernel/RollbackPackageResponse:success", "kernel/RuntimeConfig:engine", "kernel/RuntimeConfig:engineConfig", "kernel/RuntimeConfig:resourceLimits", @@ -751,42 +739,6 @@ "kernel/UninstallPackageResponse:id", "kernel/UninstallPackageResponse:message", "kernel/UninstallPackageResponse:success", - "kernel/UpgradePackageRequest:createSnapshot", - "kernel/UpgradePackageRequest:dryRun", - "kernel/UpgradePackageRequest:manifest", - "kernel/UpgradePackageRequest:mergeStrategy", - "kernel/UpgradePackageRequest:packageId", - "kernel/UpgradePackageRequest:skipValidation", - "kernel/UpgradePackageRequest:targetVersion", - "kernel/UpgradePackageResponse:conflicts", - "kernel/UpgradePackageResponse:errorMessage", - "kernel/UpgradePackageResponse:message", - "kernel/UpgradePackageResponse:phase", - "kernel/UpgradePackageResponse:plan", - "kernel/UpgradePackageResponse:snapshotId", - "kernel/UpgradePackageResponse:success", - "kernel/UpgradePlan:affectedCustomizations", - "kernel/UpgradePlan:changes", - "kernel/UpgradePlan:dependencyUpgrades", - "kernel/UpgradePlan:estimatedDuration [RETIRED]", - "kernel/UpgradePlan:estimatedDurationSeconds", - "kernel/UpgradePlan:fromVersion", - "kernel/UpgradePlan:impactLevel", - "kernel/UpgradePlan:migrationScripts", - "kernel/UpgradePlan:packageId", - "kernel/UpgradePlan:requiresMigration", - "kernel/UpgradePlan:summary", - "kernel/UpgradePlan:toVersion", - "kernel/UpgradeSnapshot:createdAt", - "kernel/UpgradeSnapshot:customizationSnapshot", - "kernel/UpgradeSnapshot:expiresAt", - "kernel/UpgradeSnapshot:fromVersion", - "kernel/UpgradeSnapshot:id", - "kernel/UpgradeSnapshot:metadataSnapshot", - "kernel/UpgradeSnapshot:packageId", - "kernel/UpgradeSnapshot:previousManifest", - "kernel/UpgradeSnapshot:tenantId", - "kernel/UpgradeSnapshot:toVersion", "kernel/ValidationError:code", "kernel/ValidationError:field", "kernel/ValidationError:message", diff --git a/packages/spec/declaration-map/kernel.json b/packages/spec/declaration-map/kernel.json index 960d01a361f..c5e9b481a30 100644 --- a/packages/spec/declaration-map/kernel.json +++ b/packages/spec/declaration-map/kernel.json @@ -113,12 +113,8 @@ "MetadataBulkResult": "kernel/MetadataBulkResult", "MetadataBulkResultSchema": "kernel/MetadataBulkResult", "MetadataCategoryEnum": "kernel/MetadataCategoryEnum", - "MetadataChangeType": "kernel/MetadataChangeType", - "MetadataChangeTypeSchema": "kernel/MetadataChangeType", "MetadataDependency": "kernel/MetadataDependency", "MetadataDependencySchema": "kernel/MetadataDependency", - "MetadataDiffItem": "kernel/MetadataDiffItem", - "MetadataDiffItemSchema": "kernel/MetadataDiffItem", "MetadataFallbackStrategy": "kernel/MetadataFallbackStrategy", "MetadataFallbackStrategySchema": "kernel/MetadataFallbackStrategy", "MetadataLock": "kernel/MetadataLock", @@ -250,10 +246,6 @@ "ResolvedPackageDependencySchema": "kernel/ResolvedPackageDependency", "ResourceType": "kernel/ResourceType", "ResourceTypeSchema": "kernel/ResourceType", - "RollbackPackageRequest": "kernel/RollbackPackageRequest", - "RollbackPackageRequestSchema": "kernel/RollbackPackageRequest", - "RollbackPackageResponse": "kernel/RollbackPackageResponse", - "RollbackPackageResponseSchema": "kernel/RollbackPackageResponse", "RuntimeConfig": "kernel/RuntimeConfig", "RuntimeConfigSchema": "kernel/RuntimeConfig", "RuntimeMode": "kernel/RuntimeMode", @@ -294,18 +286,6 @@ "UninstallPackageRequestSchema": "kernel/UninstallPackageRequest", "UninstallPackageResponse": "kernel/UninstallPackageResponse", "UninstallPackageResponseSchema": "kernel/UninstallPackageResponse", - "UpgradeImpactLevel": "kernel/UpgradeImpactLevel", - "UpgradeImpactLevelSchema": "kernel/UpgradeImpactLevel", - "UpgradePackageRequest": "kernel/UpgradePackageRequest", - "UpgradePackageRequestSchema": "kernel/UpgradePackageRequest", - "UpgradePackageResponse": "kernel/UpgradePackageResponse", - "UpgradePackageResponseSchema": "kernel/UpgradePackageResponse", - "UpgradePhase": "kernel/UpgradePhase", - "UpgradePhaseSchema": "kernel/UpgradePhase", - "UpgradePlan": "kernel/UpgradePlan", - "UpgradePlanSchema": "kernel/UpgradePlan", - "UpgradeSnapshot": "kernel/UpgradeSnapshot", - "UpgradeSnapshotSchema": "kernel/UpgradeSnapshot", "ValidationError": "kernel/ValidationError", "ValidationErrorSchema": "kernel/ValidationError", "ValidationResult": "kernel/ValidationResult", diff --git a/packages/spec/dropped-refinements.baseline.json b/packages/spec/dropped-refinements.baseline.json index d9bca71dedb..fa1f660ade2 100644 --- a/packages/spec/dropped-refinements.baseline.json +++ b/packages/spec/dropped-refinements.baseline.json @@ -2,8 +2,8 @@ "description": "Shrink-only ledger of every PUBLISHED JSON Schema that is STILL WIDER than the Zod type it was generated from, because a rule written as `.refine()` reaches the runtime and not the file (#18670). `z.toJSONSchema()` has no arm for a `custom` check: a plain record, the same record with a `.refine()`, and the same record with an ABORTING `.refine()` all project byte-identically (measured on zod 4.4.3, the version packages/spec resolves). So a document one of these files ACCEPTS can still be refused at parse time, and an author -- or an AI -- validating against packages/spec/json-schema/** finds out a release later. Each `sites` path is a position under that schema at which a refinement is dropped; the same paths are written onto the artifact itself as `x-dropped-refinements`. Item 2 closed the first patterns: a refinement DECLARED through the closed list in src/shared/refinement-projection.ts is emitted into the published file, reads `projected` rather than `dropped`, and its row LEAVES this ledger in the same PR -- which is why the ledger shrinks and never grows on a repair. Every refinement outside that closed list stays here, and adding an arm to the list is a public-contract decision, not a refactor. Hand-edited on purpose and with no `gen:` script: a generator would let a new gap be admitted by running a command instead of by a decision, which is the silence this ledger exists to end. Adding, removing or moving a site fails packages/spec/scripts/build-schemas.ts until the line moves with it, and the failure prints the corrected entry in full. ⛔ Do not delete or weaken a refinement to shorten this file -- the runtime rule is correct; it is the projection that is silent, and the remedy is to teach the closed list a NAMED pattern, never to drop the rule.", "measured": { "zod": "4.4.3", - "publishedSchemasWithDroppedRefinements": 245, - "droppedRefinementSites": 835, + "publishedSchemasWithDroppedRefinements": 243, + "droppedRefinementSites": 831, "refinementSitesThatDidProject": 369, "refinementSitesWithNoJsonFormToCompare": 0 }, @@ -1135,18 +1135,6 @@ "" ] }, - "kernel/UpgradePackageRequest": { - "sites": [ - "manifest.navigationContributions.element.items.element.lazy", - "manifest.navigationContributions.element.items.element.lazy.options[0]" - ] - }, - "kernel/UpgradeSnapshot": { - "sites": [ - "previousManifest.navigationContributions.element.items.element.lazy", - "previousManifest.navigationContributions.element.items.element.lazy.options[0]" - ] - }, "security/CriteriaSharingRule": { "sites": [ "sharedWith" diff --git a/packages/spec/export-origins/contracts.json b/packages/spec/export-origins/contracts.json index 1929d13975a..bd493c6eda4 100644 --- a/packages/spec/export-origins/contracts.json +++ b/packages/spec/export-origins/contracts.json @@ -97,7 +97,6 @@ "EngineSchemaRegistryView": "src/contracts/objectql-engine.ts#EngineSchemaRegistryView (interface)", "EngineTransactionInfo": "src/contracts/objectql-engine.ts#EngineTransactionInfo (interface)", "EngineTransactionOptions": "src/contracts/objectql-engine.ts#EngineTransactionOptions (interface)", - "ExecuteUpgradeInput": "src/contracts/package-service.ts#ExecuteUpgradeInput (interface)", "ExplainAccessRequest": "src/contracts/security-service.ts#ExplainAccessRequest (interface)", "FILE_ATTACHMENT_SERVICE": "src/contracts/storage-service.ts#FILE_ATTACHMENT_SERVICE (const)", "FLOW_UNBOUND_CAUSES": "src/contracts/automation-service.ts#FLOW_UNBOUND_CAUSES (const)", @@ -229,7 +228,6 @@ "PRINCIPAL_SESSION_MAX_TTL_SECONDS": "src/contracts/auth-service.ts#PRINCIPAL_SESSION_MAX_TTL_SECONDS (const)", "PendingActionRow": "src/contracts/ai-service.ts#PendingActionRow (interface)", "PendingActionStatus": "src/contracts/ai-service.ts#PendingActionStatus (type)", - "PlanUpgradeInput": "src/contracts/package-service.ts#PlanUpgradeInput (interface)", "Plugin": "src/contracts/plugin-validator.ts#Plugin (interface)", "PluginStartupResult": "src/kernel/startup-orchestrator.zod.ts#PluginStartupResult (type)", "PositionGrantSummary": "src/contracts/security-service.ts#PositionGrantSummary (interface)", @@ -266,7 +264,6 @@ "ResumeFailureReport": "src/contracts/approval-service.ts#ResumeFailureReport (interface)", "ResumeSignal": "src/contracts/automation-service.ts#ResumeSignal (interface)", "RlsMembershipContext": "src/contracts/rls-membership-resolver.ts#RlsMembershipContext (interface)", - "RollbackInput": "src/contracts/package-service.ts#RollbackInput (interface)", "RouteHandler": "src/contracts/http-server.ts#RouteHandler (type)", "RunListResult": "src/contracts/automation-service.ts#RunListResult (interface)", "SEED_SETTLEMENT_SERVICE": "src/contracts/seed-settlement.ts#SEED_SETTLEMENT_SERVICE (const)", diff --git a/packages/spec/export-origins/kernel.json b/packages/spec/export-origins/kernel.json index db2f33dd37e..ce8494045ea 100644 --- a/packages/spec/export-origins/kernel.json +++ b/packages/spec/export-origins/kernel.json @@ -168,13 +168,8 @@ "MetadataBulkResultSchema": "src/kernel/metadata-plugin.zod.ts#MetadataBulkResultSchema (const)", "MetadataCategory": "src/kernel/package-artifact.zod.ts#MetadataCategory (type)", "MetadataCategoryEnum": "src/kernel/package-artifact.zod.ts#MetadataCategoryEnum (const)", - "MetadataChangeType": "src/kernel/package-upgrade.zod.ts#MetadataChangeType (type)", - "MetadataChangeTypeSchema": "src/kernel/package-upgrade.zod.ts#MetadataChangeTypeSchema (const)", "MetadataDependency": "src/kernel/metadata-plugin.zod.ts#MetadataDependency (type)", "MetadataDependencySchema": "src/kernel/metadata-plugin.zod.ts#MetadataDependencySchema (const)", - "MetadataDiffItem": "src/kernel/package-upgrade.zod.ts#MetadataDiffItem (type)", - "MetadataDiffItemParsed": "src/kernel/package-upgrade.zod.ts#MetadataDiffItemParsed (type)", - "MetadataDiffItemSchema": "src/kernel/package-upgrade.zod.ts#MetadataDiffItemSchema (const)", "MetadataFallbackStrategy": "src/kernel/metadata-loader.zod.ts#MetadataFallbackStrategy (type)", "MetadataFallbackStrategySchema": "src/kernel/metadata-loader.zod.ts#MetadataFallbackStrategySchema (const)", "MetadataLock": "src/kernel/metadata-protection.zod.ts#MetadataLock (type)", @@ -365,11 +360,6 @@ "ResolvedPackageDependencySchema": "src/kernel/plugin-security.zod.ts#ResolvedPackageDependencySchema (const)", "ResourceType": "src/kernel/plugin-security-advanced.zod.ts#ResourceType (type)", "ResourceTypeSchema": "src/kernel/plugin-security-advanced.zod.ts#ResourceTypeSchema (const)", - "RollbackPackageRequest": "src/kernel/package-upgrade.zod.ts#RollbackPackageRequest (type)", - "RollbackPackageRequestParsed": "src/kernel/package-upgrade.zod.ts#RollbackPackageRequestParsed (type)", - "RollbackPackageRequestSchema": "src/kernel/package-upgrade.zod.ts#RollbackPackageRequestSchema (const)", - "RollbackPackageResponse": "src/kernel/package-upgrade.zod.ts#RollbackPackageResponse (type)", - "RollbackPackageResponseSchema": "src/kernel/package-upgrade.zod.ts#RollbackPackageResponseSchema (const)", "RuntimeConfig": "src/kernel/plugin-security-advanced.zod.ts#RuntimeConfig (type)", "RuntimeConfigParsed": "src/kernel/plugin-security-advanced.zod.ts#RuntimeConfigParsed (type)", "RuntimeConfigSchema": "src/kernel/plugin-security-advanced.zod.ts#RuntimeConfigSchema (const)", @@ -432,22 +422,6 @@ "UninstallPackageResponseSchema": "src/kernel/package-registry.zod.ts#UninstallPackageResponseSchema (const)", "UnknownAuthoringKeyFinding": "src/data/authoring-key-lint.ts#UnknownAuthoringKeyFinding (interface)", "UnmanagedInstallKeyDisposition": "src/kernel/unmanaged-install.ts#UnmanagedInstallKeyDisposition (type)", - "UpgradeImpactLevel": "src/kernel/package-upgrade.zod.ts#UpgradeImpactLevel (type)", - "UpgradeImpactLevelSchema": "src/kernel/package-upgrade.zod.ts#UpgradeImpactLevelSchema (const)", - "UpgradePackageRequest": "src/kernel/package-upgrade.zod.ts#UpgradePackageRequest (type)", - "UpgradePackageRequestParsed": "src/kernel/package-upgrade.zod.ts#UpgradePackageRequestParsed (type)", - "UpgradePackageRequestSchema": "src/kernel/package-upgrade.zod.ts#UpgradePackageRequestSchema (const)", - "UpgradePackageResponse": "src/kernel/package-upgrade.zod.ts#UpgradePackageResponse (type)", - "UpgradePackageResponseParsed": "src/kernel/package-upgrade.zod.ts#UpgradePackageResponseParsed (type)", - "UpgradePackageResponseSchema": "src/kernel/package-upgrade.zod.ts#UpgradePackageResponseSchema (const)", - "UpgradePhase": "src/kernel/package-upgrade.zod.ts#UpgradePhase (type)", - "UpgradePhaseSchema": "src/kernel/package-upgrade.zod.ts#UpgradePhaseSchema (const)", - "UpgradePlan": "src/kernel/package-upgrade.zod.ts#UpgradePlan (type)", - "UpgradePlanParsed": "src/kernel/package-upgrade.zod.ts#UpgradePlanParsed (type)", - "UpgradePlanSchema": "src/kernel/package-upgrade.zod.ts#UpgradePlanSchema (const)", - "UpgradeSnapshot": "src/kernel/package-upgrade.zod.ts#UpgradeSnapshot (type)", - "UpgradeSnapshotParsed": "src/kernel/package-upgrade.zod.ts#UpgradeSnapshotParsed (type)", - "UpgradeSnapshotSchema": "src/kernel/package-upgrade.zod.ts#UpgradeSnapshotSchema (const)", "VIEW_LAYOUT_WITHOUT_BINDING": "src/kernel/functional-completeness.ts#VIEW_LAYOUT_WITHOUT_BINDING (const)", "VIEW_ROW_COLOR_UNRESOLVABLE_VALUE": "src/kernel/functional-completeness.ts#VIEW_ROW_COLOR_UNRESOLVABLE_VALUE (const)", "VIEW_ROW_COLOR_WITHOUT_COLORS": "src/kernel/functional-completeness.ts#VIEW_ROW_COLOR_WITHOUT_COLORS (const)", diff --git a/packages/spec/json-schema.manifest/kernel.json b/packages/spec/json-schema.manifest/kernel.json index 2ab9ed9e83a..2242bf2347e 100644 --- a/packages/spec/json-schema.manifest/kernel.json +++ b/packages/spec/json-schema.manifest/kernel.json @@ -60,9 +60,7 @@ "kernel/ManifestPermissions", "kernel/MetadataBulkResult", "kernel/MetadataCategoryEnum", - "kernel/MetadataChangeType", "kernel/MetadataDependency", - "kernel/MetadataDiffItem", "kernel/MetadataFallbackStrategy", "kernel/MetadataLock", "kernel/MetadataLockSource", @@ -129,8 +127,6 @@ "kernel/ResolvedDependency", "kernel/ResolvedPackageDependency", "kernel/ResourceType", - "kernel/RollbackPackageRequest", - "kernel/RollbackPackageResponse", "kernel/RuntimeConfig", "kernel/RuntimeMode", "kernel/SBOM", @@ -152,12 +148,6 @@ "kernel/TenantRuntimeContext", "kernel/UninstallPackageRequest", "kernel/UninstallPackageResponse", - "kernel/UpgradeImpactLevel", - "kernel/UpgradePackageRequest", - "kernel/UpgradePackageResponse", - "kernel/UpgradePhase", - "kernel/UpgradePlan", - "kernel/UpgradeSnapshot", "kernel/ValidationError", "kernel/ValidationResult", "kernel/ValidationWarning", diff --git a/packages/spec/llms.txt b/packages/spec/llms.txt index 02279212134..31572f14da7 100644 --- a/packages/spec/llms.txt +++ b/packages/spec/llms.txt @@ -77,7 +77,7 @@ const query = { --- -## 3. Schema Inventory by Domain (206 schemas) +## 3. Schema Inventory by Domain (205 schemas) Counted as `*.zod.ts` modules under `packages/spec/src//` — the sources that ship in this tarball (`files` includes `src/**/*.zod.ts`), so every number @@ -86,7 +86,7 @@ here is verifiable from the installed package. | Domain | Count | Key Schemas | |--------|-------|-------------| | system | 36 | Auth, Cache, Dev Login, Email / SMS Config, Encryption, Fate Supplement (organization ownership), HTTP Server, License, Logging, Metrics | -| kernel | 31 | Plugin, Manifest, Events (6 sub-modules), Feature, Context, Package Registry | +| kernel | 30 | Plugin, Manifest, Events (6 sub-modules), Feature, Context, Package Registry | | data | 30 | Object, Field, Picklist, Query, Filter, Driver (SQL/NoSQL/Memory/Mongo/Postgres), Cube | | api | 31 | Endpoint, REST Server, Discovery, OData, Batch, WebSocket, Response Envelope, Package Lifecycle, Package API (assembled stage) | | ui | 18 | View, App, Action, Dashboard, Page, Chart, Component, Animation | diff --git a/packages/spec/scripts/build-docs.ts b/packages/spec/scripts/build-docs.ts index 3152dfab62a..53cd2957c39 100644 --- a/packages/spec/scripts/build-docs.ts +++ b/packages/spec/scripts/build-docs.ts @@ -655,7 +655,7 @@ const SECTION_GROUPS: Record kernel: [ { section: 'Plugin Lifecycle', pages: ['plugin', 'plugin-lifecycle-events', 'plugin-lifecycle-advanced', 'plugin-runtime', 'plugin-loading', 'plugin-registry', 'plugin-structure', 'plugin-validator'] }, { section: 'Plugin Security & Dependencies', pages: ['plugin-security', 'plugin-security-advanced', 'plugin-capability', 'plugin-versioning', 'dependency-resolution', 'manifest'] }, - { section: 'Packages', pages: ['package-artifact', 'package-registry', 'package-upgrade'] }, + { section: 'Packages', pages: ['package-artifact', 'package-registry'] }, { section: 'Metadata & Runtime', pages: ['metadata-plugin', 'metadata-loader', 'metadata-protection', 'metadata-persistence', 'misc', 'context', 'execution-context', 'service-registry', 'startup-orchestrator', 'cluster', 'feature', 'cli-extension', 'dev-plugin', 'state-machine'] }, ], // [#16325] The package & marketplace FORMAT half of what used to be `cloud/`. diff --git a/packages/spec/scripts/file-description.test.ts b/packages/spec/scripts/file-description.test.ts index 1ff07334fd6..0dd904eb228 100644 --- a/packages/spec/scripts/file-description.test.ts +++ b/packages/spec/scripts/file-description.test.ts @@ -2223,10 +2223,12 @@ describe('corpus — every rendered description is well-formed markdown', () => // `marketplace-admin` and the `environment-artifact` re-export; `tenant` // opened at level 2 and was never counted. `marketplace.zod.ts`, the one // level-1 opener among the four modules that moved to `marketplace/`, - // still counts.) - expect(shifted.length).toBe(31); + // still counts. 31 -> 30: `kernel/package-upgrade.zod.ts` — a level-1 + // opener (`# Package Upgrade Protocol`) — was removed whole by #25608's + // ADR-0049 retirement.) + expect(shifted.length).toBe(30); // …and every one of them was shifted because it opened at level 1. - expect(shifted.filter(d => /^ {0,3}#(?:[ \t]|$)/m.test(withoutFences(d.untouched)))).toHaveLength(31); + expect(shifted.filter(d => /^ {0,3}#(?:[ \t]|$)/m.test(withoutFences(d.untouched)))).toHaveLength(30); }); it('never opens a page on the `@module` marker (#13796)', () => { diff --git a/packages/spec/src/api/package-api.test.ts b/packages/spec/src/api/package-api.test.ts index a7807cc9bc9..c15a87ca424 100644 --- a/packages/spec/src/api/package-api.test.ts +++ b/packages/spec/src/api/package-api.test.ts @@ -290,22 +290,22 @@ describe('PackageUpgradeResponseSchema', () => { success: true, phase: 'completed', snapshotId: 'snap_001', - plan: { - packageId: 'com.acme.crm', - fromVersion: '1.0.0', - toVersion: '2.0.0', - impactLevel: 'medium', - changes: [ - { type: 'object', name: 'account', changeType: 'modified', summary: 'Added fields' }, - ], - }, message: 'Upgrade completed successfully', }, }); expect(result.data.success).toBe(true); expect(result.data.phase).toBe('completed'); expect(result.data.snapshotId).toBe('snap_001'); - expect(result.data.plan?.changes).toHaveLength(1); + }); + + it('no longer declares `plan` — retired with the upgrade-plan model (#25608), so a stray one is stripped', () => { + const result = PackageUpgradeResponseSchema.parse({ + success: true, + data: { success: true, phase: 'completed', plan: { packageId: 'com.acme.crm' } }, + }); + expect(result.data).not.toHaveProperty('plan'); + // Lit control: a declared sibling of the retired key still survives the parse. + expect(result.data.phase).toBe('completed'); }); it('should accept upgrade response with merge conflicts', () => { diff --git a/packages/spec/src/api/package-api.zod.ts b/packages/spec/src/api/package-api.zod.ts index 0b783e592de..f7410b2a370 100644 --- a/packages/spec/src/api/package-api.zod.ts +++ b/packages/spec/src/api/package-api.zod.ts @@ -4,7 +4,6 @@ import { z } from 'zod'; import { BaseResponseSchema } from './contract.zod'; import { InstalledPackageSchema } from '../kernel/package-registry.zod'; import { DependencyResolutionResultSchema } from '../kernel/dependency-resolution.zod'; -import { UpgradePlanSchema } from '../kernel/package-upgrade.zod'; import { PackageArtifactSchema } from '../kernel/package-artifact.zod'; import { ManifestSchema } from '../kernel/manifest.zod'; import { ArtifactReferenceSchema } from '../marketplace/marketplace.zod'; @@ -595,7 +594,9 @@ export const PackageUpgradeResponseSchema = lazySchema(() => BaseResponseSchema. data: z.object({ success: z.boolean().describe('Whether the upgrade succeeded'), phase: z.string().describe('Current upgrade phase'), - plan: UpgradePlanSchema.optional().describe('Upgrade plan that was executed'), + // `plan` RETIRED (#25608, ADR-0049) with `kernel/package-upgrade.zod.ts`: + // it carried the zero-consumer `UpgradePlanSchema`, and no route answers + // this body (it is bound to none, see the section note above). snapshotId: z.string().optional().describe('Snapshot ID for rollback'), conflicts: z.array(z.object({ path: z.string().describe('Conflict path'), diff --git a/packages/spec/src/contracts/package-service.test.ts b/packages/spec/src/contracts/package-service.test.ts index 14dea660e64..d50e9ff5fad 100644 --- a/packages/spec/src/contracts/package-service.test.ts +++ b/packages/spec/src/contracts/package-service.test.ts @@ -7,7 +7,6 @@ import type { } from './package-service'; import type { InstalledPackage } from '../kernel/package-registry.zod'; import type { DependencyResolutionResult } from '../kernel/dependency-resolution.zod'; -import type { UpgradePlan, UpgradePackageResponse, RollbackPackageResponse } from '../kernel/package-upgrade.zod'; describe('Package Service Contract', () => { it('should allow a minimal IPackageService implementation with required methods', () => { @@ -23,24 +22,12 @@ describe('Package Service Contract', () => { installOrder: [], }), checkNamespaces: async () => ({ available: true, conflicts: [] }), - planUpgrade: async () => ({ - packageId: 'com.acme.crm', - fromVersion: '1.0.0', - toVersion: '2.0.0', - impactLevel: 'low', - changes: [], - }), - upgrade: async () => ({ success: true, phase: 'completed' }), - rollback: async () => ({ success: true }), }; expect(typeof service.install).toBe('function'); expect(typeof service.uninstall).toBe('function'); expect(typeof service.resolveDependencies).toBe('function'); expect(typeof service.checkNamespaces).toBe('function'); - expect(typeof service.planUpgrade).toBe('function'); - expect(typeof service.upgrade).toBe('function'); - expect(typeof service.rollback).toBe('function'); }); it('should allow full implementation with optional methods', () => { @@ -56,21 +43,10 @@ describe('Package Service Contract', () => { installOrder: [], }), checkNamespaces: async () => ({ available: true, conflicts: [] }), - planUpgrade: async () => ({ - packageId: 'test', - fromVersion: '1.0.0', - toVersion: '2.0.0', - impactLevel: 'none', - changes: [], - }), - upgrade: async () => ({ success: true, phase: 'completed' }), - rollback: async () => ({ success: true }), - getSnapshot: async () => null, uploadArtifact: async () => ({ success: true }), togglePackage: async () => ({} as InstalledPackage), }; - expect(service.getSnapshot).toBeDefined(); expect(service.uploadArtifact).toBeDefined(); expect(service.togglePackage).toBeDefined(); }); @@ -99,9 +75,6 @@ describe('Package Service Contract', () => { listPackages: async () => [], resolveDependencies: async () => ({ dependencies: [], canProceed: true, requiredActions: [], installOrder: [] }), checkNamespaces: async () => ({ available: true, conflicts: [] }), - planUpgrade: async () => ({ packageId: '', fromVersion: '', toVersion: '', impactLevel: 'none', changes: [] }), - upgrade: async () => ({ success: true, phase: 'completed' }), - rollback: async () => ({ success: true }), }; const result = await service.install({ @@ -133,9 +106,6 @@ describe('Package Service Contract', () => { suggestion: 'acme_crm', }], }), - planUpgrade: async () => ({ packageId: '', fromVersion: '', toVersion: '', impactLevel: 'none', changes: [] }), - upgrade: async () => ({ success: true, phase: 'completed' }), - rollback: async () => ({ success: true }), }; const result = await service.checkNamespaces({ @@ -149,88 +119,6 @@ describe('Package Service Contract', () => { expect(result.conflicts[0].suggestion).toBe('acme_crm'); }); - it('should generate an upgrade plan with impact analysis', async () => { - const service: IPackageService = { - install: async () => ({ package: {} as InstalledPackage }), - uninstall: async () => ({ success: true }), - getPackage: async () => null, - listPackages: async () => [], - resolveDependencies: async () => ({ dependencies: [], canProceed: true, requiredActions: [], installOrder: [] }), - checkNamespaces: async () => ({ available: true, conflicts: [] }), - planUpgrade: async (): Promise => ({ - packageId: 'com.acme.crm', - fromVersion: '1.0.0', - toVersion: '2.0.0', - impactLevel: 'high', - changes: [ - { type: 'object', name: 'account', changeType: 'modified', summary: 'Added 3 new fields' }, - { type: 'view', name: 'account_list', changeType: 'modified', summary: 'Updated column layout' }, - { type: 'flow', name: 'onboarding_flow', changeType: 'added', summary: 'New onboarding automation' }, - ], - requiresMigration: true, - migrationScripts: ['migrations/v2_add_account_fields.ts'], - dependencyUpgrades: [{ packageId: 'com.acme.core', fromVersion: '1.2.0', toVersion: '2.0.0' }], - estimatedDurationSeconds: 120, - summary: 'Major upgrade with 3 metadata changes and 1 migration', - }), - upgrade: async () => ({ success: true, phase: 'completed' }), - rollback: async () => ({ success: true }), - }; - - const plan = await service.planUpgrade({ - packageId: 'com.acme.crm', - targetVersion: '2.0.0', - }); - - expect(plan.impactLevel).toBe('high'); - expect(plan.changes).toHaveLength(3); - expect(plan.requiresMigration).toBe(true); - expect(plan.migrationScripts).toHaveLength(1); - expect(plan.dependencyUpgrades).toHaveLength(1); - expect(plan.estimatedDurationSeconds).toBe(120); - }); - - it('should execute upgrade and support rollback', async () => { - const service: IPackageService = { - install: async () => ({ package: {} as InstalledPackage }), - uninstall: async () => ({ success: true }), - getPackage: async () => null, - listPackages: async () => [], - resolveDependencies: async () => ({ dependencies: [], canProceed: true, requiredActions: [], installOrder: [] }), - checkNamespaces: async () => ({ available: true, conflicts: [] }), - planUpgrade: async () => ({ packageId: '', fromVersion: '', toVersion: '', impactLevel: 'none', changes: [] }), - upgrade: async (): Promise => ({ - success: true, - phase: 'completed', - snapshotId: 'snap_001', - message: 'Upgrade completed successfully', - }), - rollback: async (): Promise => ({ - success: true, - restoredVersion: '1.0.0', - message: 'Rolled back to version 1.0.0', - }), - }; - - const upgradeResult = await service.upgrade({ - packageId: 'com.acme.crm', - targetVersion: '2.0.0', - createSnapshot: true, - mergeStrategy: 'three-way-merge', - }); - expect(upgradeResult.success).toBe(true); - expect(upgradeResult.phase).toBe('completed'); - expect(upgradeResult.snapshotId).toBe('snap_001'); - - const rollbackResult = await service.rollback({ - packageId: 'com.acme.crm', - snapshotId: 'snap_001', - rollbackCustomizations: true, - }); - expect(rollbackResult.success).toBe(true); - expect(rollbackResult.restoredVersion).toBe('1.0.0'); - }); - it('should resolve dependencies with platform compatibility', async () => { const service: IPackageService = { install: async () => ({ package: {} as InstalledPackage }), @@ -249,9 +137,6 @@ describe('Package Service Contract', () => { installOrder: ['com.acme.core', 'com.acme.ui', 'com.acme.crm'], }), checkNamespaces: async () => ({ available: true, conflicts: [] }), - planUpgrade: async () => ({ packageId: '', fromVersion: '', toVersion: '', impactLevel: 'none', changes: [] }), - upgrade: async () => ({ success: true, phase: 'completed' }), - rollback: async () => ({ success: true }), }; const result = await service.resolveDependencies({ diff --git a/packages/spec/src/contracts/package-service.ts b/packages/spec/src/contracts/package-service.ts index faa94ef5a76..52f2f9a786c 100644 --- a/packages/spec/src/contracts/package-service.ts +++ b/packages/spec/src/contracts/package-service.ts @@ -7,7 +7,6 @@ * - Package installation with dependency resolution * - Platform compatibility enforcement * - Namespace conflict detection - * - Package upgrade lifecycle (plan → snapshot → execute → validate → rollback) * * Follows Dependency Inversion Principle - consumers depend on this interface, * not on concrete package manager implementations. @@ -15,18 +14,12 @@ * ## Architecture Alignment * - **Salesforce**: PackageInstallHandler with dependency validation * - **npm**: pacote + arborist for install/resolve - * - **Helm**: helm install/upgrade/rollback lifecycle + * - **Helm**: helm install lifecycle * - **Kubernetes**: controller-runtime reconcile loop */ import type { InstalledPackage, NamespaceConflictError } from '../kernel/package-registry.zod'; import type { DependencyResolutionResult } from '../kernel/dependency-resolution.zod'; -import type { - UpgradePlan, - UpgradeSnapshot, - UpgradePackageResponse, - RollbackPackageResponse, -} from '../kernel/package-upgrade.zod'; import type { PackageArtifact } from '../kernel/package-artifact.zod'; import type { ObjectStackManifest } from '../kernel/manifest.zod'; @@ -100,51 +93,10 @@ export interface CheckNamespaceResult { conflicts: NamespaceConflictError[]; } -// ========================================== -// Upgrade Types -// ========================================== - -/** - * Input for generating an upgrade plan. - */ -export interface PlanUpgradeInput { - /** Package ID to upgrade */ - packageId: string; - /** Target version to upgrade to */ - targetVersion?: string; - /** New manifest for the target version */ - manifest?: ObjectStackManifest; -} - -/** - * Input for executing an upgrade. - */ -export interface ExecuteUpgradeInput { - /** Package ID to upgrade */ - packageId: string; - /** Target version */ - targetVersion?: string; - /** New manifest */ - manifest?: ObjectStackManifest; - /** Whether to create a pre-upgrade snapshot */ - createSnapshot?: boolean; - /** Merge strategy for customizations */ - mergeStrategy?: 'keep-custom' | 'accept-incoming' | 'three-way-merge'; - /** Whether to run in dry-run mode */ - dryRun?: boolean; -} - -/** - * Input for rolling back a package. - */ -export interface RollbackInput { - /** Package ID to rollback */ - packageId: string; - /** Snapshot ID to restore from */ - snapshotId: string; - /** Whether to also rollback customizations */ - rollbackCustomizations?: boolean; -} +// The upgrade types (`PlanUpgradeInput`, `ExecuteUpgradeInput`, +// `RollbackInput`) were RETIRED with `kernel/package-upgrade.zod.ts` (#25608, +// ADR-0049 enforce-or-remove) together with the four `IPackageService` +// members that took them: no provider ever implemented any of them. // ========================================== // Artifact Upload Types @@ -274,37 +226,11 @@ export interface IPackageService { */ checkNamespaces(input: CheckNamespaceInput): Promise; - // ---- Upgrade Lifecycle ---- - - /** - * Generate an upgrade plan (preview without executing). - * Analyzes metadata diff, impact level, migration requirements, - * and dependency cascades. - * @param input - Upgrade planning parameters - * @returns Upgrade plan with impact analysis - */ - planUpgrade(input: PlanUpgradeInput): Promise; - - /** - * Execute a package upgrade with optional snapshot and rollback support. - * @param input - Upgrade execution parameters - * @returns Upgrade response with phase, plan, and conflicts - */ - upgrade(input: ExecuteUpgradeInput): Promise; - - /** - * Rollback a package to a previous snapshot. - * @param input - Rollback parameters - * @returns Rollback result - */ - rollback(input: RollbackInput): Promise; - - /** - * Get an upgrade snapshot by ID. - * @param snapshotId - Snapshot identifier - * @returns Snapshot or null - */ - getSnapshot?(snapshotId: string): Promise; + // ---- Upgrade Lifecycle ---- RETIRED (#25608): `planUpgrade`, `upgrade`, + // `rollback` and `getSnapshot` left with the zero-consumer upgrade model. + // The rollback the platform serves is the ADR-0067 commit rollback + // (`POST /api/v1/packages/:id/rollback`, `RollbackToPackageCommitResponseSchema`); + // an upgrade is a re-install with `overwrite: true`. // ---- Artifact Management ---- diff --git a/packages/spec/src/kernel/index.ts b/packages/spec/src/kernel/index.ts index d8f85d5aff7..baf1437875c 100644 --- a/packages/spec/src/kernel/index.ts +++ b/packages/spec/src/kernel/index.ts @@ -56,7 +56,14 @@ export * from './metadata-authoring-lint'; export type { UnknownAuthoringKeyFinding } from '../data/authoring-key-lint'; export * from './package-artifact.zod'; export * from './package-registry.zod'; -export * from './package-upgrade.zod'; +// package-upgrade.zod (UpgradePlanSchema / MetadataDiffItemSchema / +// MetadataChangeTypeSchema / UpgradeImpactLevelSchema / UpgradeSnapshotSchema / +// UpgradePackageRequestSchema / UpgradePhaseSchema / UpgradePackageResponseSchema / +// RollbackPackageRequestSchema / RollbackPackageResponseSchema) RETIRED whole +// (#25608, ADR-0049 enforce-or-remove; ruling 6259332195 item 3): the upgrade +// plan / diff / conflict / snapshot / rollback model had zero runtime consumers. +// The declaration is RETIRED_DEFS_BY_MAJOR[18] plus the D3 semantic entry +// `kernel-package-upgrade-family-retired`. // [ADR-0131 D6] What an `unmanaged` install can copy: the one key // classification `defineStack` and the install door's import planner read. export * from './unmanaged-install'; diff --git a/packages/spec/src/kernel/package-upgrade-family-retirement.test.ts b/packages/spec/src/kernel/package-upgrade-family-retirement.test.ts new file mode 100644 index 00000000000..7f41bc5dbf7 --- /dev/null +++ b/packages/spec/src/kernel/package-upgrade-family-retirement.test.ts @@ -0,0 +1,349 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { describe, expect, expectTypeOf, it } from 'vitest'; + +import { + EXPORT_ENTRY_POINTS, + exportNamesOf, + holdersOf, +} from '../../scripts/lib/export-origins-testkit'; +import { listRepoWalkFiles } from '../../scripts/lib/repo-walk-testkit'; +import type { IPackageService } from '../contracts/package-service'; +import { PackageUpgradeResponseSchema } from '../api/package-api.zod'; +import { CONVERSIONS_BY_MAJOR } from '../conversions/registry'; +import { MIGRATIONS_BY_MAJOR, RETIRED_DEFS_BY_MAJOR, RETIRED_KEYS_BY_MAJOR } from '../migrations/registry'; + +// ─── [#25608] `kernel/package-upgrade.zod.ts` is RETIRED WHOLE ─────────────── +// +// ADR-0049 enforce-or-remove. Maintainer ruling A (`6259332195` on #24443, +// item 3, 「其他同意」): "Retired: packages/spec/src/kernel/package-upgrade.zod.ts +// (plan, diff, conflict, snapshot, rollback; zero runtime consumers), with its +// ADR-0087 conversion." Done directly in 18.x with no retirement stage. +// +// The measurement that decided it, re-taken at this change's base +// (objectstack `47af3dde42`) with a lit control beside every zero: +// +// 1. objectstack — no code outside `packages/spec` names any of the retired +// names; the only hits are the generated reference pages, one link in +// `content/docs/upgrading.mdx` and one ADR sentence. +// 2. objectui main `a3c0da9` and the pinned `.objectui-sha` `2958f17e9c` — +// zero files (control `@objectstack/spec`: 2123 / 2131 files). +// 3. hotcrm `a4b3560` — zero files (control `@objectstack/spec`: 293 files). +// 4. Providers — nothing implements `IPackageService`, so no `planUpgrade`, +// `upgrade`, `rollback` or `getSnapshot` body ever existed. +// +// ## Why route 3, and why there is nothing to tombstone +// +// The retired shapes are a service's return values and HTTP bodies. None is a +// stack collection or a metadata type, so there is no carrier key for a +// `retiredKey()` tombstone and no authored document for an ADR-0087 D2 +// conversion to rewrite. The ruling's "ADR-0087 conversion" is therefore the +// ledger record: the D3 `SemanticMigration` `kernel-package-upgrade-family-retired` +// plus the ten `RETIRED_DEFS_BY_MAJOR[18]` entries the manifest-deletion gate +// reads (the export-job and change-management precedents). +// +// Form follows `api/export-job-family-retirement.test.ts`. + +/** Every name the retirement removed from the public surface. */ +const RETIRED_NAMES = [ + // kernel/package-upgrade.zod.ts — ten defs + 'MetadataChangeTypeSchema', 'MetadataChangeType', + 'MetadataDiffItemSchema', 'MetadataDiffItem', 'MetadataDiffItemParsed', + 'UpgradeImpactLevelSchema', 'UpgradeImpactLevel', + 'UpgradePlanSchema', 'UpgradePlan', 'UpgradePlanParsed', + 'UpgradeSnapshotSchema', 'UpgradeSnapshot', 'UpgradeSnapshotParsed', + 'UpgradePackageRequestSchema', 'UpgradePackageRequest', 'UpgradePackageRequestParsed', + 'UpgradePhaseSchema', 'UpgradePhase', + 'UpgradePackageResponseSchema', 'UpgradePackageResponse', 'UpgradePackageResponseParsed', + 'RollbackPackageRequestSchema', 'RollbackPackageRequest', 'RollbackPackageRequestParsed', + 'RollbackPackageResponseSchema', 'RollbackPackageResponse', + // contracts/package-service.ts — the inputs of the four retired members + 'PlanUpgradeInput', 'ExecuteUpgradeInput', 'RollbackInput', +] as const; + +/** The ten def keys, spelled as `json-schema.manifest/` and the registry spell them. */ +const RETIRED_DEFS = [ + 'kernel/MetadataChangeType', 'kernel/MetadataDiffItem', 'kernel/RollbackPackageRequest', + 'kernel/RollbackPackageResponse', 'kernel/UpgradeImpactLevel', 'kernel/UpgradePackageRequest', + 'kernel/UpgradePackageResponse', 'kernel/UpgradePhase', 'kernel/UpgradePlan', 'kernel/UpgradeSnapshot', +] as const; + +const SEMANTIC_ID = 'kernel-package-upgrade-family-retired'; + +/** Same-entry neighbours that stay — the package model the retired file sat beside. */ +const MUST_SURVIVE_KERNEL = ['PackageArtifactSchema', 'InstalledPackageSchema', 'ManifestSchema'] as const; +const MUST_SURVIVE_API = ['PackageUpgradeRequestSchema', 'PackageUpgradeResponseSchema', 'RollbackToPackageCommitResponseSchema'] as const; +const MUST_SURVIVE_CONTRACTS = ['IPackageService', 'InstallPackageInput', 'UploadArtifactInput'] as const; + +const SPEC_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); +const SRC_ROOT = path.join(SPEC_ROOT, 'src'); + +describe('package upgrade model retirement — the public surface', () => { + it('every retired name has ZERO holders on any public entry; the survivors still stand', () => { + for (const needed of ['.', './kernel', './contracts', './api']) { + expect(EXPORT_ENTRY_POINTS, `exports map must include ${needed}`).toContain(needed); + } + expect(exportNamesOf('./kernel').length, './kernel must export a non-trivial surface').toBeGreaterThan(100); + + for (const name of RETIRED_NAMES) { + expect(holdersOf(name), `${name} must have zero holders after the package upgrade retirement`).toEqual([]); + } + const survivors: Array<[string, readonly string[]]> = [ + ['./kernel', MUST_SURVIVE_KERNEL], + ['./api', MUST_SURVIVE_API], + ['./contracts', MUST_SURVIVE_CONTRACTS], + ]; + for (const [entry, names] of survivors) { + const exported = exportNamesOf(entry); + for (const name of names) expect(exported, `${name} must SURVIVE this retirement`).toContain(name); + } + }); + + it('runtime namespaces agree with the compiler view', async () => { + const kernel = await import('./index'); + const root = await import('../index'); + for (const name of RETIRED_NAMES.filter((n) => n.endsWith('Schema'))) { + expect(name in kernel, `kernel must not export ${name}`).toBe(false); + expect(name in root, `the root entry must not export ${name}`).toBe(false); + } + for (const name of MUST_SURVIVE_KERNEL) expect(name in kernel, `${name} must SURVIVE at runtime`).toBe(true); + }); + + it('IPackageService carries none of the four upgrade members; its install surface stands', () => { + expectTypeOf<'planUpgrade' extends keyof IPackageService ? true : false>().toEqualTypeOf(); + expectTypeOf<'upgrade' extends keyof IPackageService ? true : false>().toEqualTypeOf(); + expectTypeOf<'rollback' extends keyof IPackageService ? true : false>().toEqualTypeOf(); + expectTypeOf<'getSnapshot' extends keyof IPackageService ? true : false>().toEqualTypeOf(); + // Lit control: the same probe answers true for a member that stays. + expectTypeOf<'install' extends keyof IPackageService ? true : false>().toEqualTypeOf(); + }); + + it('PackageUpgradeResponse.data no longer declares `plan`; its declared siblings stand', () => { + const data = PackageUpgradeResponseSchema.shape.data; + expect(Object.keys(data.shape)).not.toContain('plan'); + expect(Object.keys(data.shape)).toEqual(expect.arrayContaining(['success', 'phase', 'snapshotId', 'conflicts'])); + }); + + it('the module is gone from disk, and nothing in the package imports it any more', () => { + for (const f of ['package-upgrade.zod.ts', 'package-upgrade.test.ts']) { + expect(fs.existsSync(path.join(SRC_ROOT, 'kernel', f)), `kernel/${f} must be deleted`).toBe(false); + } + // Anti-vacuity: kept siblings prove the probe looks in the right place. + expect(fs.existsSync(path.join(SRC_ROOT, 'kernel', 'package-registry.zod.ts'))).toBe(true); + expect(fs.existsSync(path.join(SRC_ROOT, 'contracts', 'package-service.ts'))).toBe(true); + + const importers: string[] = []; + const walk = (dir: string) => { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) walk(full); + // This pin spells the specifier in its matcher fixtures, never as an import. + else if (entry.name.endsWith('.ts') && full !== fileURLToPath(import.meta.url)) { + const src = fs.readFileSync(full, 'utf-8'); + if (/(?:import|export)[^;]*['"][^'"]*\/package-upgrade\.zod(?:\.js)?['"]/.test(src)) { + importers.push(path.relative(SRC_ROOT, full)); + } + } + } + }; + walk(SRC_ROOT); + expect(importers, 'a resurrected import means the retirement is being undone — nothing implemented or read the model').toEqual([]); + }); + + it('the generated shards no longer list any of the ten defs or the retired names', () => { + const shard = (dir: string, category: string) => + fs.readFileSync(path.join(SPEC_ROOT, dir, `${category}.json`), 'utf-8'); + const listed = (JSON.parse(shard('json-schema.manifest', 'kernel')) as { schemas: string[] }).schemas; + for (const def of RETIRED_DEFS) { + expect(listed, `${def} must have left json-schema.manifest/`).not.toContain(def); + } + // Anti-vacuity: surviving neighbours are still there. + expect(listed).toContain('kernel/PackageArtifact'); + expect(listed).toContain('kernel/InstalledPackage'); + + // Word-bounded rather than quoted: the shards spell a row several ways. + const exact = (name: string) => new RegExp(`\\b${name}\\b`); + const controls: Record = { kernel: 'PackageArtifactSchema', contracts: 'IPackageService' }; + for (const dir of ['api-surface', 'declaration-map', 'export-origins']) { + for (const category of ['kernel', 'contracts']) { + const file = path.join(SPEC_ROOT, dir, `${category}.json`); + if (!fs.existsSync(file)) continue; // declaration-map carries no contracts shard + const text = fs.readFileSync(file, 'utf-8'); + for (const name of RETIRED_NAMES) { + expect(text, `${dir}/${category}.json must not list ${name}`).not.toMatch(exact(name)); + } + expect(text, `${dir}/${category}.json anti-vacuity`).toMatch(exact(controls[category]!)); + } + } + for (const dir of ['authorable-surface', 'authorable-defaults']) { + const text = shard(dir, 'kernel'); + for (const def of RETIRED_DEFS) { + expect(text, `${dir}/ must carry no row under ${def}`).not.toMatch(new RegExp(`"${def}:`)); + } + expect(text, `${dir}/ anti-vacuity`).toMatch(/"kernel\/PackageArtifact:/); + } + }); +}); + +describe('ADR-0087 registration', () => { + it('declares all ten defs under major 18, with the D3 semantic entry wired and no D2 conversion', () => { + for (const def of RETIRED_DEFS) { + expect(RETIRED_DEFS_BY_MAJOR[18], `${def} must be declared`).toContain(def); + } + const step = MIGRATIONS_BY_MAJOR[18]; + expect(step).toBeDefined(); + const entry = step!.semantic.find((s) => s.id === SEMANTIC_ID); + expect(entry, `${SEMANTIC_ID} must be wired into the step-18 chain`).toBeDefined(); + expect(entry!.reason).toMatch(/ADR-0049/); + expect(entry!.acceptanceCriteria).toMatch(/TS2305/); + // The route is stated where the next reader looks: why D3 semantic and not D2. + expect(entry!.reason).toMatch(/not a D2 conversion/); + // No backticks in `surface`: the upgrade guide renders it inside a code span AND a table cell. + expect(entry!.surface).not.toMatch(/`/); + for (const def of RETIRED_DEFS) expect(entry!.surface, `surface must name ${def}`).toContain(def); + for (const name of ['planUpgrade', 'getSnapshot', 'PlanUpgradeInput', 'PackageUpgradeResponse.data']) { + expect(entry!.surface, `surface must name ${name}`).toContain(name); + } + // The served neighbours are named as what to use, so no sweep takes them. + expect(entry!.replacement).toMatch(/RollbackToPackageCommitResponseSchema/); + expect(step!.rationale).toMatch(/retires the package upgrade model WHOLE/); + + // Deliberately no mechanical conversion: a transform over a stack that + // never carries these shapes would be a seam that never runs. + const conversionIds = Object.values(CONVERSIONS_BY_MAJOR).flatMap((entries) => entries.map((c) => c.id)); + expect(conversionIds.filter((id) => /package-upgrade|upgrade-plan|upgrade-snapshot/.test(id))).toEqual([]); + expect(step!.conversionIds.filter((id) => /package-upgrade|upgrade-plan|upgrade-snapshot/.test(id))).toEqual([]); + // Lit control: the conversion table is loaded and readable. + expect(conversionIds.length).toBeGreaterThan(10); + }); + + it('the `UpgradePlan.estimatedDuration` tombstone entries stay registered as history', () => { + expect(RETIRED_KEYS_BY_MAJOR[18]).toContain('kernel/UpgradePlan:estimatedDuration'); + expect(MIGRATIONS_BY_MAJOR[18]!.semantic.map((s) => s.id)).toContain('kernel-package-lifecycle-durations-unit-in-key'); + }); +}); + +// ─── Tree-scoped absence, inside the radius already declared ───────────────── +// +// The walk reads the five repo roots `api/export-job-family-retirement.test.ts` +// reads — `packages`, `examples`, `skills`, `content`, `scripts` — with the +// same per-root extension sets; every one of those globs is already declared +// for `@objectstack/spec` in `scripts/cross-package-test-inputs.mjs` and +// mirrored in `turbo.json`, and this file is listed in `vitest.repo-tests.json`. +// +// The bound, stated: `docs/**`, `.claude/**`, `.github/**` and the repo-root +// files are outside the walk (the one ADR sentence naming the file is corrected +// in its own governed PR); `.tsx` under `packages/` and every code file under +// `examples/` are not scanned, because a typed import of a retired name fails +// `tsc` in its own package — the enforced channel there. The residue this leg +// covers is what `tsc` does not compile: JSON, YAML, MD, MDX and untyped +// `.js` / `.mjs` / `.cjs`. +describe('tree-scoped absence: nothing inside the declared radius references a retired name', () => { + const REPO_ROOT = path.resolve(SPEC_ROOT, '../..'); + const THIS_FILE = path.relative(REPO_ROOT, fileURLToPath(import.meta.url)).split(path.sep).join('/'); + + const WALK_ROOTS = ['packages', 'examples', 'skills', 'content', 'scripts']; + const SCANNED_EXT = new Set(['.ts', '.mts', '.cts', '.js', '.mjs', '.cjs', '.json', '.md', '.mdx', '.yaml', '.yml']); + const EXAMPLES_EXT = new Set(['.json', '.md', '.mdx', '.yaml', '.yml']); + /** Build, SCM and cache state — not authored sources. */ + const SKIPPED_DIRS = new Set(['node_modules', 'dist', '.git', '.turbo', '.cache', '.objectstack', 'coverage', '.next', '.source']); + + const NAME = new RegExp('\\b(' + RETIRED_NAMES.join('|') + ')\\b'); + /** + * A reference, never a prose mention: an import/export specifier, a type + * position, a method call, a manifest def key, the module specifier, or the + * reference page's link. + */ + const REFERENCE = new RegExp( + '(?:' + + '[{,]\\s*(?:type\\s+)?' + NAME.source + '\\s*[,}]' + + '|' + + '(?:typeof\\s+|:\\s*|<)' + NAME.source + '\\b' + + '|' + + NAME.source + '\\.\\w+\\(' + + '|' + + '"(?:' + RETIRED_DEFS.join('|') + ')"' + + '|' + + "['\"][^'\"]*/package-upgrade\\.zod(?:\\.js)?['\"]" + + '|' + + '/docs/references/kernel/package-upgrade\\b' + + ')', + ); + + /** Structural exclusions, each with its reason. NOT an allowlist file. */ + const EXCLUDED = new Set([ + // This pin names them to assert their absence. + THIS_FILE, + ]); + const EXCLUDED_PREFIXES = [ + // Registers the retirement by def (entries + the generated registry). + 'packages/spec/src/migrations/', + // Generated projection of the registry. + 'packages/spec/spec-changes.json', + // `gen:schema`'s gitignored per-def output, which a local tree may still + // hold from a build before this retirement. + 'packages/spec/json-schema/', + // Written only by `gen:authorable-surface-base` (never a normal build); its + // `baseRev` is allowed to lag, and `check:authorable-surface` is the gate. + 'packages/spec/authorable-surface.base.json', + // Release prose records the removal (release-owned; never edited by a code PR). + 'content/docs/releases/', + '.changeset/', + ]; + /** tsup's transient config bundle — a copy of `tsup.config.ts`, which the walk already reads. */ + const TSUP_BUNDLED_CONFIG = /\.bundled_[^./]+\.mjs$/; + + const readIfPresent = (full: string): string | undefined => { + try { + return fs.readFileSync(full, 'utf-8'); + } catch (err) { + // A path that vanished mid-walk cannot be a surviving reference; every other fault is re-raised. + if ((err as NodeJS.ErrnoException)?.code !== 'ENOENT') throw err; + return undefined; + } + }; + + it('the matcher recognises a reference and ignores a prose mention (anti-vacuity)', () => { + expect(REFERENCE.test("import { UpgradePlanSchema } from '@objectstack/spec/kernel';")).toBe(true); + expect(REFERENCE.test("import type { PlanUpgradeInput } from '@objectstack/spec/contracts';")).toBe(true); + expect(REFERENCE.test('const s: UpgradeSnapshot = {};')).toBe(true); + expect(REFERENCE.test('RollbackPackageRequestSchema.parse(value)')).toBe(true); + expect(REFERENCE.test('"kernel/UpgradePhase",')).toBe(true); + expect(REFERENCE.test("export * from './package-upgrade.zod';")).toBe(true); + expect(REFERENCE.test('see the [protocol](/docs/references/kernel/package-upgrade).')).toBe(true); + expect(REFERENCE.test('the `UpgradePlanSchema` export was removed')).toBe(false); + // The surviving neighbours are not matched. + expect(REFERENCE.test("import { PackageUpgradeRequestSchema, PackageUpgradeResponseSchema } from './package-api.zod';")).toBe(false); + expect(REFERENCE.test('const r: RollbackToPackageCommitResponse = x;')).toBe(false); + expect(REFERENCE.test('"kernel/PackageArtifact",')).toBe(false); + }); + + it('no reference survives inside the declared radius outside the retirement kit', () => { + const offenders: string[] = []; + let visited = 0; + // What git does not ignore, never the raw filesystem (`scripts/lib/repo-walk-testkit.ts`). + const skipDir = (name: string) => SKIPPED_DIRS.has(name) || name.startsWith('.'); + for (const root of WALK_ROOTS) { + for (const full of listRepoWalkFiles(path.join(REPO_ROOT, root), { skipDir })) { + const rel = path.relative(REPO_ROOT, full).split(path.sep).join('/'); + const name = path.basename(full); + if (!(rel.startsWith('examples/') ? EXAMPLES_EXT : SCANNED_EXT).has(path.extname(name))) continue; + if (name === 'CHANGELOG.md') continue; // release prose records the removal + if (EXCLUDED.has(rel) || EXCLUDED_PREFIXES.some((p) => rel.startsWith(p))) continue; + if (TSUP_BUNDLED_CONFIG.test(name)) continue; + visited += 1; + const text = readIfPresent(full); + if (text === undefined) continue; + const m = REFERENCE.exec(text); + if (m) offenders.push(`${rel} references \`${m[0].trim()}\``); + } + } + // Anti-vacuity: the walk really covered the tree. + expect(visited).toBeGreaterThan(1000); + expect(offenders, 'a reference to a retired name means the retirement is being undone — nothing implemented or read the model').toEqual([]); + }); +}); diff --git a/packages/spec/src/kernel/package-upgrade.test.ts b/packages/spec/src/kernel/package-upgrade.test.ts deleted file mode 100644 index 0a9f6207062..00000000000 --- a/packages/spec/src/kernel/package-upgrade.test.ts +++ /dev/null @@ -1,314 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - MetadataChangeTypeSchema, - MetadataDiffItemSchema, - UpgradeImpactLevelSchema, - UpgradePlanSchema, - UpgradeSnapshotSchema, - UpgradePackageRequestSchema, - UpgradePhaseSchema, - UpgradePackageResponseSchema, - RollbackPackageRequestSchema, - RollbackPackageResponseSchema, -} from './package-upgrade.zod'; - -const validManifest = { - id: 'com.acme.crm', - version: '1.0.0', - type: 'app' as const, - name: 'Acme CRM', -}; - -describe('MetadataChangeTypeSchema', () => { - it('should accept valid change types', () => { - const types = ['added', 'modified', 'removed', 'renamed']; - types.forEach(type => { - expect(() => MetadataChangeTypeSchema.parse(type)).not.toThrow(); - }); - }); - - it('should reject invalid change types', () => { - expect(() => MetadataChangeTypeSchema.parse('updated')).toThrow(); - }); -}); - -describe('MetadataDiffItemSchema', () => { - it('should accept valid diff item', () => { - const diff = { - type: 'object', - name: 'account', - changeType: 'modified' as const, - summary: 'Added new field "priority"', - }; - const parsed = MetadataDiffItemSchema.parse(diff); - expect(parsed.hasConflict).toBe(false); - }); - - it('should accept diff item with conflict flag', () => { - const diff = { - type: 'view', - name: 'account_list', - changeType: 'modified' as const, - hasConflict: true, - summary: 'Column order changed — conflicts with customer customization', - }; - const parsed = MetadataDiffItemSchema.parse(diff); - expect(parsed.hasConflict).toBe(true); - }); - - it('should accept renamed item with previous name', () => { - const diff = { - type: 'object', - name: 'contact_v2', - changeType: 'renamed' as const, - previousName: 'contact', - }; - expect(() => MetadataDiffItemSchema.parse(diff)).not.toThrow(); - }); -}); - -describe('UpgradeImpactLevelSchema', () => { - it('should accept all impact levels', () => { - const levels = ['none', 'low', 'medium', 'high', 'critical']; - levels.forEach(level => { - expect(() => UpgradeImpactLevelSchema.parse(level)).not.toThrow(); - }); - }); -}); - -describe('UpgradePlanSchema', () => { - it('should accept minimal upgrade plan', () => { - const plan = { - packageId: 'com.acme.crm', - fromVersion: '1.0.0', - toVersion: '2.0.0', - impactLevel: 'medium' as const, - changes: [], - }; - const parsed = UpgradePlanSchema.parse(plan); - expect(parsed.affectedCustomizations).toBe(0); - expect(parsed.requiresMigration).toBe(false); - }); - - it('should accept full upgrade plan with changes and migrations', () => { - const plan = { - packageId: 'com.acme.crm', - fromVersion: '1.0.0', - toVersion: '2.0.0', - impactLevel: 'high' as const, - changes: [ - { type: 'object', name: 'account', changeType: 'modified' as const, hasConflict: true }, - { type: 'object', name: 'deal', changeType: 'added' as const }, - { type: 'view', name: 'lead_list', changeType: 'removed' as const }, - ], - affectedCustomizations: 3, - requiresMigration: true, - migrationScripts: ['migrations/v2-add-deal.ts', 'migrations/v2-update-account.ts'], - dependencyUpgrades: [ - { packageId: 'com.acme.base', fromVersion: '1.0.0', toVersion: '1.1.0' }, - ], - estimatedDurationSeconds: 120, - summary: 'Major upgrade adding Deals module and restructuring Account object', - }; - const parsed = UpgradePlanSchema.parse(plan); - expect(parsed.changes).toHaveLength(3); - expect(parsed.requiresMigration).toBe(true); - expect(parsed.dependencyUpgrades).toHaveLength(1); - }); -}); - -describe('UpgradeSnapshotSchema', () => { - it('should accept valid snapshot', () => { - const snapshot = { - id: 'snap-001', - packageId: 'com.acme.crm', - fromVersion: '1.0.0', - toVersion: '2.0.0', - previousManifest: validManifest, - metadataSnapshot: [ - { type: 'object', name: 'account', metadata: { label: 'Account' } }, - { type: 'view', name: 'account_list', metadata: { type: 'grid' } }, - ], - createdAt: '2025-06-15T10:00:00Z', - }; - const parsed = UpgradeSnapshotSchema.parse(snapshot); - expect(parsed.metadataSnapshot).toHaveLength(2); - }); - - it('should accept snapshot with customizations backup', () => { - const snapshot = { - id: 'snap-002', - packageId: 'com.acme.crm', - fromVersion: '1.0.0', - toVersion: '2.0.0', - tenantId: 'tenant-001', - previousManifest: validManifest, - metadataSnapshot: [], - customizationSnapshot: [ - { overlayId: 'overlay-001', patch: { label: 'Custom Label' } }, - ], - createdAt: '2025-06-15T10:00:00Z', - expiresAt: '2025-07-15T10:00:00Z', - }; - const parsed = UpgradeSnapshotSchema.parse(snapshot); - expect(parsed.customizationSnapshot).toHaveLength(1); - }); -}); - -describe('UpgradePackageRequestSchema', () => { - it('should accept minimal upgrade request', () => { - const request = { - packageId: 'com.acme.crm', - }; - const parsed = UpgradePackageRequestSchema.parse(request); - expect(parsed.createSnapshot).toBe(true); - expect(parsed.mergeStrategy).toBe('three-way-merge'); - expect(parsed.dryRun).toBe(false); - expect(parsed.skipValidation).toBe(false); - }); - - it('should accept full upgrade request', () => { - const request = { - packageId: 'com.acme.crm', - targetVersion: '2.0.0', - manifest: { ...validManifest, version: '2.0.0' }, - createSnapshot: true, - mergeStrategy: 'keep-custom' as const, - dryRun: false, - skipValidation: false, - }; - const parsed = UpgradePackageRequestSchema.parse(request); - expect(parsed.targetVersion).toBe('2.0.0'); - expect(parsed.mergeStrategy).toBe('keep-custom'); - }); - - it('should accept dry-run request', () => { - const request = { - packageId: 'com.acme.crm', - targetVersion: '2.0.0', - dryRun: true, - }; - const parsed = UpgradePackageRequestSchema.parse(request); - expect(parsed.dryRun).toBe(true); - }); -}); - -describe('UpgradePhaseSchema', () => { - it('should accept all upgrade phases', () => { - const phases = [ - 'pending', 'analyzing', 'snapshot', 'executing', - 'migrating', 'validating', 'completed', 'failed', - 'rolling-back', 'rolled-back', - ]; - phases.forEach(phase => { - expect(() => UpgradePhaseSchema.parse(phase)).not.toThrow(); - }); - }); -}); - -describe('UpgradePackageResponseSchema', () => { - it('should accept successful upgrade response', () => { - const response = { - success: true, - phase: 'completed' as const, - snapshotId: 'snap-001', - message: 'Package upgraded successfully from 1.0.0 to 2.0.0', - }; - expect(() => UpgradePackageResponseSchema.parse(response)).not.toThrow(); - }); - - it('should accept failed upgrade with conflicts', () => { - const response = { - success: false, - phase: 'failed' as const, - plan: { - packageId: 'com.acme.crm', - fromVersion: '1.0.0', - toVersion: '2.0.0', - impactLevel: 'high' as const, - changes: [], - }, - conflicts: [{ - path: 'fields.status.options', - baseValue: ['a'], - incomingValue: ['a', 'b'], - customValue: ['a', 'c'], - }], - errorMessage: 'Upgrade failed: unresolved merge conflicts', - }; - const parsed = UpgradePackageResponseSchema.parse(response); - expect(parsed.success).toBe(false); - expect(parsed.conflicts).toHaveLength(1); - }); -}); - -describe('RollbackPackageRequestSchema', () => { - it('should accept valid rollback request', () => { - const request = { - packageId: 'com.acme.crm', - snapshotId: 'snap-001', - }; - const parsed = RollbackPackageRequestSchema.parse(request); - expect(parsed.rollbackCustomizations).toBe(true); - }); - - it('should accept rollback without customization restore', () => { - const request = { - packageId: 'com.acme.crm', - snapshotId: 'snap-001', - rollbackCustomizations: false, - }; - const parsed = RollbackPackageRequestSchema.parse(request); - expect(parsed.rollbackCustomizations).toBe(false); - }); - - it('should reject missing required fields', () => { - expect(() => RollbackPackageRequestSchema.parse({ packageId: 'test' })).toThrow(); - expect(() => RollbackPackageRequestSchema.parse({ snapshotId: 'test' })).toThrow(); - }); -}); - -describe('RollbackPackageResponseSchema', () => { - it('should accept successful rollback', () => { - const response = { - success: true, - restoredVersion: '1.0.0', - message: 'Rolled back to version 1.0.0', - }; - const parsed = RollbackPackageResponseSchema.parse(response); - expect(parsed.success).toBe(true); - expect(parsed.restoredVersion).toBe('1.0.0'); - }); -}); - -// #15678 (stack card 3/6 of #14478) — ruling B: the unit of a duration-shaped -// number lives in the key NAME. The old spelling is a `retiredKey()` tombstone, -// so the refusal carries the RENAME rather than a bare unrecognized-key error. -describe('UpgradePlan.estimatedDuration carries its unit', () => { - const basePlan = { - packageId: 'com.acme.crm', - fromVersion: '1.0.0', - toVersion: '2.0.0', - impactLevel: 'low' as const, - changes: [], - }; - - it('REFUSES the retired `estimatedDuration` with the rename in the message', () => { - const result = UpgradePlanSchema.safeParse({ ...basePlan, estimatedDuration: 120 }); - expect(result.success).toBe(false); - const issue = result.error!.issues.find((i) => i.path.join('.') === 'estimatedDuration'); - expect(issue).toBeDefined(); - expect(issue!.code).not.toBe('unrecognized_keys'); - expect(issue!.message).toContain( - '`UpgradePlan.estimatedDuration` was renamed to `estimatedDurationSeconds`', - ); - // The prescription must name the unit, because SECONDS is the minority unit - // in this package and the sibling rename on this card is milliseconds. - expect(issue!.message).toContain('the value (seconds) is unchanged'); - }); - - it('accepts `estimatedDurationSeconds` at the same magnitude', () => { - const parsed = UpgradePlanSchema.parse({ ...basePlan, estimatedDurationSeconds: 120 }); - expect(parsed.estimatedDurationSeconds).toBe(120); - }); -}); diff --git a/packages/spec/src/kernel/package-upgrade.zod.ts b/packages/spec/src/kernel/package-upgrade.zod.ts deleted file mode 100644 index 5cbc747556b..00000000000 --- a/packages/spec/src/kernel/package-upgrade.zod.ts +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { z } from 'zod'; -import { ManifestSchema } from './manifest.zod'; - -/** - * # Package Upgrade Protocol - * - * Defines the complete lifecycle for upgrading installed packages, - * including pre-upgrade analysis, snapshot/backup, execution, validation, - * and rollback capabilities. - * - * ## Architecture Alignment - * - **Salesforce**: Managed Package upgrade with push upgrades and subscriber control - * - **ServiceNow**: Update Sets with preview, commit, and back-out support - * - **Helm**: Helm upgrade with rollback to previous release - * - **Kubernetes**: Rolling update with readiness probes and automatic rollback - * - * ## Upgrade Flow - * ``` - * 1. PreCheck → Validate compatibility, check dependencies - * 2. Plan → Generate upgrade plan with metadata diff - * 3. Snapshot → Backup current state (metadata + customizations) - * 4. Execute → Apply new package metadata with 3-way merge - * 5. Validate → Run post-upgrade health checks - * 6. Commit → Finalize upgrade (or Rollback on failure) - * ``` - */ - -// ========================================== -// Upgrade Plan & Analysis -// ========================================== - -/** - * Metadata Change Type - * Type of change detected between package versions. - */ -import { lazySchema } from '../shared/lazy-schema'; -import { retiredKey } from '../shared/retired-key'; -export const MetadataChangeTypeSchema = lazySchema(() => z.enum([ - 'added', // New metadata item added in new version - 'modified', // Existing metadata item modified - 'removed', // Metadata item removed in new version - 'renamed', // Metadata item renamed -]).describe('Type of metadata change between package versions')); - -/** - * Metadata Diff Item - * Describes a single metadata change between two package versions. - */ -export const MetadataDiffItemSchema = lazySchema(() => z.object({ - /** Metadata type (e.g. "object", "view", "flow") */ - type: z.string().describe('Metadata type'), - - /** Metadata name */ - name: z.string().describe('Metadata name'), - - /** Type of change */ - changeType: MetadataChangeTypeSchema.describe('Category of metadata modification (added, modified, removed, or renamed)'), - - /** Whether this change has potential conflicts with customizations */ - hasConflict: z.boolean().default(false) - .describe('Whether this change may conflict with customizations'), - - /** Human-readable summary of the change */ - summary: z.string().optional().describe('Human-readable change summary'), - - /** Previous name (for renames) */ - previousName: z.string().optional().describe('Previous name if renamed'), -}).describe('Single metadata change between package versions')); - -/** - * Upgrade Impact Level - * Indicates the severity of impact the upgrade will have. - */ -export const UpgradeImpactLevelSchema = lazySchema(() => z.enum([ - 'none', // No impact, seamless upgrade - 'low', // Minor changes, no user action needed - 'medium', // Some changes that may affect workflows - 'high', // Significant changes, user review recommended - 'critical', // Breaking changes, manual intervention required -]).describe('Severity of upgrade impact')); - -/** - * Upgrade Plan Schema - * The analysis result before executing an upgrade. - * Generated by comparing old version metadata with new version metadata. - */ -export const UpgradePlanSchema = lazySchema(() => z.object({ - /** Package being upgraded */ - packageId: z.string().describe('Package identifier'), - - /** Current installed version */ - fromVersion: z.string().describe('Currently installed version'), - - /** Target version to upgrade to */ - toVersion: z.string().describe('Target upgrade version'), - - /** Overall impact level */ - impactLevel: UpgradeImpactLevelSchema.describe('Severity assessment from none (seamless) to critical (breaking changes)'), - - /** List of all metadata changes between versions */ - changes: z.array(MetadataDiffItemSchema).describe('All metadata changes'), - - /** Number of customer customizations that may be affected */ - affectedCustomizations: z.number().int().min(0).default(0) - .describe('Count of customizations that may be affected'), - - /** Whether any migration scripts need to run */ - requiresMigration: z.boolean().default(false) - .describe('Whether data migration scripts are needed'), - - /** Migration script paths (relative to package root) */ - migrationScripts: z.array(z.string()).optional() - .describe('Paths to migration scripts'), - - /** Dependencies that also need upgrading */ - dependencyUpgrades: z.array(z.object({ - packageId: z.string(), - fromVersion: z.string(), - toVersion: z.string(), - })).optional().describe('Dependent packages that also need upgrading'), - - /** Estimated upgrade duration in seconds */ - // Renamed from `estimatedDuration` (#15678, #14478 ruling B): the unit lived - // only in the describe prose. - estimatedDurationSeconds: z.number().int().min(0).optional() - .describe('Estimated upgrade duration in seconds'), - - /** Tombstone for the rename above (#15678, ruling B on #14478). */ - estimatedDuration: retiredKey( - '`UpgradePlan.estimatedDuration` was renamed to `estimatedDurationSeconds` in @objectstack/spec 17 — ' - + 'the unit of a duration-shaped number lives in the key name, not only ' - + 'in the describe prose. Rename the key to `estimatedDurationSeconds`; the value (seconds) is unchanged.', - ), - - /** Human-readable summary */ - summary: z.string().optional().describe('Human-readable upgrade summary'), -}).describe('Upgrade analysis plan generated before execution')); - -// ========================================== -// Upgrade Snapshot (Pre-Upgrade Backup) -// ========================================== - -/** - * Upgrade Snapshot Schema - * Captures the complete state before an upgrade for rollback capability. - */ -export const UpgradeSnapshotSchema = lazySchema(() => z.object({ - /** Snapshot ID (UUID) */ - id: z.string().describe('Snapshot identifier'), - - /** Package being upgraded */ - packageId: z.string().describe('Package identifier'), - - /** Version being upgraded from */ - fromVersion: z.string().describe('Version before upgrade'), - - /** Version being upgraded to */ - toVersion: z.string().describe('Target upgrade version'), - - /** Tenant ID */ - tenantId: z.string().optional().describe('Tenant identifier'), - - /** Complete manifest of the old package version */ - previousManifest: ManifestSchema.describe('Complete manifest of the previous package version'), - - /** - * Snapshot of all metadata records owned by this package. - * Stored as array of { type, name, metadata } tuples. - */ - metadataSnapshot: z.array(z.object({ - type: z.string(), - name: z.string(), - metadata: z.record(z.string(), z.unknown()), - })).describe('Snapshot of all package metadata'), - - /** - * Snapshot of all customer customizations (overlays) for this package's metadata. - */ - customizationSnapshot: z.array(z.record(z.string(), z.unknown())).optional() - .describe('Snapshot of customer customizations'), - - /** When the snapshot was created */ - createdAt: z.string().datetime().describe('Snapshot creation timestamp'), - - /** Expiry time for snapshot cleanup */ - expiresAt: z.string().datetime().optional().describe('Snapshot expiry timestamp'), -}).describe('Pre-upgrade state snapshot for rollback capability')); - -// ========================================== -// Upgrade Request/Response -// ========================================== - -/** - * Upgrade Package Request - */ -export const UpgradePackageRequestSchema = lazySchema(() => z.object({ - /** Package ID to upgrade */ - packageId: z.string().describe('Package ID to upgrade'), - - /** Target version (if omitted, upgrades to latest) */ - targetVersion: z.string().optional().describe('Target version (defaults to latest)'), - - /** New manifest for the target version */ - manifest: ManifestSchema.optional().describe('New manifest (if installing from local)'), - - /** Whether to create a pre-upgrade snapshot */ - createSnapshot: z.boolean().default(true) - .describe('Whether to create a pre-upgrade backup snapshot'), - - /** Merge strategy for handling customizations */ - mergeStrategy: z.enum([ - 'keep-custom', - 'accept-incoming', - 'three-way-merge', - ]).default('three-way-merge').describe('How to handle customer customizations'), - - /** Whether to run in dry-run mode (preview only, no changes) */ - dryRun: z.boolean().default(false) - .describe('Preview upgrade without making changes'), - - /** Whether to skip pre-upgrade validation */ - skipValidation: z.boolean().default(false) - .describe('Skip pre-upgrade compatibility checks'), -}).describe('Upgrade package request')); - -/** - * Upgrade Phase - * Current phase of the upgrade process. - */ -export const UpgradePhaseSchema = lazySchema(() => z.enum([ - 'pending', // Upgrade requested but not started - 'analyzing', // Generating upgrade plan - 'snapshot', // Creating pre-upgrade snapshot - 'executing', // Applying metadata changes - 'migrating', // Running migration scripts - 'validating', // Post-upgrade validation - 'completed', // Upgrade completed successfully - 'failed', // Upgrade failed - 'rolling-back', // Rollback in progress - 'rolled-back', // Rollback completed -]).describe('Current phase of the upgrade process')); - -/** - * Upgrade Package Response - */ -export const UpgradePackageResponseSchema = lazySchema(() => z.object({ - /** Whether the upgrade was successful */ - success: z.boolean().describe('Whether the upgrade succeeded'), - - /** Current upgrade phase */ - phase: UpgradePhaseSchema.describe('Current upgrade phase'), - - /** The upgrade plan that was executed */ - plan: UpgradePlanSchema.optional().describe('Upgrade plan'), - - /** Snapshot ID for rollback */ - snapshotId: z.string().optional().describe('Snapshot ID for rollback'), - - /** Merge conflicts that need manual resolution (if any) */ - conflicts: z.array(z.object({ - path: z.string(), - baseValue: z.unknown(), - incomingValue: z.unknown(), - customValue: z.unknown(), - })).optional().describe('Unresolved merge conflicts'), - - /** Error message (if failed) */ - errorMessage: z.string().optional().describe('Error message if upgrade failed'), - - /** Human-readable summary */ - message: z.string().optional().describe('Human-readable status message'), -}).describe('Upgrade package response')); - -// ========================================== -// Rollback -// ========================================== - -/** - * Rollback Package Request - */ -export const RollbackPackageRequestSchema = lazySchema(() => z.object({ - /** Package ID to rollback */ - packageId: z.string().describe('Package ID to rollback'), - - /** Snapshot ID to restore from */ - snapshotId: z.string().describe('Snapshot ID to restore from'), - - /** Whether to also rollback customizations */ - rollbackCustomizations: z.boolean().default(true) - .describe('Whether to restore pre-upgrade customizations'), -}).describe('Rollback package request')); - -/** - * Rollback Package Response - */ -export const RollbackPackageResponseSchema = lazySchema(() => z.object({ - /** Whether the rollback was successful */ - success: z.boolean().describe('Whether the rollback succeeded'), - - /** Restored version */ - restoredVersion: z.string().optional().describe('Version restored to'), - - /** Message */ - message: z.string().optional().describe('Rollback status message'), -}).describe('Rollback package response')); - -// ========================================== -// Export Types -// ========================================== - -export type MetadataChangeType = z.input; -export type MetadataDiffItem = z.input; -/** Post-parse shape of {@link MetadataDiffItem} — defaults applied, transforms run (ADR-0122). */ -export type MetadataDiffItemParsed = z.infer; -export type UpgradeImpactLevel = z.input; -export type UpgradePlan = z.input; -/** Post-parse shape of {@link UpgradePlan} — defaults applied, transforms run (ADR-0122). */ -export type UpgradePlanParsed = z.infer; -export type UpgradeSnapshot = z.input; -/** Post-parse shape of {@link UpgradeSnapshot} — defaults applied, transforms run (ADR-0122). */ -export type UpgradeSnapshotParsed = z.infer; -export type UpgradePackageRequest = z.input; -/** Post-parse shape of {@link UpgradePackageRequest} — defaults applied, transforms run (ADR-0122). */ -export type UpgradePackageRequestParsed = z.infer; -export type UpgradePhase = z.input; -export type UpgradePackageResponse = z.input; -/** Post-parse shape of {@link UpgradePackageResponse} — defaults applied, transforms run (ADR-0122). */ -export type UpgradePackageResponseParsed = z.infer; -export type RollbackPackageRequest = z.input; -/** Post-parse shape of {@link RollbackPackageRequest} — defaults applied, transforms run (ADR-0122). */ -export type RollbackPackageRequestParsed = z.infer; -export type RollbackPackageResponse = z.input; diff --git a/packages/spec/src/migrations/entries/retired-defs/18.kernel__MetadataChangeType.ts b/packages/spec/src/migrations/entries/retired-defs/18.kernel__MetadataChangeType.ts new file mode 100644 index 00000000000..cfb76a687e0 --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-defs/18.kernel__MetadataChangeType.ts @@ -0,0 +1,10 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #25608 — `kernel/MetadataChangeType`, retired whole with `kernel/package-upgrade.zod.ts` +// (ADR-0049 enforce-or-remove; maintainer ruling A of 2026-10-19 on the +// package lifecycle classes, item 3). It declared the `added / modified / removed / renamed` enum an upgrade diff tagged each change with. +// Zero readers in objectstack, objectui and hotcrm. No carrier key and no +// authored document, so no tombstone and no D2 conversion — this table plus +// the D3 semantic entry `kernel-package-upgrade-family-retired` are the +// declaration. +export const entry = 'kernel/MetadataChangeType'; diff --git a/packages/spec/src/migrations/entries/retired-defs/18.kernel__MetadataDiffItem.ts b/packages/spec/src/migrations/entries/retired-defs/18.kernel__MetadataDiffItem.ts new file mode 100644 index 00000000000..1d74e4a2946 --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-defs/18.kernel__MetadataDiffItem.ts @@ -0,0 +1,10 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #25608 — `kernel/MetadataDiffItem`, retired whole with `kernel/package-upgrade.zod.ts` +// (ADR-0049 enforce-or-remove; maintainer ruling A of 2026-10-19 on the +// package lifecycle classes, item 3). It declared one row of the version-to-version metadata diff an upgrade plan carried. +// Zero readers in objectstack, objectui and hotcrm. No carrier key and no +// authored document, so no tombstone and no D2 conversion — this table plus +// the D3 semantic entry `kernel-package-upgrade-family-retired` are the +// declaration. +export const entry = 'kernel/MetadataDiffItem'; diff --git a/packages/spec/src/migrations/entries/retired-defs/18.kernel__RollbackPackageRequest.ts b/packages/spec/src/migrations/entries/retired-defs/18.kernel__RollbackPackageRequest.ts new file mode 100644 index 00000000000..d0fdcbc062a --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-defs/18.kernel__RollbackPackageRequest.ts @@ -0,0 +1,10 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #25608 — `kernel/RollbackPackageRequest`, retired whole with `kernel/package-upgrade.zod.ts` +// (ADR-0049 enforce-or-remove; maintainer ruling A of 2026-10-19 on the +// package lifecycle classes, item 3). It declared the request body of the snapshot rollback. +// Zero readers in objectstack, objectui and hotcrm. No carrier key and no +// authored document, so no tombstone and no D2 conversion — this table plus +// the D3 semantic entry `kernel-package-upgrade-family-retired` are the +// declaration. +export const entry = 'kernel/RollbackPackageRequest'; diff --git a/packages/spec/src/migrations/entries/retired-defs/18.kernel__RollbackPackageResponse.ts b/packages/spec/src/migrations/entries/retired-defs/18.kernel__RollbackPackageResponse.ts new file mode 100644 index 00000000000..2ce83cb0a7e --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-defs/18.kernel__RollbackPackageResponse.ts @@ -0,0 +1,10 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #25608 — `kernel/RollbackPackageResponse`, retired whole with `kernel/package-upgrade.zod.ts` +// (ADR-0049 enforce-or-remove; maintainer ruling A of 2026-10-19 on the +// package lifecycle classes, item 3). It declared the answer of the snapshot rollback. +// Zero readers in objectstack, objectui and hotcrm. No carrier key and no +// authored document, so no tombstone and no D2 conversion — this table plus +// the D3 semantic entry `kernel-package-upgrade-family-retired` are the +// declaration. +export const entry = 'kernel/RollbackPackageResponse'; diff --git a/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradeImpactLevel.ts b/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradeImpactLevel.ts new file mode 100644 index 00000000000..f95854474da --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradeImpactLevel.ts @@ -0,0 +1,10 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #25608 — `kernel/UpgradeImpactLevel`, retired whole with `kernel/package-upgrade.zod.ts` +// (ADR-0049 enforce-or-remove; maintainer ruling A of 2026-10-19 on the +// package lifecycle classes, item 3). It declared the `none … critical` severity an upgrade plan graded itself with. +// Zero readers in objectstack, objectui and hotcrm. No carrier key and no +// authored document, so no tombstone and no D2 conversion — this table plus +// the D3 semantic entry `kernel-package-upgrade-family-retired` are the +// declaration. +export const entry = 'kernel/UpgradeImpactLevel'; diff --git a/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradePackageRequest.ts b/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradePackageRequest.ts new file mode 100644 index 00000000000..2857f4b7ebf --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradePackageRequest.ts @@ -0,0 +1,10 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #25608 — `kernel/UpgradePackageRequest`, retired whole with `kernel/package-upgrade.zod.ts` +// (ADR-0049 enforce-or-remove; maintainer ruling A of 2026-10-19 on the +// package lifecycle classes, item 3). It declared the request body of the unserved package upgrade. +// Zero readers in objectstack, objectui and hotcrm. No carrier key and no +// authored document, so no tombstone and no D2 conversion — this table plus +// the D3 semantic entry `kernel-package-upgrade-family-retired` are the +// declaration. +export const entry = 'kernel/UpgradePackageRequest'; diff --git a/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradePackageResponse.ts b/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradePackageResponse.ts new file mode 100644 index 00000000000..246578e2c53 --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradePackageResponse.ts @@ -0,0 +1,10 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #25608 — `kernel/UpgradePackageResponse`, retired whole with `kernel/package-upgrade.zod.ts` +// (ADR-0049 enforce-or-remove; maintainer ruling A of 2026-10-19 on the +// package lifecycle classes, item 3). It declared the answer of the unserved package upgrade, with its merge `conflicts`. +// Zero readers in objectstack, objectui and hotcrm. No carrier key and no +// authored document, so no tombstone and no D2 conversion — this table plus +// the D3 semantic entry `kernel-package-upgrade-family-retired` are the +// declaration. +export const entry = 'kernel/UpgradePackageResponse'; diff --git a/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradePhase.ts b/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradePhase.ts new file mode 100644 index 00000000000..fb5eaebe04f --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradePhase.ts @@ -0,0 +1,10 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #25608 — `kernel/UpgradePhase`, retired whole with `kernel/package-upgrade.zod.ts` +// (ADR-0049 enforce-or-remove; maintainer ruling A of 2026-10-19 on the +// package lifecycle classes, item 3). It declared the `pending … rolled-back` phase vocabulary of an upgrade run. +// Zero readers in objectstack, objectui and hotcrm. No carrier key and no +// authored document, so no tombstone and no D2 conversion — this table plus +// the D3 semantic entry `kernel-package-upgrade-family-retired` are the +// declaration. +export const entry = 'kernel/UpgradePhase'; diff --git a/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradePlan.ts b/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradePlan.ts new file mode 100644 index 00000000000..032a0fab025 --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradePlan.ts @@ -0,0 +1,10 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #25608 — `kernel/UpgradePlan`, retired whole with `kernel/package-upgrade.zod.ts` +// (ADR-0049 enforce-or-remove; maintainer ruling A of 2026-10-19 on the +// package lifecycle classes, item 3). It declared the pre-upgrade analysis `IPackageService.planUpgrade` would have returned. +// Zero readers in objectstack, objectui and hotcrm. No carrier key and no +// authored document, so no tombstone and no D2 conversion — this table plus +// the D3 semantic entry `kernel-package-upgrade-family-retired` are the +// declaration. +export const entry = 'kernel/UpgradePlan'; diff --git a/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradeSnapshot.ts b/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradeSnapshot.ts new file mode 100644 index 00000000000..d54f238ae46 --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-defs/18.kernel__UpgradeSnapshot.ts @@ -0,0 +1,10 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #25608 — `kernel/UpgradeSnapshot`, retired whole with `kernel/package-upgrade.zod.ts` +// (ADR-0049 enforce-or-remove; maintainer ruling A of 2026-10-19 on the +// package lifecycle classes, item 3). It declared the pre-upgrade backup a rollback would have restored from. +// Zero readers in objectstack, objectui and hotcrm. No carrier key and no +// authored document, so no tombstone and no D2 conversion — this table plus +// the D3 semantic entry `kernel-package-upgrade-family-retired` are the +// declaration. +export const entry = 'kernel/UpgradeSnapshot'; diff --git a/packages/spec/src/migrations/entries/semantic/18.kernel-package-upgrade-family-retired.ts b/packages/spec/src/migrations/entries/semantic/18.kernel-package-upgrade-family-retired.ts new file mode 100644 index 00000000000..0b214d8bd44 --- /dev/null +++ b/packages/spec/src/migrations/entries/semantic/18.kernel-package-upgrade-family-retired.ts @@ -0,0 +1,66 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import type { SemanticMigration } from '../../types.js'; + +export const entry: SemanticMigration = { + id: 'kernel-package-upgrade-family-retired', + // No backticks in `surface` — build-upgrade-guide.ts renders it inside a + // code span AND a table cell. + surface: + 'the package upgrade model, retired whole: the ten defs kernel/MetadataChangeType, ' + + 'kernel/MetadataDiffItem, kernel/UpgradeImpactLevel, kernel/UpgradePlan, ' + + 'kernel/UpgradeSnapshot, kernel/UpgradePackageRequest, kernel/UpgradePhase, ' + + 'kernel/UpgradePackageResponse, kernel/RollbackPackageRequest and ' + + 'kernel/RollbackPackageResponse, and every name kernel/package-upgrade.zod.ts exported from ' + + '@objectstack/spec/kernel (the ten *Schema consts, their z.input aliases and their *Parsed ' + + 'aliases); the IPackageService members planUpgrade, upgrade, rollback and getSnapshot with ' + + 'their inputs PlanUpgradeInput, ExecuteUpgradeInput and RollbackInput from ' + + '@objectstack/spec/contracts; and the plan member of PackageUpgradeResponse.data from ' + + '@objectstack/spec/api', + replacement: + 'nothing to re-declare — no package upgrade engine exists on the platform, so there is no ' + + 'working configuration to migrate to. Nothing generated an upgrade plan, diffed two package ' + + 'versions, took a pre-upgrade snapshot, three-way merged a customization or rolled a package ' + + 'back to a snapshot. What the platform does serve: an upgrade is a re-install of the new ' + + 'version (`POST /api/v1/packages` with `overwrite: true`), and the package rollback it ' + + 'serves is the commit rollback (`POST /api/v1/packages/:id/rollback` with a `commitId`, ' + + 'answered by `RollbackToPackageCommitResponseSchema` in `@objectstack/spec/api`). ' + + '`PackageUpgradeRequestSchema` and `PackageUpgradeResponseSchema` stay published, bound to no ' + + 'route, minus the response\'s `plan`. If a package upgrade becomes a product capability it ' + + 're-declares fresh, through the enforce route of ADR-0049 — the engine first, the ' + + 'vocabulary second', + reason: + 'ADR-0049 enforce-or-remove; maintainer ruling A of 2026-10-19 on the package lifecycle ' + + 'classes, item 3 ("Retired: packages/spec/src/kernel/package-upgrade.zod.ts (plan, diff, ' + + 'conflict, snapshot, rollback; zero runtime consumers), with its ADR-0087 conversion"), done ' + + 'directly in 18.x with no retirement stage. Ten defs sat on the exported surface and in the ' + + 'generated reference docs, and were read by NOTHING: the schemas were exported from ' + + '`@objectstack/spec/kernel`, mounted by no `stack.zod.ts` key, registered as no metadata ' + + 'type, and no provider ever implemented the four `IPackageService` upgrade members that ' + + 'returned them; the reader census over every package outside `packages/spec`, over ' + + '`examples/**` and `skills/**`, and over objectui and hotcrm returned zero hits for every ' + + 'exported name, with a lit control. `UpgradePackageRequest.createSnapshot` and ' + + '`mergeStrategy: three-way-merge` read as guarantees the platform kept, and neither ever ' + + 'did — the declared-but-unenforced shape on the one surface an operator trusts to protect ' + + 'their customizations. The `UpgradePlan.estimatedDuration` tombstone of the duration-unit ' + + 'ruling (`RETIRED_KEYS_BY_MAJOR[18]`, D3 `kernel-package-lifecycle-durations-unit-in-key`) ' + + 'leaves with its def\'s source; its registry entries stay as history. Why D3 semantic and ' + + 'not a D2 conversion: the chain walks a normalized STACK and `applyConversionsToStoredItem` ' + + 'maps a metadata type onto one of its collections; none of these schemas is either, so a ' + + 'conversion would be a transform with no seam that ever runs, and with no carrier key there ' + + 'is no shape on which a tombstone could sit. ADR-0087.', + acceptanceCriteria: + 'No code imports MetadataChangeTypeSchema, MetadataDiffItemSchema, UpgradeImpactLevelSchema, ' + + 'UpgradePlanSchema, UpgradeSnapshotSchema, UpgradePackageRequestSchema, UpgradePhaseSchema, ' + + 'UpgradePackageResponseSchema, RollbackPackageRequestSchema or RollbackPackageResponseSchema ' + + '— or any of their type aliases — from @objectstack/spec or @objectstack/spec/kernel, and ' + + 'none imports PlanUpgradeInput, ExecuteUpgradeInput or RollbackInput from ' + + '@objectstack/spec/contracts: every such import is TS2305 after upgrade. No IPackageService ' + + 'implementation still declares planUpgrade, upgrade, rollback or getSnapshot (the contract ' + + 'no longer carries them, so no caller typed against it can reach them), and no reader of ' + + 'PackageUpgradeResponse.data.plan remains (TS2339). The ten defs are absent from ' + + '`json-schema.manifest/kernel.json`, the api-surface / declaration-map / export-origins ' + + 'shards and the generated reference docs. ⚠️ Runtime behaviour is deliberately UNCHANGED ' + + 'and must be verified as such: nothing ever parsed or read these shapes, so removing them ' + + 'removes no behaviour.', +}; diff --git a/packages/spec/src/migrations/registry.ts.template b/packages/spec/src/migrations/registry.ts.template index ccc3ba450b7..0bb08269cd6 100644 --- a/packages/spec/src/migrations/registry.ts.template +++ b/packages/spec/src/migrations/registry.ts.template @@ -2201,6 +2201,21 @@ const STEP18_RATIONALE: readonly RationaleFragment[] = [ + 'no tombstone, and no D2 conversion exists: a refused hook carries no intent a rewrite could ' + 'keep. Its D3 record is the semantic entry `hook-body-stored-metadata-target-refused`.', }, + { + id: 'kernel-package-upgrade-family-retired', + order: 109, + text: + 'It also retires the package upgrade model WHOLE (ADR-0049 enforce-or-remove; maintainer ' + + 'ruling A of 2026-10-19 on the package lifecycle classes, item 3): the ten defs of ' + + '`kernel/package-upgrade.zod.ts` — the upgrade plan, its metadata diff and impact level, the ' + + 'pre-upgrade snapshot, the upgrade and rollback request/response pairs and the phase ' + + 'vocabulary — exported from `@objectstack/spec/kernel`, mounted by no stack key, registered ' + + 'as no metadata type and read by nothing in this repository, objectui or hotcrm, leave via ' + + 'RETIRED_DEFS_BY_MAJOR with one D3 semantic entry, together with the four `IPackageService` ' + + 'upgrade members no provider implemented and the `plan` member of the unrouted ' + + '`PackageUpgradeResponse`. The `UpgradePlan.estimatedDuration` tombstone leaves with its ' + + 'def\'s source and its RETIRED_KEYS_BY_MAJOR[18] entry stays as history.', + }, { id: 'list-view-filterable-fields-retired', order: 105, diff --git a/packages/spec/src/system/compliance-families-retirement.test.ts b/packages/spec/src/system/compliance-families-retirement.test.ts index 81ae759e4b5..d9500ff8f55 100644 --- a/packages/spec/src/system/compliance-families-retirement.test.ts +++ b/packages/spec/src/system/compliance-families-retirement.test.ts @@ -140,9 +140,11 @@ const HISTORY_SEMANTIC_IDS = [ /** * The near-namesakes a "finish everything incident / change" sweep would - * plausibly take, each a DIFFERENT declaration with a live consumer. + * plausibly take, each a DIFFERENT declaration that stays published. (It was + * `MetadataChangeType` until #25608 retired `kernel/package-upgrade.zod.ts` + * whole; its only reader was that module itself.) */ -const MUST_SURVIVE_KERNEL = ['MetadataChangeTypeSchema', 'MetadataChangeType'] as const; +const MUST_SURVIVE_KERNEL = ['BreakingChangeSchema', 'BreakingChange'] as const; /** System-entry neighbours that stay — the ones the retired files imported from. */ const MUST_SURVIVE_SYSTEM = ['DataClassificationSchema', 'ComplianceFrameworkSchema', 'ChangeSetSchema'] as const; @@ -429,7 +431,7 @@ describe('tree-scoped absence: nothing inside the declared radius references a r expect(REFERENCE.test('"system/RollbackPlan",')).toBe(true); expect(REFERENCE.test('the `IncidentResponsePolicySchema` export was removed')).toBe(false); expect(REFERENCE.test("title: 'Incident push failed: {record.name}'")).toBe(false); - expect(REFERENCE.test('MetadataChangeTypeSchema')).toBe(false); // the live near-namesake + expect(REFERENCE.test('BreakingChangeSchema')).toBe(false); // the surviving near-namesake }); it('the build-detritus exclusion is NARROW — it names tsup\'s bundle and nothing authored', () => { diff --git a/packages/spec/src/type-alias-convention.pin.test.ts b/packages/spec/src/type-alias-convention.pin.test.ts index a9971f38565..a81d569bda7 100644 --- a/packages/spec/src/type-alias-convention.pin.test.ts +++ b/packages/spec/src/type-alias-convention.pin.test.ts @@ -162,7 +162,6 @@ import type * as M88 from './kernel/metadata-plugin.zod.js'; import type * as M89 from './kernel/metadata-protection.zod.js'; import type * as M90 from './kernel/package-artifact.zod.js'; import type * as M91 from './kernel/package-registry.zod.js'; -import type * as M92 from './kernel/package-upgrade.zod.js'; import type * as M93 from './kernel/plugin-capability.zod.js'; import type * as M94 from './kernel/plugin-lifecycle-advanced.zod.js'; import type * as M95 from './kernel/plugin-loading.zod.js'; @@ -279,7 +278,7 @@ import type * as M190 from './kernel/execution-context.zod.js'; import type * as M191 from './automation/api-trigger-secret-ref.zod.js'; // --------------------------------------------------------------------------- -// 790 isomorphic aliases: `z.input` === `z.infer`, so no `XParsed` is declared. +// 786 isomorphic aliases: `z.input` === `z.infer`, so no `XParsed` is declared. // // That number is machine-checked, not hand-kept. The runtime companion at the // bottom of this file recomputes the pin count from the source and asserts that @@ -1024,12 +1023,6 @@ export type Iso_kernel_packageRegistry__PackageStatusEnum = Assert, z.infer< typeof M91.UninstallPackageRequestSchema > >>; export type Iso_kernel_packageRegistry__UninstallPackageResponseSchema = Assert, z.infer< typeof M91.UninstallPackageResponseSchema > >>; -// kernel/package-upgrade.zod.ts -export type Iso_kernel_packageUpgrade__MetadataChangeTypeSchema = Assert, z.infer< typeof M92.MetadataChangeTypeSchema > >>; -export type Iso_kernel_packageUpgrade__RollbackPackageResponseSchema = Assert, z.infer< typeof M92.RollbackPackageResponseSchema > >>; -export type Iso_kernel_packageUpgrade__UpgradeImpactLevelSchema = Assert, z.infer< typeof M92.UpgradeImpactLevelSchema > >>; -export type Iso_kernel_packageUpgrade__UpgradePhaseSchema = Assert, z.infer< typeof M92.UpgradePhaseSchema > >>; - // kernel/plugin-capability.zod.ts export type Iso_kernel_pluginCapability__CapabilityConformanceLevelSchema = Assert, z.infer< typeof M93.CapabilityConformanceLevelSchema > >>; export type Iso_kernel_pluginCapability__ProtocolReferenceSchema = Assert, z.infer< typeof M93.ProtocolReferenceSchema > >>; @@ -1708,7 +1701,7 @@ describe('ADR-0122 type-alias convention', () => { // this title and the section header above the pin list — are now asserted // against the recomputed count below, so neither can go stale without a red // test naming it. - it('still declares all 790 isomorphic pins', () => { + it('still declares all 786 isomorphic pins', () => { // The truth of each pin is proved by tsc, not here — an `Assert>` // that stops holding is a compile error with the alias named. What tsc // cannot notice is a pin that was DELETED: removing the assertion removes @@ -2573,7 +2566,18 @@ describe('ADR-0122 type-alias convention', () => { // start node gives its hook secret — a plain string, so it is pinned here // (Iso_automation_apiTriggerSecretRef__ApiTriggerSecretRefSchema) rather // than given a `…Parsed` synonym. +1 added. - expect(pins).toHaveLength(790); + // + // 790 -> 786 is #25608's retirement of `kernel/package-upgrade.zod.ts` + // whole (ADR-0049 enforce-or-remove, `RETIRED_DEFS_BY_MAJOR[18]`): four of + // its schemas were isomorphic and pinned here — + // Iso_kernel_packageUpgrade__MetadataChangeTypeSchema, + // Iso_kernel_packageUpgrade__RollbackPackageResponseSchema, + // Iso_kernel_packageUpgrade__UpgradeImpactLevelSchema and + // Iso_kernel_packageUpgrade__UpgradePhaseSchema — and they leave with the + // module, its `M92` import with them (the slot stays empty; the numbering + // is a label, not a count). The module's other defs each carried an + // `XParsed` alias and were never on this list. -4 removed. + expect(pins).toHaveLength(786); // The count is stated in PROSE twice as well — this case's title and the // section header above the pin list — and until commit c6b05c76a nothing read either diff --git a/packages/spec/vitest.repo-tests.json b/packages/spec/vitest.repo-tests.json index 333c6bf50d7..085a091478e 100644 --- a/packages/spec/vitest.repo-tests.json +++ b/packages/spec/vitest.repo-tests.json @@ -52,6 +52,7 @@ "src/integration/connector-sync-retirement.test.ts", "src/integration/connector-triggers-retirement.test.ts", "src/kernel/metadata-type-registry-allow-org-override-absence.test.ts", + "src/kernel/package-upgrade-family-retirement.test.ts", "src/qa/requires-plugins-retirement.test.ts", "src/security/admin-scope-binding-authoring-retirement.test.ts", "src/security/permission-set-managed-by-retirement.test.ts",