Repository navigation
fix(cloud-connection): install-local's hotLoaded reports what the hot-register did - #22728
Conversation
…-register did The install answer wrote hotLoaded: true as a literal, so a cloud-fetched manifest whose manifest.register threw (the lenient path) answered 200 claiming the running kernel held a package it did not. Step 3 now records its outcome: hotLoaded is false on the lenient path, the register error's message rides beside it as hotLoadError, and the note stops saying the app is now available. The lenient path itself is unchanged. Claude-Session: https://claude.ai/code/session_019SvPnd2bzECRNmAU9i6E4k Co-authored-by: Claude <noreply@anthropic.com>
…path, with both controls Claude-Session: https://claude.ai/code/session_019SvPnd2bzECRNmAU9i6E4k Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019SvPnd2bzECRNmAU9i6E4k Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 3 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 3f6011ab77e7a5e6604276db8b5e084b2495a3e8 && git checkout 3f6011ab77e7a5e6604276db8b5e084b2495a3e8
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 762db996ad2e353d2ddfc553aba11cd6ca67b57f 58fccccf2ca08bac444709973ffa705b0b89adc8 && git checkout -B drift-repro 762db996ad2e353d2ddfc553aba11cd6ca67b57f && git merge --no-ff 58fccccf2ca08bac444709973ffa705b0b89adc8
node scripts/docs-audit/affected-docs.mjs --json 762db996ad2e353d2ddfc553aba11cd6ca67b57f |
Fixes #22695
Clause-②: no
What changes
POST /api/v1/marketplace/install-local(packages/cloud-connection/src/marketplace-install-local-plugin.ts) answeredhotLoaded: trueas a literal. A cloud-fetched manifest whosemanifest.registerthrows takes the lenient path: the install writes its ledger entry and answers200, and the package loads at the next restart. That answer told its caller the running kernel already held the package.hotLoadError, stays undefined exactly whenmanifest.registersucceeds. The answer'shotLoadedis derived from it, never written as a literal.hotLoadError(string): the register error's message. It is present only besidehotLoaded: false. On a hot-loaded install it is omitted (notnull), so the success answer's key set is unchanged. console: install answers say what the runtime served —hotLoaded: falsereads "installed, loads at the next restart", and an upgrade waits for the new version's app objectui#12098 readshotLoaded, and can showhotLoadErroras the reason.notefollows the same record. It makes the same claimhotLoadeddoes, so on the lenient path it no longer says "App is now available in this runtime". It says the package is installed and cached, the running kernel could not load it, and the runtime registers it again at its next restart.422 PLUGIN_REGISTER_FAILED, and nothing is written. Whether the lenient path should stay lenient is ⛔ not decided here (triage6098821061: "Not in scope").The lenient path's answer, abridged:
{ "success": true, "data": { "manifestId": "com.example.crm", "hotLoaded": false, "hotLoadError": "MESSAGE", "upgradedFrom": null, "note": "App installed and cached on this runtime, but the running kernel could not load it (`hotLoadError` says why). The runtime registers it again at its next restart." } }MESSAGEstands for the register error's message, passed through as the register threw it.Premise check, against
origin/maineae3368aemanifestService.register(manifest)is awaited at:1257; an inline manifest's failure answers422 PLUGIN_REGISTER_FAILEDat:1266; a cloud-fetched manifest's failure only warns at:1269;hotLoaded: trueat:1348is a literal.notLoadedmarker ({ code, requiredRange }) records only an entry the boot rehydrate refused on the protocol handshake. The DELETE answer'scleanups[].errorrecords a failed withdrawal from the running kernel. Neither is this event.marketplace-install-local-jobs.test.tsis about a package's declared scheduled jobs. It readshotLoadedonly as one key of the success answer's key set (Object.keys(data)). That set is unchanged here, and the pin stays green.Tests
packages/cloud-connection/src/marketplace-install-local-hot-loaded.test.ts, 3 cases. It imports the plugin from source, not throughdist.200,hotLoaded: false,hotLoadErrorequal to the thrown message. The ledger entry is written, and the note does not say "now available".hotLoaded: true, with nohotLoadErrorkey.422 PLUGIN_REGISTER_FAILEDin the declared envelope, and nothing is written.83034b34c.scripts/ablation-replace.mjsputhotLoaded: true,back (anchor hits 1 then 0, blob10b51ce6aaecthencb1f247d39c6). Result:Tests 1 failed | 2 passed (3). The lenient case went red (expected true to be false), and both controls stayed green. Restore: blob equal to HEAD (10b51ce6aaec),git diff HEADempty.pnpm --filter @objectstack/cloud-connection test:Test Files 43 passed (43),Tests 517 passed (517).pnpm --filter @objectstack/cloud-connection typecheck: exit 0.tsc --noEmit -p tsconfig.test.json --listFileslists the new test (43 package test files in the program).83034b34c.58fccccf2adds only the changeset, so the code is byte-identical.Gates, at
58fccccf2(this PR's head)dispatch-gates --ran: 64 derived, 64 run, 0 NOT-MEASURED, all exit 0.check:dual-build-cjs-loadsfirst answered PREREQUISITE NOT MET (nodist/). After a fullpnpm build(72 of 72 tasks) it passed: 107 require entry points across 66 packages.pnpm lint, narrowed to the touched files, with the narrowing proven:--print-configapplies 3 active rules to the plugin file and 5 to the test. eslint ignores the changeset.md: "File ignored because no matching configuration was supplied".--format json: 3 results. The 2 linted files have 0 errors and 0 warnings.parserOptionsis{ ecmaVersion, sourceType }, with noprojectorprojectService, the only parser is@typescript-eslint/parser, and every rule is per-file. So this diff cannot move an untouched file's verdict.Acceptance notes
notLoadedmarker. The marker covers only a protocol refusal at rehydrate, and a rehydrate whose register throws is not marked either. Carrier: none.syncSchemas, binds handlers, seeds and announces the hot install, over a kernel that does not hold the package. This belongs to the open question of whether the lenient path stays lenient, and is not touched here.os package installprints "Package installed into the running kernel" for any200, whateverhotLoadedsays (packages/cli/src/commands/package/install.ts:224). I measured it with a scratch probe that ran the command against a stubbed answer carryinghotLoaded: false. The probe is not committed.Generated by Claude Code