Skip to content

feat(meta): add meta_raw_* schema and E2 effective merge - #244

Merged
buke merged 7 commits into
mainfrom
feat/meta-effective-dual-store-eds1
Aug 4, 2026
Merged

feat(meta): add meta_raw_* schema and E2 effective merge#244
buke merged 7 commits into
mainfrom
feat/meta-effective-dual-store-eds1

Conversation

@buke

@buke buke commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

User description

Summary

  • Add declaration-layer meta_raw_* GORM entities (RawModel / RawField / …) and register them in meta.Entities() so AutoMigrate creates empty raw tables alongside existing meta_model* (EDS13 naming).
  • Extract E2 merge (MergeSameNameModelsByExtensionChain / MergeEffectiveModel) into pkg/meta; codegen now calls the shared implementation (gold-standard parity with prior generator tests).
  • Add wipe/test helpers EnsureDualStoreTables and MigrateIMDCatalogToDualStore to copy IMD rows into raw, recompute one effective row per (application, name) (reuse tip id), and create a unique index on live effective keys.

Out of scope (EDS-2+): Persist/Uninstall rewire away from materializeEffectiveModels, Auth/loader reader migration, ModelData tip retirement, TS Raw facades.

Upgrade note: Do not run MigrateIMDCatalogToDualStore on a live DB that will keep writing IMD rows into meta_model until EDS-2 switches Persist to raw. Empty raw tables from AutoMigrate are safe.

Test plan

  • go test ./pkg/meta/ ./internal/module/artifact/generate/ -count=1
  • Wipe/reinstall smoke after merge (optional): confirm meta_raw_* tables exist and install still writes IMD meta_model until EDS-2

Made with Cursor


PR Type

Enhancement, Tests


Description

  • Go Core: Introduce declaration-layer meta_raw_* GORM entities with LGPL-3.0 SPDX headers and register them in meta.Entities().

  • Go Core: Extract E2 effective merge logic (MergeSameNameModelsByExtensionChain/MergeEffectiveModel) into pkg/meta and update generator to call shared package.

  • Go Core: Add EnsureDualStoreTables and MigrateIMDCatalogToDualStore migration helpers alongside Go unit test suites.

  • TypeScript Module: Update JSDoc documentation in modules/meta/service/models/model.ts for E2 effective projection and legacy ModuleId.


File Walkthrough

Relevant files
Refactoring
1 files
generator.go
Delegate model extension chain merge to pkg/meta                 
+1/-115 
Enhancement
10 files
dual_store_migrate.go
Implement dual-store table creation and IMD catalog migration
+478/-0 
effective_merge.go
Extract E2 effective model merge logic to pkg/meta             
+265/-0 
meta_raw_argument.go
Define RawArgument entity schema with SPDX headers             
+24/-0   
meta_raw_decorator.go
Define RawDecorator entity schema with SPDX headers           
+31/-0   
meta_raw_field.go
Define RawField entity schema with SPDX headers                   
+114/-0 
meta_raw_model.go
Define RawModel entity schema with SPDX headers                   
+39/-0   
meta_raw_parameter.go
Define RawParameter entity schema with SPDX headers           
+23/-0   
meta_raw_service.go
Define RawService entity schema with SPDX headers               
+32/-0   
meta_raw_typeparameter.go
Define RawTypeParameter entity schema with SPDX headers   
+23/-0   
model.go
Register dual-store raw entities in metadata entity registry
+34/-0   
Tests
3 files
dual_store_migrate_test.go
Add Go unit tests for dual-store migration helpers             
+152/-0 
effective_merge_test.go
Add Go unit tests for extension chain model merging           
+168/-0 
model_test.go
Update metadata entity count and table name test assertions
+14/-0   
Documentation
2 files
meta_model.go
Document effective projection behavior and ModuleId field usage
+12/-5   
model.ts
Update TS model JSDoc comments for dual-store effective representation
+6/-0     

Summary by CodeRabbit

  • New Features

    • Added dual-store metadata migration, preserving source declarations while generating effective models.
    • Added effective model generation that combines extensions and retains fields, services, decorators, and metadata.
    • Added support for recomputing effective models and maintaining unique application/name records.
  • Bug Fixes

    • Improved merge consistency and conflict handling.
    • Prevented unsafe migrations when destination data already exists.
    • Added validation for inheritance cycles and invalid field selections.
  • Documentation

    • Clarified the distinction between source declarations and effective models.

- Introduce declaration-layer Raw* entities and register them in meta.Entities for AutoMigrate.
- Extract MergeSameNameModelsByExtensionChain into pkg/meta and wire codegen to the shared E2 implementation.
- Add EnsureDualStoreTables / MigrateIMDCatalogToDualStore helpers for wipe/test backfill with stable tip ids.

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: 38 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: bc66decc-580c-4666-abbd-2af266417afb

📥 Commits

Reviewing files that changed from the base of the PR and between 34ef11b and cdc7932.

📒 Files selected for processing (2)
  • pkg/meta/dual_store_migrate.go
  • pkg/meta/dual_store_migrate_coverage_test.go
📝 Walkthrough

Walkthrough

Adds raw metadata entities, extension-chain effective-model merging, and IMD-to-dual-store migration. The migration copies declaration trees, recomputes effective projections, clears legacy ModuleId, and enforces unique live application/name pairs.

Changes

Dual-store metadata catalog

Layer / File(s) Summary
Raw metadata contracts
pkg/meta/meta_raw_*.go, pkg/meta/model.go, pkg/meta/model_test.go, pkg/meta/meta_model.go, modules/meta/service/models/model.ts
Adds raw declaration entities, table mappings, entity lists, resolved-spec serialization, and documentation for effective projections and legacy ModuleId.
Effective model merge
pkg/meta/effective_merge.go, pkg/meta/effective_merge_test.go, pkg/meta/effective_merge_coverage_test.go, internal/module/artifact/generate/generator.go, internal/module/artifact/generate/generator_test.go
Adds extension-chain merging, raw-model conversion, conflict validation, merge coverage, and generator delegation to the shared implementation.
Catalog migration and recomputation
pkg/meta/dual_store_migrate.go
Creates dual-store tables, copies IMD trees into raw storage, rebuilds effective projections, and enforces uniqueness.
Migration and merge validation
pkg/meta/dual_store_migrate_test.go, pkg/meta/dual_store_migrate_coverage_test.go, pkg/meta/meta_raw_field_test.go
Tests migration, recomputation, merge behavior, error propagation, dialect-specific indexes, nil handling, and resolved-spec serialization.

Estimated code review effort: 5 (Critical) | ~90 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% 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 primary changes: adding meta_raw_* schema entities and E2 effective-model merging.
Description check ✅ Passed The description explains the changes, scope, upgrade constraints, and test plan in sufficient detail.
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 feat/meta-effective-dual-store-eds1

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: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Missing Database Transaction

RecomputeAllEffectiveFromRaw calls clearEffectiveShapeTrees, which hard-deletes all rows from meta_model, meta_field, meta_service, and related effective shape tables before persisting the new effective projection. Because these steps are not executed within a database transaction (db.Transaction), if an error occurs during MergeEffectiveModel or persistEffectiveProjection (for instance, an extends cycle, field selection conflict, or write error), the function aborts leaving the database in a truncated or partially restored state where effective catalog data is lost. Wrap the clear and persist steps inside a database transaction so failure rolls back cleanly.

// RecomputeAllEffectiveFromRaw replaces effective meta_model* content from all live raw rows.
func RecomputeAllEffectiveFromRaw(db *gorm.DB) error {
	if db == nil {
		return fmt.Errorf("db is nil")
	}

	var raws []*RawModel
	if err := db.
		Preload("Fields").
		Preload("Services").
		Preload("Services.Parameters").
		Preload("Services.TypeParameters").
		Find(&raws).Error; err != nil {
		return fmt.Errorf("load meta_raw_model: %w", err)
	}

	groups := map[string][]*RawModel{}
	tipIDs := map[string]string{}
	for _, raw := range raws {
		if raw == nil {
			continue
		}
		key := logicalModelKey(raw.Application, raw.Name)
		groups[key] = append(groups[key], raw)
		prev := tipIDs[key]
		if prev == "" || rawIsNewerTip(raw, findRawByID(groups[key], prev)) {
			tipIDs[key] = raw.Id.String
		}
	}

	// Soft-delete / clear existing effective shape trees before rewrite.
	if err := clearEffectiveShapeTrees(db); err != nil {
		return err
	}

	for key, group := range groups {
		merged, err := MergeEffectiveModel(group)
		if err != nil {
			return fmt.Errorf("E2 merge %s: %w", key, err)
		}
		if merged == nil {
			continue
		}
		effID := tipIDs[key]
		if effID == "" {
			effID = xid.New().String()
		}
		if err := persistEffectiveProjection(db, merged, effID); err != nil {
			return fmt.Errorf("persist effective %s: %w", key, err)
		}
	}

	if err := ensureEffectiveAppNameUniqueIndex(db); err != nil {
		return err
	}
	return nil
}

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

No code suggestions found for the PR.

Comment thread pkg/meta/dual_store_migrate.go
Comment thread pkg/meta/dual_store_migrate.go
Comment thread pkg/meta/dual_store_migrate.go Outdated
@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!

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

🧹 Nitpick comments (5)
pkg/meta/effective_merge.go (1)

189-230: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated field mapping risks silent column drops.

rawFieldAsField here and rawFieldFromField in pkg/meta/dual_store_migrate.go (lines 254-296) map the same ~35 Field columns in opposite directions. rawServiceAsService and rawServiceFromService duplicate the Service columns the same way. When a column is added to Field or RawField, both mappers must be updated, and an omission fails silently: the column is dropped from the raw copy or from the effective projection with no error.

Two options reduce the hazard:

  1. Extract the shared declaration columns into an embedded struct that both Field and RawField embed, so the mapping becomes a single assignment.
  2. Add a reflection-based test that asserts the exported field sets of Field and RawField match, so a new column fails the build until both mappers are updated.
🤖 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 `@pkg/meta/effective_merge.go` around lines 189 - 230, Reduce drift between
rawFieldAsField and rawFieldFromField, and likewise rawServiceAsService and
rawServiceFromService, by adding reflection-based tests that compare the
exported field sets of each raw/effective type pair. Ensure the tests fail when
a field exists on only one side, forcing both conversion mappers to be updated
for new columns.
pkg/meta/dual_store_migrate_test.go (1)

123-132: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a soft-delete case to the uniqueness test.

This case inserts a second live row and asserts a unique violation. It does not cover the soft-deleted row, which is where the index name uidx_meta_model_app_name_alive and the doc comment in pkg/meta/meta_model.go lines 11-14 diverge from the DDL. Add a case that soft-deletes the effective Partner row and then inserts a new live row with the same (application, name). Assert the intended behavior. With the current non-partial index that insert fails, which pins the semantics either way. The root cause is described on pkg/meta/dual_store_migrate.go lines 468-478.

🤖 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 `@pkg/meta/dual_store_migrate_test.go` around lines 123 - 132, Extend the
uniqueness coverage in the existing test around the duplicate Partner insertion
by soft-deleting the effective live Partner row, then attempting to create
another live row with the same (application, name). Assert the intended
post-soft-delete behavior, preserving the existing duplicate-live-row assertion
and using the current index semantics to pin the result.
pkg/meta/effective_merge_test.go (1)

13-143: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for service merging.

The tests cover field union, raw conversion, cycles, selectionAdd rejection, and tie-breaking. No test covers the Services branch at pkg/meta/effective_merge.go lines 120-130, which uses last-write-wins semantics that differ from the field path. Add a case where a base row and an extension row declare a service with the same Name, and assert that the extension row's service replaces the base one and that distinct service names are unioned.

🤖 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 `@pkg/meta/effective_merge_test.go` around lines 13 - 143, Add a test alongside
TestMergeSameNameModelsByExtensionChain_PartnerStyleUnion that creates base and
extension models with same-name services plus distinct service names, then calls
MergeSameNameModelsByExtensionChain. Assert the extension service replaces the
base service with the same Name while services unique to either model remain
present, covering the Services merge branch’s last-write-wins behavior.
pkg/meta/dual_store_migrate.go (2)

54-77: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

Load and insert in batches.

Line 55-66 loads every meta_model row with six nested preloads into one slice. copyModelTreeToRaw then issues one Create per model, field, service, parameter, type parameter, decorator, and argument. For a catalog with thousands of models the peak memory holds the entire metadata tree, and the write count is one round-trip per row.

Page the source query with FindInBatches, and group the child writes with CreateInBatches. The same applies to RecomputeAllEffectiveFromRaw at lines 89-96 and to persistEffectiveProjection.

🤖 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 `@pkg/meta/dual_store_migrate.go` around lines 54 - 77, Update the migration
flow around the source query and copyModelTreeToRaw to process models with
FindInBatches instead of loading the entire preloaded tree, and batch each
corresponding child insertion with CreateInBatches. Apply the same batching
approach to RecomputeAllEffectiveFromRaw and persistEffectiveProjection,
preserving existing preload relationships and migration behavior while avoiding
one write round-trip per row.

98-110: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Track the tip row by pointer, not by id lookup.

findRawByID(groups[key], prev) rescans the whole group on every row, so tip selection is O(n²) per (application, name) group. The lookup exists only to recover a *RawModel that the loop already held.

Two further details:

  • A raw row with an empty Id.String sets tipIDs[key] to "". The next iteration reads prev == "" and replaces the tip unconditionally, bypassing the rawIsNewerTip comparison.
  • Tip selection orders by CreatedAt then Id, while MergeSameNameModelsByExtensionChain picks canonical scalars by extends-depth, then UpdatedAt, then Id. The effective row can therefore reuse the id of one raw row while carrying the scalars of another. The doc comment at line 37 states this is intended; a short note here would record why the two orderings differ.

Holding the tip pointer removes both the quadratic scan and the empty-id edge case, and lets findRawByID be deleted.

♻️ Proposed refactor
 	groups := map[string][]*RawModel{}
-	tipIDs := map[string]string{}
+	tips := map[string]*RawModel{}
 	for _, raw := range raws {
 		if raw == nil {
 			continue
 		}
 		key := logicalModelKey(raw.Application, raw.Name)
 		groups[key] = append(groups[key], raw)
-		prev := tipIDs[key]
-		if prev == "" || rawIsNewerTip(raw, findRawByID(groups[key], prev)) {
-			tipIDs[key] = raw.Id.String
+		if rawIsNewerTip(raw, tips[key]) {
+			tips[key] = raw
 		}
 	}

Then read the id at the projection site:

var effID string
if tip := tips[key]; tip != nil && tip.Id.Valid {
    effID = tip.Id.String
}
if effID == "" {
    effID = xid.New().String()
}
🤖 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 `@pkg/meta/dual_store_migrate.go` around lines 98 - 110, Update tip selection
in the migration flow to track the current *RawModel in a tips map keyed by
logical model key, comparing each row directly with the stored pointer via
rawIsNewerTip; do not use raw Id.String as the sentinel or call findRawByID. At
the projection site, derive effID from the selected tip only when tip.Id is
valid, otherwise generate a new xid, and add a brief note explaining that tip
ordering intentionally differs from canonical scalar selection in
MergeSameNameModelsByExtensionChain; remove findRawByID once unused.
🤖 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 `@pkg/meta/dual_store_migrate.go`:
- Around line 112-115: Update the comments near clearEffectiveShapeTrees and the
ordering note around the effective-table cleanup to state that the effective
tables are truncated, not soft-deleted. Extend the RecomputeAllEffectiveFromRaw
doc comment to warn that it destroys the entire effective catalog and rebuilds
only (application, name) groups present in meta_raw_model, so rows absent from
the raw store cannot be recovered.
- Around line 38-80: Make MigrateIMDCatalogToDualStore run all validation,
copying, and recomputation work inside a single db.Transaction, but invoke
EnsureDualStoreTables and its AutoMigrate DDL before opening that transaction.
Also update RecomputeAllEffectiveFromRaw to execute its clear-and-rebuild
workflow transactionally, while moving ensureEffectiveAppNameUniqueIndex outside
the transaction so DDL is not performed within it.
- Around line 166-184: Update the migration flow around copyModelTreeToRaw to
explicitly copy only live rows or use unscoped queries and preserve DeletedAt
consistently. Before copying, preflight duplicate (Path, ModuleId) keys,
counting existing raw rows with Unscoped so soft-deleted rows reserve unique
keys. Wrap the duplicate validation, raw-row copying, and recompute steps in one
database transaction so any failure rolls back all changes.
- Around line 468-478: Update ensureEffectiveAppNameUniqueIndex in
pkg/meta/dual_store_migrate.go:468-478 to drop/recreate
uidx_meta_model_app_name_alive as a partial unique index with WHERE deleted_at
IS NULL, and rename sql to stmt. Keep the index definition consistent in
pkg/meta/meta_model.go:10-14 and modules/meta/service/models/model.ts:39-43.
Extend pkg/meta/dual_store_migrate_test.go:123-132 to verify a soft-deleted row
permits reuse while duplicate live (application, name) rows remain rejected.
- Around line 401-429: Update RecomputeAllEffectiveFromRaw and
persistEffectiveProjection to load and project raw project, field, and service
decorators plus arguments into the effective records before
clearEffectiveShapeTrees removes existing rows. Clone these associations with
the new effective IDs and parent references, preserving the metadata expected by
backend builds rather than nil-ing or omitting them.

In `@pkg/meta/effective_merge.go`:
- Around line 19-24: Remove the len(models)==0/1 early-return fast paths in the
effective merge entry point and route every non-empty input through the existing
merge loop. Preserve the nil result for empty input while ensuring single-model
merges perform FieldHasSelectionAdd validation and return a copy rather than the
caller’s *Model, matching multi-model behavior.

In `@pkg/meta/meta_raw_model.go`:
- Around line 15-34: Update RawModel’s ModuleId/path uniqueness around the model
definition and copyModelTreeToRaw flow so moduleless rows cannot share the same
Path despite nullable ModuleId values. Choose an appropriate fix by requiring a
module owner, migrating legacy null ModuleId rows, or enforcing null-safe
uniqueness, and add coverage proving duplicate moduleless RawModel paths are
rejected.

---

Nitpick comments:
In `@pkg/meta/dual_store_migrate_test.go`:
- Around line 123-132: Extend the uniqueness coverage in the existing test
around the duplicate Partner insertion by soft-deleting the effective live
Partner row, then attempting to create another live row with the same
(application, name). Assert the intended post-soft-delete behavior, preserving
the existing duplicate-live-row assertion and using the current index semantics
to pin the result.

In `@pkg/meta/dual_store_migrate.go`:
- Around line 54-77: Update the migration flow around the source query and
copyModelTreeToRaw to process models with FindInBatches instead of loading the
entire preloaded tree, and batch each corresponding child insertion with
CreateInBatches. Apply the same batching approach to
RecomputeAllEffectiveFromRaw and persistEffectiveProjection, preserving existing
preload relationships and migration behavior while avoiding one write round-trip
per row.
- Around line 98-110: Update tip selection in the migration flow to track the
current *RawModel in a tips map keyed by logical model key, comparing each row
directly with the stored pointer via rawIsNewerTip; do not use raw Id.String as
the sentinel or call findRawByID. At the projection site, derive effID from the
selected tip only when tip.Id is valid, otherwise generate a new xid, and add a
brief note explaining that tip ordering intentionally differs from canonical
scalar selection in MergeSameNameModelsByExtensionChain; remove findRawByID once
unused.

In `@pkg/meta/effective_merge_test.go`:
- Around line 13-143: Add a test alongside
TestMergeSameNameModelsByExtensionChain_PartnerStyleUnion that creates base and
extension models with same-name services plus distinct service names, then calls
MergeSameNameModelsByExtensionChain. Assert the extension service replaces the
base service with the same Name while services unique to either model remain
present, covering the Services merge branch’s last-write-wins behavior.

In `@pkg/meta/effective_merge.go`:
- Around line 189-230: Reduce drift between rawFieldAsField and
rawFieldFromField, and likewise rawServiceAsService and rawServiceFromService,
by adding reflection-based tests that compare the exported field sets of each
raw/effective type pair. Ensure the tests fail when a field exists on only one
side, forcing both conversion mappers to be updated for new columns.
🪄 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

Run ID: 31a0d51f-8bf9-4fc0-b4a2-177a5c38d39e

📥 Commits

Reviewing files that changed from the base of the PR and between 080e4f0 and fe4f81d.

📒 Files selected for processing (16)
  • internal/module/artifact/generate/generator.go
  • modules/meta/service/models/model.ts
  • pkg/meta/dual_store_migrate.go
  • pkg/meta/dual_store_migrate_test.go
  • pkg/meta/effective_merge.go
  • pkg/meta/effective_merge_test.go
  • pkg/meta/meta_model.go
  • pkg/meta/meta_raw_argument.go
  • pkg/meta/meta_raw_decorator.go
  • pkg/meta/meta_raw_field.go
  • pkg/meta/meta_raw_model.go
  • pkg/meta/meta_raw_parameter.go
  • pkg/meta/meta_raw_service.go
  • pkg/meta/meta_raw_typeparameter.go
  • pkg/meta/model.go
  • pkg/meta/model_test.go

Comment thread pkg/meta/dual_store_migrate.go
Comment thread pkg/meta/dual_store_migrate.go Outdated
Comment thread pkg/meta/dual_store_migrate.go
Comment thread pkg/meta/dual_store_migrate.go
Comment thread pkg/meta/dual_store_migrate.go
Comment thread pkg/meta/effective_merge.go Outdated
Comment thread pkg/meta/meta_raw_model.go
- Wrap IMD→dual-store migrate/recompute in a DB transaction and document hard-delete rebuild semantics.
- Preload and project decorator/argument trees into effective rows; require ModuleId and preflight (module,path) duplicates.
- Use a partial unique index on live (application, name); unify single-model merge validation/copy with the multi-row path.

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

buke commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Review triage (applied in follow-up commit)

Finding Verdict Action
Recompute/migrate non-transactional (Bugbot + CodeRabbit) ✅ Reasonable Wrapped copy+recompute in db.Transaction; DDL stays outside
Effective rebuild drops decorators (Bugbot + CodeRabbit) ✅ Reasonable Preload + map + persist decorator/argument trees
Soft-delete wording vs hard-delete ✅ Reasonable Comments/docs corrected
Soft-deleted raw still occupies unique keys / live-only copy ✅ Reasonable Unscoped raw count; document live-only source load; ModuleId required + duplicate preflight
Partial unique index WHERE deleted_at IS NULL ✅ Reasonable SQLite/Postgres partial index; MySQL keeps full unique
Single-model merge fast path skips validation / aliases pointer ✅ Reasonable Always runs full merge loop (copy + selectionAdd check)
NULL ModuleId breaks composite uniqueness on SQLite ✅ Reasonable Migrate rejects missing ModuleId
Field/RawField mapper drift ✅ Reasonable (nit) Added exported-column parity test
Recompute should filter via selectSameNameModelsInPrimaryExtensionChain ❌ Skip Conflicts with EDS2: effective = union of all live same-name raws (incl. sibling branches). Codegen may still pre-filter when reading legacy IMD; dual-store E2 must not.

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/meta/dual_store_migrate.go (1)

556-579: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Make ensureEffectiveAppNameUniqueIndex idempotent for MySQL. MySQL rejects DROP INDEX IF EXISTS and requires ON meta_model. It does not support CREATE UNIQUE INDEX IF NOT EXISTS. Since line 558 discards the drop error, line 571 fails with a duplicate key name on every later RecomputeAllEffectiveFromRaw call. Use DROP INDEX <name> ON meta_model and ignore only the missing-index error, or query information_schema.STATISTICS first.

🤖 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 `@pkg/meta/dual_store_migrate.go` around lines 556 - 579, Update
ensureEffectiveAppNameUniqueIndex for MySQL so index removal uses the required
DROP INDEX <name> ON meta_model syntax and treats only a missing-index result as
ignorable; preserve other drop errors and return them. Keep the existing
partial-index handling for SQLite/Postgres and ensure repeated
RecomputeAllEffectiveFromRaw calls do not fail with duplicate index-name errors.
🧹 Nitpick comments (1)
pkg/meta/dual_store_migrate_test.go (1)

161-166: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert the decorator argument payload.

This test only checks the number of decorator arguments. A migration that changes Argument.Type or Argument.Value will still pass. Assert the seeded values to protect the declaration payload.

Proposed test assertion
 if len(nameField.Decorators[0].Arguments) != 1 {
     t.Fatalf("expected decorator argument preserved, got %#v", nameField.Decorators[0].Arguments)
 }
+arg := nameField.Decorators[0].Arguments[0]
+if arg == nil || arg.Type != "object" || arg.Value != `{"type":"varchar"}` {
+    t.Fatalf("expected decorator argument payload preserved, got %#v", arg)
+}
🤖 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 `@pkg/meta/dual_store_migrate_test.go` around lines 161 - 166, Strengthen the
assertions in the migration test around nameField.Decorators[0].Arguments by
validating the seeded Argument.Type and Argument.Value, not only the argument
count. Preserve the existing decorator and argument-presence checks while
failing with the actual argument details when either payload field changes.
🤖 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 `@pkg/meta/dual_store_migrate.go`:
- Around line 169-171: The call to ensureEffectiveAppNameUniqueIndex at line 169
inside recomputeAllEffectiveFromRawTx violates the documented contract (lines
40-41) that DDL operations run outside transactions, since DDL causes implicit
commits on MySQL and breaks transaction atomicity. Remove the
ensureEffectiveAppNameUniqueIndex call from inside
recomputeAllEffectiveFromRawTx and instead invoke
ensureEffectiveAppNameUniqueIndex from the exported wrapper functions that call
recomputeAllEffectiveFromRawTx, placing it after the transaction completes.

---

Outside diff comments:
In `@pkg/meta/dual_store_migrate.go`:
- Around line 556-579: Update ensureEffectiveAppNameUniqueIndex for MySQL so
index removal uses the required DROP INDEX <name> ON meta_model syntax and
treats only a missing-index result as ignorable; preserve other drop errors and
return them. Keep the existing partial-index handling for SQLite/Postgres and
ensure repeated RecomputeAllEffectiveFromRaw calls do not fail with duplicate
index-name errors.

---

Nitpick comments:
In `@pkg/meta/dual_store_migrate_test.go`:
- Around line 161-166: Strengthen the assertions in the migration test around
nameField.Decorators[0].Arguments by validating the seeded Argument.Type and
Argument.Value, not only the argument count. Preserve the existing decorator and
argument-presence checks while failing with the actual argument details when
either payload field changes.
🪄 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

Run ID: 0c00c45a-4fe5-4b42-b56a-481e103a4a43

📥 Commits

Reviewing files that changed from the base of the PR and between fe4f81d and aa33fe6.

📒 Files selected for processing (5)
  • internal/module/artifact/generate/generator_test.go
  • pkg/meta/dual_store_migrate.go
  • pkg/meta/dual_store_migrate_test.go
  • pkg/meta/effective_merge.go
  • pkg/meta/effective_merge_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/meta/effective_merge.go

Comment thread pkg/meta/dual_store_migrate.go Outdated
- Run ensureEffectiveAppNameUniqueIndex after migrate/recompute commit so MySQL DDL cannot implicitly commit mid-TX.
- Make MySQL unique-index ensure idempotent via HasIndex/DropIndex before CREATE.
- Assert migrated decorator argument Type/Value in the dual-store migrate test.

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

buke commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Review triage (review)

Finding Verdict Action
Index DDL inside recomputeAllEffectiveFromRawTx (MySQL implicit commit) ✅ Reasonable Moved ensureEffectiveAppNameUniqueIndex to after TX commit in exported wrappers
MySQL ensureEffectiveAppNameUniqueIndex not idempotent ✅ Reasonable HasIndexDropIndexCREATE UNIQUE INDEX
Assert decorator argument Type/Value in migrate test ✅ Reasonable (nit) Strengthened assertion

@buke

buke commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@cursor review

Comment thread pkg/meta/dual_store_migrate.go Outdated
Comment thread pkg/meta/dual_store_migrate.go Outdated
Comment thread pkg/meta/effective_merge.go
- Skip recompute when migrate finds no live IMD sources so soft-deleted effective rows are not hard-wiped.
- Omit synthetic service parameter this from effective merge/persist, matching codegen readers.

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

buke commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Review triage (Bugbot review)

Finding Verdict Action
Recompute keeps synthetic this parameters ✅ Reasonable Omit this in rawServiceAsService + persistEffectiveProjection (codegen parity)
Empty migrate wipes effective catalog ✅ Reasonable Empty live source set is a no-op (no recompute/hard-delete)
Model-level decorators not union-merged across chain ❌ Skip Matches EDS E2 §4.6 (“挂件随胜出 Field/Service/Model”) and codegen gold standard — canonical row wins; not a Field/Service-style union

- Cover meta_raw_field Set/GetResolvedSpec including injectable marshal failures.
- Expand E2 merge unit cases for nil/empty/service/conflict/tie-break paths.
- Add dual-store migrate/recompute coverage for happy trees, dialect index DDL, and error hooks.

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

buke commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Codecov follow-up

Brought these files to 100% statement coverage (go tool cover -func):

  • pkg/meta/dual_store_migrate.go
  • pkg/meta/effective_merge.go
  • pkg/meta/meta_raw_field.go

@buke

buke commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@cursor review

@buke

buke commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Codecov follow-up

Brought these files to 100% statement coverage (go tool cover -func):

  • pkg/meta/dual_store_migrate.go
  • pkg/meta/effective_merge.go
  • pkg/meta/meta_raw_field.go

@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 9a506bd. Configure here.

@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 (5)
pkg/meta/dual_store_migrate_test.go (1)

303-317: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Also assert that the raw side keeps the this parameter.

The test proves the effective projection omits this. It does not prove the raw declaration still holds it. That pair is the dual-store invariant: raw preserves the declaration, effective omits the synthetic parameter. A regression that dropped this during the copy into meta_raw_parameter would still pass this test.

♻️ Suggested addition
 	params := eff[0].Services[0].Parameters
 	if len(params) != 1 || params[0].Name != "vals" {
 		t.Fatalf("expected only vals on effective service, got %#v", params)
 	}
+	var rawParams []*RawParameter
+	if err := db.Where("service_id = ?", svc.Id).Find(&rawParams).Error; err != nil {
+		t.Fatalf("load raw params: %v", err)
+	}
+	if len(rawParams) != 2 {
+		t.Fatalf("expected raw service to retain this and vals, got %#v", rawParams)
+	}
 }
🤖 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 `@pkg/meta/dual_store_migrate_test.go` around lines 303 - 317, Extend the test
after the effective projection assertions to load the raw service and its
parameters, then assert that the raw declaration retains the synthetic “this”
parameter alongside “vals”. Keep the existing effective-side assertion unchanged
so the test verifies both sides of the dual-store invariant.
pkg/meta/dual_store_migrate_coverage_test.go (4)

430-573: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consolidate the six near-identical failure cases.

Each sub-case repeats the same four steps: open a database, ensure tables, drop one raw table, and call copyModelTreeToRaw. A table-driven loop over (name, dropTable, srcModel) removes most of this boilerplate and makes a missing case obvious.

Two related points:

  • Line 437 contains a leftover working note. Remove it or replace it with a statement of intent.
  • This function name is nearly identical to TestCopyModelTreeToRaw_FieldAndServiceDecoratorFailures at Line 893. Rename one of them, for example to TestCopyModelTreeToRaw_ChildTableFailures and TestCopyModelTreeToRaw_NestedDecoratorFailures.
🤖 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 `@pkg/meta/dual_store_migrate_coverage_test.go` around lines 430 - 573,
Refactor TestCopyModelTreeToRaw_FieldServiceDecoratorFailures into a
table-driven loop covering the six raw-table failure cases, with each case
defining its name, table-drop operation, and source Model while preserving the
expected copy error assertions. Remove the leftover working note near the first
case or replace it with a concise intent comment, and rename this test and the
similarly named test near the later decorator cases to distinct names such as
TestCopyModelTreeToRaw_ChildTableFailures and
TestCopyModelTreeToRaw_NestedDecoratorFailures.

671-679: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Two persistDecoratorTree cases accept any outcome. Both sites call persistDecoratorTree and then tolerate success or failure, logging instead of asserting. Each block raises statement coverage without pinning behavior, so a regression in argument persistence passes. The outcome is deterministic in both cases, because the test controls whether meta_argument exists and SQLite foreign keys are off in this helper.

  • pkg/meta/dual_store_migrate_coverage_test.go#L671-L679: Argument was dropped at Line 670, so replace the empty if body and the else if/t.Logf chain with t.Fatal on err == nil. The empty if body also triggers staticcheck SA9003.
  • pkg/meta/dual_store_migrate_coverage_test.go#L966-L973: meta_argument still exists at this point, so assert err == nil, or remove the call and keep only the deterministic drop-table case at Lines 974-979.
🤖 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 `@pkg/meta/dual_store_migrate_coverage_test.go` around lines 671 - 679, Make
both persistDecoratorTree test sites assert deterministic outcomes instead of
accepting and logging either result. At
pkg/meta/dual_store_migrate_coverage_test.go lines 671-679, replace the empty
if/else chain with t.Fatal when err is nil, since the dropped Argument requires
failure. At pkg/meta/dual_store_migrate_coverage_test.go lines 966-973, assert
err is nil while meta_argument still exists, or remove that call and retain the
deterministic drop-table case.

686-711: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Note the fidelity limit of name-only dialect faking.

namedDialector overrides Name() only. The statements still run on SQLite. So these cases prove branch selection, not that the Postgres and MySQL DDL is valid on those engines. If the MySQL branch emits engine-specific DDL, add a dialect-tagged integration test, or assert the generated SQL text instead of only the returned error.

Mutating db.Dialector on an open *gorm.DB also persists for the rest of the test. That is contained here because each case reopens a database, but a comment stating the intent would help future edits.

🤖 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 `@pkg/meta/dual_store_migrate_coverage_test.go` around lines 686 - 711, Clarify
TestEnsureEffectiveAppNameUniqueIndex_Dialects that namedDialector only
exercises dialect branch selection while SQL still executes against SQLite, and
document that each database is reopened to isolate persistent db.Dialector
mutation. To validate engine-specific MySQL or PostgreSQL DDL, add
dialect-tagged integration coverage or assert generated SQL rather than relying
only on returned errors.

202-224: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider asserting the copied field tree.

The query preloads Fields.Decorators but the test does not assert on eff[0].Fields. Add an assertion for the migrated Name field and its Field decorator to make the preload meaningful.

🤖 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 `@pkg/meta/dual_store_migrate_coverage_test.go` around lines 202 - 224, Extend
the assertions after loading eff in the migration test to validate the copied
field tree: assert the expected migrated Name field exists and that it has the
expected Field decorator. Use eff[0].Fields and its decorators, alongside the
existing model and service assertions.
🤖 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 `@pkg/meta/dual_store_migrate.go`:
- Around line 595-601: The index migration block in the dual-store migration
currently ignores the error from dropping effectiveAppNameUniqueIndex. Capture
and return the db.Exec DROP INDEX error immediately, before constructing or
executing the CREATE UNIQUE INDEX statement, so the replacement is not attempted
when removal fails.

---

Nitpick comments:
In `@pkg/meta/dual_store_migrate_coverage_test.go`:
- Around line 430-573: Refactor
TestCopyModelTreeToRaw_FieldServiceDecoratorFailures into a table-driven loop
covering the six raw-table failure cases, with each case defining its name,
table-drop operation, and source Model while preserving the expected copy error
assertions. Remove the leftover working note near the first case or replace it
with a concise intent comment, and rename this test and the similarly named test
near the later decorator cases to distinct names such as
TestCopyModelTreeToRaw_ChildTableFailures and
TestCopyModelTreeToRaw_NestedDecoratorFailures.
- Around line 671-679: Make both persistDecoratorTree test sites assert
deterministic outcomes instead of accepting and logging either result. At
pkg/meta/dual_store_migrate_coverage_test.go lines 671-679, replace the empty
if/else chain with t.Fatal when err is nil, since the dropped Argument requires
failure. At pkg/meta/dual_store_migrate_coverage_test.go lines 966-973, assert
err is nil while meta_argument still exists, or remove that call and retain the
deterministic drop-table case.
- Around line 686-711: Clarify TestEnsureEffectiveAppNameUniqueIndex_Dialects
that namedDialector only exercises dialect branch selection while SQL still
executes against SQLite, and document that each database is reopened to isolate
persistent db.Dialector mutation. To validate engine-specific MySQL or
PostgreSQL DDL, add dialect-tagged integration coverage or assert generated SQL
rather than relying only on returned errors.
- Around line 202-224: Extend the assertions after loading eff in the migration
test to validate the copied field tree: assert the expected migrated Name field
exists and that it has the expected Field decorator. Use eff[0].Fields and its
decorators, alongside the existing model and service assertions.

In `@pkg/meta/dual_store_migrate_test.go`:
- Around line 303-317: Extend the test after the effective projection assertions
to load the raw service and its parameters, then assert that the raw declaration
retains the synthetic “this” parameter alongside “vals”. Keep the existing
effective-side assertion unchanged so the test verifies both sides of the
dual-store invariant.
🪄 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

Run ID: 56a09dc8-816d-4323-9498-975bf3157ae6

📥 Commits

Reviewing files that changed from the base of the PR and between aa33fe6 and 9a506bd.

📒 Files selected for processing (8)
  • pkg/meta/dual_store_migrate.go
  • pkg/meta/dual_store_migrate_coverage_test.go
  • pkg/meta/dual_store_migrate_test.go
  • pkg/meta/effective_merge.go
  • pkg/meta/effective_merge_coverage_test.go
  • pkg/meta/effective_merge_test.go
  • pkg/meta/meta_raw_field.go
  • pkg/meta/meta_raw_field_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/meta/effective_merge.go
  • pkg/meta/meta_raw_field.go

Comment thread pkg/meta/dual_store_migrate.go Outdated
- Return sqlite/postgres DROP INDEX failures before recreating the partial unique index.
- Assert raw retains this while effective omits it; strengthen migrate/persist coverage assertions.
- Table-drive child-table copy failures and clarify dialect-branch test limits.

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

buke commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Review triage (review)

Finding Verdict Action
Ignore DROP INDEX error before partial recreate ✅ Reasonable Return drop error; do not CREATE IF NOT EXISTS over a leftover full unique index
Assert raw still keeps this ✅ Reasonable Extended TestRecomputeAllEffectiveFromRaw_OmitsThisParameter
Table-drive six copy failure cases + rename ✅ Reasonable TestCopyModelTreeToRaw_ChildTableFailures / _NestedDecoratorFailures
persistDecoratorTree accepts any outcome ✅ Reasonable Assert deterministic success/failure
Dialect faking only selects branch ✅ Partial Documented limit; no real MySQL/PG DDL integration in EDS-1
Assert migrated field tree ✅ Reasonable Assert Name + Field decorator
OpenGrep SQL injection on index DDL ❌ Skip Index name is a package const, not user input

@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

🤖 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 `@pkg/meta/dual_store_migrate.go`:
- Around line 595-598: Update the index migration flow around
effectiveAppNameUniqueIndex so the existing uniqueness constraint remains
available if replacement CREATE UNIQUE INDEX fails: create the replacement under
a temporary name before removing the old index, or implement an equivalent
atomic swap/restore path, then finalize the intended index name. Add a
failure-injection test that forces creation to fail and verifies the original
unique index still protects live (application, name) rows.
🪄 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

Run ID: d013234b-b595-4d54-a7a7-71d685fdc6c2

📥 Commits

Reviewing files that changed from the base of the PR and between 9a506bd and 34ef11b.

📒 Files selected for processing (3)
  • pkg/meta/dual_store_migrate.go
  • pkg/meta/dual_store_migrate_coverage_test.go
  • pkg/meta/dual_store_migrate_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/meta/dual_store_migrate_test.go

Comment thread pkg/meta/dual_store_migrate.go Outdated
- Create the replacement (application, name) unique index under a temp name before dropping the final index.
- Keep the temp index if final recreate fails, and cover the failure path with injection tests.

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

buke commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Review triage (review)

Finding Verdict Action
Drop-before-create can leave catalog with no unique index if CREATE fails ✅ Reasonable Create *_new first, then replace final name; temp remains on final-create failure
Failure-injection test for preserved uniqueness ✅ Reasonable Added (soft-delete reuse + live dup still blocked under temp)
SQL injection on fmt.Sprintf index DDL ❌ Skip Index name is a package const, not user input

@buke
buke merged commit 2844af4 into main Aug 4, 2026
43 checks passed
@buke
buke deleted the feat/meta-effective-dual-store-eds1 branch August 4, 2026 11:43
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