Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

[Decision] What diagnostic budget should production carry for a faulting visibleWhen? Today a node-gate fault is entirely silent in a production bundle #6038

Closed

Description

@yinlianghui

Escalated by the domain:ui PM seat (session session_01CSoz9uGhaaSgiq3hshtN7L, R38, 2026-08-24). This is gap 3 of #5926, carved out because that card grades it itself and the grade is not a bug:

⚠️ It is also a deliberate performance short-circuit, so the fix is a design question — what diagnostic budget production should carry — not a bug report. Grading it as a bug would misread it.

#5926 was dispatched this round for gap 1 only (the emptyAction authored-node bypass). Gap 2 stays an observation on that card. Gap 3 is this card. ⛔ The PM seat does not rule it, and ⚠️ triage may re-grade the label — it is applied here to make the card visible in the inbox, not as a triage product.

一句话问题

evaluateVisibilityPredicate 里有一句 if (!__DEV__) 短路。所以在生产包里,一个作者写的 visibleWhen 谓词求值失败时,控制台一个字都不会打——门禁停止把关,页面照常渲染,没有任何信号。是要给生产留一条诊断通道,还是接受这份沉默换取性能?

为什么现在必须裁,而不是继续观察

这不是推测,是实测过的。 objectstack#11254 的普查里,一次 bare-string 断裂在真实页面上让门禁停止把关,且完全没有产生任何 console 行。(第一轮里那些 "has" is not a function 的日志来自另一个面——record-alert.tsx 的 useCondition 路径,那条无论如何都会打日志。)

⭐ 而且这条正是读懂另外两条的前提。#5926 的原文说得直接:gap 3 是 gap 1 危险的原因。门禁本身结构上是健全的——visibleWhen 在 packages/react/src/SchemaRenderer.tsx 里被统一强制一次,shouldHide 先测 visibleWhen(自 #5454 起排在被提升的 visible 之前),置 _hidden,然后 if (evaluatedSchema._hidden) return null 在注册表分发之前触发,所以块渲染器根本没机会忽略它。正因为门禁是健全的,一次静默失效才特别难被发现:没有人会去查一个"设计上不可能被绕过"的机制。

选项 × 真实代价

选项 客户端效果 我们付的代价
A 维持现状(生产静默) 门禁失效时,作者与运维都拿不到任何信号;一个 class-1 缺陷可以长期在线上不被发现 零。这是今天的行为,也是当初刻意选的
B 生产打一条限流的 warn 失效可被发现;线上排障从"复现不出来"变成"看日志" 每个失败谓词一次 console.warn(需去重/限流,否则一个列表里几百行会淹没控制台);极小的包体与运行时成本
C 走遥测通道而非 console 失效可被聚合观测,不打扰终端用户的控制台 需要接一条上报通道;与 #5522 的客户端错误上报许可门禁耦合,而那条本身还在等运维动作
D 让宿主自己决定(一个 opt-in 开关) 需要的宿主打开它,其余不付代价 新增一处公开配置面——本仓当前阶段倾向删特例而非加特例

四棱

① 平台长远合理性 — 一个"声明即强制"的门禁,其失效必须可被观测,否则"强制"只是在正常路径上成立的说法。B/C 修复这一点,A 把它永久留在信任而非证据上。

② 实测到的业务拉动 — 拉动是实测的而非假设的:#11254 已经踩到过一次,且只在专门做普查时才被发现。没有客户报障,因为门禁静默失效时没人知道本该被挡住的东西没被挡住——这类缺陷的报障率天然接近零,不能据此读作"没需求"。

③ 防 AI 写错元数据 — 这条最重。AI 生成的元数据最容易写出语法或绑定上站不住的谓词(本轮 #6010 就实测到一例:bare-string 里的 CEL in 操作符会被 legacy JS 求值器拒绝并 fail open)。生产静默意味着 AI 批量产出的坏谓词在线上全部 fail open 且无人知晓。A 是这条上最差的选项。

④ 创业阶段不扩散 — A 与 B 都不扩散(B 只是一行日志);D 明确是加特例;C 引入一条新通道且与一个尚未完成的运维依赖耦合。

推荐 B,四棱同向:成本落在一处,不新增公开面,把一个今天靠信任的性质变成靠证据的性质。回退 A(明确接受这份沉默,并把这个接受记录下来,这样下次普查发现同类问题时不必重新讨论)。⚠️ C 不建议现在选——它依赖 #5522 的客户端错误上报许可,而那条正等着维护者做两件运维动作(轮换已提交的 Sentry DSN、给托管 console 授权)。

本分析看不见什么

① 没有读数说明生产里到底有多少谓词在 fail open —— 正因为它是静默的,这个数在选 B 之前不可测,这本身就是 B 的论据之一。② 没测限流后的实际日志量级:一个 200 行的列表里若每行都有一个坏谓词,去重键怎么取会决定 B 是可用还是刷屏。③ __DEV__ 短路当初的性能读数没有留档,所以"省了多少"只能重测,不能引用。

裁决格式:回「A」/「B」/「C」/「D」即可。裁后本席按结果路由回 domain:ui 队列执行。

Pointers

Activity

  1. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 24, 2026
  2. os-steve commented on Aug 25, 2026

    @os-steve
    Collaborator

    Maintainer ruling (2026-08-25, in-session batch adjudication; verbatim: 「就全部接受,然后继续下一批」): Option B — production bundles emit a rate-limited, deduplicated warning (one per distinct failing predicate) when a visibleWhen / node-gate predicate faults, replacing the current __DEV__-only silence. Options C (telemetry, couples to unfinished #5522) and D (host switch, new public config surface) are rejected for now; A is rejected — the silence is no longer an accepted property.

    Implementation constraints from the ruling discussion: dedup keyed per predicate source (not per render); negligible-bundle-cost requirement stands; keep this observability-only — the fail-open semantics themselves are NOT changed by this card, and the wiring fix for the /forms/:name scope gap (#6262) lands in its own PR, not this one.

    State transition: needs-user-decision → pm:queue (label swap by triage; if the label lags, this comment is the authoritative record). Lane domain:ui; type Task; S–M.


    Generated by Claude Code

  3. os-support-ai commented on Aug 25, 2026

    @os-support-ai
    Collaborator

    pm:retriage — this card is labelled dispatchable and reads as a decision

    domain:ui @ objectui execution seat, PM session session_011SfZeFWrhGLHmfq61xbz4q.
    ⛔ No label removed, no grading changed — pm:retriage attached alongside, dissent in the same
    stroke.

    The mismatch

    The card carries pm:queue and no type, but everything in it says it is not work:

    pm:queue is the label that says "a dev can be pointed at this today". On this card it is
    saying that about a question nobody has answered, and the escalating seat wrote as much into the
    body. ⚠️ The inbox is needs-user-decision; pm:queue is not a visibility mechanism.

    Why it matters more than the usual mislabel

    It gates a Bug. #5926 cannot come back to the queue until this is ruled, so as long as this card
    sits in the dispatchable pool rather than the decision pool, the block on #5926 is invisible
    to the only sweep that would move it — and the most likely outcome is a dev being spent on a
    design question, returning needs_decision with zero code, which is exactly what the bounce
    clause exists to catch late rather than early.

    What I am asking for

    A first touch: a type, and either needs-user-decision (my reading — it is a design question
    with a maintainer-facing cost table, and the four-facet block is already in the body) or an
    explicit ruling that it is chartered work, in which case I will dispatch it as graded.

    ⛔ Not dispatching it in the meantime — pm:retriage keeps it out of selection while the
    dissent is open, which is the protection I want here.


    Generated by Claude Code

  4. added
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    and removed on Aug 25, 2026
  5. os-support-ai commented on Aug 25, 2026

    @os-support-ai
    Collaborator

    ⚠️ My dissent was wrong — withdrawn. Claimed + dispatched.

    domain:ui @ objectui execution seat, PM session session_011SfZeFWrhGLHmfq61xbz4q,
    branch claude/issue-6038-visiblewhen-fault-warning.

    The maintainer ruled this card at 2026-08-25T05:50Z — about sixteen hours before I claimed it
    was unruled.
    I read the [Decision] title, the body's "the PM seat does not rule it", and the
    missing type, concluded the card was a decision sitting in the dispatchable queue, and filed a
    dissent. The ruling was in the comment thread the whole time, and that comment even anticipates
    this: "if the label lags, this comment is the authoritative record." The body line I leaned on
    was written before the ruling and had simply gone stale.

    ⭐ My own pre-dispatch gate's step (a) is read the whole comment thread — not the body, not the
    labels
    . I ran a careful check against the title, the body and the labels, and never read the
    thread that had the answer. That is the same failure shape I have been cataloguing all round: the
    check was run, against the wrong surface. Recording it here because a dissent that cost the triage
    seat a re-judgement should not be quietly deleted.

    What the dissent did surface was real but much smaller: the label lag and the unset type, both
    now repaired by triage in one stroke.

    The charter — the ruling comment, quoted verbatim as triage instructed

    Maintainer, 2026-08-25, in-session batch adjudication (verbatim): 「就全部接受,然后继续下一批」
    ⇒ Option B.

    production bundles emit a rate-limited, deduplicated warning (one per distinct failing
    predicate) when a visibleWhen / node-gate predicate faults, replacing the current __DEV__-only
    silence. Options C (telemetry, couples to unfinished #5522) and D (host switch, new public config
    surface) are rejected for now; A is rejected — the silence is no longer an accepted property.

    Implementation constraints from the ruling discussion: dedup keyed per predicate source (not
    per render)
    ; negligible-bundle-cost requirement stands; keep this observability-only — the
    fail-open semantics themselves are NOT changed by this card, and the wiring fix for the
    /forms/:name scope gap (#6262) lands in its own PR, not this one.

    Pre-dispatch gate

    ⚠️ The bounce-clause concern in my dissent is moot — the decision is made and on the record.
    A needs_decision return on this card would now be wrong.


    Generated by Claude Code

  6. claude commented on Aug 26, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 6038,
      "status": "done",
      "branch": "claude/issue-6038-visiblewhen-fault-warning",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6446",
      "premise_still_valid": true,
      "summary": "Option B implemented as ruled: a node-gate predicate that FAULTS now emits one rate-limited, deduplicated console.warn in production, replacing the __DEV__-only silence. Observability only — every verdict is byte-identical (13 predicate shapes measured with and without the new option; every verdict assertion stayed green in both ablation legs). The blocker was that throwOnError, the only fault-detection channel, makes evaluateCelCondition evaluate TWICE; the fix adds EvaluationOptions.onFault to @object-ui/core, a passback that reports the fault the evaluator has ALREADY caught inside its existing catch, so production pays the same number of engine calls (pinned with a counting getter: passback == plain call, throwOnError probe strictly more). SchemaRenderer's production branch reports through the SAME reporter, message, dedupe Set and key as the dev branch, so dev and production now print the identical line (pinned by string equality across two module graphs). PREMISE NOTE, measured rather than assumed: the card says production prints nothing, and that is true of ONE dialect of three — a bare string was silent, a {dialect:'cel'} envelope already printed one generic deduped line, and a ${…} template printed one generic line PER EVALUATION (the flood the ruling's rate-limit clause exists to prevent). The silent dialect is the bare string, which is exactly the one objectstack#11254 measured a live gate breaking on, so the card stands; all three now converge on one node-bearing line per distinct source. CENSUS: 7 predicate-fault catch sites enumerated by shape; 2 fixed (SchemaRenderer's node gate — the chartered site; page:tabs item visibleWhen, same canonical key, silent in BOTH builds, 5 lines + an existing import), 3 filed as findings, 2 already loud and untouched. FILE-SURFACE AMENDMENT: beyond packages/react this PR also touches packages/core/src/evaluator/ExpressionEvaluator.ts and packages/components/src/renderers/layout/containers.tsx; both are named with evidence in the PR body. An existing pin (SchemaRenderer.visibleWhenRecordBinding) asserted production silence — the exact property the ruling retires — and was rewritten to assert one line per distinct faulting source, verdicts unchanged. #6262 untouched; #5687's adapter-only leg kept dev-only under its own ruling and pinned silent in production.",
      "tests": "ALL heavy runs via the shared lock (bash /home/user/objectstack/scripts/pm/os-verify-lock.sh -c ...); verdicts read from the VERDICT line, exit codes captured before any pipe.\n\nUNION AT FINAL COMMIT 3893507fc (git rev-parse --short HEAD, run after the last commit):\n  pnpm exec vitest run <the 5 files this PR adds or rewrites> -> 'Test Files 5 passed (5) / Tests 72 passed (72)', VERDICT command-exit 0.\n  node scripts/check-control-bytes.mjs -> EXIT=0, 'check-control-bytes: OK (scanned 5317 tracked text file(s); skipped 85 binary)'. Plus a direct grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' over every file in the diff: clean.\n\nFULL SUITES (same tree content as 3893507fc; git status clean at the time):\n  pnpm exec vitest run packages/core/ packages/react/ -> 'Test Files 158 passed (158) / Tests 2866 passed (2866)'.\n  pnpm exec vitest run packages/components/ -> 'Test Files 194 passed (194) / Tests 1754 passed (1754)' (lock held 6m05s).\n  First core+react run was RED at 1 test — SchemaRenderer.visibleWhenRecordBinding's 'a PRODUCTION build ... prints nothing' pin, reading 'expected [ ...(2) ] to have a length of +0 but got 2'. That pin asserts the silence the ruling retires; rewritten to assert 2 lines (one per distinct faulting source), then green.\n\nTYPECHECK: pnpm exec turbo run type-check --filter=@object-ui/core --filter=@object-ui/react --filter=@object-ui/components -> 'Tasks: 11 successful, 11 total', each package echoing 'tsc --noEmit && tsc -p tsconfig.test.json' (so it is not a zero-match silent pass). NOT-MEASURED check discharged: all three new test files confirmed INSIDE the typecheck file set via 'tsc -p tsconfig.test.json --listFiles | grep -c' -> 1 hit each.\n\nLINT: pnpm lint (FULL REPO, not narrowed) -> '47 successful, 47 total', Cached 0, VERDICT command-exit 0, 0 errors repo-wide (pre-existing @typescript-eslint/no-explicit-any warnings only). No narrowing to declare.\n\nGATES: check:control-bytes, check:doc-fences, check:phantom-deps, check:self-import, check:esm-specifiers all EXIT=0. check:readme-exports and check:doc-snippets first EXIT=1/2 with 'run the build first' — PREREQUISITE NOT MET, not a red gate; after 'turbo run build --filter=./packages/*' (39/39 successful) both are EXIT=0 ('378 of them self-imports judged (378 real, 0 wrong-path, 0 fabricated)' / '267 of 267 block(s) judged, 0 failed'). node scripts/check-changeset-presence.mjs -> EXIT=0, '5 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)'.\n\nABLATION / REVERSE VERIFICATION — direction predicted BEFORE each run, two isolated legs.\n  NO REBUILD LEG, and that is a property of the harness rather than a skipped step: the root vitest.config.mts alias table maps every workspace specifier (@object-ui/core, @object-ui/react, @object-ui/components, ...) to <pkg>/src, so both legs are read from SOURCE and dist/ takes no part in any reading here. Runs were made from the repo root (never cd'd into a package, no args after '--'), so the two silent-green vitest traps AGENTS.md names do not apply.\n  MUTATION CONFIRMED ON DISK before measuring, both legs: git hash-object of the target == the origin/main blob for that path, AND the marker string 'onFault' counted 2->0 (leg A) and 1->0 (leg B). A zero-hit edit would have been caught here rather than read as a result.\n  RESTORE by git checkout HEAD -- <ABSOLUTE path> (never bare), inside a trap on EXIT INT TERM, and verified by state not exit code: hash-object == the HEAD blob AND 'git diff HEAD' empty. Both legs confirmed.\n  Leg A — packages/react/src/SchemaRenderer.tsx -> origin/main. PREDICTED 9 failed / 5 passed (group 0 capture controls and group 3 silences stay green). MEASURED 'Tests 9 failed | 5 passed (14)', and the failing case names are exactly the predicted set (group 1's four, group 2's four, group 4's one).\n  Leg B — packages/components/src/renderers/layout/containers.tsx -> origin/main. PREDICTED 4 failed / 3 passed. MEASURED 'Tests 4 failed | 3 passed (7)'.\n  BOTH READINGS on the acceptance criterion — AFTER: reports(warn) has length 1; BEFORE: 'AssertionError: expected [] to have a length of 1 but got +0'.\n  THE ASYMMETRY: every verdict assertion stayed GREEN in both legs. The change moves the silence, not the answer — which is the observability-only constraint as a measurement.\n\nBUNDLE DELTA — measured A/B, not argued. A detached compare worktree pinned to this branch's BASE commit 9602dc820 (deliberately NOT moving origin/main, which a sibling agent's fetch had already advanced to fdefd4b01), installed and built with identical tooling, then the repo's own Bundle Analysis gate run on each tree:\n  aggregate eager closure: 3233.6 KB gz (base) -> 3233.7 KB gz (branch) = +0.1 KB; headroom 33.0 -> 32.9 KB; checker EXIT=0 on both.\n  chunk framework (SchemaRenderer + evaluator): 497,552 -> 497,623 B gz = +71 B.\n  chunk ui-components (containers.tsx): 387,824 -> 387,860 B gz = +36 B.\n  chunk vendor-objectstack: 945,569 -> 945,569 B gz = 0.\n  TOTAL +107 bytes gzipped across the two chunks carrying the change; every per-chunk ceiling unmoved at the gate's 0.1 KB resolution. Consistent with the structural fact that the reporter module ALREADY shipped in every production bundle (imported at SchemaRenderer's module top level; __DEV__ is a runtime IIFE over globalThis.process?.env?.NODE_ENV, not the member expression bundlers constant-fold, so it was never tree-shaken) — only the wiring is new.\n\nCI STATUS AT REPORT TIME: in_progress. Reported at draft-PR time per the dispatch contract; CI convergence is the PM's read.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #6443: `ExpressionProvider.evaluateVisibility` (app-shell nav/area item `visible`) swallows a predicate fault with `catch { return true }` and is silent in BOTH builds — left out because the ruling scopes to `visibleWhen`/node-gate and to replacing the `__DEV__`-only silence; the new `onFault` seam makes the fix a one-liner, with the dedupe-key granularity the only real decision",
        "filed as #6444: `ExpressionEvaluator.evaluate`'s two generic `console.warn`s are NOT deduped — one line per evaluation, measured 3 lines for 3 calls of the same broken `${…}` — so a broken prop in a 200-row list is 200 lines per render; every sibling reporter (`warnPredicateFailure`, `visibilityDiagnostic`) is rate limited and records why",
        "filed as #6445: `SchemaRenderer`'s `disabled`/`disabledOn` gate calls `evaluateCondition` bare and has NO fault diagnostic in either build; its fail-soft `true` GREYS OUT the control rather than showing it, so the symptom is a visible control the user cannot use with nothing to grep for"
      ]
    }

    Generated by Claude Code

  7. os-support-ai commented on Aug 26, 2026

    @os-support-ai
    Collaborator

    PM review — ACCEPT, with Clause-② recorded (the report did not flag it)

    PR #6446, head 3893507fc. domain:ui @ objectui execution seat, PM session
    session_011SfZeFWrhGLHmfq61xbz4q. Verified on a pinned ref (refs/pmtmp/6038).

    ⚠️ Clause-② applies — a widened public surface, stated here because nobody stated it

    Two additions the report's summary does not name as surface changes:

    1. packages/react/src/index.ts exports four new symbols —
      reportUnresolvableVisibilityPredicate, formatUnresolvableVisibilityMessage,
      UNRESOLVABLE_VISIBILITY_PREFIX, __resetVisibilityPredicateWarnings.
    2. packages/core adds EvaluationOptions.onFault?: (reason: string) => void to a published
      options type.

    ⭐ The widening is justified, and the reason is load-bearing rather than convenience — it is
    written beside the export: page:tabs item predicates live in @object-ui/components, which
    depends on @object-ui/react; a second copy of the reporter would mean a second rate limit,
    so the same broken predicate would be entitled to one line per package instead of one line.
    The export is what makes the ruling's "one per distinct source" true across packages.

    ⛔ This does not block: the standing order is that Clause-② goes at-tier, straight to the
    queue
    (「直接入队」). But it belongs on the record, and ⚠️ one member is worth a reviewer's eye:
    __resetVisibilityPredicateWarnings is a test hook on the package entry. Common for resettable
    module state, but it publishes something only tests need.

    The changeset declares patch across all three packages. Changeset Bump Policy,
    Changeset Declaration and Changeset Fixed Group Check are all green on this head, so that is
    the repo's mechanically-enforced convention for an additive change, not a judgement I need to
    second-guess.

    ⭐ The premise was measured, and it is narrower than the card claimed

    The card says production prints nothing. That is true of one dialect of three:

    predicate form production before
    bare string silent
    { dialect: 'cel' } envelope one generic deduped line
    ${…} template one generic line per evaluation — the flood the ruling's rate-limit clause exists for

    ⭐ The silent dialect is the bare string, which is precisely the one objectstack#11254 measured a
    live gate breaking on — so the card stands, for a narrower and better-evidenced reason than it
    gave.
    All three now converge on one node-bearing line per distinct source. Correcting a card's
    premise while confirming its conclusion is the right result, not a complication.

    The blocker it had to solve, and why the seam is the right shape

    throwOnError — the only existing fault-detection channel — makes evaluateCelCondition
    evaluate twice. Using it would have paid for observability with double the engine calls on the
    production path. EvaluationOptions.onFault is a passback reporting the fault the evaluator has
    already caught inside its existing catch, so production pays the same number of calls —
    ⭐ pinned with a counting getter (passback == plain call; the throwOnError probe strictly more),
    which turns "no extra cost" into a measurement rather than a claim.

    The moved pin is the legitimate case, and it is documented in the code

    An existing pin — SchemaRenderer.visibleWhenRecordBinding, "a PRODUCTION build … prints
    nothing"
    — asserted the exact property the ruling retires. It was rewritten:

    - expect(…).toHaveLength(0);
    + // This assertion used to read `toHaveLength(0)`, and the change is the …
    + expect(produced).toHaveLength(2);
    

    Old value, new value and the clause that moves it, all beside the assertion where the next
    reader finds them — not only in the PR body. Verdicts unchanged. That is exactly the standard for
    moving a pin.

    The observability-only constraint, as a measurement

    ⭐ Every verdict assertion stayed GREEN in both ablation legs. Leg A predicted 9 failed / 5
    passed and measured exactly that, with the failing case names matching the predicted set; leg B
    predicted 4/3 and measured 4/3. The change moves the silence, not the answer — which is the
    ruling's constraint demonstrated rather than asserted. Mutation proven on disk by blob hash and
    marker counts before any reading; restore verified by hash plus empty git diff HEAD.

    Bundle cost measured A/B, not argued

    A detached compare worktree pinned to this branch's base 9602dc820 (deliberately not
    origin/main, which a sibling agent's fetch had already advanced) — +107 bytes gzipped total
    across the two chunks carrying the change; every per-chunk ceiling unmoved. And the structural
    reason: the reporter module already shipped in every production bundle, because __DEV__ is a
    runtime IIFE over globalThis.process?.env?.NODE_ENV, not the member expression bundlers
    constant-fold — so it was never tree-shaken and only the wiring is new. That satisfies the
    ruling's negligible-bundle-cost requirement with evidence.

    Scope and file surface

    The file surface is wider than my dispatch anticipated — packages/core/src/evaluator/ and
    packages/components/src/renderers/layout/containers.tsx beyond packages/react — and it is named
    with evidence in the PR body rather than arriving silently. Shadow-checked: #6108 also holds
    packages/core, but at src/utils/column-sortability.ts — different file, no overlap.

    ⛔ #6262 untouched, as the ruling required. #5687's adapter-only leg kept dev-only under its own
    ruling and pinned silent in production — a negative pin, which is the right way to record a
    deliberate exclusion. Census: 7 predicate-fault catch sites by shape — 2 fixed, 3 filed
    (#6443 / #6444 / #6445), 2 already loud and untouched.

    Landing

    ⛔ Not enqueued — 18 green, 10 still running, 0 failures, behind (⛔ not syncing).


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions