ObjectStackObjectStack

Startup Orchestrator — Kernel Protocol

One schema, describing the one startup datum the kernel actually produces.

Plugin Startup Result Protocol

One schema, describing the one startup datum the kernel actually produces.

ObjectKernel.startPluginWithTimeout() (packages/core/src/kernel.ts) races a plugin's start() against its startupTimeout and returns exactly this record for every plugin it starts — on the success path, on the failure path, and for a plugin that declares no start() at all. The kernel's boot loop is its only caller and its only reader: a success: false result is logged, and with rollbackOnFailure set the already-started plugins are destroyed and the original error is rethrown as the new error's cause.

What each member means, and when it is present:

  • pluginName — always. The plugin's registered name; the result carries the NAME, never a plugin object.
  • success — always. false means start() threw or the timeout fired.
  • durationMs — Date.now() elapsed across the start() race, on BOTH the success and the failure path. Absent for a plugin with no start(), which returns { success: true, pluginName } without racing anything.
  • error — the failure path only: the value start() threw. Declared here as the serializable projection every consumer of this record can carry (name / message / stack / code), which a thrown Error satisfies — the kernel hands the live instance through, so instanceof Error still narrows at the read site and the original cause survives the rethrow.
  • timedOut — the failure path only, and only when the failure was the TIMEOUT rather than a throw from inside start(): the kernel sets it from the raced rejection's message. Absent on the success path; absent, not false, when a plugin's own start() threw.

The deprecated startTime alias the kernel used to set beside durationMs ends here: it never held an instant, and a member whose name promises one while carrying an elapsed duration is the confusion the duration-unit rule exists to stop. It is a tombstone on this schema and the kernel no longer populates it.

── [#16059] What this module used to declare, and why it no longer does ────

Until this major it also declared a whole startup-ORCHESTRATION vocabulary — StartupOptionsSchema (with timeoutMs, rollbackOnFailure, healthCheck, parallel, context), HealthStatusSchema, StartupOrchestrationResultSchema — beside the IStartupOrchestrator contract interface in contracts/startup-orchestrator.ts that tied them together (orchestrateStartup / rollback / checkHealth / startWithTimeout).

Maintainer ruling on #16059 (director seat, decision batch #60, 2026-09-06): the spec KEEPS a startup-result contract and it describes what the kernel actually produces. Neither enforcing the never-landed design nor dropping the contract was adopted. So the orchestrator vocabulary is retired under ADR-0049 enforce-or-remove and this result schema is re-declared against the shipped shape, with @objectstack/core importing the type from here rather than declaring a twin — the drift that made the two disagree cannot recur.

Nothing implemented IStartupOrchestrator and nothing parsed the three schemas: measured across this repository and the pinned objectui checkout with lit same-corpus controls, every reference outside this module's own tests was a generated artifact or a released CHANGELOG.md. healthCheck and HealthStatus in particular named a probe system that does not exist — the kernel never checks a plugin's health at startup — which is the #3950 shape: a published vocabulary an author (ADR-0033) reads as proof of a capability, that parses clean and is received by nobody.

Route 3 of the retirement playbook: with no authored document carrying the defs there is no seam for a D2 conversion and nobody to hand a tombstone to, so RETIRED_DEFS_BY_MAJOR[18] plus the D3 semantic entry startup-orchestrator-retired ARE the declaration. Two keys of THIS surviving schema are tombstoned rather than dropped, because this def keeps emitting and its type is imported by @objectstack/core: a construction site still writing them gets the prescription through tsc.

Following ObjectStack "Zod First" principle — all data structures have Zod schemas for runtime validation and JSON Schema generation.

Source: packages/spec/src/kernel/startup-orchestrator.zod.ts

TypeScript Usage

import { PluginStartupResultSchema } from '@objectstack/spec/kernel';
import type { PluginStartupResult } from '@objectstack/spec/kernel';

// Validate data
const result = PluginStartupResultSchema.parse(data);

PluginStartupResult

Properties

PropertyTypeRequiredDescription
pluginNamestring✅Name of the plugin that was started
successboolean✅Whether the plugin started successfully
durationMsnumberoptionalTime taken to start the plugin in milliseconds; absent when the plugin declares no start()
error{ name: string; message: string; stack?: string; code?: string }optionalSerializable error representation if startup failed
timedOutbooleanoptionalWhether startup failed because the startup timeout fired, rather than start() throwing
startTimeneveroptional[REMOVED] PluginStartupResult.startTime was removed in @objectstack/spec 17 (ADR-0049) — it never held an instant: the kernel filled it with the SAME elapsed milliseconds as durationMs, so a reader who took the name at its word and computed Date.now() - startTime got an age near the epoch instead of a wait. Delete the key and read durationMs, which has always carried the same value.
durationneveroptional[REMOVED] PluginStartupResult.duration was renamed to durationMs 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 durationMs; the value (milliseconds) is unchanged.
pluginneveroptional[REMOVED] PluginStartupResult.plugin was removed in @objectstack/spec 17 (ADR-0049) — the kernel has never put a plugin OBJECT in this result, so the nested { name, version } shape described a value nothing ever built. Replace the key with pluginName and carry the plugin name string.
healthneveroptional[REMOVED] PluginStartupResult.health was removed in @objectstack/spec 17 (ADR-0049) — it carried a HealthStatus, and that vocabulary is retired with the startup orchestrator that declared it: no probe system ever ran a health check at startup, so nothing ever filled the key. Delete the key; a plugin that reports health does it through a service it registers, not through this result.

Nested Shape: PluginStartupResult.error

PropertyTypeRequiredDescription
namestring✅Error class name
messagestring✅Error message
stackstringoptionalStack trace
codestringoptionalError code

On this page