Repository navigation
fix(runtime): on a kernel-resolver host, resolve a service on the request's own kernel first — record-bound POST /actions stops answering 404 RECORD_NOT_FOUND - #22945
Conversation
…a kernel-resolver host On the hosted multi-tenant runtime the host kernel holds singleton protocol / metadata / objectql, and a singleton answers getServiceAsync(name, scopeId) whatever the scope. resolveService asked that host lookup first, so every dispatcher door passing context.environmentId read the host's data plane: POST /actions/:object/:action loaded its subject row through a protocol that registers none of the environment's objects (OBJECT_NOT_FOUND, swallowed by loadActionSubjectRecord) and answered 404 RECORD_NOT_FOUND for a row the caller can read. The REST data routes never had this, because RestServer.resolveProtocol takes the environment kernel's own protocol. A request whose kernel is not the default kernel now resolves on that kernel first and falls back to the host's scoped lookup. A host with no request kernel of its own resolves exactly as before. resolveServiceOrLoud (the identity step's tenancy read) keeps its order on purpose. Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 8 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 26 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 ec2e2e8b868ab96f2aa57ffdbc46b33cb7916f94 && git checkout ec2e2e8b868ab96f2aa57ffdbc46b33cb7916f94
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e357a441c6423798a673d4a39ecee4b31adacf79 62ca3debd456b4b313fa21c4c3e9e2b0968b76f1 && git checkout -B drift-repro e357a441c6423798a673d4a39ecee4b31adacf79 && git merge --no-ff 62ca3debd456b4b313fa21c4c3e9e2b0968b76f1
node scripts/docs-audit/affected-docs.mjs --json e357a441c6423798a673d4a39ecee4b31adacf79
|
Fixes #22944
Clause-②: no
What was wrong
On a
kernel-resolverhost (cloud's hosted objectos),HttpDispatcher.resolveServiceasked the host kernel's scoped lookup first. The host kernel's singletonprotocol/metadata/objectqlanswergetServiceAsync(name, scopeId)whatever the scope. So every dispatcher door that passescontext.environmentIdread the host's data plane.The user-visible break:
POST /api/v1/actions/:object/:actionwith arecordId.OBJECT_NOT_FOUND).loadActionSubjectRecordswallowed that error.refuseDeniedSubjectLoadanswered404 RECORD_NOT_FOUNDfor a row the caller reads withGET /data/:object/:idin the same session.The REST data routes never had this, because
RestServer.resolveProtocoltakes the environment kernel's ownprotocol. Measured on a hosted-shape rig at cloud v1.36.0's pins; the full reading is on #22944.The change
resolveServicegains one leg. When the request carries a kernel of its own (kernel !== this.defaultKernel), it resolves there first (kernel.getServiceAsync(name, scopeId)), then falls back to the existing chain.resolveServiceOrLoud/classifyServicekeep their order on purpose. That lookup decides which kernel'stenancyanswers the identity step, which is a separate question, so its docblock now says so.Evidence
http-dispatcher.request-kernel-first.test.ts, 7 cases on realObjectKernels.callDatadelivers the row.RECORD_NOT_FOUND.http-dispatcher.tsrestored tomain, the two cases that carry the defect go red (2 failed / 5 passed); with the fix, 7 of 7 pass.@objectstack/runtimesuite: 359 files, 5884 passed, 19 skipped.main, framework56bf27affb, objectuid758f2f20d, HotCRM 4.0.1; the same change patched into the rig's runtime build):converted, with its account, contact and opportunity created.objectql,metadata,protocol. Nothing else.Downstream
The consumer is objectstack-ai/hotcrm#2065. It unlocks when cloud's framework pin carries this fix, which cloud tracks separately.
Generated by Claude Code