Skip to content

fix(webapp): scan every run-ops store for the batches list - #4806

Merged
d-cs merged 6 commits into
mainfrom
fix/batches-list-gen2-shard-legs-tri-13498
Aug 28, 2026
Merged

fix(webapp): scan every run-ops store for the batches list#4806
d-cs merged 6 commits into
mainfrom
fix/batches-list-gen2-shard-legs-tri-13498

Conversation

@d-cs

@d-cs d-cs commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

Batches created on a run-ops store other than the two the list reads were missing from the Batches page. No error, nothing logged: the page just showed fewer batches than exist. This is only reachable once additional run-ops stores are configured, so nothing changes for anyone today.

Fix

The list scanned exactly two databases and merged them by keyset. It now covers one leg per configured store, in ascending precedence order, all issued together.

The existing keyset merge generalises without change. Every leg runs the same query, with the same cursor predicate, ordering and over-fetch, so a row's rank within its own leg is never worse than its global rank, and the merged first page is still the true first page. That argument holds for any number of legs, not just two.

The empty-state check keeps its existing sequential pair, since a project with no batches is the common case for that path, then issues the remaining checks in a single round trip.

A store that declares itself an alias of another shares its client by reference, so it contributes no leg. Scanning it would query the same database twice for rows the other leg already returned. This matches how the routing store and the boot checks treat an alias.

The fan-out deliberately fails the page if any store is unreachable, rather than returning a short page. A tolerant merge would recreate the same silent absence this change removes, with a wider blast radius.

Verification

Covered by container tests against real databases: gen-1, legacy and additional stores merged into one ordered page, paging forward and back across a boundary that spans stores, and the empty-state check.

Also verified end to end against a live environment with a real corpus: the missing rows reproduce with the new leg removed and appear correctly with it present, ordering interleaves across stores as expected, paging across a store boundary loses and repeats nothing, and the page is byte-identical to before when no extra store is configured.

Merge precedence is pinned by its own test: one id seeded on two stores, asserting the higher-authority copy is the one shown. Verified by mutation, since a union-only test passes regardless of leg order.

Boot interlocks

Two related boot checks changed alongside the read path, since configuring an extra store is what makes them reachable.

A store configured while split reads are disabled is dropped in silence: no client is built, no leg is added, and rows already resident there disappear from every list with no error. The other two ways the split ends up disabled already refuse to start; this closes the one that did not, and names the stores it is refusing. A store that declares itself an alias of another owns no database, so it is exempt.

The distinct-database probe fails closed, which meant one store being briefly unreachable collapsed the deployment to single-DB and then refused the boot entirely. Each target now gets a bounded number of attempts with a short backoff before the probe gives up. Failing closed is unchanged once that budget is exhausted, and a genuine duplicate is still a final answer that is never retried.

@changeset-bot

changeset-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 2c8a051

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@d-cs d-cs self-assigned this Aug 27, 2026
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • No files to review - (🔄 Check again to try again)

Walkthrough

The batch list route passes configured non-aliased Gen-2 shard replicas to BatchListPresenter. Split-mode reads query the new store, legacy replica, and shards in parallel. Results are merged, de-duplicated, sorted, and limited. Empty-state checks also probe shards in parallel. Shard handle typing and filtering use RunOpsPrismaClient. Tests cover routing, ordering, paging, empty states, duplicate precedence, and aliases. Boot-time validation now rejects configured shards when split mode is disabled.

Merge Risk: 🔵 Low · up to 0455a

Batch pages now query every configured owning shard and fail if one is unavailable, preventing silent omissions but expanding page availability dependencies and potentially adding read load to shard writers without replicas. The PR is mergeable with owner awareness and follow-up monitoring for shard latency, failures, and primary load.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 8 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the primary change: scanning every configured run-ops store for the batches list.
Description check ✅ Passed The description explains the problem, implementation, testing, merge precedence, failure behavior, and boot interlocks. It does not include the template's issue reference, checklist, changelog, or scr…
Full details: Description check

Explanation

The description explains the problem, implementation, testing, merge precedence, failure behavior, and boot interlocks. It does not include the template's issue reference, checklist, changelog, or screenshots sections, but the core information is complete.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/batches-list-gen2-shard-legs-tri-13498

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/webapp/app/presenters/v3/BatchListPresenter.server.ts (1)

113-136: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Reject configured shards when split reads are disabled

RUN_OPS_SHARDS is parsed independently of RUN_OPS_SPLIT_ENABLED, and validation allows this combination. selectRunOpsTopology() returns an empty shards map when splitEnabled is false. The batch presenter then skips shard scans and can omit existing gen-2 batches.

Reject this combination at boot.

🧹 Nitpick comments (1)
apps/webapp/test/batchListPresenter.readroute.test.ts (1)

19-30: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move nonAliasedShardReplicas to a non-server module.

shardHandles.server.ts imports runOpsShardHandles from ~/db.server, which imports ~/env.server and resolves shard handles during module initialization. This test therefore indirectly imports env.server.ts, which violates the webapp test guideline. Re-export the pure helper from shardHandles.server.ts.

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 573413b1-1ef6-46e7-a975-b1093faa9802

📥 Commits

Reviewing files that changed from the base of the PR and between a3af29f and 895d554.

📒 Files selected for processing (5)
  • apps/webapp/app/presenters/v3/BatchListPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.batches/route.tsx
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.ts
  • apps/webapp/test/batchListPresenter.readroute.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.

📜 Review details
⏰ Context from checks skipped due to timeout. (33)
  • GitHub Check: report
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (14, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (24, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (11, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (22, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (18, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (17, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (23, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (20, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (15, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (19, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (10, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (13, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (12, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (21, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (16, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 24)
  • GitHub Check: obsmap / 🧪 Unit Tests: Observability Map
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (1, 2)
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (2, 2)
  • GitHub Check: runops-guard / runops-guard
  • GitHub Check: typecheck / typecheck
  • GitHub Check: fk-cascade-guard / fk-cascade-guard
  • GitHub Check: code-quality / code-quality
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (14)
New code must target Run Engine V2 through the singleton in `app/v3/runEngine.server.ts`; do not reintroduce V1 execution paths. V1 branches may only reject or finalize gracefully with a clean 4xx.

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Files:

  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
Never use `request.signal` to detect client disconnects. Use `getRequestAbortSignal()` from `app/services/httpAsyncStorage.server.ts`, which is wired to Express response close events.

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Files:

  • apps/webapp/app/presenters/v3/BatchListPresenter.server.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
We use vitest exclusively. **Never mock anything** - use testcontainers instead.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • apps/webapp/test/batchListPresenter.readroute.test.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
Test files must not import `app/env.server.ts`; pass configuration as options instead.

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Files:

  • apps/webapp/test/batchListPresenter.readroute.test.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
For dashboard changes, visually verify the running Remix app with Chrome DevTools MCP, using snapshots, screenshots, interaction, and console-message checks as appropriate.

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.batches/route.tsx
  • apps/webapp/app/presenters/v3/BatchListPresenter.server.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
**Prefer static imports over dynamic imports.** Only use dynamic `import()` when:

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.batches/route.tsx
  • apps/webapp/app/presenters/v3/BatchListPresenter.server.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.ts
  • apps/webapp/test/batchListPresenter.readroute.test.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
Add crumbs as you write code — not just when debugging. Mark lines with

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.batches/route.tsx
  • apps/webapp/app/presenters/v3/BatchListPresenter.server.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.ts
  • apps/webapp/test/batchListPresenter.readroute.test.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
Use zod for validation in packages/core and apps/webapp

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.batches/route.tsx
  • apps/webapp/app/presenters/v3/BatchListPresenter.server.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.ts
  • apps/webapp/test/batchListPresenter.readroute.test.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
Do not import `env.server.ts` directly or indirectly into test files; instead pass environment-dependent values through options/parameters to make code testable

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Files:

  • apps/webapp/test/batchListPresenter.readroute.test.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
Access environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env`

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.batches/route.tsx
  • apps/webapp/app/presenters/v3/BatchListPresenter.server.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.ts
  • apps/webapp/test/batchListPresenter.readroute.test.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
Use vitest for all tests in the Trigger.dev repository

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • apps/webapp/test/batchListPresenter.readroute.test.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
Use function declarations instead of default exports

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.batches/route.tsx
  • apps/webapp/app/presenters/v3/BatchListPresenter.server.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.ts
  • apps/webapp/test/batchListPresenter.readroute.test.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
Use types over interfaces for TypeScript

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.batches/route.tsx
  • apps/webapp/app/presenters/v3/BatchListPresenter.server.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.ts
  • apps/webapp/test/batchListPresenter.readroute.test.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

Files:

  • apps/webapp/app/presenters/v3/BatchListPresenter.server.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.ts
  • apps/webapp/test/batchListPresenter.readroute.test.ts
  • apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts
🧠 Learnings (2)
📚 Learning: 2026-06-16T09:19:47.637Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3960
File: apps/webapp/test/prismaInfrastructureErrorCapture.test.ts:0-0
Timestamp: 2026-06-16T09:19:47.637Z
Learning: In this repo’s Vitest setup, `vitest.config.ts` uses `globals: true`, so identifiers like `vi`, `describe`, `it`, and `expect` are available as globals in Vitest test files. During code review, do not flag missing `vi`/`describe`/`it`/`expect` imports as a runtime error or correctness issue when they’re used in `*.test.ts/tsx` or `*.spec.ts/tsx` files. Explicit imports are still preferred for consistency, but they’re not required for runtime behavior.

Applied to files:

  • apps/webapp/test/batchListPresenter.readroute.test.ts
📚 Learning: 2026-05-12T21:04:05.815Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3542
File: apps/webapp/app/components/sessions/v1/SessionStatus.tsx:1-3
Timestamp: 2026-05-12T21:04:05.815Z
Learning: In this Remix + TypeScript codebase, do not flag a server/client boundary violation when a file imports only types from a module matching `*.server`.

Specifically, it’s safe to import types using `import type { Foo } from "*.server"` or `import { type Foo } from "*.server"` because TypeScript erases type-only imports at compile time and they emit no JavaScript, so they won’t cross the Remix server/client bundle boundary.

Only raise the boundary concern for value imports (e.g., `import { Foo }` without `type`, or `import Foo`), since those produce JavaScript output.

Applied to files:

  • apps/webapp/test/batchListPresenter.readroute.test.ts
🔇 Additional comments (7)
apps/webapp/app/v3/runOpsMigration/shardHandles.server.ts (3)

13-18: LGTM!

Also applies to: 28-29


46-68: LGTM!


70-77: LGTM!

apps/webapp/app/v3/runOpsMigration/shardHandles.server.test.ts (1)

2-2: LGTM!

Also applies to: 38-62

apps/webapp/app/presenters/v3/BatchListPresenter.server.ts (1)

78-85: LGTM!

Also applies to: 117-160, 176-177, 204-226

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.batches/route.tsx (1)

59-59: LGTM!

Also applies to: 108-108

apps/webapp/test/batchListPresenter.readroute.test.ts (1)

169-192: LGTM!

Also applies to: 628-894

@d-cs
d-cs marked this pull request as ready for review August 28, 2026 10:19

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Observability map

As of 2c8a051.

Nothing in this pull request moves the report any more. The findings an earlier push reported are gone.

The score and findings here are report-only and never gate the merge. Separately, a required test suite keeps this tool's symbol and route lists in sync with the code they name, and can fail a pull request that renames or removes a symbol they reference, or that adds the first route with a segment they anticipate. Each failure names the list to edit. The rules and their reasons: internal-packages/observability-map/README.md.

@d-cs
d-cs force-pushed the fix/batches-list-gen2-shard-legs-tri-13498 branch from b6f3886 to 530a352 Compare August 28, 2026 10:53
@pkg-pr-new

pkg-pr-new Bot commented Aug 28, 2026

Copy link
Copy Markdown

Open in StackBlitz

@trigger.dev/build

npm i https://pkg.pr.new/@trigger.dev/build@74d3b05

trigger.dev

npm i https://pkg.pr.new/trigger.dev@74d3b05

@trigger.dev/core

npm i https://pkg.pr.new/@trigger.dev/core@74d3b05

@trigger.dev/python

npm i https://pkg.pr.new/@trigger.dev/python@74d3b05

@trigger.dev/react-hooks

npm i https://pkg.pr.new/@trigger.dev/react-hooks@74d3b05

@trigger.dev/redis-worker

npm i https://pkg.pr.new/@trigger.dev/redis-worker@74d3b05

@trigger.dev/rsc

npm i https://pkg.pr.new/@trigger.dev/rsc@74d3b05

@trigger.dev/schema-to-json

npm i https://pkg.pr.new/@trigger.dev/schema-to-json@74d3b05

@trigger.dev/sdk

npm i https://pkg.pr.new/@trigger.dev/sdk@74d3b05

commit: 74d3b05

@d-cs

d-cs commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks, both checked against the code.

Shards configured while split reads are disabled. The mechanism is accurate: selectRunOpsTopology returns shards: new Map() on the split-off arm (db.server.ts:334-335), so no shard leg is built.

I don't think it's a defect this PR introduces, though. On that same arm newRunOps is also replaced by the control-plane fallback, so disabling split reads already hides everything resident on the dedicated stores, not just shard-resident rows. That is the existing meaning of turning the split off, and it predates this change. Reaching a state where rows are stranded needs split enabled, rows written to a shard, then split disabled again.

A boot-time interlock rejecting the combination sounds reasonable, but it changes startup behaviour and belongs with the config and topology validation rather than a read-path fix, so I'd rather it were raised there than bolted on here. This PR deliberately leaves the single-database passthrough arm untouched.

Moving nonAliasedShardReplicas out of the .server module. This one doesn't apply: the test never loads env.server. It stubs ~/db.server at the top of the file (lines 10-14), and that mock is hoisted above the imports, so shardHandles.server.ts resolves runOpsShardHandles from the stub rather than from the real module. The import chain to env.server is cut there. If it were not, the suite would throw at import time instead of passing, which is exactly why that mock exists. The helper is already a pure function taking its handles as an argument, so there is nothing environment-dependent in what the test calls.

coderabbitai[bot]

This comment was marked as resolved.

@d-cs

d-cs commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

Fixed rather than deferred, in 6409e146a.

You were right that the combination should be rejected at boot. What made it worth closing here is that the silence is specific: the other two ways the split ends up disabled, a missing URL and a sentinel that cannot confirm distinct databases, already refuse to start. Only the flag-off case slipped through, because assertRunOpsSplitSentinel returns early on it and never reaches any shard check.

assertShardsRequireSplit is a pure predicate in the same shape as the existing realtime interlock, and it is called before that early return. It names the shards it is refusing so the misconfiguration is visible in the failure rather than in a diff of what did not happen. Four tests cover it: shards with the flag on, no shards with the flag off, the refusal itself, and that the message names the dropped keys.

On the second point, moving the helper out of the .server module: that one does not apply. The test stubs ~/db.server at the top of the file and vi.mock is hoisted above the imports, so shardHandles.server.ts resolves runOpsShardHandles from the stub and the chain to env.server is never formed. If it were, the suite would throw at import time rather than pass. The helper itself is pure and takes its handles as an argument.

devin-ai-integration[bot]

This comment was marked as resolved.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/webapp/app/v3/runOpsMigration/splitMode.server.ts (1)

81-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add temporary crumbs for the changed shard interlock flow.

Add a // @Crumbs marker or a `// `#region` `@crumbs block while developing this behavior. Strip the instrumentation with agentcrumbs strip before merge.

  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts#L81-L95: mark the new assertion path.
  • apps/webapp/app/db.server.ts#L621-L626: mark the boot-time assertion call.
  • apps/webapp/test/runOpsSplitMode.test.ts#L159-L186: mark the new test path.

As per coding guidelines, “Add crumbs as you write code — not just when debugging.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d770bf91-336c-4911-8caf-0d1069f6814e

📥 Commits

Reviewing files that changed from the base of the PR and between 659fcda and 6409e14.

📒 Files selected for processing (3)
  • apps/webapp/app/db.server.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
  • apps/webapp/test/runOpsSplitMode.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.

📜 Review details
⏰ Context from checks skipped due to timeout. (35)
  • GitHub Check: report
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (24, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (19, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (17, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (21, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (13, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (16, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (20, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (22, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (15, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (10, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (23, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (14, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (18, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (11, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (12, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 24)
  • GitHub Check: runops-guard / runops-guard
  • GitHub Check: fk-cascade-guard / fk-cascade-guard
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (2, 2)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (1, 2)
  • GitHub Check: obsmap / 🧪 Unit Tests: Observability Map
  • GitHub Check: code-quality / code-quality
  • GitHub Check: audit
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: audit
🧰 Additional context used
📓 Path-based instructions (14)
New code must target Run Engine V2 through the singleton in `app/v3/runEngine.server.ts`; do not reintroduce V1 execution paths. V1 branches may only reject or finalize gracefully with a clean 4xx.

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Files:

  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
Never use `request.signal` to detect client disconnects. Use `getRequestAbortSignal()` from `app/services/httpAsyncStorage.server.ts`, which is wired to Express response close events.

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Files:

  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
  • apps/webapp/app/db.server.ts
We use vitest exclusively. **Never mock anything** - use testcontainers instead.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
Test files must not import `app/env.server.ts`; pass configuration as options instead.

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
For dashboard changes, visually verify the running Remix app with Chrome DevTools MCP, using snapshots, screenshots, interaction, and console-message checks as appropriate.

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Files:

  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
  • apps/webapp/app/db.server.ts
**Prefer static imports over dynamic imports.** Only use dynamic `import()` when:

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
  • apps/webapp/app/db.server.ts
Add crumbs as you write code — not just when debugging. Mark lines with

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
  • apps/webapp/app/db.server.ts
Use zod for validation in packages/core and apps/webapp

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
  • apps/webapp/app/db.server.ts
Do not import `env.server.ts` directly or indirectly into test files; instead pass environment-dependent values through options/parameters to make code testable

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
Access environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env`

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
  • apps/webapp/app/db.server.ts
Use vitest for all tests in the Trigger.dev repository

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
Use function declarations instead of default exports

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
  • apps/webapp/app/db.server.ts
Use types over interfaces for TypeScript

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
  • apps/webapp/app/db.server.ts
When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
  • apps/webapp/app/db.server.ts
🧠 Learnings (1)
📚 Learning: 2026-06-04T18:16:35.386Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 3836
File: apps/supervisor/src/backpressure/backpressureMonitor.ts:3-5
Timestamp: 2026-06-04T18:16:35.386Z
Learning: When reviewing TypeScript in this repo, apply the rule “prefer type aliases over interfaces” only to data/object shapes and union/intersection type modeling. If an interface is being used as a behavioral contract for collaborators to implement (e.g., method-shape interfaces that define required behavior, such as `BackpressureLogger` / `BackpressureSignalSource` in `apps/supervisor/src/backpressure/backpressureMonitor.ts`), keep it as an `interface` and do not flag it as a type-alias-vs-interface violation.

Applied to files:

  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/webapp/app/db.server.ts (1)

623-626: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add required @crumbs instrumentation to the new code paths.

  • apps/webapp/app/db.server.ts#L623-L626: add a crumb for the boot-time shard interlock.
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts#L82-L105: add crumbs for owning-shard filtering and rejection.
  • apps/webapp/test/runOpsSplitMode.test.ts#L160-L208: add crumbs for the new structured descriptor cases.

As per coding guidelines: “Add crumbs as you write code — not just when debugging.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6d1ec123-e5c6-46e6-8d0f-1cd78e808501

📥 Commits

Reviewing files that changed from the base of the PR and between 6409e14 and 0455ab2.

📒 Files selected for processing (3)
  • apps/webapp/app/db.server.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
  • apps/webapp/test/runOpsSplitMode.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.

📜 Review details
⏰ Context from checks skipped due to timeout. (34)
  • GitHub Check: report
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (14, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (17, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (19, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (15, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (24, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (12, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (23, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (16, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (11, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (21, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (10, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (13, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (18, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (22, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (20, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 24)
  • GitHub Check: fk-cascade-guard / fk-cascade-guard
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (1, 2)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: runops-guard / runops-guard
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (2, 2)
  • GitHub Check: obsmap / 🧪 Unit Tests: Observability Map
  • GitHub Check: code-quality / code-quality
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: audit
🧰 Additional context used
📓 Path-based instructions (14)
New code must target Run Engine V2 through the singleton in `app/v3/runEngine.server.ts`; do not reintroduce V1 execution paths. V1 branches may only reject or finalize gracefully with a clean 4xx.

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Files:

  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
Never use `request.signal` to detect client disconnects. Use `getRequestAbortSignal()` from `app/services/httpAsyncStorage.server.ts`, which is wired to Express response close events.

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Files:

  • apps/webapp/app/db.server.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
We use vitest exclusively. **Never mock anything** - use testcontainers instead.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
Test files must not import `app/env.server.ts`; pass configuration as options instead.

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
For dashboard changes, visually verify the running Remix app with Chrome DevTools MCP, using snapshots, screenshots, interaction, and console-message checks as appropriate.

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Files:

  • apps/webapp/app/db.server.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
**Prefer static imports over dynamic imports.** Only use dynamic `import()` when:

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/db.server.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
Add crumbs as you write code — not just when debugging. Mark lines with

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/db.server.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
Use zod for validation in packages/core and apps/webapp

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/db.server.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
Do not import `env.server.ts` directly or indirectly into test files; instead pass environment-dependent values through options/parameters to make code testable

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
Access environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env`

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/db.server.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
Use vitest for all tests in the Trigger.dev repository

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
Use function declarations instead of default exports

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/db.server.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
Use types over interfaces for TypeScript

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/db.server.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

Files:

  • apps/webapp/test/runOpsSplitMode.test.ts
  • apps/webapp/app/db.server.ts
  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
🧠 Learnings (3)
📚 Learning: 2026-06-16T09:19:47.637Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3960
File: apps/webapp/test/prismaInfrastructureErrorCapture.test.ts:0-0
Timestamp: 2026-06-16T09:19:47.637Z
Learning: In this repo’s Vitest setup, `vitest.config.ts` uses `globals: true`, so identifiers like `vi`, `describe`, `it`, and `expect` are available as globals in Vitest test files. During code review, do not flag missing `vi`/`describe`/`it`/`expect` imports as a runtime error or correctness issue when they’re used in `*.test.ts/tsx` or `*.spec.ts/tsx` files. Explicit imports are still preferred for consistency, but they’re not required for runtime behavior.

Applied to files:

  • apps/webapp/test/runOpsSplitMode.test.ts
📚 Learning: 2026-06-04T18:16:35.386Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 3836
File: apps/supervisor/src/backpressure/backpressureMonitor.ts:3-5
Timestamp: 2026-06-04T18:16:35.386Z
Learning: When reviewing TypeScript in this repo, apply the rule “prefer type aliases over interfaces” only to data/object shapes and union/intersection type modeling. If an interface is being used as a behavioral contract for collaborators to implement (e.g., method-shape interfaces that define required behavior, such as `BackpressureLogger` / `BackpressureSignalSource` in `apps/supervisor/src/backpressure/backpressureMonitor.ts`), keep it as an `interface` and do not flag it as a type-alias-vs-interface violation.

Applied to files:

  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
📚 Learning: 2026-05-12T21:04:05.815Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3542
File: apps/webapp/app/components/sessions/v1/SessionStatus.tsx:1-3
Timestamp: 2026-05-12T21:04:05.815Z
Learning: In this Remix + TypeScript codebase, do not flag a server/client boundary violation when a file imports only types from a module matching `*.server`.

Specifically, it’s safe to import types using `import type { Foo } from "*.server"` or `import { type Foo } from "*.server"` because TypeScript erases type-only imports at compile time and they emit no JavaScript, so they won’t cross the Remix server/client bundle boundary.

Only raise the boundary concern for value imports (e.g., `import { Foo }` without `type`, or `import Foo`), since those produce JavaScript output.

Applied to files:

  • apps/webapp/app/v3/runOpsMigration/splitMode.server.ts
🔇 Additional comments (3)
apps/webapp/app/db.server.ts (1)

31-31: LGTM!

apps/webapp/app/v3/runOpsMigration/splitMode.server.ts (1)

10-14: LGTM!

apps/webapp/test/runOpsSplitMode.test.ts (1)

6-6: LGTM!

d-cs added 5 commits August 28, 2026 14:01
The batches list read exactly two databases, so a batch living on any
other configured run-ops store was missing from the page with no error
and nothing logged.

The scan now covers one leg per store, in ascending precedence order,
and the existing keyset merge generalises unchanged: every leg runs the
same query, so the merged first page is still the true first page. The
empty-state check keeps its existing pair and issues the rest in one
round trip. A store that declares itself an alias of another shares its
client by reference and contributes no leg, matching how the routing
store and the boot sentinels treat one.

With no extra store configured the page is byte-identical to today.
The merge was only ever exercised as a union, with each id on exactly one
store, so nothing caught a leg-order regression: a union is insensitive to
the order its legs are applied in.

Seed one id on two stores and assert the higher-authority copy is the one
the page shows. Verified by mutation: making the lower-authority insert
unconditional, and applying the legs in the wrong order, both fail it.

Also corrects a comment that justified the leg order with a claim about
duplicate ids being impossible. A single writer routes creates by id shape,
but a row can still sit on two stores while data is moved between them,
which is why the routing store dedupes batches without alarming.
The precedence test now guards the leg order that one of them warned about.
…ut the split

Shard clients are only built on the split-on arm of the topology selector, so a
shard configured while the split flag is off was dropped in silence: no client,
no fan-out leg, and any row already resident on that database missing from every
list with no error.

The other two ways the split ends up disabled, a missing URL and a sentinel that
cannot confirm distinct databases, already refuse to boot. This closes the one
that did not, and names the dropped shards so the misconfiguration is obvious.
An aliased shard owns no database: it shares its target's client by reference,
so its rows are still read with the split off and nothing is dropped. Feeding
every configured key to the interlock refused an alias-only config for no
reason.

The exemption now lives inside the interlock rather than at its call site, so
it takes the raw descriptors and no caller can forget to apply it. This matches
where the distinctness sentinel, the coresidency loop and replication already
draw the line.
@d-cs
d-cs force-pushed the fix/batches-list-gen2-shard-legs-tri-13498 branch from 74d3b05 to 6973ca5 Compare August 28, 2026 13:02
The probe fails closed, so one store being briefly unreachable collapsed the
deployment to single-DB and the boot interlock then refused the boot. With more
than two stores configured that turns a transient blip on any one of them into
a fleet-wide startup failure.

Each target now gets a bounded number of attempts with a short backoff before
the probe gives up. Failing closed is unchanged once the budget is exhausted:
"distinct" stays a positive claim a failed probe cannot support. A genuine
duplicate is a final answer and is never retried, so a misconfigured deployment
still fails on the first pass.

Retries are per target, so one slow store does not re-probe the stores that
already answered.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Devin Review

Comment thread apps/webapp/app/v3/runOpsMigration/shardHandles.server.ts
@d-cs
d-cs merged commit 63b8e6e into main Aug 28, 2026
54 checks passed
@d-cs
d-cs deleted the fix/batches-list-gen2-shard-legs-tri-13498 branch August 28, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants