Skip to content

fix: preserve nullable cloud task bindings - #2405

Closed
FicoHu wants to merge 2 commits into
wecode-ai:mainfrom
FicoHu:fix/mysql-cloud-task-binding
Closed

fix: preserve nullable cloud task bindings#2405
FicoHu wants to merge 2 commits into
wecode-ai:mainfrom
FicoHu:fix/mysql-cloud-task-binding

Conversation

@FicoHu

@FicoHu FicoHu commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Bug Fixes

    • Improved MySQL handling for optional loop-item, delivery, project, identifier, and task values.
    • Ensured status and TODO resets use database-compatible values across supported databases.
    • Added automatic schema updates for existing and newly created MySQL databases.
  • Tests

    • Added coverage for MySQL and SQLite nullable-value behavior.
    • Added end-to-end coverage for cloud-project task binding and loop-item resolution.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds MySQL loop_items schema normalization with sentinel values, generated projections, and indexes. Alembic initialization and migrations invoke the normalization. Delivery services now write dialect-specific unset values. Tests cover schema contracts, dialect adaptation, and cloud-project task binding.

Changes

MySQL loop-item alignment

Layer / File(s) Summary
MySQL loop_items schema normalization
backend/app/db/mysql_loop_items_schema.py
Defines sentinel columns, constraint mappings, index management, normalization, and nullable-schema restoration for MySQL.
Migration and initialization wiring
backend/alembic/env.py, backend/alembic/versions/..., backend/tests/models/test_loop_items_mysql_schema.py
Runs normalization during fresh-database initialization and Alembic upgrades. Tests verify schema coverage and migration revision metadata.
Dialect-aware delivery and service updates
backend/app/models/delivery.py, backend/app/services/cloud_projects/service.py, backend/app/services/loop_items/service.py, backend/tests/schemas/test_delivery.py
Adds MySQL sentinel mappings and applies dialect-specific unset values during status removal and loop-item restoration. Tests cover MySQL and SQLite behavior.
Cloud-project task binding API coverage
frontend/e2e/tests/api/cloud-project-task-binding-api.spec.ts
Adds API coverage for project-task binding, loop-item binding, cleanup, and runtime-context resolution.

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

Possibly related PRs

Suggested reviewers: micro66

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main objective of the pull request: implementing MySQL sentinel-based schema normalization to preserve nullable cloud task bindings across different database dialects.
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 unit tests (beta)
  • Create PR with unit tests

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.

Actionable comments posted: 1

🤖 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 `@backend/app/db/mysql_loop_items_schema.py`:
- Around line 326-343: The _restore_nullable_constraint_values function restores
only a partial list of nullable columns, but _backfill_mysql_sentinels converts
all mapped nullable fields. Update _restore_nullable_constraint_values to build
the restoration assignments from MYSQL_LOOP_ITEM_SENTINEL_COLUMNS instead of the
hardcoded string_columns and other column lists, ensuring that all
sentinel-backed nullable fields (including task_id, task_user_id, task_title,
linked_at, and unlinked_at) are properly restored to NULL during downgrade.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b6c0a50-3933-4b1a-b057-d2848720e32f

📥 Commits

Reviewing files that changed from the base of the PR and between c983ada and 2a5c3c4.

📒 Files selected for processing (8)
  • backend/alembic/env.py
  • backend/alembic/versions/20260804_c0d1e2f3a4b5_align_mysql_loop_items_schema.py
  • backend/app/db/mysql_loop_items_schema.py
  • backend/app/models/delivery.py
  • backend/app/services/cloud_projects/service.py
  • backend/app/services/loop_items/service.py
  • backend/tests/models/test_loop_items_mysql_schema.py
  • backend/tests/schemas/test_delivery.py

Comment on lines +326 to +343
def _restore_nullable_constraint_values(connection: Connection) -> None:
assignments = []
string_columns = (
"cloud_project_id",
"parent_id",
"loop_item_id",
"delivery_id",
"public_id",
"project_key",
"storage_prefix",
)
for name in string_columns:
column = _quote(connection, name)
assignments.append(f"{column} = NULLIF({column}, '')")
for name in ("local_project_id", "backend_task_id"):
column = _quote(connection, name)
assignments.append(f"{column} = NULLIF({column}, 0)")
connection.exec_driver_sql("UPDATE `loop_items` SET " + ", ".join(assignments))

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Restore every sentinel-backed nullable value during downgrade.

_backfill_mysql_sentinels converts every mapped nullable field. This function restores only foreign-key and unique-index fields. A prior NULL task_id, task_user_id, task_title, linked_at, or unlinked_at remains a sentinel after an upgrade and downgrade.

Build restoration from MYSQL_LOOP_ITEM_SENTINEL_COLUMNS instead of this partial list. If a sentinel can be valid business data, preserve the original null state before upgrade because NULLIF cannot reverse COALESCE. Add a round-trip migration test with null and sentinel-valued task bindings.

As per coding guidelines, backend persistent model changes require verification of both upgrade head and rollback before handoff.

🧰 Tools
🪛 Ruff (0.16.0)

[error] 343-343: Possible SQL injection vector through string-based query construction

(S608)

🤖 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 `@backend/app/db/mysql_loop_items_schema.py` around lines 326 - 343, The
_restore_nullable_constraint_values function restores only a partial list of
nullable columns, but _backfill_mysql_sentinels converts all mapped nullable
fields. Update _restore_nullable_constraint_values to build the restoration
assignments from MYSQL_LOOP_ITEM_SENTINEL_COLUMNS instead of the hardcoded
string_columns and other column lists, ensuring that all sentinel-backed
nullable fields (including task_id, task_user_id, task_title, linked_at, and
unlinked_at) are properly restored to NULL during downgrade.

Source: Coding guidelines

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.

1 participant