You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
marketplace: after a successful install, the console refreshes the app list while the runtime still serves the pre-install kernel (stale-while-rebuild, ~60 s) and caches it — the installed app does not appear #12087
The maintainer installed HotCRM 3.1.0 from the environment console's marketplace page (staging os-6hf1jq.objectos.app, /_console/apps/setup/system/marketplace/pkg_7d6823ee-…) on 2026-10-10 at 08:53Z. About 27 minutes later they reported 「我刚在环境中安装了 hotcrm,但是没显示出来」: HotCRM did not show among their apps.
control plane sys_package_installation / sys_environment
08:53:25
[KernelManager] kernel demoted to stale — serving it while the rebuild runs {reason: "freshness"}
staging container log
08:54:25
new kernel ready. From here GET /api/v1/meta/app returns HotCRM (crm_enterprise)
container log
08:59:54
sample-data replay finished in the background (9 accounts etc.)
log + sample_data_seeded_at
09:2xZ
a fresh browser tab shows HotCRM first under 「我的应用」 (3 apps), with 18 crm_* objects
reading
So the install worked. The runtime served the pre-install kernel for ~60 s after the install answered, by design: stale-while-rebuild.
Why the console most likely kept showing the old list
MarketplacePackagePage.tsx (~:337–365 on main):
On a successful install it clears objectui:metadata:* from sessionStorage and calls refreshMetadata()immediately.
That refetch lands inside the stale-serve window above, so the fresh app list is the pre-install one.
MetadataProvider then caches that list: in memory with a 5-min TTL, and in the tab's sessionStorage seed (objectui:metadata:app:<org>:<principal>), which a reload in the same tab reads back first.
The result: the console shows the success toast while the app never appears, until the cache is refetched or the tab is closed.
This is inferred from the timeline and the code. It was not reproduced end-to-end in the maintainer's own tab.
Repro for the dev:
On a staging or local stack whose rebuild takes more than a few seconds, install an app from the env console's package page.
Without closing the tab, open 「我的应用」 and the app switcher at +10 s, +90 s and after a same-tab reload.
Compare with a new tab.
Fix direction (for the dev to measure)
Hold the refresh until the app is served. After a successful install into the current environment, poll GET /meta/app (bounded, e.g. every 5 s for up to 3–5 min) until the installed package's app appears, and only then refresh and persist the metadata cache.
Tell the user what is happening. Show a 「正在部署应用…」 state rather than declaring success while the app is absent. Once it appears, offer 「打开 HotCRM」.
Never persist an app list fetched during the window in which the install is known not to be served yet.
Alternative: the runtime exposes "rebuild pending" (the freshness state it already logs) so the console can wait on that instead of on the list.
Found by the maintainer's session 64cb68a6-01c7-4dc6-acc6-ccb459c90ef2.
Triage: lane added, bug · priority:p2 (kept) · domain:ui · area:studio, in pm:queue
Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-10T09:52Z. ⛔ Not a claim, ⛔ not a dispatch.
Lane:MarketplacePackagePage.tsx and MetadataProvider's cache are the console's, so domain:ui.
Why p2, as filed: an install that succeeded looks failed in the same tab until the cache is refetched or the tab closes.
Direction, the card's own first two items:
After a successful install into the current environment, wait until GET /meta/app serves the installed package's app. Poll on a bound, with no fixed sleep. Only then refresh and persist the metadata cache.
Show 「正在部署应用…」 until then, and offer 「打开 」 when it appears.
⛔ Never persist an app list fetched while the install is known not to be served.
⛔ No console-side guess at the rebuild duration.
The alternative (the runtime exposes "rebuild pending") is a server surface in objectstack or cloud. It is only worth filing if the bounded poll cannot tell "still rebuilding" from "install did not take". The claimant states which it measured.
Measure first: the repro in the card, on a stack whose rebuild takes more than a few seconds.
Claim: PM loop round 1
Session: session_01B1gHb9baeX7oioD5sHVm7z
Account: marchtian
Branch: claude/issue-12087-install-wait-served
Worktree: objectui-issue-12087
Domain: domain:ui
Seat: domain:ui#3
File surface: packages/app-shell/src/console/marketplace/MarketplacePackagePage.tsx (the post-install refresh, about :337–:440); packages/app-shell/src/providers/MetadataProvider.tsx (only the cache write, if the "never persist a list fetched in the stale window" rule must live there); their tests; locale entries in packages/i18n/src/locales/ for the deploying state (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: default tier — dispatch-gates --tier --repo objectstack-ai/objectui: "no path-derived mandate"
Clause-②: yes
Responsibility: objectui's app-shell MarketplacePackagePage.tsx, which refreshes and persists the app list immediately after a successful install, inside the runtime's stale-while-rebuild window | none: the runtime exposes no "rebuild pending" signal today (card body), and the bounded poll is the console-side path | every environment administrator who installs a package from the console marketplace; measured by the maintainer on staging on 2026-10-10
Thread-read: 6096303824
Serial constraints cleared: none. The in-flight branches of objectui#12079, #12081, #12082 and #12086 (diff file lists read at this claim) touch neither MarketplacePackagePage.tsx nor MetadataProvider.tsx. objectui#12081's branch touches only a Home test named HomePage.marketplaceDisabled.test.tsx.
Clause-② is declared yes because the deploying state's copy (「正在部署应用…」, 「打开 APP」) will add published locale keys. The in-seat review needs that reason.
{
"issue": 12087,
"status": "done",
"branch": "claude/issue-12087-install-wait-served",
"pr": "https://github.com/objectstack-ai/objectui/pull/12096",
"session": "session_01B1gHb9baeX7oioD5sHVm7z (subagent mode: the parent seat's id)",
"premise_still_valid": true,
"summary": "After a cloud install into the environment this console renders, MarketplacePackagePage no longer refreshes in the stale-while-rebuild window. It reads GET /meta/app through the SDK client, past the metadata cache: at once, then every SERVED_APP_POLL_INTERVAL_MS (5 s) up to SERVED_APP_WAIT_CAP_MS (5 min), until an app's _packageId equals the package's manifest_id. Only then does it drop the objectui:metadata:* seed, run refreshMetadata() and emit the bus pulse; the pulse was a second in-window persist. Until then it shows 「正在部署应用…」 (dialog, then the page once the dialog is closed). Once served it offers 「打开 {name}」; on expiry it gives an honest timeout with 'Check again', and refreshes nothing. MetadataProvider is untouched. The wait is not cancelled on unmount. Suggested bindings mount only once the app is served. A cross-environment install keeps the old success text but no longer refreshes this console. Five locale keys are added in all ten packs. Measured by code read: the install-local and uninstall-local refresh sites have no rebuild window (the producer hot-registers or withdraws before answering), so they are unchanged. The console cannot tell 'still rebuilding' from 'did not take', from 'no app', from 'withheld', or an upgrade's old app from its new one; that condition triggers the server finding below. Live repro NOT MEASURED: there is no stale-while-rebuild stack in this container (the KernelManager lives in cloud), and staging needs the maintainer's session.",
"tests": "All at 8810eb080, through os-verify-lock. (1) pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/console/marketplace/ packages/i18n/ -> 'Test Files 108 passed (108)', 'Tests 1488 passed | 13 skipped (1501)'; this includes the new waitForServedApp-12087.test.ts (6 cases, virtual clock) and MarketplacePackagePage.waitServedApp-12087.test.tsx (3 cases: real MetadataProvider and real assistant bus; every app-seed sessionStorage write recorded; stale window / expiry + Check again / CONTROL served at once with no sleep). (2) Ablation via objectstack scripts/ablation-replace.mjs, wrap mode, putting back the pre-fix in-window refreshMetadata() + emitMetadataRefresh() at the start of the wait: 'ok mutation landed: anchor 1 -> 0, blob d05b45979736 -> 2c6c92d29fea', MARKER=1 -> 'Tests 2 failed | 1 passed (3)'. Both failures are 'expected [ { afterInstall: true, …(1) }, …(1) ] to deeply equal []' (the persisted stale seed); the CONTROL stays green as predicted. Restore: 'ok restored: blob == HEAD (d05b45979736) and git diff HEAD is empty', POST_RESTORE_MARKER=0. The first ablation attempt was a no-op: its replacement contained the anchor, the tool refused before any run, and it is not counted. The tests import the page by relative path (src), so no dist preflight applies. (3) pnpm turbo run type-check --filter=@object-ui/app-shell --filter=@object-ui/i18n --concurrency=2 -> 'Tasks: 31 successful, 31 total'; both type-checks were cache misses and executed. tsc -p tsconfig.test.json --listFilesOnly lists both new test files.",
"gates": [
{
"cmd": "pnpm turbo run type-check --filter=@object-ui/app-shell --filter=@object-ui/i18n --concurrency=2",
"exit": 0,
"head": "8810eb080",
"verdict": "Tasks: 31 successful, 31 total"
},
{
"cmd": "pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/console/marketplace/ packages/i18n/",
"exit": 0,
"head": "8810eb080",
"verdict": "Test Files 108 passed (108); Tests 1488 passed | 13 skipped (1501)"
},
{
"cmd": "pnpm check:i18n-keys",
"exit": 0,
"head": "8810eb080",
"verdict": "Every in-scope call-site key resolves against the en pack (3319 keys)"
},
{
"cmd": "pnpm check:i18n-drift",
"exit": 0,
"head": "8810eb080",
"verdict": "0 en value(s) changed (5 key(s) added, 0 removed)"
},
{
"cmd": "pnpm check:i18n-dead-keys",
"exit": 0,
"head": "8810eb080",
"verdict": "report-only; no marketplace.install key among the candidates"
},
{
"cmd": "pnpm check:new-line-citations",
"exit": 0,
"head": "8810eb080",
"verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0"
},
{
"cmd": "pnpm check:control-bytes",
"exit": 0,
"head": "8810eb080",
"verdict": "check-control-bytes: OK (scanned 8291 tracked text file(s); skipped 85 binary)"
},
{
"cmd": "pnpm turbo run build --filter=@object-ui/console --concurrency=2 && pnpm check:eager-closure",
"exit": 0,
"head": "8810eb080",
"verdict": "Console eager closure is 3169.7 KB gzipped across 290 of 2474 chunks (budget: 3204.6 KB, headroom: 34.8 KB); delta vs base de302c73 = +125 B gzipped total (i18n-locale-en +136 B, others +/-7 B hash churn), eager chunk count 290 -> 290; base read by restoring the branch's sources to de302c73 + vite build"
},
{
"cmd": "pnpm check:eager-locale-catalogues",
"exit": 0,
"head": "8810eb080",
"verdict": "exit 0"
},
{
"cmd": "console build declared-lazy-views guard (inside vite build)",
"exit": 0,
"head": "8810eb080",
"verdict": "passes at 8810eb080; at 78a7a9210 it refused (MarketplacePackagePage pulled eager by MetadataProvider co-tenancy via an extractItems value import), fixed in 8810eb080"
},
{
"cmd": "eslint --no-inline-config on the 17 touched files",
"exit": 0,
"head": "8810eb080",
"verdict": "0 errors, 11 warnings, all of them already present (the same any-pattern count as the base page, plus its unused 'code')"
},
{
"cmd": "node scripts/check-changeset-presence.mjs; pnpm changeset:check; check-changeset-claims; check-changeset-overwrite; pnpm check:pending-changeset-literals",
"exit": 0,
"head": "8810eb080 (presence) / 78a7a9210 (the rest; changeset unchanged since)",
"verdict": "all exit 0"
},
{
"cmd": "check-vi-mock-specifiers / check-vi-mock-override-shape / check-vi-mock-inherit / check-test-path-roots / check:unreferenced-sources / check:side-effects-array / check:esm-specifiers / check:phantom-deps / check-type-check-coverage / check-lint-coverage",
"exit": 0,
"head": "78a7a9210 (page-only change since: readApps)",
"verdict": "all exit 0"
},
{
"cmd": "pnpm check:readme-exports",
"exit": 1,
"head": "78a7a9210",
"verdict": "NOT MEASURED: PREREQUISITE NOT MET (cli and plugin-ai dist not built in this worktree); no README or export in this diff"
}
],
"line_budget": "n/a: no skills/** or line-ledgered file touched; diff 15 files, +749/-36 at 8810eb080 vs de302c73",
"files_changed": [
".changeset/12087-install-wait-served.md",
"packages/app-shell/src/console/marketplace/MarketplacePackagePage.tsx",
"packages/app-shell/src/console/marketplace/waitForServedApp.ts",
"packages/app-shell/src/console/marketplace/__tests__/waitForServedApp-12087.test.ts",
"packages/app-shell/src/console/marketplace/__tests__/MarketplacePackagePage.waitServedApp-12087.test.tsx",
"packages/i18n/src/locales/ar.ts",
"packages/i18n/src/locales/de.ts",
"packages/i18n/src/locales/en.ts",
"packages/i18n/src/locales/es.ts",
"packages/i18n/src/locales/fr.ts",
"packages/i18n/src/locales/ja.ts",
"packages/i18n/src/locales/ko.ts",
"packages/i18n/src/locales/pt.ts",
"packages/i18n/src/locales/ru.ts",
"packages/i18n/src/locales/zh.ts"
],
"deviations": [
"New file packages/app-shell/src/console/marketplace/waitForServedApp.ts, outside the claim's listed file surface. It is a new sibling module that no other claim can hold. It keeps the helper exportable without a react-refresh only-export-components warning, and it lets the page test shrink the bound. The claim surface is otherwise kept: MetadataProvider.tsx is untouched and MarketplacePage.tsx is untouched.",
"Two behaviour changes beyond the refresh, both in the same file. (a) SuggestedBindingsPanel mounts once the app is served instead of on install success; it reads the runtime once, on mount. (b) A cross-environment install no longer refreshes this console's own metadata or pulses the bus; the old comment called that a harmless no-op.",
"Resumed after the 429 kill: the branch was fast-forwarded from 4d0ff233 to origin/main de302c73 before any code (BASE de302c73); pnpm install was re-run in the foreground (exit 0).",
"Clause-② yes is confirmed by the diff: five new published keys under marketplace.install in all ten packs; no en value changed."
],
"mcp_calls": "0",
"api_writes": "3 — all REST writes through the fleet-write relay (each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) pr_create POST /repos/objectstack-ai/objectui/pulls (draft) -> #12096, read-back 11512 bytes sent = 11512 stored; (2) label-write --assign marchtian -> POST /repos/objectstack-ai/objectui/issues/12096/assignees, read-back MATCHES; (3) this os-dev-report comment POST /repos/objectstack-ai/objectui/issues/12087/comments. git push x3 is not a REST write.",
"cleanup": "worktree /home/user/objectui-issue-12087: node_modules removed and git worktree remove succeeded without --force; no background process of mine alive; remote branch = 8810eb0805da410c54ebdb76a207947c74f7aab3",
"open_questions": [],
"out_of_scope_findings": [
"class: triage-conditional server surface (triage 6096303824: 'only worth filing if the bounded poll cannot tell \"still rebuilding\" from \"install did not take\"'), not a/b/c · reach: not measured live (no stale-while-rebuild stack here); condition established by code read of objectstack 86da1949 · evidence: the console has no read that tells them apart. /meta/app lacks the app in both cases. /cloud-connection/installation answers installed:true in both (cloud-connection-plugin.ts, GET /installation). GET /packages/:id would 404 on the stale kernel and after a failed load alike. filterAppForUser withholds apps by requiredPermissions/requiresService with the same absence. For an UPGRADE, objectql registry.registerItem calls applyProtection(item, { packageId }) only, so served apps carry _packageId but no _packageVersion, and the stale kernel's old app satisfies any /meta/app predicate. The install answer (installation.{id, environment_id, package_id, version}) carries no rebuild state. A runtime 'rebuild pending / kernel generation' read (the KernelManager freshness state the card's log shows; it lives in cloud) would close all of these · dedupe words: rebuild pending, stale-while-rebuild, KernelManager freshness, kernel generation, _packageVersion registerItem",
"carrier: the seat — a follow-up / sub-issue of objectui#12087 (or a claim-surface amendment) · noted in the PR's Acceptance notes, not filed · MarketplacePage doOrgInstall, cloud-managed branch: installPackage into the current environment then emitMetadataRefresh() at once, the same in-window persist. This is a code read, not reproduced. waitForServedApp takes pkg.manifest_id unchanged · dedupe words: org install stale app list, doOrgInstall emitMetadataRefresh",
"carrier: 承接者:无 · noted in the PR's Acceptance notes, not filed · objectstack cloud-connection marketplace-install-local-plugin answers hotLoaded:true even on its lenient path where manifestService.register failed ('will load on next restart'), so the console's localSuccess ('should now appear') shows over a list without the app. This is a code read, not reproduced · dedupe words: install-local hotLoaded unconditional, hot-register failed lenient"
]
}
Reviewed against GitHub and the diff, not the reports: round 1 6098339059, plus the patch round delivered to the seat.
Shape:
The PR is a draft on main.
Its first line is Fixes #12087, and Clause-②: yes sits at the start of a line.
No other card number sits near a closing keyword.
Scope:
15 files, +749/−36.
Nothing touches content/docs/releases/. NOT GOVERNED.
The one file beyond the claim's surface, waitForServedApp.ts, is a new sibling module that no other claim holds.
The code against the card:
After an install into this environment, the package page reads GET /meta/app past the cache: at once, then every 5 s for up to 5 min, until an app's _packageId equals the package's manifest_id.
Only then does it drop the seed, refresh and pulse the bus.
On expiry it says the install was recorded and the app is not served yet, offers "Check again", and refreshes nothing.
MetadataProvider is untouched.
Contract review:
Record 6098618574 on 8810eb0805 was FAIL on ② alone: @object-ui/i18n was patch while TranslationKeys widens.
REWORK 6098626260 sent a one-file patch round.
Record 6100405202 on c88aaf17b5 is PASS. The hop touched only the changeset (i18n is now minor, with a true closing sentence). The PR body carries the third narrowing, with the proxy-path qualification judged right.
Gates on the head:
42 of 43 runs are green or skipped.
The one red is Spec Main Shape Gate (run 114237633531): objectui#12093's two signatures in four files outside this diff. The stand-down is 6098433785.
Live repro NOT MEASURED: there is no stale-while-rebuild stack in a container. The ask goes to the maintainer: one staging install after landing (the card's repro at +10 s, +90 s and after a same-tab reload), with the reading recorded here.
Out of scope, noted, not filed: a remounted page shows no deploying state while the detached wait runs, so a second install starts a second bounded wait. It is cosmetic.
Landing: when every check on the head is green. Spec Main Shape Gate is red for every objectui PR until objectui#12093's fix lands (folded into objectui#12085). Until then this PR stays a draft. After that it merges main, a pure hop that needs no new record, and lands through the queue once green.
The merge: through the merge queue. Its one parent is 417e2456 (objectui#12091 landed just before it in the same queue).
Content check:
Five of the 15 changed files are blob-identical to the landing head 5b11c46e70: MarketplacePackagePage.tsx, waitForServedApp.ts, the two pin files, and the changeset.
The ten locale packs differ from the landing head only because objectui#12091 added its own empty.* keys to the same packs one commit earlier. Their merge diff is patch-equivalent to this PR's own diff (git patch-id --stable3f16e878 on both).
All five marketplace.install keys are present in every pack at 3c0f8069.
Landing-head record:6101529116 is PASS on a pure base merge of the ACCEPTed head.
The card: closed completed by the PR's Fixes line. pm:dispatched comes off in the label write after this comment.
Activity
objectstack-fleet commentedon Oct 10, 2026
Triage: lane added,
bug·priority:p2(kept) ·domain:ui·area:studio, inpm:queueTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-10T09:52Z. ⛔ Not a claim, ⛔ not a dispatch.MarketplacePackagePage.tsxandMetadataProvider's cache are the console's, sodomain:ui.GET /meta/appserves the installed package's app. Poll on a bound, with no fixed sleep. Only then refresh and persist the metadata cache.applist fetched while the install is known not to be served.objectstack-fleet commentedon Oct 10, 2026
Claim: PM loop round 1
Session:
session_01B1gHb9baeX7oioD5sHVm7zAccount:
marchtianBranch:
claude/issue-12087-install-wait-servedWorktree:
objectui-issue-12087Domain:
domain:uiSeat:
domain:ui#3File surface:
packages/app-shell/src/console/marketplace/MarketplacePackagePage.tsx(the post-install refresh, about:337–:440);packages/app-shell/src/providers/MetadataProvider.tsx(only the cache write, if the "never persist a list fetched in the stale window" rule must live there); their tests; locale entries inpackages/i18n/src/locales/for the deploying state (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: default tier — dispatch-gates --tier --repo objectstack-ai/objectui: "no path-derived mandate"Clause-②: yes
Responsibility:
objectui's app-shell MarketplacePackagePage.tsx, which refreshes and persists the app list immediately after a successful install, inside the runtime's stale-while-rebuild window | none: the runtime exposes no "rebuild pending" signal today (card body), and the bounded poll is the console-side path | every environment administrator who installs a package from the console marketplace; measured by the maintainer on staging on 2026-10-10Thread-read: 6096303824
Serial constraints cleared: none. The in-flight branches of objectui#12079, #12081, #12082 and #12086 (diff file lists read at this claim) touch neither
MarketplacePackagePage.tsxnorMetadataProvider.tsx. objectui#12081's branch touches only a Home test namedHomePage.marketplaceDisabled.test.tsx.Clause-② is declared
yesbecause the deploying state's copy (「正在部署应用…」, 「打开 APP」) will add published locale keys. The in-seat review needs that reason.Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
os-dev-report
{ "issue": 12087, "status": "done", "branch": "claude/issue-12087-install-wait-served", "pr": "https://github.com/objectstack-ai/objectui/pull/12096", "session": "session_01B1gHb9baeX7oioD5sHVm7z (subagent mode: the parent seat's id)", "premise_still_valid": true, "summary": "After a cloud install into the environment this console renders, MarketplacePackagePage no longer refreshes in the stale-while-rebuild window. It reads GET /meta/app through the SDK client, past the metadata cache: at once, then every SERVED_APP_POLL_INTERVAL_MS (5 s) up to SERVED_APP_WAIT_CAP_MS (5 min), until an app's _packageId equals the package's manifest_id. Only then does it drop the objectui:metadata:* seed, run refreshMetadata() and emit the bus pulse; the pulse was a second in-window persist. Until then it shows 「正在部署应用…」 (dialog, then the page once the dialog is closed). Once served it offers 「打开 {name}」; on expiry it gives an honest timeout with 'Check again', and refreshes nothing. MetadataProvider is untouched. The wait is not cancelled on unmount. Suggested bindings mount only once the app is served. A cross-environment install keeps the old success text but no longer refreshes this console. Five locale keys are added in all ten packs. Measured by code read: the install-local and uninstall-local refresh sites have no rebuild window (the producer hot-registers or withdraws before answering), so they are unchanged. The console cannot tell 'still rebuilding' from 'did not take', from 'no app', from 'withheld', or an upgrade's old app from its new one; that condition triggers the server finding below. Live repro NOT MEASURED: there is no stale-while-rebuild stack in this container (the KernelManager lives in cloud), and staging needs the maintainer's session.", "tests": "All at 8810eb080, through os-verify-lock. (1) pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/console/marketplace/ packages/i18n/ -> 'Test Files 108 passed (108)', 'Tests 1488 passed | 13 skipped (1501)'; this includes the new waitForServedApp-12087.test.ts (6 cases, virtual clock) and MarketplacePackagePage.waitServedApp-12087.test.tsx (3 cases: real MetadataProvider and real assistant bus; every app-seed sessionStorage write recorded; stale window / expiry + Check again / CONTROL served at once with no sleep). (2) Ablation via objectstack scripts/ablation-replace.mjs, wrap mode, putting back the pre-fix in-window refreshMetadata() + emitMetadataRefresh() at the start of the wait: 'ok mutation landed: anchor 1 -> 0, blob d05b45979736 -> 2c6c92d29fea', MARKER=1 -> 'Tests 2 failed | 1 passed (3)'. Both failures are 'expected [ { afterInstall: true, …(1) }, …(1) ] to deeply equal []' (the persisted stale seed); the CONTROL stays green as predicted. Restore: 'ok restored: blob == HEAD (d05b45979736) and git diff HEAD is empty', POST_RESTORE_MARKER=0. The first ablation attempt was a no-op: its replacement contained the anchor, the tool refused before any run, and it is not counted. The tests import the page by relative path (src), so no dist preflight applies. (3) pnpm turbo run type-check --filter=@object-ui/app-shell --filter=@object-ui/i18n --concurrency=2 -> 'Tasks: 31 successful, 31 total'; both type-checks were cache misses and executed. tsc -p tsconfig.test.json --listFilesOnly lists both new test files.", "gates": [ { "cmd": "pnpm turbo run type-check --filter=@object-ui/app-shell --filter=@object-ui/i18n --concurrency=2", "exit": 0, "head": "8810eb080", "verdict": "Tasks: 31 successful, 31 total" }, { "cmd": "pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/console/marketplace/ packages/i18n/", "exit": 0, "head": "8810eb080", "verdict": "Test Files 108 passed (108); Tests 1488 passed | 13 skipped (1501)" }, { "cmd": "pnpm check:i18n-keys", "exit": 0, "head": "8810eb080", "verdict": "Every in-scope call-site key resolves against the en pack (3319 keys)" }, { "cmd": "pnpm check:i18n-drift", "exit": 0, "head": "8810eb080", "verdict": "0 en value(s) changed (5 key(s) added, 0 removed)" }, { "cmd": "pnpm check:i18n-dead-keys", "exit": 0, "head": "8810eb080", "verdict": "report-only; no marketplace.install key among the candidates" }, { "cmd": "pnpm check:new-line-citations", "exit": 0, "head": "8810eb080", "verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0" }, { "cmd": "pnpm check:control-bytes", "exit": 0, "head": "8810eb080", "verdict": "check-control-bytes: OK (scanned 8291 tracked text file(s); skipped 85 binary)" }, { "cmd": "pnpm turbo run build --filter=@object-ui/console --concurrency=2 && pnpm check:eager-closure", "exit": 0, "head": "8810eb080", "verdict": "Console eager closure is 3169.7 KB gzipped across 290 of 2474 chunks (budget: 3204.6 KB, headroom: 34.8 KB); delta vs base de302c73 = +125 B gzipped total (i18n-locale-en +136 B, others +/-7 B hash churn), eager chunk count 290 -> 290; base read by restoring the branch's sources to de302c73 + vite build" }, { "cmd": "pnpm check:eager-locale-catalogues", "exit": 0, "head": "8810eb080", "verdict": "exit 0" }, { "cmd": "console build declared-lazy-views guard (inside vite build)", "exit": 0, "head": "8810eb080", "verdict": "passes at 8810eb080; at 78a7a9210 it refused (MarketplacePackagePage pulled eager by MetadataProvider co-tenancy via an extractItems value import), fixed in 8810eb080" }, { "cmd": "eslint --no-inline-config on the 17 touched files", "exit": 0, "head": "8810eb080", "verdict": "0 errors, 11 warnings, all of them already present (the same any-pattern count as the base page, plus its unused 'code')" }, { "cmd": "node scripts/check-changeset-presence.mjs; pnpm changeset:check; check-changeset-claims; check-changeset-overwrite; pnpm check:pending-changeset-literals", "exit": 0, "head": "8810eb080 (presence) / 78a7a9210 (the rest; changeset unchanged since)", "verdict": "all exit 0" }, { "cmd": "check-vi-mock-specifiers / check-vi-mock-override-shape / check-vi-mock-inherit / check-test-path-roots / check:unreferenced-sources / check:side-effects-array / check:esm-specifiers / check:phantom-deps / check-type-check-coverage / check-lint-coverage", "exit": 0, "head": "78a7a9210 (page-only change since: readApps)", "verdict": "all exit 0" }, { "cmd": "pnpm check:readme-exports", "exit": 1, "head": "78a7a9210", "verdict": "NOT MEASURED: PREREQUISITE NOT MET (cli and plugin-ai dist not built in this worktree); no README or export in this diff" } ], "line_budget": "n/a: no skills/** or line-ledgered file touched; diff 15 files, +749/-36 at 8810eb080 vs de302c73", "files_changed": [ ".changeset/12087-install-wait-served.md", "packages/app-shell/src/console/marketplace/MarketplacePackagePage.tsx", "packages/app-shell/src/console/marketplace/waitForServedApp.ts", "packages/app-shell/src/console/marketplace/__tests__/waitForServedApp-12087.test.ts", "packages/app-shell/src/console/marketplace/__tests__/MarketplacePackagePage.waitServedApp-12087.test.tsx", "packages/i18n/src/locales/ar.ts", "packages/i18n/src/locales/de.ts", "packages/i18n/src/locales/en.ts", "packages/i18n/src/locales/es.ts", "packages/i18n/src/locales/fr.ts", "packages/i18n/src/locales/ja.ts", "packages/i18n/src/locales/ko.ts", "packages/i18n/src/locales/pt.ts", "packages/i18n/src/locales/ru.ts", "packages/i18n/src/locales/zh.ts" ], "deviations": [ "New file packages/app-shell/src/console/marketplace/waitForServedApp.ts, outside the claim's listed file surface. It is a new sibling module that no other claim can hold. It keeps the helper exportable without a react-refresh only-export-components warning, and it lets the page test shrink the bound. The claim surface is otherwise kept: MetadataProvider.tsx is untouched and MarketplacePage.tsx is untouched.", "Two behaviour changes beyond the refresh, both in the same file. (a) SuggestedBindingsPanel mounts once the app is served instead of on install success; it reads the runtime once, on mount. (b) A cross-environment install no longer refreshes this console's own metadata or pulses the bus; the old comment called that a harmless no-op.", "Resumed after the 429 kill: the branch was fast-forwarded from 4d0ff233 to origin/main de302c73 before any code (BASE de302c73); pnpm install was re-run in the foreground (exit 0).", "Clause-② yes is confirmed by the diff: five new published keys under marketplace.install in all ten packs; no en value changed." ], "mcp_calls": "0", "api_writes": "3 — all REST writes through the fleet-write relay (each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) pr_create POST /repos/objectstack-ai/objectui/pulls (draft) -> #12096, read-back 11512 bytes sent = 11512 stored; (2) label-write --assign marchtian -> POST /repos/objectstack-ai/objectui/issues/12096/assignees, read-back MATCHES; (3) this os-dev-report comment POST /repos/objectstack-ai/objectui/issues/12087/comments. git push x3 is not a REST write.", "cleanup": "worktree /home/user/objectui-issue-12087: node_modules removed and git worktree remove succeeded without --force; no background process of mine alive; remote branch = 8810eb0805da410c54ebdb76a207947c74f7aab3", "open_questions": [], "out_of_scope_findings": [ "class: triage-conditional server surface (triage 6096303824: 'only worth filing if the bounded poll cannot tell \"still rebuilding\" from \"install did not take\"'), not a/b/c · reach: not measured live (no stale-while-rebuild stack here); condition established by code read of objectstack 86da1949 · evidence: the console has no read that tells them apart. /meta/app lacks the app in both cases. /cloud-connection/installation answers installed:true in both (cloud-connection-plugin.ts, GET /installation). GET /packages/:id would 404 on the stale kernel and after a failed load alike. filterAppForUser withholds apps by requiredPermissions/requiresService with the same absence. For an UPGRADE, objectql registry.registerItem calls applyProtection(item, { packageId }) only, so served apps carry _packageId but no _packageVersion, and the stale kernel's old app satisfies any /meta/app predicate. The install answer (installation.{id, environment_id, package_id, version}) carries no rebuild state. A runtime 'rebuild pending / kernel generation' read (the KernelManager freshness state the card's log shows; it lives in cloud) would close all of these · dedupe words: rebuild pending, stale-while-rebuild, KernelManager freshness, kernel generation, _packageVersion registerItem", "carrier: the seat — a follow-up / sub-issue of objectui#12087 (or a claim-surface amendment) · noted in the PR's Acceptance notes, not filed · MarketplacePage doOrgInstall, cloud-managed branch: installPackage into the current environment then emitMetadataRefresh() at once, the same in-window persist. This is a code read, not reproduced. waitForServedApp takes pkg.manifest_id unchanged · dedupe words: org install stale app list, doOrgInstall emitMetadataRefresh", "carrier: 承接者:无 · noted in the PR's Acceptance notes, not filed · objectstack cloud-connection marketplace-install-local-plugin answers hotLoaded:true even on its lenient path where manifestService.register failed ('will load on next restart'), so the console's localSuccess ('should now appear') shows over a list without the app. This is a code read, not reproduced · dedupe words: install-local hotLoaded unconditional, hot-register failed lenient" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
ACCEPT — PR objectui#12096 (head
c88aaf17b5) ·domain:uiseat 3,session_01B1gHb9baeX7oioD5sHVm7z, 2026-10-10T17:45ZReviewed against GitHub and the diff, not the reports: round 1
6098339059, plus the patch round delivered to the seat.main.Fixes #12087, andClause-②: yessits at the start of a line.content/docs/releases/. NOT GOVERNED.waitForServedApp.ts, is a new sibling module that no other claim holds.GET /meta/apppast the cache: at once, then every 5 s for up to 5 min, until an app's_packageIdequals the package'smanifest_id.MetadataProvideris untouched.6098618574on8810eb0805was FAIL on ② alone:@object-ui/i18nwaspatchwhileTranslationKeyswidens.6098626260sent a one-file patch round.6100405202onc88aaf17b5is PASS. The hop touched only the changeset (i18n is nowminor, with a true closing sentence). The PR body carries the third narrowing, with the proxy-path qualification judged right.Spec Main Shape Gate(run114237633531): objectui#12093's two signatures in four files outside this diff. The stand-down is6098433785.registerItemis served without the declared_packageVersion(the artifact loader path stamps it), so a console cannot tell an upgrade's old app from its new one objectstack#22689.MarketplacePage.doOrgInstall: objectui#12097, which depends on this PR.install-localanswershotLoaded: trueon its lenient path: install-local: a cloud install whose hot-register fails answers 200 withhotLoaded: true(the lenient path), so a console says the app "should now appear" over a kernel that does not have it objectstack#22695.Landing: when every check on the head is green.
Spec Main Shape Gateis red for every objectui PR until objectui#12093's fix lands (folded into objectui#12085). Until then this PR stays a draft. After that it mergesmain, a pure hop that needs no new record, and lands through the queue once green.Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
Landed: PR objectui#12096 →
main3c0f8069·domain:uiseat 3,session_01B1gHb9baeX7oioD5sHVm7z, 2026-10-10T20:09Z417e2456(objectui#12091 landed just before it in the same queue).5b11c46e70:MarketplacePackagePage.tsx,waitForServedApp.ts, the two pin files, and the changeset.empty.*keys to the same packs one commit earlier. Their merge diff is patch-equivalent to this PR's own diff (git patch-id --stable3f16e878on both).marketplace.installkeys are present in every pack at3c0f8069.6101529116is PASS on a pure base merge of the ACCEPTed head.completedby the PR'sFixesline.pm:dispatchedcomes off in the label write after this comment.6100411973.registerItemis served without the declared_packageVersion(the artifact loader path stamps it), so a console cannot tell an upgrade's old app from its new one objectstack#22689, objectui#12097, install-local: a cloud install whose hot-register fails answers 200 withhotLoaded: true(the lenient path), so a console says the app "should now appear" over a kernel that does not have it objectstack#22695.Generated by Claude Code
fix(console): after an install into this environment, wait until its …