fix(cli): os migrate plan/apply compose the requires-supplied provider a connector hard-depends on - #21739
fix(cli): os migrate plan/apply compose the requires-supplied provider a connector hard-depends on#21739objectstack-fleet[bot] wants to merge 3 commits into
Conversation
…r a connector hard-depends on Claude-Session: https://claude.ai/code/session_018zT8d8NpiQ1ExhuNd5TxY6 Co-authored-by: Claude <noreply@anthropic.com>
…ma-migration boot Claude-Session: https://claude.ai/code/session_018zT8d8NpiQ1ExhuNd5TxY6 Co-authored-by: Claude <noreply@anthropic.com>
… schema-migration boot Claude-Session: https://claude.ai/code/session_018zT8d8NpiQ1ExhuNd5TxY6 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 27 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 8079f1b2581ed44c25103ad9381edfa85641406f && git checkout 8079f1b2581ed44c25103ad9381edfa85641406f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 316be321ef2405a12e8d016f07b25fad7039b4f9 826d5ce44b4ef79a3874533e703b5db2f73f3c51 && git checkout -B drift-repro 316be321ef2405a12e8d016f07b25fad7039b4f9 && git merge --no-ff 826d5ce44b4ef79a3874533e703b5db2f73f3c51
node scripts/docs-audit/affected-docs.mjs --json 316be321ef2405a12e8d016f07b25fad7039b4f9
|
Fixes #21732
Clause-②: no
What was wrong
os migrate planandos migrate applyexited 1 on a freshcreate-objectstack -t blankapp and onexamples/app-showcase:The four connectors (
connector-rest,-openapi,-mcp,-slack) declaredependencies = ['com.objectstack.service-automation']. The blank template and the showcase ask for automation only throughrequires: ['automation', …].os serveturns that token into the provider throughServe.CAPABILITY_PROVIDERS.buildSchemaMigrationPluginscomposedconfig.pluginsand never readrequires, so the kernel could not order the boot.What changed
packages/cli/src/utils/schema-migration-plugins.tsgainsresolveRequiredProviders, called once the host config has loaded:serveuses. That meansServe.CAPABILITY_PROVIDERS, exact identity matching throughServe.providesCapability, and the rule that an explicit instance inpluginswins. It keeps no second copy of the token table.requiresplus the always-on slateserveappends (Serve.ALWAYS_ON_CAPABILITIES). The search runs to a fixed point, so the provider's own hard dependencies resolve the same way. A dependency that no token supplies is left to the kernel, which refuses it the same wayos servedoes for that config.DECLARATION_PROVIDER_POSTUREShas one row today: automation, constructed{ armRuntime: false, packageRoot }. Inert mode brings the engine and the node registry up. It then registers no flow, binds no trigger or job, materializes no connector and resumes no suspended run.suspendedRunStore: 'memory'. In inert mode the store is never attached, becausestart()returns before that step. The default is what makesinit()declaresys_automation_runandsys_flow_dispatchbesidesys_flow_credential. Those are the tablesos servecreates for this capability, so the plan now covers them.start()inside a dry run with a posture nobody measured.serve's module.Which other
requirestokens get this treatment: only automation, because it is the one provider any shipped plugin hard-depends on (checked withgit grepoverpackages/**/srcfor plugindependenciesdeclarations). Composing every declared provider would boot the served tier inside a dry run (triggers,email,approvalsand so on, each with its ownstart()). That is out of scope here; see Acceptance notes.Proof: the two apps, run under
/tmpThe CLI was built at
826d5ce44. The showcase was copied to/tmp/i21732/showcaseand a blank app was scaffolded at/tmp/i21732/qa(create-objectstack qa -t blank --skip-install). Each copy'snode_modulesis a symlink to the workspace'sexamples/app-showcase/node_modules, so each resolves this tree's packages. Every run used a freshfile:/tmp/i21732/*.db.316be321e)planplan --jsonapply --json --yesplan --jsonDependency … not found for plugin 'com.objectstack.connector.openapi'… for plugin 'com.objectstack.connector.rest'Nothing is armed. The plan prints
[Automation] inert mode (armRuntime: false) — … no flow registered, no trigger or schedule armed, no connector materialized, no suspended run resumed. Afterapply, every table in both databases has zero rows (35 tables in the showcase database, 13 in the blank one). The composition note reads:Composed AutomationServicePlugin for `requires: ['automation']` — 'com.objectstack.connector.openapi' depends on it — in its declaration posture (engine up, nothing armed).Tests
src/utils/schema-migration-plugins.test.ts(unit tier) has five new cases:packageRoot, andtriggersis not composed;pluginswins and no second instance is composed;job, from the always-on slate) is refused by name.src/utils/schema-migrate.requires-providers.integration.test.ts(integration tier, because it importsbootSchemaStack) runs a real kernel boot of a config withrequires: ['automation']and a plugin that hard-depends on the provider. The pin checks four things: the dependentinit()ran after the provider's (it registers a connector provider factory), the config's flow is not registered (listFlows()is[]),sys_automation_runandsys_flow_dispatchare in the object set, and the composition note is printed.scripts/ablation-replace.mjs. The mutation replacedplugins.push(...resolved.plugins);with a no-op. The anchor went from 1 to 0 and the blob from1ad093cfbcd6to7465100641c3. Both new pins went red: the unit composition case, and the integration boot with exactly the defect's message,[Kernel] Dependency 'com.objectstack.service-automation' not found for plugin 'com.example.os21732.connector'(2 failed, 36 passed). After the restore, the blob equals HEAD andgit diff HEADis empty. The suite readssrcdirectly, so nodistleg applies.Local gates (head
826d5ce44)pnpm --filter @objectstack/cli typecheck: exit 0 (tsc --noEmitpluscheck:test-typecheckOK).pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2: 3776 tests, 11 failed in 3 files on the first run. Two of those files,published-subpath-console.pinandpublished-subpath-hook-body.pin, read the packed.d.ts, and this worktree's CLIdisthad been built withOS_SKIP_DTS=1. The third was a 5000 ms timeout inhook-timeout-override-refusal, on a shared box. After a real-typespnpm --filter @objectstack/cli build, all three files pass (33/33). The integration tier is left to CI, except for the new file, which ran locally and passed.node scripts/pm/dispatch-gates.mjs --commands: 64 derived commands, all run. The final exit is 0 for all 64.check:dts-closureandcheck:dual-build-cjs-loadswere red on the first pass for the sameOS_SKIP_DTSreason and went green after the real-types build.--ranreconciliation: 64 derived, 64 run, 0 unrun..tsfiles. I raneslint --no-inline-config --format json: 3 files, 0 errors, 0 warnings. The lint population comes fromeslint.config.mjs(eslint ., and these files are not ignored). The narrowing does not hide anything: this config never enables type-aware linting (noparserOptions.project, as the config itself states), so this diff cannot change the verdict on any file it does not touch. The fullpnpm lintrun is CI's.Acceptance notes
approvals,messagingandwebhooksinrequires, and their providers registersys_approval_*,sys_inbox_message/sys_notification_*andsys_webhook. The showcase plan above lists none of them, because this composition boots only the providers the kernel cannot order without.os servecomposes all of them. Whether the plan should declare every requires-supplied provider's objects, and if so what posture would be safe for eachstart(), is a separate decision.WARN [ObjectQLPlugin] sys_metadata_activation is registered but could not be read, the federated-bindingERRORfor the showcase's two external objects, andCore service missing: auth, job. They come from the platform plugins on a database that does not exist yet, and this diff does not introduce them.node_modulesrather than installing published17.6.0. That way the run measures this tree's CLI and connectors.Generated by Claude Code