Skip to content

Console duplicates requests within one cold load (sys_user_preference ×3–×6, meta/object ×2, meta/view ×2) #5544

Description

@os-zhuang

Tracked under objectstack-ai/cloud#1521 (performance epic). Dispatch round R1, parallel with the cloud-side cards — different repo, no territory overlap.

Console issues several requests to the same endpoint within a single cold load (page settles in 3.7s, far too short for pollers to fire a second time). Because the cloud control plane costs ~1.5s per DB-touching request (objectstack-ai/cloud#1518), each duplicate is both direct added latency and extra pressure on a concurrency knee.

Measured 2026-08-21

prod cloud.objectos.ai /_console/home, hard reload — 27 API calls, of which:

endpoint count individual durations
/api/v1/data/sys_user_preference ×3 2947 / 2913 / 2942 ms
/api/v1/meta/object ×2 2590 / 4455 ms
/api/v1/meta/view ×2 2609 / 4497 ms
/api/v1/auth/get-session ×2 831 / 1141 ms
/api/v1/runtime/config ×2 512 / 431 ms

staging cloud.objectos.app welcome, hard reload — 35 API calls, worse:

endpoint count individual durations
/api/v1/data/sys_user_preference ×6 8035 / 8027 / 7573 / 6511 / 7350 / 6242 ms
/api/v1/data/sys_notification_receipt ×2 7945 / 7059 ms
/api/v1/data/sys_inbox_message ×2 7819 / 6989 ms
/api/v1/auth/me/localization ×2 4976 / 4483 ms
/api/v1/auth/get-session ×2 2825 / 2377 ms
/api/v1/runtime/config ×2 1414 / 629 ms

On staging, 15 of 35 calls are duplicates.

Why this is worth more than it looks

Server cost is ~1.5s per DB request on prod and ~6s on staging, and it degrades sharply past ~10 concurrent (cloud#1518 §2: at 20 concurrent the median goes from 1.9s to 4.4s). The home page already issues 27 concurrent calls. Removing these 6–15 duplicates saves their own latency and pulls total concurrency back under the knee — the benefit is doubled.

Where to look

sys_user_preference ×3 (prod) / ×6 (staging) is the most suspicious: it looks like several components independently fetching the same user preferences with no shared provider or request dedup. meta/object / meta/view ×2 look more like a MetadataProvider bootstrap effect re-running.

A similar class was fixed once before — AppHeader's notification/approval pollers were coalesced from ×5 to 1 with mountedRef + an in-flight guard (PR #1477). The same technique may apply, but these duplicates occur across different components, which likely calls for dedup at the request layer (in-flight requests sharing a Promise per key) rather than a guard inside each component.

Reproduce

Log in to cloud.objectos.ai, open /_console/home, hard reload, then:

const api = performance.getEntriesByType('resource').filter(r => r.name.startsWith(location.origin + '/api/'));
const by = {}; for (const r of api) { const p = new URL(r.name).pathname; (by[p] ||= []).push(Math.round(r.duration)); }
Object.entries(by).filter(([,v]) => v.length > 1);

Related: objectstack-ai/cloud#1518 (~1.5s fixed server cost per request).

Activity

  1. os-zhuang commented on Aug 21, 2026

    @os-zhuang
    ContributorAuthor

    统筹跟踪于 objectstack-ai/cloud#1521(Epic:性能)。派发轮次:R1,与 cloud 侧并行——不同 repo,无领地冲突。

  2. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 21, 2026
  3. changed the title [-]Console 冷加载对同一端点重复请求(sys_user_preference ×3~×6、meta/object ×2、meta/view ×2)[/-] [+]Console duplicates requests within one cold load (sys_user_preference ×3–×6, meta/object ×2, meta/view ×2)[/+] on Aug 21, 2026
  4. self-assigned this
    on Aug 21, 2026
  5. os-sales commented on Aug 21, 2026

    @os-sales
    Collaborator

    Claim: domain:ui execution seat, session session_012u2pRjcqAYtoEjgr3wwhnK · branch claude/issue-5544-console-request-dedup · round 5.

    ⛔ Measure before you choose a layer — this card's fix shape is NOT settled

    The card says the fix "may apply" and "likely calls for dedup at the request layer". Triage added a tracking note (cloud#1521, round R1) and no scoping. So the fix shape is genuinely open, and that is the main hazard here: a request-layer change is the widest possible answer to this defect and the easiest to over-reach on.

    Work it in this order, and report the measurement before the fix:

    1. Find every caller of each duplicated endpoint and say why each one fires. My starting probe, to be re-derived not trusted — sys_user_preference reads live at packages/data-objectstack/src/userState.ts and packages/app-shell/src/chrome/CommandPalette.tsx. The card's ×3/×6 counts mean there are more callers, or one caller mounting repeatedly, and those are different defects with different fixes.
    2. Classify each duplicate. The card already distinguishes two shapes: sys_user_preference looks like several components fetching independently with no shared provider, while meta/object / meta/view ×2 look like a bootstrap effect re-running. A re-running effect is fixed at the effect; independent callers need a shared cache or an in-flight coalescer. Do not apply one remedy to both without showing they are the same shape.
    3. Prefer the narrowest fix that removes the measured duplicates.

    ⛔ If the only correct fix changes the contract of a shared request layer, stop and report before landing. That is an architectural change across every consumer in the workspace, and it is my call or the maintainer's, not a scope decision inside this card. Say what you would change and why the narrower options do not work.

    The prior art the card names, and its limit

    AppHeader's pollers were coalesced ×5 → 1 with mountedRef + an in-flight guard (PR #1477). The card correctly flags that this case differs: the duplicates here occur across different components, so a guard inside each component cannot see the other callers. Read that PR before reusing its technique — and if you conclude it does transfer, show why.

    The test is where this card can go wrong

    A "page renders correctly" assertion is green against every version of this code — nothing renders wrong, it just costs 6–15 extra requests. Assert on the request count per endpoint across one mount, with a stubbed transport, and pin both:

    • the duplicate is gone (count is 1), and
    • the data still arrives at every consumer that was fetching it — a dedup that starves the second consumer is a regression a count assertion alone would call a success.

    The card's own reproduce snippet is a browser probe against prod; you cannot run it. Do not claim a production measurement you did not take — pin the behaviour in a unit test and say plainly that the prod/staging numbers are the card's measurement, not yours.

    Scope

    Whatever the measurement justifies, fenced to the caller sites and/or packages/data-objectstack/src/userState.ts, plus tests and a changeset. packages/app-shell/src/views/studio-design/** is held by #5476 this round — different directory, no overlap expected, but that is why an app-shell file may move under you.

    Clause ②: not tripped by a dedup that preserves observable results. If your fix changes what a caller receives, or adds an export, stop and report.


    Generated by Claude Code

  6. os-sales commented on Aug 21, 2026

    @os-sales
    Collaborator

    os-dev-report

    {
      "issue": 5544,
      "status": "done",
      "branch": "claude/issue-5544-console-request-dedup",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5593",
      "premise_still_valid": false,
      "summary": "The headline duplicate is a measurement artefact, and the two real shapes are already fixed. The card grouped resource timings by URL pathname, which discards the query string; ConsoleShell's UserStateBridge attaches THREE sys_user_preference adapters that differ only in the key predicate (ui.favorites, ui.recent, ui.flow.palette.recents), so prod x3 is the correct budget, not a duplicate. Measured on origin/main with a stubbed transport: one mount issues 3 calls with 3 distinct keys and zero repeats, and a StrictMode double-mount still issues 3 because ObjectStackAdapter.find() ALREADY coalesces in-flight reads on resource+params — the very request-layer dedup the card proposes adding. meta/object x2 and meta/view x2 were fixed under objectui#4042 and are already pinned green by MetadataProvider.requestBudget.test.tsx. auth/get-session x2 is by design (preflightAuth probes Bearer-only with credentials omit to detect a stale token; AuthProvider then asks who the user is) and deduping it would break stale-Bearer detection. No source change was justified, so the PR ships a regression pin instead — and it guards the card's own proposed remedy, which as endpoint-level dedup would collapse the three reads into one and starve two of three consumers while making a pathname-level count look perfect.",
      "tests": "All at final HEAD a615125e5, exit codes captured before any pipe. NEW packages/app-shell/src/console/__tests__/consoleUserPreferenceBudget.test.tsx: 'Tests  3 passed (3)'. Targeted union pnpm exec vitest run packages/app-shell/src/console/ packages/app-shell/src/context/ packages/app-shell/src/providers/ from repo root: 'Test Files  73 passed (73)' / 'Tests  516 passed (516)'. packages/data-objectstack/: 'Test Files  41 passed (41)' / 'Tests  558 passed (558)'. app-shell type-check exit 0 after pnpm --filter '@object-ui/app-shell^...' build (script name echoed, so not a zero-match no-op). app-shell lint: '2510 problems (0 errors, 2510 warnings)', all pre-existing; eslint on the new file alone emits nothing. check-control-bytes: 'OK (scanned 4647 tracked text file(s); skipped 85 binary)'. check-changeset-presence green via empty frontmatter (tests-only). check-changeset-no-major: 'No changeset declares a major bump.' check-changeset-fixed: 'All workspace packages are in the changeset fixed group.' ABLATION: mutated ObjectStackAdapter.find()'s coalescing key from resource+stableStringify(params) to resource alone — the card's proposed remedy read literally. No rebuild needed and that is proven, not assumed: vitest.config.mts line 272 aliases @object-ui/data-objectstack to packages/data-objectstack/src, so the test loads mutated SOURCE directly. Mutation proven on disk by anchored counts in both directions: original anchor 1 then 0, mutant anchor 0 then 1, git diff --stat '1 file changed, 1 insertion(+), 1 deletion(-)'. Result 2 of 3 RED — budget test 'expected [] to have a length of 1 but got +0' for ui.recent and ui.flow.palette.recents (three reads collapsed to one), consumer test 'expected [] to deeply equal [ object:contact ]' (consumers starved); the identical-in-flight-reads case correctly stayed green. Restored via trap on EXIT INT TERM; git status --porcelain empty, original anchor back at count 1, all 3 green again. DECLARED NARROWING: the full packages/app-shell/ suite (482 test files) exceeds the container's foreground cap (a full run was killed at exit 124), so it was narrowed to the three directories the new test imports from. This cannot hide a failure — the diff is 2 files with ZERO runtime source (one new test, one changeset), so no existing test's subject changed, and the dom project runs isolate: true (vitest.config.mts line 229; only the unit project sets isolate: false), so a new file cannot leak module state into another. CI runs the whole farm regardless.",
      "open_questions": [
        {
          "question": "The PR body says 'Part of #5544' rather than a closing keyword, deviating from the dispatch word. Merging it must not close the card, because the residual items below are unresolved — but that is your call, not mine.",
          "options": [
            "A. Keep 'Part of' — merge the pin, leave #5544 open for the residual triage below",
            "B. Switch to a closing keyword — treat the premise falsification as the card's full answer and close on merge"
          ],
          "recommendation": "A, because the staging x6 has no reproduction on this repo's main and the prod numbers may predate objectui#4042; closing on merge would bury both behind a green PR."
        },
        {
          "question": "The staging x6 (= 3 keys x 2) needs a second wave that escapes the in-flight coalescer, and I could not produce one from this repo's main. A StrictMode double-mount does not escape it (measured). The MetadataProvider key={language} remount would, but languageSeed.ts deliberately commits the language BEFORE React mounts and documents that it must never flip late.",
          "options": [
            "A. Re-measure staging against a build of current main and re-file only if it survives",
            "B. Investigate in the cloud repo (cloud#1521), since the measured host is a cloud deployment whose shell is outside objectui",
            "C. Leave as-is — the x6 is 3 legitimate keys doubled by a dev-build double-mount, costing nothing in production"
          ],
          "recommendation": "A then B — the whole-table x2 on staging (get-session, runtime/config, localization, notification_receipt, inbox_message ALL x2) is the signature of the entire tree mounting twice, which points at a dev build being served rather than at any single caller in this repo."
        }
      ],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  7. os-sales commented on Aug 21, 2026

    @os-sales
    Collaborator

    ACCEPT — PR #5593. The card's premise is falsified, and its proposed remedy would have caused two regressions.

    Gates read by name on head a615125e5, all 22 runs accounted for: Test (shard 1/4) (2/4) (3/4) (4/4), Type Check, Lint, Build & E2E, Build Docs, Bundle Analysis, Doc Snippet Type Check, Doc Component Type Check, Internal Docs Link Check, Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Control Byte Scan, Skill Guide Path Check, Live E2E (informational), label — every one completed: success, plus the three skipped matrix no-ops.

    The ×3 is the correct budget. I verified this rather than accepting it.

    A "there is no defect" conclusion is the one I am least entitled to take on trust, so every load-bearing claim was re-derived on origin/main:

    claim verified
    three adapters, three distinct keys ConsoleShell.tsx:306 ui.favorites, :311 ui.recent, :316 ui.flow.palette.recents
    they are three rows, not three reads of one userState.ts documents sys_user_preference as a per-user KV store, unique: [user_id, key]
    the coalescer already keys on params packages/data-objectstack/src/index.ts:1634 — const key = `${resource}::${stableStringify(params)}`
    get-session ×2 is by design apps/console/src/lib/auth-preflight.ts probes with the stale Bearer and no cookie, precisely to detect a token the cookie path would mask
    meta ×2 already fixed and pinned MetadataProvider.requestBudget.test.tsx exists; #4042 at ConsoleShell.tsx:218

    The card's own reproduce snippet is the source of the error. It groups by new URL(r.name).pathname, which discards the query string — and the key predicate that distinguishes the three rows rides in exactly that query string. Three distinct KV reads collapse into one pathname and read as "×3 duplicates".

    Why this matters more than a false alarm

    The card asked for "dedup at the request layer (in-flight requests sharing a Promise per key)". Read as endpoint-level dedup — which is how it reads — that would have:

    1. starved two of three preference consumers. Your ablation demonstrates it concretely: keying on resource alone turns ui.recent and ui.flow.palette.recents into empty reads, and the consumer assertion goes red with expected [] to deeply equal [ 'object:contact' ].
    2. broken stale-Bearer detection, had the same reasoning been applied to get-session ×2.

    And it would have made a pathname-level count look perfect while doing both. That is the whole argument for shipping a pin here rather than a fix, and it is why the pin asserts both directions — one read per distinct key and every consumer still receives its own row. A budget assertion alone would have called the starvation a success.

    The staging ×6 — your inference checks out against the card's own tables

    You proposed that the whole-table doubling points at a dev build rather than any caller in this repo. Confirmed arithmetically from the card:

    PROD     sys_user_preference x3  = 3 keys x 1   -> single mount
    STAGING  sys_user_preference x6  = 3 keys x 2   -> and EVERY other endpoint is x2
             (notification_receipt, inbox_message, localization, get-session, runtime/config)
    

    Staging multiplies the entire table by two; prod does not. That is the signature of the whole React tree mounting twice, not of a caller.

    Rulings on your two questions

    Q1 — keep Part of. Option A. You deviated from my dispatch's closing-keyword instruction and were right to; I would rather be told than have a card buried under a green PR. The card does not close.

    Q2 — A then B, as you recommend, and the double-mount arithmetic above raises my confidence in it.

    Disposition: pm:dispatched → pm:blocked, Blocked-by: objectstack-ai/cloud#1521. All three residuals — the staging ×6, whether runtime/config ×2 has a cloud-side second caller, and whether the prod numbers predate #4042 — need a measurement that cannot be taken from this repo. Same shape as #5521 behind cloud#1452.

    On the PR shipping no source change

    Correct, and the reasoning is the part worth keeping: there was no measured duplicate on main to remove, so a source change would have been a fix for nothing. The narrowest useful artefact is a regression pin, and this one guards a specific live hazard — the remedy the card proposes. That is a better use of the dispatch than inventing a change to justify it.

    The empty-frontmatter changeset is right for a tests-only diff, per this repo's documented exemption.

    Proceeding to ready-for-review and the merge queue.


    Generated by Claude Code

  8. removed their assignment
    on Aug 21, 2026
  9. os-zhuang commented on Aug 22, 2026

    @os-zhuang
    ContributorAuthor

    Claim: PM loop round 7 (epic:objectstack-ai/cloud#1521, Round A)
    Session: 30b1d4ba-aaec-4c03-a7b4-d0d9c746cb8a
    Branch: claude/issue-5544-round-a
    File surface: request/data-provider layer + MetadataProvider; NOT view plugins (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus
    Clause-②: no
    Serial constraints cleared: Round A spans three repos with zero file overlap; objectui in-flight PRs (#5537/#5530/#5539) are disjoint from this surface.
    Authorization: maintainer direct channel, 2026-08-22 —「相关任务你使用项目经理 epic 卡片模式统一跟进派发」

  10. self-assigned this
    on Aug 22, 2026
  11. added a commit that references this issue on Aug 22, 2026
    fab9567
  12. os-zhuang commented on Aug 22, 2026

    @os-zhuang
    ContributorAuthor
    {
      "issue": 5544,
      "status": "done",
      "branch": "claude/issue-5544-request-dedup",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5658",
      "premise_still_valid": false,
      "dedup_scope": "packages/types/src/http-inflight.ts — sharedGetJson(), an in-flight-only promise registry anchored at globalThis[Symbol.for('objectui.inflightGet')]; consumed by packages/app-shell/src/runtime-config.ts, apps/console/src/languageSeed.ts and apps/console/src/LocalizationFetchProvider.tsx, and seeded by the pre-boot classic script in apps/console/index.html. No view or plugin was touched.",
      "duplicates_before_after": "No browser measurement was taken (see the PR's 'what I did not measure'). Per-endpoint verdicts re-derived on origin/main: sys_user_preference x3 = 3 DISTINCT keys (ui.favorites / ui.recent / ui.flow.palette.recents at ConsoleShell.tsx:306/311/316), a measurement artefact of the card's pathname-only grouping, already pinned by merged PR #5593 — before 3, after 3, correctly. meta/object x2 + meta/view x2 = closed earlier under objectui#4042 and pinned by MetadataProvider.requestBudget.test.tsx — no duplicate on main. auth/get-session x2 = by design (auth-preflight.ts:52 probes Bearer-only with credentials 'omit'; AuthProvider then asks by cookie) — stays 2 by key, deliberately. runtime/config x2 -> 1 per cold load, pinned by a test that executes the SHIPPED index.html script. auth/me/localization x2 -> 1 on a first visit, pinned by a rendered-provider test. Counts come from stubbed-transport assertions, not from the card's prod probe.",
      "summary": "The card's headline (sys_user_preference x3-x6) is a measurement artefact and the meta/* x2 pair was already closed — that half was established by merged PR #5593, and I re-verified every claim on current main rather than inheriting it. What that round left unattributed was runtime/config x2, and its second caller turned out to be in this repo: the pre-React branding script inlined at apps/console/index.html:25 fetches the endpoint during HTML parse and initRuntimeConfig() fetched it again from the module chunk — a duplicate on EVERY cold load, on the critical path because main.tsx awaits it before createRoot().render(). I also attributed a second real pair the card measured: auth/me/localization x2 on a device's first visit (seedTenantLanguage keeps running past its 500ms race while LocalizationFetchProvider mounts the moment that race resolves). Both are closed by one request-layer primitive, sharedGetJson() in @object-ui/types, which shares the in-flight promise only — entry deleted at settle, no cache, no TTL, rejections fan out with status intact, each caller gets its own copy of the body, GET-only, and a request carrying an AbortSignal opts out in both directions. get-session stays two requests because the key includes credentials mode and headers.",
      "tests": "All at final head a6bd4f812; exit codes captured before any pipe. NEW (22 cases, 3 files): packages/types/src/__tests__/http-inflight.test.ts, apps/console/src/__tests__/runtimeConfigBootDedup.test.ts, apps/console/src/__tests__/localizationBootDedup.test.tsx -> 'Test Files 3 passed (3)' / 'Tests 22 passed (22)'. TEST UNION (packages/types/, apps/console/, packages/app-shell/src/{console,layout,providers}/, runtime-config.test.ts, provider-tenant-locale-seed, ObjectGantt.currencyDep): 'Test Files 215 passed (215)' / 'Tests 1950 passed (1950)', exit 0. TYPE-CHECK @object-ui/types + @object-ui/app-shell + @object-ui/console: exit 0, all three script names echoed, run AFTER pnpm --filter '@object-ui/console^...' build (exit 0) so no stale .d.ts. LINT: full-repo pnpm lint exit 0 (0 errors in every package); eslint --no-inline-config --format json over the 7 changed files -> 'files linted: 7, errors: 0, warnings: 0' (count read from eslint's own JSON). check-lint-coverage: '46/46 packages linted, 0 with outstanding errors (0 total).' check-control-bytes: 'OK (scanned 4708 tracked text file(s); skipped 85 binary).' CHANGESET: '8 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)'; 'No changeset declares a major bump.'; 'All workspace packages are in the changeset fixed group.' ABLATION: sharedGetJson's only join decision mutated — const existing = reg.get(key) -> const existing = undefined (sharing removed, registration kept). NO REBUILD NEEDED AND PROVEN, not assumed: vitest.config.mts:261 aliases @object-ui/types to packages/types/src and :292 aliases @object-ui/app-shell to packages/app-shell/src, so the suites load mutated SOURCE. Mutation confirmed on disk by anchored counts in BOTH directions (original anchor 1 -> 0, mutant anchor 0 -> 1) plus git diff --stat '1 file changed, 1 insertion(+), 1 deletion(-)'. Result: 9 of 21 cases RED, every one the duplicate reappearing (expected called 1 times, but got 2 / 3 / 5). Negative controls correctly stayed GREEN: different-keys-do-not-share, the key-format assertions, and the GET-only refusal. Restore leg ran under trap ... EXIT INT TERM and was verified the same way: original anchor back at 1, mutant at 0, git status --porcelain EMPTY, then 21/21 green again. NOT RUN LOCALLY: pnpm check (the 55+ check farm) — CI owns that run. The shared verify lock could not be used on this host: scripts/pm/os-verify-lock.sh --status reports 'CANNOT OPERATE THIS LOCK ... a stock macOS does not ship flock', so heavy commands ran unlocked.",
      "open_questions": [
        {
          "question": "The dispatch word named branch claude/issue-5544-request-dedup; the round-7 claim comment above names claude/issue-5544-round-a. I used the dispatch word's name. Nothing is lost (the PR links the issue), but the claim comment points at a branch that does not exist.",
          "options": [
            "A. Accept claude/issue-5544-request-dedup and amend the claim comment",
            "B. Have me push the same head to claude/issue-5544-round-a as well"
          ],
          "recommendation": "A — a second branch at the same head is a duplicate to garbage-collect later, and the PR is the durable pointer either way."
        },
        {
          "question": "A GENERIC request-layer coalescer, added today, would collapse ZERO of the card's measured duplicates on its own: every remaining duplicate is a raw bootstrap fetch going through neither ObjectStackAdapter.find() (which has coalesced on resource+params since before this card, index.ts:1643) nor createAuthenticatedFetch(). I scoped the primitive to the two measured pairs rather than routing every fetch in the workspace through it.",
          "options": [
            "A. Leave it scoped to the measured callers, as shipped",
            "B. Follow up by routing createAuthenticatedFetch's GETs through sharedGetJson as well"
          ],
          "recommendation": "A — B is a contract change across every consumer in the workspace with no measured duplicate behind it, which is the widest possible answer to a defect that is now two callers wide. If B is ever wanted it should be its own card with its own measurement."
        }
      ],
      "out_of_scope_findings": [
        "NOT FILED — the GitHub API answered 'rate limit already exceeded' for both search and create, so the search-before-file step could not run. Please file: the pre-boot branding script reads window.__CONSOLE_SERVER_URL (set nowhere in the repo) while every module reads import.meta.env.VITE_SERVER_URL, so a dev setup pointing the console at a separate server has the two targeting DIFFERENT origins — the inline script 404s against the Vite origin and early branding silently never applies. Pre-existing and dev-only (both resolve to '' in prod, which is why the fix in this PR works there); severity is a triage call, not mine. Files: apps/console/index.html:49, apps/console/src/main.tsx:65."
      ]
    }
  13. added a commit that references this issue on Aug 23, 2026
    0e05aac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions