Skip to content

fix(core): batch taxonomy term counts under D1's compound-SELECT limit - #2331

Merged
khoinguyenpham04 merged 6 commits into
emdash-cms:mainfrom
MA2153:fix/taxonomy-term-counts-compound-select
Aug 5, 2026
Merged

fix(core): batch taxonomy term counts under D1's compound-SELECT limit#2331
khoinguyenpham04 merged 6 commits into
emdash-cms:mainfrom
MA2153:fix/taxonomy-term-counts-compound-select

Conversation

@MA2153

@MA2153 MA2153 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

fetchVisibleTermCounts built one UNION ALL branch per declared collection. Cloudflare D1 sets SQLITE_LIMIT_COMPOUND_SELECT to 5 — SQLite's own default is 500 — so a taxonomy declaring six or more collections produced SQL the backend rejected outright. Because the counts decorate the admin term list, the failure took the whole list down: GET /_emdash/api/taxonomies/:name/terms returned 500 and the admin rendered an empty list for a taxonomy whose terms were perfectly intact.

The ceiling is 5, measured against a live D1 rather than inferred: with the same query shape and only the branch count varying, five UNION ALL branches compile and six fail with too many terms in compound SELECT. That also settles #895, where the dashboard hit this at 9 arms and was fixed by fan-out without anyone bisecting the real threshold.

The fix splits the collections into batches the backend can carry, runs the batches concurrently, and sums the resulting maps — per-collection sums are commutative, so partitioning cannot change a total. The missing-ec_*-table fallback moves per batch, so one absent table only degrades its own batch instead of the whole computation.

The ceiling belongs to the adapter, not to core. compoundSelectLimit(db) (database/dialect-helpers.ts) reads it off the Kysely adapter and returns null when the backend declares none — which is every backend except D1, so Node/SQLite, libSQL and Postgres keep counting every collection in a single statement no matter how many there are. @emdash-cms/cloudflare's three D1 dialects (raw binding, session, coalescing) share a D1Adapter base that declares compoundSelectLimit = 5. The limit is a property of the SQLite build behind the dialect rather than of the SQL flavour, which is why detectDialect()'s sqlite | postgres answer can't carry it.

On D1 the batch size is the measured 5, not a more conservative 4: a taxonomy at or below the ceiling still issues exactly one query, so nothing on the logged-out render path regresses. 4 would push every 5-collection taxonomy from one query to two. The perf fixture's taxonomies declare one collection each, so the query-count snapshots are unchanged.

handleTermList's bare catch now logs the original error. Diagnosing this required reconstructing the generated SQL by hand, because nothing anywhere recorded why the list failed.

Closes #2330

Notes on the issue's other two suggestions

Deliberately not included, happy to be overruled:

  • Widening the fallback's isMissingTableError predicate. With the ceiling gone, the only errors a wider predicate would newly swallow are genuine ones.
  • Degrading handleTermList to counts-free terms on a count failure. It trades a loud 500 for silently wrong-looking admin data, and the failure that motivated it no longer exists. That reads like a maintainer's call about how much decoration is allowed to fail, not something to slip into a bug fix.

Testing

TDD: the regression test failed with exactly the reported error at runCounts before the fix.

better-sqlite3 uses SQLite's upstream default of 500 and offers no way to lower it, so a query shape D1 rejects runs happily in tests. setupTestDatabaseWithCompoundSelectLimit(limit) stands in for either kind of backend: given a number its dialect declares the ceiling the way D1's does and prepare() rejects statements past it — where SQLite raises the error too — with D1's error text; given null it declares nothing, like better-sqlite3 itself. It also hands back every prepared statement, so a test can assert how many round trips the count took.

Three cases, all at LIMIT + 1 collections:

  • with a declared ceiling, counts aggregate across every collection and handleTermList returns them (the reported symptom), in 2 statements;
  • with no declared ceiling, the same counts come back in 1 statement;
  • a missing ec_* table beyond the first batch is still skipped. Mutation-checked by restricting the fallback to the first batch — the test fails as intended.

On the @emdash-cms/cloudflare side, each of the three D1 dialects is asserted to declare the ceiling on its adapter — an adapter that drops it silently sends D1 compound SELECTs it rejects.

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change) — packages/core unit suite (3448 passed), plus the taxonomy, database, utils and loader suites and packages/cloudflare's tests/db re-run after the adapter change
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable) — n/a, no admin UI strings; the added log line is server-side and English-only by convention
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion — n/a, bug fix

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 5 (Claude Code)

Screenshots / test output

Before the fix, with the D1 ceiling imposed on the test database:

FAIL  tests/unit/taxonomies/term-counts.test.ts > visible term counts past the
      compound-SELECT ceiling > aggregates every declared collection when
      there are more than one statement can carry
Error: too many terms in compound SELECT: SQLITE_ERROR
 ❯ runCounts src/taxonomies/term-counts.ts:69:17
 ❯ Module.fetchVisibleTermCounts src/taxonomies/term-counts.ts:104:10

After, with the ceiling now read off the adapter — packages/core taxonomies (unit + integration) and packages/cloudflare tests/db:

Test Files  10 passed (10)
     Tests  87 passed (87)

Test Files  11 passed (11)
     Tests  176 passed (176)

Review response

Comment discipline — #2330 in the test file (40fae20). Fixed, and the review is right on both counts. The JSDoc block above the new describe is deleted outright rather than reworded: it was narrative about how the bug presented, which is exactly what the commit message and this description are for. The describe name is now visible term counts past the compound-SELECT ceiling — it already said what the block covers without the number. Nothing else in the diff carries an issue reference; the #581 in term-counts.ts:2 is pre-existing and untouched.

Targeted suite re-run after the edit: tests/unit/taxonomies/term-counts.test.ts — 10 passed.

D1's ceiling was applied to every database — six collections went from one query to two on Node/SQLite. Correct, and the measurement matches: batching was unconditional, so every backend paid for a limit only D1 has. The ceiling now comes from the adapter (compoundSelectLimit(db), described above), so a backend that declares nothing runs the counts as a single statement at any collection count — Node/SQLite, libSQL and Postgres included — and only D1 splits. The new test asserts the single statement directly rather than trusting the shape: it fails on the previous commit.

Durable Object SQLite (DOSqlDialect, PreviewDODialect) deliberately declares nothing. D1 may well share the build, but I have no measurement for a DO the way I do for D1, and guessing here would cost the extra query on a backend that may not need it. Happy to add it if you know the answer.

Why the changeset is still a single emdash patch. The batching and the adapter lookup both live in core; @emdash-cms/cloudflare contributes one declared constant. Since .changeset/config.json puts the two packages in the same fixed group, listing emdash alone already versions cloudflare in lockstep — adding it to the changeset would only copy the same note into a second changelog without changing what anyone installs.

compoundSelectLimit isn't validated — 0/negative loop forever, NaN/fractions build invalid batches (060499c). Agreed, and the reasoning holds for every one of those values; the probe now requires a positive integer and throws naming the adapter class otherwise. The ceiling is read off a third-party Kysely adapter by duck-typing, so core has no compile-time guarantee about what it gets, and each malformed value fails silently rather than loudly:

  • 0 or negative: chunks() never advances i, so fetchVisibleTermCounts hangs — worse than the 500 this PR set out to fix.
  • fractional (2.5): slice() truncates both bounds, so batch 2 starts at index 2 after batch 1 ended there — an element lands in two batches and its count is added twice. Silently wrong numbers, no error anywhere.
  • NaN: one empty batch, so the UNION has no terms and the statement is invalid SQL.

Declared-but-not-a-number ("5") throws too rather than falling through to null. Silently ignoring a ceiling an adapter meant to declare is exactly the failure this PR exists to remove — it would send D1 the compound SELECTs it rejects, which is the reported bug with an extra step.

Throwing is the right severity here: this is a static property of the dialect, so it is wrong on the first request or never, and it can only be fixed by the person who wrote the adapter. A silent null would hide that from them.

New unit test (tests/unit/database/compound-select-limit.test.ts) covers null for an adapter declaring nothing, pass-through for 5 and 1, and a throw for 0, -1, NaN, Infinity, 2.5, "5" and null — 8 of its 10 cases fail on the previous commit. packages/core taxonomies + database and packages/cloudflare tests/db re-run clean.

🤖 Generated with Claude Code

fetchVisibleTermCounts built one UNION ALL branch per declared collection.
Cloudflare D1 sets SQLITE_LIMIT_COMPOUND_SELECT to 5 (SQLite's own default is
500), so a taxonomy declaring six or more collections produced SQL the backend
rejected outright. The counts decorate the admin term list, so the whole list
500'd and rendered empty while the terms themselves were intact.

Batch the branches into groups of SQL_COMPOUND_SELECT_LIMIT and sum the
resulting maps. Taxonomies at or below the ceiling still issue exactly one
query, so nothing on the logged-out render path regresses. The missing-ec_*-
table fallback moves per batch so an absent table only degrades its own batch.

The ceiling was measured against a live D1: five UNION ALL branches compile,
six fail with "too many terms in compound SELECT".

better-sqlite3 offers no way to lower the limit, so the regression test imposes
it at prepare() — where SQLite raises it too — with D1's error text.

handleTermList's bare catch now logs the original error; reconstructing the
generated SQL by hand was the only way to diagnose this.

Closes emdash-cms#2330

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Aug 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 060499c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 17 packages
Name Type
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/plugin-mcp-smoke Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/admin Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

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

@pkg-pr-new

pkg-pr-new Bot commented Aug 3, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@2331

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@2331

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@2331

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@2331

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@2331

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@2331

emdash

npm i https://pkg.pr.new/emdash@2331

create-emdash

npm i https://pkg.pr.new/create-emdash@2331

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@2331

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@2331

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@2331

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@2331

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@2331

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@2331

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@2331

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@2331

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@2331

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@2331

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@2331

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@2331

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@2331

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@2331

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@2331

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@2331

commit: 060499c

@emdashbot emdashbot 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.

This PR correctly addresses the D1 compound-SELECT ceiling: batching UNION ALL branches at the measured limit of 5 keeps the single-query fast path for taxonomies with ≤5 collections while avoiding the D1 error for larger ones, and moving the missing-table fallback inside each batch preserves the graceful-degradation behavior. I read the changed implementation, the new test harness, callers (handleTermList/handleTermGet and the request-cached getVisibleTermCounts), SQL construction, and checked against AGENTS.md conventions.

The logic is sound, the regression tests cover the reported failure (six collections) and the missing-table-across-batches case, and the changeset is user-facing and properly scoped. The only blocker-ish issue is a comment-discipline violation: the new test block references the closed issue #2330 in both a JSDoc comment and the describe() string, which AGENTS.md explicitly forbids. Everything else is clean.

Comment thread packages/core/tests/unit/taxonomies/term-counts.test.ts Outdated
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-review No maintainer or bot review yet labels Aug 3, 2026
Comments are evergreen; the compound-SELECT background belongs in the
commit message and PR description, not in the test file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/awaiting-author Reviewed; waiting on the author to respond labels Aug 3, 2026
@MA2153 MA2153 added the bot:review Trigger an emdashbot code review on this PR label Aug 3, 2026

@emdashbot emdashbot 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.

The approach is sound: batching a taxonomy's collections at D1's measured compound-SELECT limit of 5 keeps the single-query fast path for the common ≤5-collection case, while letting larger taxonomies survive. Moving the missing-table fallback per batch preserves the existing graceful-degradation behavior without one absent collection poisoning the whole count. This re-read checks the author addressed the prior review note and re-scrutinizes the changed code against AGENTS.md.

What's fixed versus the last review:

  • The #2330 issue reference is gone: the JSDoc block above the new describe was deleted, and the describe string now reads visible term counts past the compound-SELECT ceiling without an issue number.

Fresh checks on the current diff:

  • term-counts.ts chunks by SQL_COMPOUND_SELECT_LIMIT (5) and runs batches concurrently; per-group sums are commutative, so partitioning cannot change totals.
  • The missing-ec_*-table fallback is now scoped to each batch, verified by the new regression test for the missing table falling beyond the first batch.
  • handleTermList now logs the original server-side error before returning a safe envelope; this is diagnostics only and does not leak to clients.
  • SQL construction still uses Kysely's tagged templates and sql.ref(); no raw interpolation, no new identifier validation gaps.
  • The public render path calls fetchVisibleTermCounts through the request-cached getVisibleTermCounts, which already keys on the sorted collection scope; the only hot-path change is that an invalid case (>5 collections) now works instead of 500ing.
  • Changeset is present, present-tense, and user-facing.
  • No new comments reference issues/PRs/review threads; the remaining #581 references are pre-existing and were not introduced by this change.

I found nothing else that needs fixing.

@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Aug 3, 2026
@github-actions github-actions Bot added review/approved Approved; no new commits since and removed review/needs-rereview Author pushed changes since the last review labels Aug 4, 2026

@khoinguyenpham04 khoinguyenpham04 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

could we avoid applying d1’s five-collection limit to every database? i tested six collections end to end: node/sqlite goes from one query to two on both admin and public pages. d1 needs this split, but other databases do not.

plz keep the single query where the limit doesn’t apply.

Batching the term-count branches was unconditional, so every database paid
for a limit only D1 has: a taxonomy with six collections went from one query
to two on Node/SQLite and Postgres, on both the admin and public paths.

The ceiling now comes from the Kysely adapter. compoundSelectLimit(db) reads
it off the adapter and returns null when none is declared, which is every
backend except D1 — those keep counting all collections in one statement at
any collection count. The three D1 dialects share a D1Adapter base declaring
compoundSelectLimit = 5. The limit is a property of the SQLite build behind
the dialect rather than of the SQL flavour, so detectDialect()'s
sqlite | postgres answer cannot carry it; SQL_COMPOUND_SELECT_LIMIT is gone
from utils/chunks.ts.

setupTestDatabaseWithCompoundSelectLimit() now models either kind of backend
and records prepared statements, so the new test asserts the single statement
directly: it fails against the previous commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review area/cloudflare and removed review/approved Approved; no new commits since labels Aug 5, 2026
@MA2153 MA2153 added the bot:review Trigger an emdashbot code review on this PR label Aug 5, 2026

@emdashbot emdashbot 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.

The approach is still sound: reading the compound-SELECT ceiling from the adapter keeps the single-query fast path for most taxonomies and lets larger ones survive on D1, while the per-batch missing-table fallback preserves graceful degradation. The new server-side log in handleTermList stays internal and does not leak to clients.

This is a re-review after the prior approval. The core test file's #2330 reference is gone as promised, and I re-checked all changed files against AGENTS.md. I found only one consistency slip: the Cloudflare test file still labels its new describe block with #2330. A describe label is not a code comment, but it is persistent narrative that will show up in test output, and leaving it contradicts the comment-discipline cleanup you just applied in the core test file. No logic bugs, SQL safety issues, or logged-out query-count regressions remain.

Comment thread packages/cloudflare/tests/db/d1-dialect.test.ts Outdated
@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Aug 5, 2026
Co-authored-by: emdashbot[bot] <273199577+emdashbot[bot]@users.noreply.github.com>
@MA2153

MA2153 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

@khoinguyenpham04 done, it is now isolated per adapter. could you re-check?

@khoinguyenpham04 khoinguyenpham04 added the bot:review Trigger an emdashbot code review on this PR label Aug 5, 2026

@emdashbot emdashbot 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.

The approach remains sound: reading the compound-SELECT ceiling from the Kysely adapter keeps the single-statement fast path for most taxonomies while splitting only when a backend like D1 declares a low limit. Batching is commutative for per-collection sums, and moving the missing-ec_*-table fallback into each batch preserves graceful degradation without letting one absent table kill the whole count. The added console.error in handleTermList logs server-side without leaking to clients.

This is a re-review after the prior round. The only outstanding item then was a #2330 reference in the Cloudflare test file's new describe block; that reference is gone — the block now reads describe("D1 compound-SELECT ceiling", ...). I also re-read all changed files against AGENTS.md conventions and checked:

  • SQL safety: collectionBranch still validates collection slugs with validateIdentifier before interpolating them into sql.ref(ec_${collection}), and the module uses Kysely parameter binding for values.
  • Logged-out query counts: the change is in the admin term-list handler; no new query is added to a public route, and the request-cached public path keeps its existing behavior.
  • Logic: chunks(unique, limit) with limit === 5 yields one batch for ≤5 collections and multiple batches for more, matching the D1 measurement. Promise.all across independent SELECT batches is safe.
  • Error handling: runBatch retries only on missing-table errors and rethrows everything else; non-missing errors still surface as a 500 through the existing catch.
  • Comments / changeset: no new issue references in changed hunks; the changeset is a concise, user-facing patch note.

No logic bugs, regressions, security issues, or convention violations remain in the diff. LGTM.

@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Aug 5, 2026
@github-actions github-actions Bot added review/approved Approved; no new commits since and removed review/needs-rereview Author pushed changes since the last review labels Aug 5, 2026
Comment thread packages/core/src/database/dialect-helpers.ts
The ceiling is read off a third-party Kysely adapter by duck-typing, so
core cannot assume it is well-formed. Only a positive integer batches
correctly: chunks() never advances its cursor on 0 or a negative, so
fetchVisibleTermCounts hangs instead of erroring; a fractional limit
truncates on the second slice bound and repeats an element across
batches, double-counting it; NaN produces a single empty batch and a
UNION with no terms.

Every one of those fails silently or not at all, which is the failure
mode this branch exists to remove. A malformed declaration now throws
where the message can name the adapter class.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/approved Approved; no new commits since labels Aug 5, 2026
@MA2153 MA2153 added the bot:review Trigger an emdashbot code review on this PR label Aug 5, 2026

@emdashbot emdashbot 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.

Re-review: LGTM

This is a clean follow-up to the prior review. The core approach—reading the D1 compound-SELECT ceiling from the Kysely adapter so only D1 pays the batching cost—remains sound.

What's fixed since the prior review:

  • Comment discipline. The #2330 reference in the new describe block is gone; the block is now named after what it tests, with no issue references in changed code.
  • Unconditional batching on non-D1 backends. compoundSelectLimit(db) returns null for any adapter that doesn't declare a ceiling, so Node/SQLite, libSQL and Postgres still issue a single statement regardless of collection count. A new test asserts the single-statement path directly.
  • Malformed ceiling validation. compoundSelectLimit now rejects 0, negatives, NaN, Infinity, fractions and non-numbers, and the error names the offending adapter class. A dedicated unit test covers all the malformed cases.

What I checked:

  • SQL safety. Collection slugs are still validateIdentifier'd before sql.ref(ec_${collection}); values stay parameter-bound.
  • Logged-out query counts. The split only happens on adapters that declare a ceiling, and only when a taxonomy has more collections than that ceiling. For the common ≤5-collection case on D1, it's still one query. Public render consumers go through the existing request-cached wrapper.
  • Correctness of batching. Sums are per translation group and commutative; runBatch keeps the missing-ec_* fallback inside each batch so an absent table only degrades its batch.
  • D1 dialect coverage. All three D1 dialects (raw binding, session, coalescing) extend the shared D1Adapter and expose compoundSelectLimit = 5.
  • AGENTS.md conventions. The changeset correctly uses a single emdash patch (cloudflare is in the same fixed group). No new admin UI strings; no logged-out hot-path regressions; comment references are gone.

No logic bugs, security issues, regressions or convention violations remain.

@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Aug 5, 2026
@khoinguyenpham04

Copy link
Copy Markdown
Collaborator

Thanks! @MA2153

@khoinguyenpham04
khoinguyenpham04 merged commit 121b333 into emdash-cms:main Aug 5, 2026
76 of 78 checks passed
@emdashbot emdashbot Bot mentioned this pull request Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Taxonomy term list 500s when a taxonomy declares more collections than the backend's compound-SELECT limit

2 participants