Skip to content

fix(nextly): stop verifying a shape the generators do not produce - #562

Merged
mobeenabdullah merged 1 commit into
mainfrom
fix/builder-shape-check-removal
Aug 5, 2026
Merged

fix(nextly): stop verifying a shape the generators do not produce#562
mobeenabdullah merged 1 commit into
mainfrom
fix/builder-shape-check-removal

Conversation

@mobeenabdullah

@mobeenabdullah mobeenabdullah commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

🔴 #556 merged at 95948afb3, two commits behind the branch head. The two commits that were not
included are the ones that REMOVE a check which fails working operations, so main currently carries
two live regressions. This PR is those two commits, rebased onto main.

Proven on merged main, not argued

Ran the branch's own regression guards against a worktree at ccaa14089, PostgreSQL and SQLite:

× keeps a create with a required upload applied
  Table "single_..._up" does not match this schema: hero is NOT NULL, expected nullable

× completes a localization enable sent with fields
  Table "single_..._enable" does not match this schema after the update:
    title is still NOT NULL but is no longer declared;
    slug is still NOT NULL but is no longer declared;
    headline is still NOT NULL but is no longer declared

1. A create with a required relationship or upload is recorded as failed

field-column-descriptor.ts:230 reports every fkSingle column as nullable on purpose
requiredness is enforced in application code — while the create DDL emits NOT NULL. The
nullability comparison therefore fails a table that was created correctly.

2. Enabling localization is recorded as failed AND strands content

This is the serious one. A field moving to the companion is legitimately still a NOT NULL column
on the main table until the companion transition seeds and drops it. The check runs first, sees
columns "no longer declared" — including the system title and slug — marks the migration failed,
and skips the companion transition while localized: true has already been persisted. Reads
then point at an unseeded companion while the content is still on main.

A verification that strands data is worse than no verification.

Why removal rather than another narrowing

The check was narrowed twice already, dropping type and then index comparison. Each time the false
positives moved to whatever remained. The cause is structural: it compares against the diff engine's
ideal schema, and the Builder's own generators do not render that. Those divergences are real
and already tracked as their own work; a post-apply gate is not where they should be discovered.

Measured tally over the review of #556:

the check broke the check caught
float creates · unique creates · every field-group create · required relationship/upload creates · localization enables nothing outside the repair scenarios written to exercise it

Also included: the write ordering returns to recording the outcome with the row

Writing the intent first is better only once something can finish an interrupted attempt. Without
that, the half-written row owns the slug and refuses every retry — worse than the orphan table it
prevents, because an orphan at least leaves the slug free. The ordering moves with the migration
lock that completes it.

Removed with it: the adoption branch, the atomic claim and the relaxed owner check. Nothing without
a caller is left behind.

Kept: planning before anything is persisted, so a request the generator refuses leaves nothing
at all. That was a genuine fix.

What #556's work still delivers after this

The relocation itself (−356 lines from the handler), the extracted companion reconcile, the shared
statement runner that fixes the MySQL retry dead end, and 445 lines of integration tests proving a
create actually creates a table on all three dialects.

Verification

Build clean · check-types 0 · lint 0 · drizzle gate passed · the create suite 16 passed on
PostgreSQL, 16 on MySQL
· dispatcher suites back to the main baseline exactly.

The three cases the check broke are kept as regression tests asserting they stay applied.

Summary by CodeRabbit

  • Bug Fixes

    • Improved schema migration handling by surfacing rename conflicts instead of silently ignoring them.
    • Preserved existing schema metadata when migrations fail.
    • Prevented duplicate single-table ownership during creation.
  • New Features

    • Added support and coverage for localization tables, required uploads, and schema updates.
  • Tests

    • Expanded migration tests for conflicting column renames and schema-change scenarios.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@mobeenabdullah, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 5 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 99ac3622-f53c-49c4-9756-78603dacb1eb

📥 Commits

Reviewing files that changed from the base of the PR and between 7ea8d9d and 474334c.

📒 Files selected for processing (1)
  • packages/nextly/src/domains/schema/pipeline/pre-resolution/__tests__/drop-index-constraint.integration.test.ts
📝 Walkthrough

Walkthrough

The change revises schema migration handling and Single creation registration. Rename idempotency errors now propagate. Single and Component creation no longer perform post-migration shape checks. Failed Single migrations preserve prior schema metadata.

Changes

Schema migration and creation flow

Layer / File(s) Summary
Rename migration error handling
packages/nextly/src/domains/schema/services/apply-migration-statements.ts, packages/nextly/src/domains/schema/services/__tests__/apply-migration-statements.test.ts
Rename statements now propagate duplicate-column errors. Tests cover rename targets and an ADD COLUMN rename case.
Single creation registration and failure state
packages/nextly/src/domains/singles/services/single-metadata-service.ts, packages/nextly/src/domains/singles/services/single-registry-service.ts, packages/nextly/src/dispatcher/handlers/single-dispatcher.ts, packages/nextly/src/dispatcher/handlers/__tests__/single-dispatcher-ddl.test.ts
Single creation applies DDL before registry insertion and records the migration status during registration. Existing owners are rejected. Failed migrations retain prior schema metadata.
Creation success criteria and integration coverage
packages/nextly/src/dispatcher/handlers/component-dispatcher.ts, packages/nextly/src/dispatcher/handlers/single-dispatcher.ts, packages/nextly/src/domains/schema/services/verify-applied-shape.ts, packages/nextly/src/domains/singles/__tests__/single-create-schema-change.integration.test.ts
Post-migration shape verification is removed for Single and Component creation. Integration tests cover localization, required uploads, and schema updates.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SingleDispatcher
  participant SingleMetadataService
  participant MigrationRunner
  participant SingleRegistryService
  SingleDispatcher->>SingleMetadataService: createSingle
  SingleMetadataService->>SingleMetadataService: planCreate
  SingleMetadataService->>MigrationRunner: Apply migration SQL
  MigrationRunner-->>SingleMetadataService: Return migration status
  SingleMetadataService->>SingleRegistryService: registerSingle with status
  SingleRegistryService-->>SingleMetadataService: Return registered record
  SingleMetadataService-->>SingleDispatcher: Return creation result
Loading

Possibly related PRs

  • nextlyhq/nextly#556: Modifies the same Single creation, migration, and shape-verification paths.
  • nextlyhq/nextly#382: Shares Single schema creation and localized companion-table behavior.
  • nextlyhq/nextly#504: Modifies the same Single and Component creation and schema-validation flows.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: removing incompatible schema shape verification.
Description check ✅ Passed The description provides detailed rationale, regressions, scope, and verification, but it omits the required template sections and checklist confirmations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/builder-shape-check-removal

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.

@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@codex please review this PR

@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@greptileai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 32 minutes.

@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

8a902f4c8 adds a second fix to this PR, from a Codex finding on #556 that is a defect I introduced and is now on main.

A failed rename is silently swallowed, and the content becomes unreachable

updateSingleSchema emits ALTER TABLE ... RENAME COLUMN (dynamic-collection-schema-service.ts:728). When the target name is already taken, PostgreSQL says "already exists" and MySQL/SQLite report a duplicate column — exactly the wordings isIdempotencyError matches.

For a CREATE or an ADD, "already exists" means the schema is in the state the statement asked for, which is what makes re-running safe. For a RENAME it means the opposite: the target is occupied, so the move did not happen. Swallowed, the values stay in the old column while the registry records the new field name, and every later read addresses the new column and finds it empty.

The user's content is still in the database and no longer reachable.

Renames are now excluded from the tolerance. The wording is indistinguishable between the two cases, so the statement is the discriminator rather than the error. Two unit tests, one per wording, proven load-bearing by removing the exclusion.

This PR now carries three fixes, all measured against merged main

  1. A create with a required relationship or upload is recorded as failed.
  2. Enabling localization is recorded as failed and skips the companion transition while localized: true is persisted, stranding content on the main table.
  3. A blocked rename is swallowed and reads are pointed at an empty column.

Verified at 8a902f4c8: build clean · check-types 0 · lint 0 · drizzle gate passed · unit 363 vs 363 against a freshly measured baseline at the merge commit ccaa14089, zero new · create suite 16 passed on PostgreSQL, 16 on MySQL.

@codex please review this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a902f4c89

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/nextly/src/domains/schema/services/apply-migration-statements.ts Outdated
Comment thread packages/nextly/src/dispatcher/handlers/single-dispatcher.ts Outdated
Comment thread packages/nextly/src/domains/singles/services/single-metadata-service.ts Outdated
Comment thread packages/nextly/src/domains/schema/services/apply-migration-statements.ts Outdated
@pkg-pr-new

pkg-pr-new Bot commented Aug 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

@nextlyhq/adapter-drizzle

npm i https://pkg.pr.new/@nextlyhq/adapter-drizzle@474334c

@nextlyhq/adapter-mysql

npm i https://pkg.pr.new/@nextlyhq/adapter-mysql@474334c

@nextlyhq/adapter-postgres

npm i https://pkg.pr.new/@nextlyhq/adapter-postgres@474334c

@nextlyhq/adapter-sqlite

npm i https://pkg.pr.new/@nextlyhq/adapter-sqlite@474334c

@nextlyhq/admin

npm i https://pkg.pr.new/@nextlyhq/admin@474334c

@nextlyhq/admin-css

npm i https://pkg.pr.new/@nextlyhq/admin-css@474334c

@nextlyhq/blocks-engine

npm i https://pkg.pr.new/@nextlyhq/blocks-engine@474334c

@nextlyhq/blocks-react

npm i https://pkg.pr.new/@nextlyhq/blocks-react@474334c

create-nextly-app

npm i https://pkg.pr.new/create-nextly-app@474334c

nextly

npm i https://pkg.pr.new/nextly@474334c

@nextlyhq/plugin-form-builder

npm i https://pkg.pr.new/@nextlyhq/plugin-form-builder@474334c

@nextlyhq/plugin-page-builder

npm i https://pkg.pr.new/@nextlyhq/plugin-page-builder@474334c

@nextlyhq/plugin-sdk

npm i https://pkg.pr.new/@nextlyhq/plugin-sdk@474334c

@nextlyhq/plugin-seo

npm i https://pkg.pr.new/@nextlyhq/plugin-seo@474334c

@nextlyhq/storage-s3

npm i https://pkg.pr.new/@nextlyhq/storage-s3@474334c

@nextlyhq/storage-uploadthing

npm i https://pkg.pr.new/@nextlyhq/storage-uploadthing@474334c

@nextlyhq/storage-vercel-blob

npm i https://pkg.pr.new/@nextlyhq/storage-vercel-blob@474334c

@nextlyhq/ui

npm i https://pkg.pr.new/@nextlyhq/ui@474334c

commit: 474334c

@github-actions github-actions Bot added scope: core nextly type: docs Documentation only labels Aug 5, 2026
@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

f822addc2 — five findings, two fixed, three rebutted with file:line evidence.

Fixed

P1: a blocked rename still persisted the new field list. My rename fix was necessary and not sufficient. The throw takes the failed branch, and execution continued to the updateData.fields assignment made before any statement ran. Runtime schemas rebuild from the registry row regardless of status, so after a restart reads address the target column while the values sit in the source one.

A failed migration no longer persists the new field list or hash. Stated as a rule, not a rename special case: the stored fields are what the runtime is rebuilt from, so a list the table does not have redirects reads whatever caused the failure.

🔴 Applied on reasoning, not a test, and that is on the record. I wrote the integration test, it passed against the broken code, and I deleted it. The probe showed why: dropping body while content exists resolves as a DROP, not a rename, and returns applied. A genuinely blocked rename needs the table to carry a column the registry does not know about — real drift, not constructible through the public API. A vacuous test is worse than none.

My rename regex matched the word anywhere, so a column literally named rename made its own ADD non-idempotent. Anchored to the statement form, with a test.

Rebutted — all three are pre-existing, verified at 6512e2fa4 (the commit before #556)

finding evidence
a failed create cannot be retried :655 same owner check, :795 registerSingle last with the final status
an orphan table plus changed fields records applied :708-709 gates on tableExists alone
concurrent creates rebind the resolver before the unique insert same order: DDL → :708:740:795

This PR restores that sequence rather than creating it — the relocation had to keep the order identical so the move could be reviewed as a move.

All three are closed by the migration lock, which is the next task and the thing this relocation exists to unblock: a lock could not enclose these steps before, because one taken inside the registry service is acquired after the tables have already changed.

Verified at f822addc2: build clean · check-types 0 · lint 0 · drizzle gate passed · unit 363 vs 363 against a freshly measured baseline at ccaa14089, zero new · create suite 16 passed on PostgreSQL, 16 on MySQL.

@codex please review this PR

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/nextly/src/dispatcher/handlers/__tests__/single-dispatcher-ddl.test.ts (1)

148-152: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Update the stale updateMigrationStatus comment in wireRegistry.

The assertion now reads the status from the first registerSingle call. The create path no longer writes pending first and no longer calls updateMigrationStatus. The comment at Lines 97-99 still describes that removed sequence, so it now contradicts both the service and this assertion.

Replace it with a statement of why the double is still present, or remove the member if nothing under test calls it.

♻️ Proposed comment update
     registerSingle: vi.fn(async (row: unknown) => row),
-    // The confirm write. A create persists its intent as `pending` before touching the table and
-    // records the outcome here afterwards, so a double without it fails the whole create.
+    // Present for the update paths that still record an outcome separately; the create path
+    // records its status inside the `registerSingle` payload instead.
     updateMigrationStatus: vi.fn(),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/nextly/src/dispatcher/handlers/__tests__/single-dispatcher-ddl.test.ts`
around lines 148 - 152, Update the stale updateMigrationStatus comment in
wireRegistry to reflect the current create flow and first registerSingle call
assertion; explain why the mocked member remains, or remove it if no tested code
invokes it.
packages/nextly/src/domains/singles/__tests__/single-create-schema-change.integration.test.ts (1)

187-293: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the failed-migration branch that preserves the prior field list.

The three new tests all assert the success path stays applied. The PR also adds a failure-path behavior in single-dispatcher.ts at Lines 1117-1129: when a migration fails after it began, the handler drops fields and schemaHash so the registry keeps the previous schema. The removed tests covered failure and resumption scenarios, so that branch now has no integration coverage.

Add a test that forces a blocked rename or another post-migrationBegan failure, then asserts both facts:

  • migrationStatus is failed.
  • The stored fields still match the pre-update list.

That test would also expose the localized and status gap raised on single-dispatcher.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/nextly/src/domains/singles/__tests__/single-create-schema-change.integration.test.ts`
around lines 187 - 293, Add an integration test alongside the existing schema
migration tests that triggers a post-migrationBegan failure, such as a blocked
field rename. Assert the registry row reports migrationStatus as "failed" and
that its stored fields remain identical to the pre-update field list, covering
the failure handling in the single-dispatcher migration path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/nextly/src/dispatcher/handlers/single-dispatcher.ts`:
- Around line 1117-1129: When migrationStatus is failed, remove
updateData.localized and updateData.status alongside fields and schemaHash
before persistence, preserving the previous runtime schema flags. Update the
failed-migration response in the update path to use the same three-way messaging
behavior as the create path around its existing status message, rather than
reporting that changes are merely pending.

---

Nitpick comments:
In
`@packages/nextly/src/dispatcher/handlers/__tests__/single-dispatcher-ddl.test.ts`:
- Around line 148-152: Update the stale updateMigrationStatus comment in
wireRegistry to reflect the current create flow and first registerSingle call
assertion; explain why the mocked member remains, or remove it if no tested code
invokes it.

In
`@packages/nextly/src/domains/singles/__tests__/single-create-schema-change.integration.test.ts`:
- Around line 187-293: Add an integration test alongside the existing schema
migration tests that triggers a post-migrationBegan failure, such as a blocked
field rename. Assert the registry row reports migrationStatus as "failed" and
that its stored fields remain identical to the pre-update field list, covering
the failure handling in the single-dispatcher migration path.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 287cc942-1238-4230-9e8e-f0fb75f2d2a7

📥 Commits

Reviewing files that changed from the base of the PR and between ccaa140 and f822add.

⛔ Files ignored due to path filters (1)
  • .changeset/builder-schema-change-outcomes.md is excluded by !.changeset/**
📒 Files selected for processing (9)
  • packages/nextly/src/dispatcher/handlers/__tests__/single-dispatcher-ddl.test.ts
  • packages/nextly/src/dispatcher/handlers/component-dispatcher.ts
  • packages/nextly/src/dispatcher/handlers/single-dispatcher.ts
  • packages/nextly/src/domains/schema/services/__tests__/apply-migration-statements.test.ts
  • packages/nextly/src/domains/schema/services/apply-migration-statements.ts
  • packages/nextly/src/domains/schema/services/verify-applied-shape.ts
  • packages/nextly/src/domains/singles/__tests__/single-create-schema-change.integration.test.ts
  • packages/nextly/src/domains/singles/services/single-metadata-service.ts
  • packages/nextly/src/domains/singles/services/single-registry-service.ts
💤 Files with no reviewable changes (2)
  • packages/nextly/src/domains/schema/services/verify-applied-shape.ts
  • packages/nextly/src/domains/singles/services/single-registry-service.ts

Comment thread packages/nextly/src/dispatcher/handlers/single-dispatcher.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f822addc2f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/nextly/src/dispatcher/handlers/single-dispatcher.ts Outdated
Comment thread packages/nextly/src/dispatcher/handlers/component-dispatcher.ts Outdated
Comment thread packages/nextly/src/dispatcher/handlers/single-dispatcher.ts Outdated
@mobeenabdullah
mobeenabdullah force-pushed the fix/builder-shape-check-removal branch from f822add to 807c58c Compare August 5, 2026 06:44
@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

807c58caf — CodeRabbit's first real review of this work, and it caught that my P1 fix was one-third complete.

fields was not the only input to the runtime schema. localized and status shape it too, so an enable that failed part-way still recorded the Single as localized while the companion was never provisioned and the translatable columns were still on the main table — the same stranding the removed shape check caused, reintroduced through the flag. All three are now withheld.

It also led me to a second failure path CodeRabbit did not name, which needs a narrower answer. The companion-transition catch is reached with the main ALTER already applied, so the new fields do describe the table and withholding them would misdescribe it in the other direction. Only localized is dropped there.

The update response no longer calls a failed change "pending" — advice that could not work, and worse now that the field list is not saved either.

🔴 Both fixes are applied on reasoning, not on tests, and that is on the record. I wrote integration tests for each and both passed against the unfixed code, so I deleted them. Probing showed why: my fixtures produce refusals that throw before any statement runs, which persist nothing. Reaching a genuinely part-applied failure needs one statement to succeed and a later one to fail, which I could not construct through the public API — and that is probably why these paths went unnoticed in the first place.

Rebased onto c0cee63f9 with a freshly measured baseline. Build clean · check-types 0 · lint 0 · drizzle gate passed · unit 363 vs 363, zero new · create suite 16 passed on PostgreSQL, 16 on MySQL.

@codex please review this PR

@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 807c58caf5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/nextly/src/dispatcher/handlers/single-dispatcher.ts Outdated
Comment thread packages/nextly/src/dispatcher/handlers/single-dispatcher.ts Outdated
@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

0ac22b51f — three findings, two fixed, one answered with its scope corrected.

My previous fix opened a hole. updateSingle writes migration_status only inside its if (data.fields) branch, so deleting fields — exactly what a failed migration must do — silently deleted the status write too, and the row kept saying applied. Written separately now. I chose that over loosening the shared registry guard, which exists for a real reason.

The retry tolerance is now opt-in, and only the create paths ask for it. A duplicate ADD COLUMN on an ALTER says the column is there, not that it matches — a retry that changed a field's type would have been recorded as applied over a column that has neither. Create keeps the tolerance because that is where "already exists" genuinely means the desired state, and where MySQL has no alternative. A new test asserts the default refuses, so it cannot drift back silently.

On the orphan component table, one correction to my own earlier framing. It is mostly pre-existing — CREATE TABLE IF NOT EXISTS no-ops everywhere and the path has always gated on tableExists alone. But #556 did move MySQL from loud to silent, because the splitter it replaced had no tolerance. I am not going to call that purely pre-existing. Narrowing the tolerance this round keeps the silence to the case where re-running is genuinely the repair.

The guard being asked for in two of these is the post-apply shape check, which was built and removed on #556 because it failed five working operations — including every field-group create, which is this exact path. It would have refused every create it was meant to protect.

Verified at 0ac22b51f: build clean · check-types 0 · lint 0 · drizzle gate passed · unit 363 vs 363 against c0cee63f9, zero new · create suite 16 passed on PostgreSQL, 16 on MySQL.

@codex please review this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0ac22b51fe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/nextly/src/dispatcher/handlers/single-dispatcher.ts Outdated
@mobeenabdullah
mobeenabdullah force-pushed the fix/builder-shape-check-removal branch from 0ac22b5 to c267482 Compare August 5, 2026 07:13
@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

c267482f5 — a P1 and a P2, both on my own last change.

The P1: my withholding was in the wrong catch. There are two companion catches. :1084 sits inside the field-update path after the main ALTER; :1213 is the flag-only path below it. I added the withholding to the second, so it never fired for a save that sends both fields and a localization change — the commonest way anyone enables it. The fix was real and unreachable. Now in both.

The P2: I removed the status withholding entirely rather than condition it. The asymmetry I relied on does not hold for that flag. For fields and localized, withholding is strictly safer — the old description keeps reads pointed where the data still is. status maps to a physical column whose ADD or DROP the generator emits first, so by the time a later statement fails it may already have landed, and withholding would claim a lifecycle column the table no longer has. Both choices are wrong in one direction and this code cannot tell which.

🔴 That is the third finding in this family, so the general point is worth stating: a part-applied migration leaves no registry value that is correct in every case. Withholding is right where one direction is provably safer and wrong where it is not. What would settle it is knowing which statements reached the database — the migration journal's job, not this handler's.

Rebased onto 1d8d8c12f with a freshly measured baseline. Build clean · check-types 0 · lint 0 · drizzle gate passed · unit 363 vs 363, zero new · create suite 16 passed on PostgreSQL, 16 on MySQL.

@codex please review this PR

@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

61c5aa6a5 — a flag-only localization or Draft/Published change that failed was recorded as applied.

Three things had to line up, and all three did: the catch set the status on the update payload but not the local; updateSingle ignores migrationStatus unless fields are present, and a flag-only change sends none; and the follow-up write I added last round reads the local. Both routes to failed were closed.

I also fixed the success branch, which was the same defect pointing the other way: a flag-only change that worked reported whatever the row said before, so a Single already at failed got a failure message for a save that succeeded.

🔴 Third finding in a row where the withholding logic was right and wired to something that never saw it. The shape of it: the migration status is derived in four places and consumed in two, and nothing checks that a producer reaches its consumer. Recorded rather than restructured at the end of this PR.

Verified at 61c5aa6a5: build clean · check-types 0 · lint 0 · drizzle gate passed · unit 363 vs 363 against 1d8d8c12f, zero new · create suite 16 passed on PostgreSQL, 16 on MySQL.

@codex please review this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 61c5aa6a5d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/nextly/src/dispatcher/handlers/single-dispatcher.ts Outdated
Comment thread packages/nextly/src/dispatcher/handlers/single-dispatcher.ts Outdated
Comment thread packages/nextly/src/dispatcher/handlers/single-dispatcher.ts Outdated
@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

780bc51d8 — two fixed, one answered by consistency with a decision already taken on this PR.

A repair could never clear its own failure. Round 20 taught the local variable about success but left the write gated on failed; and a flag-only change carries no fields, so updateSingle ignores the status. The row had no route out of failed at all — it would have stayed broken in every dashboard until an unrelated edit rewrote it. The write now fires whenever the outcome differs from the row.

The running server disagreed with the row it had just persisted. Withholding the localization flag without touching the resolver left this process resolving the Single through the schema the save was attempting. Re-registered with the state that will actually be saved.

Withholding fields on a companion failure: answered, not fixed. It is the same symmetric case as status earlier on this PR. Withholding misdescribes main (the ALTER applied and may have dropped a column the old list still names); keeping misdescribes the companion. Applying the reasoning already accepted for status means leaving it, rather than trading one silent mismatch for another.

🔴 The pattern, stated plainly

Rounds 18, 19, 20 and 22 were all the same shape: the logic was right and the wiring reached something that never saw it — the wrong catch, the wrong flag, the wrong variable, the wrong gate.

The cause is structural, not carelessness: the migration status is derived in six places and consumed in two, and nothing checks that a producer reaches its consumer. Every incremental fix in this area has spawned the next finding, which is exactly what a fan-out with no convergence point produces.

I am not restructuring that at the end of this PR. It is recorded as the thing to fix before anyone adds a seventh producer, and as the reason the part-applied-migration class needs the migration journal rather than more handler logic.

Verified at 780bc51d8: build clean · check-types 0 · lint 0 · drizzle gate passed · unit 363 vs 363 against 1d8d8c12f, zero new · create suite 16 passed on PostgreSQL, 16 on MySQL. Every CI check on the previous head passed, including all three integration legs.

@codex please review this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 780bc51d8d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/nextly/src/dispatcher/handlers/single-dispatcher.ts Outdated
…k-types

The response was read before the follow-up status write, so whenever that write
was the only thing to change the status the body carried the previous value —
most visibly reporting a successful repair as still failed.

Also adds the builder argument a test merged from an older branch was missing.
check-types fails on main itself without it, which blocks every PR.
@mobeenabdullah
mobeenabdullah force-pushed the fix/builder-shape-check-removal branch from 780bc51 to 474334c Compare August 5, 2026 08:30
@github-actions github-actions Bot removed the type: docs Documentation only label Aug 5, 2026
@mobeenabdullah
mobeenabdullah merged commit 6e82cf8 into main Aug 5, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant