Skip to content

fix(meta): resolve MetaModelData ModelId to newest IMD tip - #243

Merged
buke merged 4 commits into
mainfrom
fix/meta-model-data-imd-model-id-tip
Aug 4, 2026
Merged

fix(meta): resolve MetaModelData ModelId to newest IMD tip#243
buke merged 4 commits into
mainfrom
fix/meta-model-data-imd-model-id-tip

Conversation

@buke

@buke buke commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

User description

Summary

  • Implements PR-EID-6 / E14: IMD may register multiple meta_model rows with the same (application, name).
  • Add meta.ResolveMetaModelTip (created_at DESC, id DESC) and route loader resolves through it (including modelRef / search / seed apply).
  • On module uninstall, after soft-deleting that module’s MetaModels, rebind other modules’ meta_model_data.model_id values that pointed at victims onto the surviving tip (when one remains).

Test plan

  • go test ./pkg/meta/ -run ResolveMetaModelTip
  • go test ./internal/module/lifecycle/ -run 'CleanModels|MetaModelData|Rebind'
  • go test ./internal/module/evolution/data/
  • go test ./internal/module/lifecycle/ ./pkg/meta/ ./internal/module/metadata/
  • CI green on PR

Made with Cursor


PR Type

Bug fix, Enhancement, Tests


Description

  • Add meta.ResolveMetaModelTip to pick the newest meta_model tip by created_at DESC, id DESC.

  • Update Go core seed data loader and model resolutions to use model tips for extended models.

  • Rebind MetaModelData.ModelId records to surviving model tips when uninstalling extension modules.

  • Add Go unit tests for tip resolution and uninstall rebinding; new files include required SPDX headers.


File Walkthrough

Relevant files
Enhancement
model_tip.go
Add ResolveMetaModelTip helper function                                   

pkg/meta/model_tip.go

  • Implement ResolveMetaModelTip to fetch the latest meta_model row
    ordered by created_at DESC, id DESC
  • Support excludeIDs option to filter out models being soft-deleted
  • Add SPDX license header
+51/-0   
Tests
model_tip_test.go
Add unit tests for ResolveMetaModelTip                                     

pkg/meta/model_tip_test.go

  • Add unit tests verifying tip selection by created_at timestamp and id
    tie-breaking
  • Test exclusion of specific IDs and soft-deleted model records
  • Add SPDX license header
+109/-0 
uninstaller_model_data_test.go
Add unit test for model tip rebinding during module uninstallation

internal/module/lifecycle/uninstaller_model_data_test.go

  • Add TestModuleUninstallerCleanModelsRebindsMetaModelDataTip to test
    MetaModelData rebinding when extension modules are removed
+92/-0   
Bug fix
loader.go
Update data loader to resolve model references via ResolveMetaModelTip

internal/module/evolution/data/loader.go

  • Update applyRecord, resolveSearchModel, detectFieldCardinality, and
    resolveModelRef to use ResolveMetaModelTip
  • Ensure model resolution points to the latest IMD model tip
+8/-8     
translated_seed.go
Use ResolveMetaModelTip in language code check                     

internal/module/evolution/data/translated_seed.go

  • Update languageCodeExists to lookup base Language model using
    ResolveMetaModelTip
+2/-2     
uninstaller.go
Rebind MetaModelData model references on module uninstallation

internal/module/lifecycle/uninstaller.go

  • Capture victim model records prior to soft deletion during module
    uninstallation
  • Implement rebindMetaModelDataTips to rebind orphaned
    MetaModelData.ModelId values to remaining model tips
+71/-0   

Summary by CodeRabbit

  • Bug Fixes
    • Model resolution now consistently selects the newest matching model.
    • Improved language and field lookup reliability when multiple matching records exist.
    • Preserved metadata mappings when extension models are uninstalled by reconnecting them to remaining models.
    • Improved handling of deleted or unavailable models during cleanup, with clearer error reporting.
  • Data Integrity
    • Metadata now automatically tracks the current matching model, reducing stale or broken references.

- Add ResolveMetaModelTip (created_at DESC, id DESC) and use it in loader model resolves.

- Rebind MetaModelData.ModelId onto surviving tips when uninstall soft-deletes IMD MetaModel rows.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 32 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3e5f89f5-4d36-4211-8b5f-45d60be2439a

📥 Commits

Reviewing files that changed from the base of the PR and between ebdcfe2 and 5b0afc0.

📒 Files selected for processing (2)
  • internal/module/lifecycle/uninstaller_model_data_test.go
  • modules/meta/service/tests/model_data_ref.test.ts
📝 Walkthrough

Walkthrough

The change makes model selection deterministic by choosing the newest matching record. MetaModelData resolves its model association through SQL. During uninstallation, metadata references move from deleted models to surviving models.

Changes

Meta model resolution and cleanup

Layer / File(s) Summary
Deterministic model lookup ordering
internal/module/evolution/data/loader.go, internal/module/evolution/data/translated_seed.go, internal/module/evolution/data/loader_test.go
Loader and language lookups select matching records by descending created_at and id. The loader test covers missing model resolution.
Computed MetaModelData association
modules/meta/service/models/model_data.ts, modules/meta/service/tests/model_data_ref.test.ts
MetaModelData.ModelId is readonly and uses sqlModelId() to select the newest live matching meta_model row. Tests verify SQL compute registration and column dependencies.
Metadata rebinding during uninstallation
internal/module/lifecycle/uninstaller.go, internal/module/lifecycle/uninstaller_model_data_test.go
The uninstaller captures model identifiers before soft deletion, then rebinds metadata mappings to surviving models. Tests cover successful rebinding and lookup, update, and cleanup errors.

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

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: resolving MetaModelData ModelId to the newest IMD tip.
Description check ✅ Passed The description is detailed and covers the objectives, implementation changes, tests, and known CI status.
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 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/meta-model-data-imd-model-id-tip

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

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Compilation Error

Passing db.DB to rebindMetaModelDataTips will cause a compilation error. In GORM, db is of type *gorm.DB, and db.DB is a method (func() (*sql.DB, error)), not a field of type *gorm.DB. You should pass db directly instead of db.DB.

if err := rebindMetaModelDataTips(db.DB, victims); err != nil {

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

- Drop the ResolveMetaModelTip helper and resolve IMD tips inline with Order(created_at DESC, id DESC).

- Project MetaModelData.ModelId as an @SqlCompute tip over live meta_model rows instead of the stored FK.

Co-authored-by: Cursor <cursoragent@cursor.com>
@buke

buke commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

/improve

@buke

buke commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@cursor review

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

Latest suggestions up to b215ae4

CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix invalid argument type in function call

**Pass db instead of db.DB to rebindMetaModelDataTips. Since db is a gorm.DB
instance, db.DB evaluates to the DB() method value rather than a gorm.DB pointer,
causing a Go compilation failure.

internal/module/lifecycle/uninstaller.go [101]

-	if err := rebindMetaModelDataTips(db.DB, victims); err != nil {
+	if err := rebindMetaModelDataTips(db, victims); err != nil {
Suggestion importance[1-10]: 8

__

Why: db is already a *gorm.DB instance in cleanModels(), so passing db.DB passes a method value (func() (*sql.DB, error)) rather than *gorm.DB, causing a Go compilation error.

Medium

Previous suggestions

Suggestions up to commit 5d045ff
CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix GORM DB argument compilation error

**The variable db is of type *gorm.DB. Passing db.DB (which is a method DB() (sql.DB,
error) in GORM v2) to rebindMetaModelDataTips (which expects gorm.DB) will cause a
compilation error. Pass db directly instead.

internal/module/lifecycle/uninstaller.go [99-103]

 	// IMD: other modules' MetaModelData rows may still point at soft-deleted MetaModel ids.
 	// Rebind to the surviving tip for the same (application, name) when one remains.
-	if err := rebindMetaModelDataTips(db.DB, victims); err != nil {
+	if err := rebindMetaModelDataTips(db, victims); err != nil {
 		return err
 	}
Suggestion importance[1-10]: 9

__

Why: Passing db.DB (which is a method in GORM v2 returning (*sql.DB, error)) to rebindMetaModelDataTips which expects *gorm.DB will cause a compilation error. Passing db directly fixes this.

High

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit b215ae4. Configure here.

- Exercise rebindMetaModelDataTips edge branches and cleanModels error returns for IMD tip rebinding.

- Cover applyRecord model-resolve failure and MetaModelData.sqlModelId tip projection.

Co-authored-by: Cursor <cursoragent@cursor.com>

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

🧹 Nitpick comments (2)
internal/module/lifecycle/uninstaller_model_data_test.go (1)

332-346: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Leaked failure-injection triggers in internal/module/lifecycle/uninstaller_model_data_test.go. Both new tests create a SQLite BEFORE UPDATE OF model_id trigger on meta_model_data to force a rebind failure, and neither drops it. The shared root cause is a missing cleanup step for the fixture trigger. The leak couples subtest order inside TestRebindMetaModelDataTipsBranches, and it breaks later tests if newLifecycleCommitTestScope returns a shared database.

  • internal/module/lifecycle/uninstaller_model_data_test.go#L332-L346: add t.Cleanup that runs DROP TRIGGER IF EXISTS block_meta_model_data_rebind after the CREATE TRIGGER succeeds.
  • internal/module/lifecycle/uninstaller_model_data_test.go#L396-L415: add t.Cleanup that runs DROP TRIGGER IF EXISTS block_meta_model_data_rebind_clean after the CREATE TRIGGER succeeds.
🤖 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 `@internal/module/lifecycle/uninstaller_model_data_test.go` around lines 332 -
346, The failure-injection triggers created in
TestRebindMetaModelDataTipsBranches are not cleaned up. At
internal/module/lifecycle/uninstaller_model_data_test.go lines 332-346, register
t.Cleanup after creating block_meta_model_data_rebind to drop it with DROP
TRIGGER IF EXISTS; do the same at lines 396-415 for
block_meta_model_data_rebind_clean.
modules/meta/service/tests/model_data_ref.test.ts (1)

97-102: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the SQL fragment semantics.

Replace the truthiness check with an assertion on out.toOperationNode().sqlFragments.join('') that covers m.deleted_at is null, order by m.created_at desc, m.id desc, and limit 1. Keep the colCalls assertion for the correlated columns.

🤖 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 `@modules/meta/service/tests/model_data_ref.test.ts` around lines 97 - 102,
Update the test around proto.sqlModelId to assert the generated SQL semantics
via out.toOperationNode().sqlFragments.join('') instead of only checking
truthiness; verify it contains m.deleted_at is null, order by m.created_at desc,
m.id desc, and limit 1, while preserving the existing colCalls assertion.
🤖 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.

Nitpick comments:
In `@internal/module/lifecycle/uninstaller_model_data_test.go`:
- Around line 332-346: The failure-injection triggers created in
TestRebindMetaModelDataTipsBranches are not cleaned up. At
internal/module/lifecycle/uninstaller_model_data_test.go lines 332-346, register
t.Cleanup after creating block_meta_model_data_rebind to drop it with DROP
TRIGGER IF EXISTS; do the same at lines 396-415 for
block_meta_model_data_rebind_clean.

In `@modules/meta/service/tests/model_data_ref.test.ts`:
- Around line 97-102: Update the test around proto.sqlModelId to assert the
generated SQL semantics via out.toOperationNode().sqlFragments.join('') instead
of only checking truthiness; verify it contains m.deleted_at is null, order by
m.created_at desc, m.id desc, and limit 1, while preserving the existing
colCalls assertion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 32503b81-78fc-4643-b507-7f49bac9ff24

📥 Commits

Reviewing files that changed from the base of the PR and between b215ae4 and ebdcfe2.

📒 Files selected for processing (3)
  • internal/module/evolution/data/loader_test.go
  • internal/module/lifecycle/uninstaller_model_data_test.go
  • modules/meta/service/tests/model_data_ref.test.ts

- Drop failure-injection SQLite triggers via t.Cleanup after rebind error cases.

- Assert MetaModelData.sqlModelId tip SQL fragments instead of a truthiness check.

Co-authored-by: Cursor <cursoragent@cursor.com>
@buke
buke merged commit 080e4f0 into main Aug 4, 2026
43 checks passed
@buke
buke deleted the fix/meta-model-data-imd-model-id-tip branch August 4, 2026 08:09
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.

1 participant