Skip to content

test(runtime): pin the measured loadMany vs by-name divergence for #14423 (a) - #14741

Merged
os-musk merged 5 commits into
mainfrom
claude/issue-14423-loadmany-vs-byname-fixture
Sep 3, 2026
Merged

test(runtime): pin the measured loadMany vs by-name divergence for #14423 (a)#14741
os-musk merged 5 commits into
mainfrom
claude/issue-14423-loadmany-vs-byname-fixture

Conversation

@os-musk

@os-musk os-musk commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Part of #14423

Test-only. This pins a CURRENT DIVERGENCE; it does not assert fixed behaviour, and it fixes nothing. Should asymmetry (a) ever be repaired, these expectations are the ones that must be REWRITTEN, not defended — the fixture header says so in the file.

Triage ruled one deliverable on that card: a fixture attempt for asymmetry (a), then stop and report. The measurement is posted on the card at #14423 (comment). ⛔ No behaviour change ships here — not the injected by-name rung, not the ADR-0078 registry enumeration, and (b) is untouched.

What it measures

Can meta.loadMany('action') omit a name that meta.load / loadDiagnosed serves, so that runActionGovernanceInventory and resolveRouteActionDeclaration disagree about whether a declaration exists? Yes. Four positive configurations and two controls:

configuration needs
C1 control: same plane, name column and body.name agreeing — both reads answer, audit and router AGREE
C2 DatabaseLoader row keyed by the name COLUMN, body carries none nothing
C3 the plural read throws while the by-name read answers, one plane, one scope a partial read
C4 env-scoped kernel: the audit's sync lookup cannot see a SCOPED instance and throws; the router's getServiceAsync(name, envId) gets the env's plane a SCOPED registration
C5 control: singleton metadata, one scope — both lookups return the SAME instance, nothing diverges
C6 real NodeMetadataManager over a real FilesystemLoader, body carries no name nothing

The mechanism: loadMany is the unkeyed plural read. Identity in the plane is the key the store holds an item under, not body.name (#14205), and collectEngineActionDeclarations requires typeof action.name === 'string' — so a name whose identity lives only in the store's key never reaches the audit, while the router asks load for that exact name and is served. listNames('action') returns it in both shipped loaders, which is what makes the divergence unambiguous.

C6 is the load-bearing one: the shipped in-process composition, no failure injection and no scoping. C4's SCOPED registration is architecturally supported (ServiceLifecycle.SCOPED, clearScope, HttpDispatcher.resolveService's scoped first branch) but no in-repo registration of metadata uses itpackages/metadata/src/plugin.ts:374 registers a static instance. That qualifier is in the fixture and in the card comment; the measured population rests on C2, C3 and C6.

Real: runActionGovernanceInventory, collectEngineActionDeclarations, resolveRouteActionDeclaration, MetadataManager, DatabaseLoader, FilesystemLoader, NodeMetadataManager, ObjectKernel + PluginLoader scoping. Doubled: the row store under DatabaseLoader in C1-C5, and deps.resolveService (a transcription of HttpDispatcher.resolveService's scoped branch, quoted in the header because the method is private). C6 uses no engine double at all.

Verification, at 51fced926

pnpm --filter '@objectstack/runtime^...' build
pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 src/action-governance-scope-divergence.test.ts
  -> Test Files 1 passed (1) · Tests 6 passed (6)

Gate union re-derived from the diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (26 families / 33 commands) and run at this same commit: 31 green, 1 NOT MEASURED. check:type-check-debt --re-measure is green on a built workspace — 22 ledger entries re-measured, 755 raw errors, "none above its recorded number", "surplus: none", which is the only channel that sees this file at all (packages/runtime/tsconfig.json excludes **/*.test.ts, so the package's own typecheck says nothing about it). check:test-completeness exits 3, PREREQUISITE NOT MET — it wants a saved turbo run test log, so it is recorded as NOT MEASURED rather than as a pass.

Three gate findings were real and are fixed in the fixture, not baselined around:

  • check:engine-double-contract — the double's findOne now routes through assertEngineFindOnePredicate; the RETAINED ledger learned the new pin via --write (coverage GROWS, the shrink-only baseline is untouched).
  • check:objectql-double-limit — the find double applies the caller's bound after the filter, by presence. The outage in C3 became a separate listDownEngine double rather than an injected failFind hook, because the hook made the base double undrivable by that gate's control probe.
  • check:slot-lookup — the audit's lookup is typed by passing the slot's contract IMetadataService as the getService type argument, instead of erased to any.

ESLint, narrowed and declared: 1 of the 2 changed files is in the population (the other is refused by eslint's own config — "File ignored because no matching configuration was supplied"), --format json reports 1 file linted, 0 errors, 0 warnings, and the repo runs one eslint.config.mjs which "never enables type-aware linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file", so this diff cannot move the verdict on any file it does not touch.

Not in scope

#14099 holds packages/objectql/src/engine.ts; nothing here touches it. action-governance.ts and plugin.ts were read only. #14423 (b) is untouched and, per the triage note, is not put to the maintainer on the strength of that comment alone.

🤖 Generated with Claude Code

https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68


Generated by Claude Code

…vs router by-name

Measurement only; no behaviour change. Pins the CURRENT divergence between
`runActionGovernanceInventory`'s metadata-plane source (`meta.loadMany('action')`)
and `resolveRouteActionDeclaration`'s third rung (`meta.loadDiagnosed('action', name)`).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
…ith no injection

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions github-actions Bot added the size/m label Sep 2, 2026
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json ba1806db6659d5ea24521543848e5d618d5bf94dpackageMentionDocs.

os-musk commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator Author

Landing provenance — domain:engine execution seat

Enqueued at 51fced926.

1. The ruling's ⛔ was verified by content, not taken on the dev's word. Triage bound this card to a measurement with no behaviour change. The diff is +421 insertions, 0 deletions, 2 files — one new test file and five lines of ledger — and touches no source:

packages/runtime/src/action-governance-scope-divergence.test.ts | 416 +++++
scripts/engine-double-contract.pinned.json                      |   5 +

action-governance.ts and plugin.ts are absent from the diff, as is packages/objectql/src/engine.ts (held by in-flight #14099). Asymmetry (b) is untouched.

2. Clause ② is no — no published export, accept set, wire shape or authorable key moves; a new test file and a pinned-ledger row reach no consumer. In-seat review is therefore permitted and no contract-review fuse was hung. skip-changeset is the landed precedent for a comment-and-test-only diff.

3. Governed-surface predicate, re-run on the FINAL two-file list — ⛔ not recalled:

node scripts/pm/check-governed-merges.mjs --test \
  packages/runtime/src/action-governance-scope-divergence.test.ts \
  scripts/engine-double-contract.pinned.json
→ 0 of 2 path(s) hit the register (5 surfaces) — NOT governed; ordinary queue landing applies.

4. Every check run green or skipped — 40 check runs, read at perPage: 50, each by its own completed + conclusion, ⛔ never by an aggregate. ⚠️ Worth recording: Test Core (1/6) took 12m50s here against ~4m for shards 2-6, and Lint & Repo Gates took 16m34s. Both passed. That shard is the one #14648's queue flake lives in, so a long green is the outcome to note rather than the one to celebrate.

5. Flipped draft: false, read back draft: false, then armed auto-merge SQUASH. ⚠️ The API echo reads method: MERGE regardless of the method requested — a known platform artifact; the merge-queue build ref is what actually says how it lands.

What this PR does NOT do, restated because a green test file is easy to misread as a fix: it pins a current divergence. If asymmetry (a) is ever repaired, these six expectations are the ones to rewrite, not defend — the fixture header says so in the file, which is the only place a future reader will look.

The card #14423 has re-graded to needs-user-decision carrying the measurement and three options; ⛔ this seat did not choose among them and did not re-grade its priority.


Generated by Claude Code

@os-musk
os-musk added this pull request to the merge queue Sep 2, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33699706473 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test:  FAIL   integration  test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
      ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 34 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

os-musk commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

Ejected from the merge queue by #14648 — evidence, not inference

This PR was armed at 23:29Z, entered the queue, and left it without landing. The seat refused to call that an ejection until it had the evidence; here it is.

Merge-queue build: run 33699706473 (event: merge_group, head c25c053c0, created 2026-09-03T00:29:15Z) — completed/failure. Failing job: Test Core (1/6).

::error file=…/packages/cli/test/run-dev-unbuilt-workspace.e2e.test.ts,
        line=317, column=27
AssertionError: expected 'SIGKILL' to be null
Test Files  1 failed | 234 passed (235)
Duration  783.18s (transform 42.76s, import 323.83s, tests 1989.47s)

Same file, same case, same line 317 as #14648's anchor signature — and the same structural oversubscription, 783.18s wall against 1989.47s of self-reported test time (2.54×).

Ejected by #14648, not by anything in this diff. This PR is +421 insertions, 0 deletions, a new test file plus five ledger lines, and touches no source at all.

How this was established, since the obvious route does not exist

The recorded platform fact is that the dequeue signal is the timeline event removed_from_merge_queue — and no available tool exposes PR timelines. The route that does work: merge-queue builds run on: merge_group against the gh-readonly-queue/main/pr-<n>-<parent> refs, so actions_list with event: merge_group lists them with their conclusions, and the branch name carries the PR number. ⭐ That is now the instrument for "was it ejected, and why" — better than the timeline would have been, because it also gives the cause in one step.

Disposition

Held on #14648, per the standing rule. ⛔ Auto-merge deliberately not re-armed · ⛔ no re-run (the one permitted re-run confirms a not-this-PR failure whose cause is unknown; this cause is established) · ⛔ the test is not weakened, skipped or quarantined.

⚠️ Card #14423 stays needs-user-decision and is not flipped to pm:blocked — the two are mutually exclusive, and the decision that card carries (three options on asymmetry (a), with C recommended) is genuinely open and independent of this PR's landing. The queue hold lives here on the PR, which is the thing actually blocked.

⚠️ A related measurement, because it changes what "wait for the fix" is worth

PR #14715 — the open fix for #14648has itself been ejected by #14648 twice, and its own new assertion message refutes its approach: cap 180000 ms … this child ran 180105 ms; case 1 measured the same child at 6125 ms on this runner minutes earlier. A ~29× blowup is a stall, not a slow run, and no constant ceiling closes it. Recorded on the anchor at #14648 (comment 5519383375), including a correction the seat owed: it had twice described #14715 as asserting the child's own exit reason, read off the title — the diff still asserts the absence of a signal.


Generated by Claude Code

@os-musk
os-musk added this pull request to the merge queue Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33721322980 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test:  FAIL   integration  test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
      ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️ 本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 80 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33722712527 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test:  FAIL   integration  test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
      ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️ 本 PR 过去 24h 已在队列失败 2 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 85 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33724231534 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test:  FAIL   integration  test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
      ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️ 本 PR 过去 24h 已在队列失败 3 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 90 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

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

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants