|
| 1 | +--- |
| 2 | +"@objectstack/spec": minor |
| 3 | +"@objectstack/core": minor |
| 4 | +--- |
| 5 | + |
| 6 | +feat(spec,core)!: the startup contract describes what the kernel produces — the orchestrator vocabulary is retired and `PluginStartupResult` is declared once (#16059) |
| 7 | + |
| 8 | +<!-- adr-0087: registered startup-orchestrator-retired --> |
| 9 | + |
| 10 | +**BREAKING** — a published exported surface is removed, landing in the launch window as |
| 11 | +`minor` (the lockstep convention: `major` is refused by `check-changeset-no-major`, and |
| 12 | +breaking-ness is carried by this banner plus the ADR-0087 disposition above). |
| 13 | + |
| 14 | +`@objectstack/spec` declared a plugin startup ORCHESTRATOR that was never built, and its |
| 15 | +one shape that *is* real had drifted away from the kernel that produces it. The maintainer |
| 16 | +ruling on this card keeps a startup-result contract, and makes it describe what the kernel |
| 17 | +actually returns. |
| 18 | + |
| 19 | +## What is removed |
| 20 | + |
| 21 | +`IStartupOrchestrator` (`orchestrateStartup` / `rollback` / `checkHealth` / |
| 22 | +`startWithTimeout`) and the three schemas it tied together. Nothing in any repository |
| 23 | +implemented the interface and nothing parsed the schemas; `healthCheck` and `HealthStatus` |
| 24 | +named a per-plugin startup health probe the runtime has never had. |
| 25 | + |
| 26 | +| removed | from | what to write instead | |
| 27 | +|:--|:--|:--| |
| 28 | +| `IStartupOrchestrator` | `@objectstack/spec/contracts` | nothing — plugin startup is the kernel's own boot loop | |
| 29 | +| `StartupOptionsSchema` / `StartupOptions` / `StartupOptionsParsed` | `@objectstack/spec/kernel`, `/contracts` | `startupTimeout` on the plugin; `rollbackOnFailure` on the kernel config | |
| 30 | +| `StartupOptions.healthCheck` | (with the schema) | **no replacement** — no startup probe system exists | |
| 31 | +| `HealthStatusSchema` / `HealthStatus` | `@objectstack/spec/kernel`, `/contracts` | **no replacement** — see above | |
| 32 | +| `StartupOrchestrationResultSchema` / `StartupOrchestrationResult` | `@objectstack/spec/kernel` | `ObjectKernel.getPluginStartupDurations()` | |
| 33 | + |
| 34 | +`StartupOptions.parallel` and `StartupOptions.context` have no replacement either: the |
| 35 | +kernel starts plugins sequentially and passes its own `PluginContext`. |
| 36 | + |
| 37 | +## What survives, re-declared |
| 38 | + |
| 39 | +`PluginStartupResultSchema` / `PluginStartupResult` stay on both entries, rewritten to the |
| 40 | +shape `@objectstack/core` has always returned from `ObjectKernel.startPluginWithTimeout()`. |
| 41 | +`@objectstack/core` now **imports** that type instead of declaring a twin, so the two |
| 42 | +cannot drift again. |
| 43 | + |
| 44 | +| member | before (spec) | after (spec and core, one declaration) | |
| 45 | +|:--|:--|:--| |
| 46 | +| `plugin: { name, version? }` | required | **removed** — write `pluginName: string` | |
| 47 | +| `pluginName` | absent | `string`, required | |
| 48 | +| `success` | `boolean`, required | unchanged | |
| 49 | +| `durationMs` | `number`, **required** | `number`, **optional** (absent when the plugin declares no `start()`) | |
| 50 | +| `startTime` | absent (it was core's own deprecated alias) | **removed** — read `durationMs`, which always carried the same value | |
| 51 | +| `error` | serializable projection | unchanged (a thrown `Error` satisfies it) | |
| 52 | +| `timedOut` | absent | `boolean`, optional — set when the failure was the timeout | |
| 53 | +| `health: HealthStatus` | optional | **removed** — no probe ever filled it | |
| 54 | + |
| 55 | +**The one-line fix:** rename `plugin: { name }` to `pluginName`, delete `health`, and read |
| 56 | +`durationMs` wherever you read `startTime`. All three old spellings are `retiredKey()` |
| 57 | +tombstones on the surviving schema, so each is a `tsc` error at the construction site and a |
| 58 | +parse error carrying the prescription. |
| 59 | + |
| 60 | +`startTime` is the one member whose removal a reader can OBSERVE: `@objectstack/core` |
| 61 | +populated it beside `durationMs` with the identical elapsed value, under its own ADR-0087 |
| 62 | +L1 deprecation, and `ObjectKernel.startPluginWithTimeout()` stops setting it here. Mirroring |
| 63 | +it on the contract was the alternative and the tree refuses it — `check:duration-unit-keys` |
| 64 | +(ruling B on #14478) fails an elapsed number whose key name carries no unit, and neither of |
| 65 | +that rule's two schema-declared exemptions fits: it is not an `EpochMs` instant and it |
| 66 | +mirrors no external standard. Renaming it to `startTimeMs` would mint a spelling nothing has |
| 67 | +ever produced, for a member already documented as slated for removal. |
| 68 | + |
| 69 | +For `@objectstack/core` consumers the members are unchanged; the one narrowing is that |
| 70 | +`PluginStartupResult.error` is now typed as the serializable projection |
| 71 | +(`name` / `message` / `stack?` / `code?`) rather than `Error`. The kernel still puts the |
| 72 | +thrown instance there, so `result.error instanceof Error` still narrows — only code that |
| 73 | +reads an `Error`-only member such as `cause` off it without that guard needs the guard. |
| 74 | + |
| 75 | +## The retirement kit |
| 76 | + |
| 77 | +Route 3 of the `spec-property-retirement` playbook: no authored document carried any of |
| 78 | +the three defs, so there is no seam for a D2 conversion and no author to hand a tombstone |
| 79 | +to. `RETIRED_DEFS_BY_MAJOR[18]` (`kernel/StartupOptions`, `kernel/HealthStatus`, |
| 80 | +`kernel/StartupOrchestrationResult`) plus the D3 semantic entry |
| 81 | +`startup-orchestrator-retired` **are** the declaration, and the three |
| 82 | +`json-schema.manifest/kernel.json` keys plus their 16 `authorable-surface/kernel.json` |
| 83 | +lines are deleted deliberately in this same change. The two keys of the SURVIVING result |
| 84 | +schema (`plugin`, `health`) take the tombstone route instead, registered in |
| 85 | +`RETIRED_KEYS_BY_MAJOR[18]`, because that def keeps emitting and its type is imported by |
| 86 | +`@objectstack/core`. |
| 87 | + |
| 88 | +Runtime behaviour is deliberately unchanged: nothing ever read the retired surfaces, and |
| 89 | +the kernel boot loop is untouched. |
0 commit comments