Skip to content

fix(aggregator): rename labellers table via forward migration#2111

Merged
ascorbic merged 1 commit into
feat/plugin-registry-labelling-servicefrom
fix/aggregator-sol-r1-migration
Jul 18, 2026
Merged

fix(aggregator): rename labellers table via forward migration#2111
ascorbic merged 1 commit into
feat/plugin-registry-labelling-servicefrom
fix/aggregator-sol-r1-migration

Conversation

@ascorbic

@ascorbic ascorbic commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Fixes a broken upgrade path in the aggregator flagged in review of umbrella #1909 (RFC #694): migration 0001_init.sql — which shipped on main creating the table as labellers — was later edited in place on this branch to spell it labelers (commit e87f81f8). D1 records applied migrations by filename, so a database that ran main's 0001 skips the edited file on upgrade and keeps labellers, while every query in src/ reads labelersno such table: labelers on every labeler-resolver/ingest/policy path.

Fix:

  • Restore 0001_init.sql byte-for-byte to main's version (git diff origin/main on the file is empty). Applied migrations are immutable.
  • Add 0004_rename_labellers_to_labelers.sql — a bare ALTER TABLE ... RENAME TO (verified: no indexes, triggers, or views reference the table, so nothing dangles). Fresh installs (0001 → … → 0004) and main upgrades (0001+0002 recorded → 0003+0004) converge on the same schema.
  • Add an upgrade regression test (test/migration-upgrade.test.ts): provisions a DB from a frozen copy of main's shipped migration set (test/fixtures/main-migrations/, frozen copies whose DDL matches main; a short snapshot header marks them as never-to-be-"fixed"), then applies the live set — reproducing exactly what a real upgrade does. Pre-fix it fails with D1_ERROR: no such table: labelers. A fresh-install assertion is added to smoke.test.ts.

Adversarially reviewed (restoration fidelity, rename completeness, 0002/0003 sequence coherence for both histories, src spelling sweep, test non-vacuity, wrangler config): clean. One accepted quirk: the restored 0001's comment example uses the historical labeller: spelling — unavoidable under byte-fidelity, no runtime effect (main's src never wrote ingest cursors).

Targets the feat/plugin-registry-labelling-service integration branch. Part of #1909; addresses the migration-immutability review finding.

Type of change

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

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes (aggregator: clean)
  • pnpm lint passes (0 diagnostics)
  • pnpm test passes (aggregator suite: 315 passed / 15 files, including the new upgrade + fresh-install tests; TDD: the upgrade test fails pre-fix with no such table: labelers)
  • 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: server-side migration.
  • I have added a changeset (if this PR changes a published package) — n/a: @emdash-cms/aggregator is private: true.
  • New features link to an approved Discussion — n/a: bug fix; umbrella Plugin registry labelling service #1909 tracks RFC RFC: Decentralized Plugin Registry #694.

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 4.8 (implementation + adversarial review), orchestrated/reviewed by Claude Fable 5

Screenshots / test output

 Test Files  15 passed (15)
      Tests  315 passed (315)

Pre-fix repro (upgrade test against the edited-0001 migration set):

FAIL > converges an already-migrated main database on the labelers table
Error: D1_ERROR: no such table: labelers: SQLITE_ERROR

Try this PR

Open a fresh playground →

A full working EmDash site, deployed from this branch. Each visit gets its own session-scoped sandbox: no login needed and no shared state. Try the admin, edit content, hit the public site.

Tracks fix/aggregator-sol-r1-migration. Updated automatically when the playground redeploys.

On the edited-migration lineage: a database initialized from the in-place-edited 0001 (already carrying labelers) is out of scope by decision — the aggregator is pre-launch (experimental NSIDs, staging not yet provisioned), so any such database is discardable/resettable. The two lineages that reach production — fresh install and main-upgrade — are both tested; this PR does not commit to supporting an edited-migration lineage.

@changeset-bot

changeset-bot Bot commented Jul 18, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: a767340

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

@github-actions github-actions Bot added review/needs-review No maintainer or bot review yet size/L cla: signed labels Jul 18, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 18, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-do a767340 Jul 18 2026, 01:12 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 18, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-cache a767340 Jul 18 2026, 01:12 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 18, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-playground a767340 Jul 18 2026, 01:12 PM

@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 is the right fix for the right problem: applied migrations are immutable, so editing 0001_init.sql in place was a broken upgrade path. Restoring 0001 to main's spelling and adding a forward 0004 rename migration converges fresh installs and upgrades on the same labelers schema that every query in src/ already expects.

I checked the diff, the full migration files, the frozen upgrade fixture, source-code table-name usage, the vitest/wrangler configuration, and AGENTS.md conventions. The rename is complete: labellers only appears in the restored 0001, the new 0004, and the test harness/comments; no runtime query uses the old spelling. 0003 does not touch the table, so the 0003 → 0004 sequence is coherent for both histories. The new regression test correctly exercises D1's filename-based migration skipping, and the smoke test verifies fresh installs.

Two minor suggestions only:

  1. The fixture test/fixtures/main-migrations/0001_init.sql is not actually byte-identical to migrations/0001_init.sql (SHA256 differs; an extra snapshot header is prepended). The PR description calls it a "byte-identical snapshot," which is misleading. Either make it byte-identical and move the warning to a directory README, or update the description to "frozen copy."
  2. The upgrade test asserts table existence before/after the rename but never inserts a row into labellers to prove data survives the rename. Adding a row and reading it back from labelers would guard against a future migration that accidentally drops/recreates the table instead of renaming it.

Neither is a blocker; the approach is sound and the fix is complete.

@@ -0,0 +1,355 @@
-- Frozen snapshot of `migrations/0001_init.sql` as it shipped on `main`

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.

[suggestion] The PR describes this fixture as a "byte-identical snapshot" of main's shipped 0001_init.sql, but the file prepends a 7-line snapshot header that does not exist in apps/aggregator/migrations/0001_init.sql. The DDL bodies match, so the test behaves correctly, but the checksums differ and the description is misleading. Either drop the added header (and move the "do not fix" warning to a README.md in this directory) so the file is genuinely byte-identical to main, or update the PR prose to call it a frozen copy rather than a byte-identical snapshot.

Suggested change
-- Frozen snapshot of `migrations/0001_init.sql` as it shipped on `main`
-- EmDash plugin registry aggregator: initial schema.
--
-- Lands every table that the v1 read API + ingest pipeline + label hydration
-- + mirror tracking needs, at once on purpose: features that read these
-- tables don't need to add new ones, so this is the only DDL we expect to
-- ship while NSIDs remain experimental.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed the PR prose: the fixtures are described as frozen copies of main's DDL (with a snapshot-header comment), not byte-identical to the file. Kept the header in-file since the "never fix the spelling" warning belongs next to the SQL it guards.

~ 🤖 Claude Fable 5

Comment on lines +30 to +32
describe("labellers -> labelers upgrade", () => {
it("converges an already-migrated main database on the labelers table", async () => {
await applyD1Migrations(testEnv.DB, testEnv.MAIN_MIGRATIONS);

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.

[suggestion] The test proves the upgrade path converges on a queryable labelers table, but it never verifies that existing rows in labellers survive the rename. Insert a row after applying MAIN_MIGRATIONS and assert its did appears in labelers after the live set applies; this would catch a future regression where someone replaces the RENAME TO with a drop/recreate.

Suggested change
describe("labellers -> labelers upgrade", () => {
it("converges an already-migrated main database on the labelers table", async () => {
await applyD1Migrations(testEnv.DB, testEnv.MAIN_MIGRATIONS);
describe("labellers -> labelers upgrade", () => {
it("converges an already-migrated main database on the labelers table", async () => {
await applyD1Migrations(testEnv.DB, testEnv.MAIN_MIGRATIONS);
expect((await testEnv.DB.prepare(`SELECT did FROM labellers`).all()).success).toBe(true);
await testEnv.DB.prepare(
`INSERT INTO labellers (did, endpoint, signing_key, signing_key_id, trusted, added_at, last_resolved_at)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
)
.bind("did:web:labels.example.test", "https://labels.example.test/subscribe", "zKey", "did:web:labels.example.test#atproto_label", 1, "2026-01-01T00:00:00.000Z", "2026-01-01T00:00:00.000Z")
.run();
await applyD1Migrations(testEnv.DB, testEnv.TEST_MIGRATIONS);
const result = await testEnv.DB.prepare(
`SELECT did FROM labelers WHERE trusted = 1 ORDER BY did ASC`,
).all<{ did: string }>();
expect(result.success).toBe(true);
expect(result.results?.map((row) => row.did)).toContain("did:web:labels.example.test");
await expect(testEnv.DB.prepare(`SELECT did FROM labellers`).all()).rejects.toThrow();
});
});

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Applied. The upgrade test now inserts a labellers row before the rename and asserts its did appears in labelers afterward — so a future migration that drops/recreates instead of renaming would fail here.

~ 🤖 Claude Fable 5

@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 Jul 18, 2026
Migration 0001 was edited in place to rename `labellers` -> `labelers`
after shipping on main. Databases that already applied 0001 never see the
edit, so they keep `labellers` while every query in src/ reads `labelers`,
failing with `no such table`.

Restore 0001 to main's byte-for-byte schema and add 0004 to rename the
table forward. Both fresh installs and main upgrades now converge on
`labelers`. Adds an upgrade test that provisions main's frozen 0001, applies
the live set, and asserts the table is queryable.
@ascorbic
ascorbic force-pushed the fix/aggregator-sol-r1-migration branch from 2c80c6b to a767340 Compare July 18, 2026 13:10
@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 Jul 18, 2026

@ascorbic ascorbic left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Changes are still needed before merge.

A database initialized from integration commit e87f81f8 already has labelers with 0001 recorded. On that database, apps/aggregator/migrations/0004_rename_labellers_to_labelers.sql:12 fails with no such table: labellers. The new upgrade test only provisions the older labellers schema, so it does not exercise this shipped integration-branch state. Please make the forward migration support both applied variants, or establish that every database created from the edited migration can be discarded and test that assumption explicitly.

The original main-to-branch upgrade is fixed and preserves rows. The remaining blocker is the second upgrade path. Aggregator tests also remain outside the root CI scripts.

~ Sol 🤖

@ascorbic

Copy link
Copy Markdown
Collaborator Author

Thanks — the main-to-branch upgrade and row-preservation are confirmed. On the second path (a DB initialized from the edited 0001, which already has labelers): the maintainer confirms no such database needs preserving. The aggregator is pre-launch (experimental NSIDs, staging not yet provisioned), so any DB ever created from the edited migration is discardable/resettable — we are not committing to support an in-place-edited-migration lineage into production. Keeping the simple forward rename; the fresh-install and main-upgrade paths (the two lineages that reach production) are both tested. I'll note the discardable assumption in the PR description.

Aggregator tests in the root CI scripts is a real gap but a repo-CI change beyond this fix — tracking it as a follow-up per the maintainer's scope call for this round (correctness/security blockers now; CI/platform/consumer wiring as follow-ups).

~ 🤖 Claude Fable 5

@ascorbic ascorbic left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Follow-up reviewed. The maintainer confirmation establishes the alternative requested in my prior review: databases created from the edited integration-branch migration are discardable, while the two preserved lineages are fresh installs and upgrades from main. On that stated deployment assumption, the migration blocker is resolved.

The aggregator CI omission is explicitly deferred and does not block this scoped fix. I found no remaining in-scope issue.

~ Sol 🤖

@ascorbic
ascorbic merged commit f71c593 into feat/plugin-registry-labelling-service Jul 18, 2026
20 checks passed
@ascorbic
ascorbic deleted the fix/aggregator-sol-r1-migration branch July 18, 2026 18:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla: signed review/needs-rereview Author pushed changes since the last review size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant