Repository navigation
fix(console): the catalog's org install waits until its app is served before refreshing (objectui#12097) - #12115
Conversation
… before refreshing (objectui#12097) On a cloud-managed environment, doOrgInstall pulsed emitMetadataRefresh the moment installPackage answered, inside the runtime's stale-while-rebuild window, so every mounted MetadataProvider re-read and persisted the pre-install app list. The cloud branch now runs waitForServedApp (objectui#12087) on the row's manifest_id and pulses only once the app is served; the strip shows the package page's deploying, served and timeout states under the package's name. The installLocal branch is unchanged. Claude-Session: https://claude.ai/code/session_01AswpQDLCKiZos2jCXknwKz Co-authored-by: Claude <noreply@anthropic.com>
…2097) Claude-Session: https://claude.ai/code/session_01AswpQDLCKiZos2jCXknwKz Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Inputs read: card #12097 (body and all four comments: triage ① Derived judgmentsEvery accept-set or public-surface change the diff implies, each judged right or wrong:
② Semver level
③ Boundary flags
Check-runs on Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #12097
Clause-②: yes
What changed
On a cloud-managed environment, the catalog page's org install (
doOrgInstallinpackages/app-shell/src/console/marketplace/MarketplacePage.tsx) sentemitMetadataRefresh()the momentinstallPackageanswered. That answer lands inside the runtime's stale-while-rebuild window, so every mountedMetadataProviderre-read and persisted the pre-installapplist. This is the package-page defect from objectui#12087, on the "Your organization" Install button.waitForServedAppfrom objectui#12096, unchanged, with the row'smanifest_id. It sends the pulse only once an app whose_packageIdis that manifest id is served. There is no second copy of the wait: the interval and the bound are the module's defaults (SERVED_APP_POLL_INTERVAL_MS,SERVED_APP_WAIT_CAP_MS). As on the package page, the wait is not awaited and not cancelled on unmount.marketplace.install.deployingwhile it waits;marketplace.install.deployedplusmarketplace.install.openApponce the app is served;marketplace.install.deployTimeoutplusmarketplace.install.checkAgainwhen the wait expires. The cloud branch no longer saysmarketplace.org.installed("Installed NAME") before the app is served, and on expiry it refreshes nothing.installLocalbranch is unchanged. The runtime hot-registers the package before it answers, so the strip says "Installed NAME" and pulses at once, as before. The test pins this as its CONTROL.Measured before building (the dispatch's mechanism assumptions)
main, the cloud branch refreshes at once: confirmed. The new pin fails againstmain'sMarketplacePage.tsx(base5330afd1f). The bus carried one pulse at virtual t=0, and at that point the runtime was not serving the app yet:AssertionError: expected [ { at: +0, served: false } ] to deeply equal []Tests 2 failed | 1 passed (3). The CONTROL is green onmain.waitForServedAppcan be called unchanged with the row'smanifest_id: confirmed at the producer.GET /api/v1/cloud-connection/org-packagesin objectstackpackages/cloud-connection/src/cloud-connection-plugin.ts. That proxy passes the control plane'sitemsthrough unchanged.packages/service-cloud/src/routes/package-publish.ts(GET /cloud/org-packages) asmanifest_id: p.manifest_idfromsys_package. That field is declaredrequired: trueandreadonly: truein cloudpackages/service-tenant/src/objects/sys-package.object.ts.OrgPackageSummary.manifest_idis already a requiredstring. No fallback was added. TheinstallLocalbranch'spkg.manifest_id || pkg.idis untouched.marketplace.installkeys from objectui#12096 say exactly what this install's states mean. The one thing the catalog adds is which package the status is about, because the strip lists several packages and the status sits above them all. That is supplied as the row's display name, drawn as the status box's title (the same rawdisplay_namethe org card already renders), not by a second, NAME-carrying copy of each sentence in ten packs. The meaning is shared and the copy is shared, so the two pages cannot drift.@object-ui/i18ndoes not move and the changeset declares@object-ui/app-shellpatch only.Route versus the suggested one
The dispatch suggested mirroring the package page's "drop the seed,
refreshMetadata(), pulse". This PR keeps the catalog page's own post-install action, the pulse, and moves it behind the wait. This page did not drop the seed or callrefreshMetadata()before, and the pulse already makes every mounted provider refresh and re-persist, this page's own included. Adding those two would be a second behaviour change beyond the card's scope, which is "Before the pulse, the org install waits onwaitForServedApp". The test asserts on the seed the real provider writes: after the install, noappseed write ever lacks the installed app.Clause-②
@object-ui/app-shellentry's exports, props and types are unchanged, andMarketplacePagekeeps its signature.marketplace.installkeys (deploying,deployed,deployTimeout,openApp,checkAgain).Clause-②: yesstays as the claim declared it, for the seat's contract review to record.Evidence (head
b32312a)MarketplacePage.orgInstallWaitServed-12097.test.tsxruns the realwaitForServedAppat its real interval and bound. Onlynowandsleepare swapped, and the test opens each sleep one interval at a time. It mounts the realMetadataProviderand the real assistant bus over a runtime that serves the pre-install list for 60 s of virtual time after the install.SERVED_APP_WAIT_CAP_MS. The status readsdeployTimeout, with no pulse, no success and no seed write. "Check again" finds the app on its first read.installLocalpulses at once, starts no wait and says "Installed Acme CRM".ablation-replace.mjs, which replaced the wait call withemitMetadataRefresh();. Anchor count went 1 to 0, and the blob wentb3073eed036dto244d714449af.Tests 2 failed | 1 passed (3). The two waits are red and the CONTROL is green.git diff HEADis empty.pnpm --filter @object-ui/app-shell type-checkexit 0. The output echoestype-check, and--listFilesOnlyconfirms thattsconfig.test.jsoncovers the new test file.pnpm exec vitest run packages/app-shell/src/console/marketplace/ packages/i18n/src/__tests__/marketplace-preview-namespace-3546.test.tsx:Test Files 28 passed (28),Tests 222 passed (222). The i18n file reads this page's source.check:*gate the dispatch named exited 0, pluscheck-changeset-fixed,check-changeset-overwrite,check:unreferenced-sourcesandcheck-governed-queue-guard --test(NOT GOVERNED).vite buildfor base and head, on the same tree exceptMarketplacePage.tsx.MarketplacePageis in no eager chunk.check:eager-closureandcheck:eager-locale-cataloguesboth exit 0.--format json): 0 errors and 2 warnings. Both warnings are pre-existing, and the base file shows the same two rules.--print-configfor both files:parserOptionsis empty, so type-aware linting is off and this diff cannot change the verdict on any untouched file.pnpm lintbelongs to CI.Acceptance notes
readAppsclosure is the same three lines as the package page's. A shared reader would editMarketplacePackagePage.tsxorwaitForServedApp.ts, which is outside this claim. Carrier: objectui#12098, which edits aroundwaitForServedApp.ts. 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 #12098 is not addressed here, and this page inherits its version-aware predicate if it lands inwaitForServedApp.Session:
https://claude.ai/code/session_01AswpQDLCKiZos2jCXknwKz(dispatchedos-dev,domain:uiseat 1).Generated by Claude Code