feat(spec,core)!: the startup contract describes what the kernel produces — retire the orchestrator, declare PluginStartupResult once (#16059) - #18303
Conversation
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
… once Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
…refuses Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
📓 Docs Drift CheckThis PR changes 2 package(s): 12 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 4 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 141 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 28e9a06b7dc4ddabf6b5f7a0b8297649c7933214 && git checkout 28e9a06b7dc4ddabf6b5f7a0b8297649c7933214
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 53dd5aaaafbe877a1cc72aef26511bd15c927ecf e345ffa9e179ac123cc983a68714df45db430641 && git checkout -B drift-repro 53dd5aaaafbe877a1cc72aef26511bd15c927ecf && git merge --no-ff e345ffa9e179ac123cc983a68714df45db430641
node scripts/docs-audit/affected-docs.mjs --json 53dd5aaaafbe877a1cc72aef26511bd15c927ecf
|
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
Contract reviewServed-tier: Reviewed first-hand in a detached worktree at the head (merge base ① Derived judgmentsRetired defs (3), judged correct. Exported names (8) and api-surface rows (11), judged correct and complete. Re-declared Core re-exports the spec type, verified by reverse verification at THIS head. Retired-keys entries (3), judged correct. Docs. ② Semver level
③ Boundary flagsQ1 — is the Q2 — measurable readers of Q3 — does the ruling's last sentence bite? My reading: the diff sits inside the grant's letter and brushes its spirit at exactly one point; the evidence weighs toward inside, and I do not decide it. The sentence is conditioned: "If any out-of-repo consumer of the retired interface is found … a deprecation window becomes a maintainer question." Two facts bear on it. First, the trigger did not fire: Deviation 3 — the three same-major rename registrations kept: verified, and the keep is right. Reverse verification — the implementer's leg was NOT taken at this head; the claim holds at this head on my own run. The report names HEAD blob Tombstone dating contradicts ADR-0087's 2026-09-13 amendment; same spelling as main, three strings, ungated. The three new Stale comment in the diff. Not findings. My census counts differ slightly from the report's (e.g. Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #16059
Executes the maintainer ruling (director seat, decision batch #60, 2026-09-06, maintainer reply 「同意」): the spec keeps a startup-result contract and it describes what the kernel actually produces. The never-landed orchestrator is retired under ADR-0049 enforce-or-remove; the one shape that is real is re-declared against what
@objectstack/coreships and core now imports it instead of declaring a twin.What is retired
IStartupOrchestrator(orchestrateStartup/rollback/checkHealth/startWithTimeout) plus the three schemas it tied together — 3 emitted defs, 8 exported names across./kerneland./contracts(11 api-surface rows).IStartupOrchestratorStartupOptionsSchema/StartupOptions/StartupOptionsParsedoptionsargument oforchestrateStartup; the kernel readsPluginMetadata.startupTimeoutandKernelConfig.rollbackOnFailureHealthStatusSchema/HealthStatuscheckHealthand the value ofPluginStartupResult.health— no probe system has ever existed behind eitherStartupOrchestrationResultSchema/StartupOrchestrationResultorchestrateStartupreturned; no implementation, so no aggregate was ever builtRoute 3 of the
spec-property-retirementplaybook: no authored document carried any of the three defs, so there is no seam for a D2 conversion and no author to hand a tombstone to.RETIRED_DEFS_BY_MAJOR[18]plus the D3 semantic entrystartup-orchestrator-retiredare the declaration, and the threejson-schema.manifest/kernel.jsonkeys with their 16authorable-surface/kernel.jsonlines are deleted deliberately in this change. The build printed all three proofs:What survives, re-declared
PluginStartupResultSchema/PluginStartupResulton both entries, rewritten to the shapeObjectKernel.startPluginWithTimeout()has always returned:pluginName,success, optionaldurationMs,timedOut, anderroras the serializable projection.packages/core/src/plugin-loader.tsno longer declares an interface — it re-exports the spec type, so the two cannot drift again.Three keys of the surviving def take the tombstone route rather than a bare deletion, registered in
RETIRED_KEYS_BY_MAJOR[18], because this def keeps emitting and its type is now imported by@objectstack/core— thetscchannel makes the prescription receivable even though nothing parses the schema:plugin(a nested plugin object the kernel never built) — writepluginNamehealth(aHealthStatusnothing ever filled) — delete itstartTime— see belowThe one member the ruling left open, and what decided it
The ruling did not settle whether core's deprecated
startTimeis mirrored or tombstoned, and neither does the playbook:startTimewas never on the spec surface, so "retirement" was not its frame. A repo gate decided it. Mirroring it redscheck:duration-unit-keys(maintainer ruling B on #14478, no baseline, no allowlist):Neither of that rule's two schema-declared exemptions fits: it is not an
EpochMsinstant (it holds an elapsed duration, which is exactly the confusion the rule separates) and it mirrors no external standard. The remaining options were to rename it tostartTimeMs— minting a spelling nothing has ever produced, for a member core's own TSDoc calls "slated for removal" — or to end the ADR-0087 L1 window here. This PR ends it: the spec tombstonesstartTime, andObjectKernel.startPluginWithTimeout()stops setting it.startTimeMsrename in both packages; say so and it is a one-commit follow-up.Evidence
Consumers, re-measured on this card (the ruling's assertion was written 2026-09-06; the prerequisite was to re-verify it):
827cacbf4b: zero files outsidepackages/spec/srcnameStartupOptionsSchema,HealthStatusSchemaorStartupOrchestrationResultSchema.IStartupOrchestratorandorchestrateStartuphit 3 and 5 files — every one a releasedCHANGELOG.mdor a generated artifact (api-surface/contracts.json,export-origins/contracts.json). Same-corpus controls on the same query lit up:defineStack347 files,ManifestSchema62,PluginStartupResult15..objectui-sha53ded82bf7: all six symbols zero, controlsdefineStack27 files andManifestSchema6.cloudis NOT MEASURED — not attached to this session, so no reading exists for it.Reverse verification (this is a cross-package type change, so the point is proving core reads the rebuilt
.d.tsrather than a cache). From the committed state,health: { healthy: true, checkedAt: 1 }was inserted into the success-path literal inpackages/core/src/kernel.ts, proven on disk by occurrence count and bygit hash-objectdiffering from the HEAD blob:The tombstone's
nevercrossed the package boundary and the restore reproduced the HEAD blob byte for byte (git diff HEADempty).Same-major bookkeeping. The
timeout/timestamp/totalDurationrename registrations that #15678 and #15676 landed in this same unreleased major 18 are KEPT, not absorbed. The playbook's absorption clause is motivated by the conversion table's fixture-disjointness contract, and those three entries record "No D2 conversion" themselves, so that mechanism does not exist here. The tree's own answer agrees: 14RETIRED_KEYS_BY_MAJOR[18]entries already name keys whose def is retired in the same major 18 (the #15513 incident-response / training / change-management family). Gate (b2) states this steady state explicitly — "an entry naming a key the build no longer emits at all is NOT an error".Acceptance notes
title: Startup Orchestrator/description: Startup Orchestrator protocol schemas. Those two strings are derived from the module FILENAME bybuild-docs.ts; the page BODY is rewritten and now describes the result contract. Renamingstartup-orchestrator.zod.tswould move the docs route, theexport-originspaths and the pin file's module slot, and the ruling names the file to edit rather than to move — so it is noted here, not done.content/docs/references/index.mdxmoves 1525 → 1522 schemas and the Kernel row 162 → 159 — the whole-def-removal reading the playbook's ratchet-visibility table predicts, and the counter-evidence to a zero-delta run.packages/spec/src/type-alias-convention.pin.test.tsdropsIso467/Iso469with their schemas (785 → 783 pins);Iso468stays, still isomorphic — every new member is.optional()with no default and no transform.Generated by Claude Code