refactor(meta): move dual-store engine into internal/module/meta - #257
Conversation
- Relocate dual-store, declaration APIs, and raw tables to internal/module/meta so pkg/meta stays catalog entities and utils. - Fold former metadata ops tables and meta_lock_lease into CatalogEntities; remove internal/module/metadata and state/lease/model. - Delete unused Ensure/Upsert/Prefer/Has*Catalog, IMD migrate, and ACL remap; unexport internal helpers and wire the live unique index. Co-authored-by: Cursor <cursoragent@cursor.com>
📝 WalkthroughWalkthroughThe PR consolidates metadata models and persistence APIs under ChangesMetadata consolidation
Estimated code review effort: 3 (Moderate) | ~30 minutes Sequence Diagram(s)sequenceDiagram
participant ModuleLifecycle
participant InternalMeta
participant RawCatalog
participant EffectiveCatalog
ModuleLifecycle->>InternalMeta: RemoveModuleDeclarations
InternalMeta->>RawCatalog: Delete declaration trees
InternalMeta-->>ModuleLifecycle: Return affected logical keys
ModuleLifecycle->>InternalMeta: FlushEffective
InternalMeta->>EffectiveCatalog: Rebuild effective projections
EffectiveCatalog-->>ModuleLifecycle: Return flush result
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
|
Failed to generate code suggestions for PR |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/module/artifact/build/backend/builder.go (1)
886-905: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winMake declaration mutation and effective recomputation atomic.
ReplaceModuleDeclarationsorRemoveModuleDeclarationschanges the raw declaration store beforeFlushEffectiveruns. IfFlushEffectivefails,meta_modelcan retain stale effective projections.Run each operation pair in one outer transaction. Add a regression test that forces recomputation to fail and verifies rollback.
internal/module/artifact/build/backend/builder.go#L886-L905: wrapReplaceModuleDeclarationsandFlushEffectivein one transaction.internal/module/lifecycle/uninstaller.go#L64-L70: wrapRemoveModuleDeclarationsandFlushEffectivein one transaction.🤖 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/artifact/build/backend/builder.go` around lines 886 - 905, Make declaration mutation and effective recomputation atomic by running ReplaceModuleDeclarations and FlushEffective together in one outer transaction in internal/module/artifact/build/backend/builder.go:886-905, preserving the existing key collection and error propagation. Apply the same transaction boundary to RemoveModuleDeclarations and FlushEffective in internal/module/lifecycle/uninstaller.go:64-70. Add regression coverage that forces recomputation to fail and verifies both the declaration changes and effective projections roll back.
🧹 Nitpick comments (7)
internal/module/lifecycle/uninstaller_clean_models_test.go (1)
193-204: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider exporting the raw table names instead of duplicating literals.
The helpers now hardcode seven raw table names.
internal/module/metaalready defines raw table constants, for examplerawServiceTable. The literals here drift silently if a raw table is renamed. Exporting the constants, or adding a small test helper ininternal/module/meta, removes the duplication.🤖 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_clean_models_test.go` around lines 193 - 204, Update deleteRawCleanModelsPrefix and deleteRawModelTree to reuse the raw table-name constants defined in internal/module/meta, such as rawServiceTable, instead of hardcoded literals. Export the required constants if necessary, preserving the existing deletion order and behavior.internal/module/meta/declaration_test.go (2)
176-179: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the redundant
ensureDualStoreTablescall.
openDeclarationTestDBalready callsensureDualStoreTablesat line 25 ofdeclaration_coverage_test.go. Line 177 repeats it.🤖 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/meta/declaration_test.go` around lines 176 - 179, Remove the redundant ensureDualStoreTables call and its error handling from the test setup after openDeclarationTestDB in the remove-decls test, relying on openDeclarationTestDB to initialize the tables.
67-97: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtend the cascade test to cover decorators, arguments, and parameters.
The seeded tree contains only one field and one service.
DeleteDeclarationTreesbranches ondecoratorIDs,rawArgument,rawParameter, andrawTypeParameterat lines 116-134 ofdeclaration.go. Those branches never run in this test. Add a decorator with an argument, plus a service parameter and type parameter, and assert that all rows are removed. This matches the_Cascadename.🤖 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/meta/declaration_test.go` around lines 67 - 97, Extend TestDeleteDeclarationTrees_Cascade’s seeded Model tree with a decorator containing an argument, and add a parameter and type parameter to the service, using the existing metadata relationships and raw models. After DeleteDeclarationTrees, assert that decorator, argument, parameter, and type-parameter rows for the model’s descendants are all absent, alongside the existing model, field, and service assertions.internal/module/meta/declaration_coverage_test.go (2)
119-131: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRename the test to match what it verifies.
The name says
NilPrevDeclRow, but the inline comment states that path is unreachable. The test actually covers an invalid logical key produced from a zero-valued previous effective model. Rename it to, for example,TestReplaceModuleDeclarations_SkipsInvalidPrevEffectiveKey, and remove the obsolete first comment line.🤖 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/meta/declaration_coverage_test.go` around lines 119 - 131, Rename TestReplaceModuleDeclarations_NilPrevDeclRow to reflect that it covers skipping an invalid previous-effective key, such as TestReplaceModuleDeclarations_SkipsInvalidPrevEffectiveKey. Remove the obsolete comment stating that ListDeclarations can return a nil element, while retaining the comment describing the zero-valued pkgmeta.Model coverage.
41-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert the last-wins result for duplicate paths.
Lines 44-45 give
"A"and"A2"the same path/a.ts.ReplaceModuleDeclarationsoverwritesmodelByPath, so"A2"wins while the path order comes from"A". The test checks onlylen(keys) != 0. Add an assertion that the persisted declaration for/a.tsis named"A2". This locks down the deduplication behavior.🤖 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/meta/declaration_coverage_test.go` around lines 41 - 52, Extend the test around ReplaceModuleDeclarations to retrieve the persisted declaration for /a.ts and assert its name is "A2". Keep the existing non-empty keys assertion and error handling unchanged, using the test’s available database or lookup mechanism to verify the last duplicate wins.internal/module/meta/declaration.go (2)
213-244: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winDeduplicate the returned logical keys.
appendKeyappends without deduplication. The same(application, name)pair usually appears inprevDecls,prevEff, and the new models, so the returned slice repeats keys. Callers pass these keys toFlushEffective, which then repeats work for the same key.RemoveModuleDeclarationsat line 163 already deduplicates with aseenmap. Apply the same approach here for consistency.♻️ Proposed deduplication
keys := make([]LogicalKey, 0) + seen := map[string]struct{}{} appendKey := func(application, name string) { k := LogicalKey{Application: application, Name: name}.Normalized() - if k.Valid() { - keys = append(keys, k) + if !k.Valid() { + return } + id := k.Application + "\x00" + k.Name + if _, ok := seen[id]; ok { + return + } + seen[id] = struct{}{} + keys = append(keys, k) }🤖 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/meta/declaration.go` around lines 213 - 244, Deduplicate logical keys in the appendKey closure within the surrounding declaration-processing function by tracking normalized (application, name) pairs in a seen map before appending to keys. Preserve the existing Valid check and ensure repeated keys from prevDecls, prevEff, and orderedPaths are added only once, consistent with RemoveModuleDeclarations.
246-262: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueConsider setting
ModuleIdinside the transaction.Line 242 mutates each caller-supplied
*pkgmeta.Modelbefore the transaction runs. If the transaction fails, the database rolls back, but the caller's structs keep the assignedModuleId. Callers that reuse the slice after an error then observe partially mutated input.🤖 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/meta/declaration.go` around lines 246 - 262, Move the `ModuleId` assignment for caller-supplied `*pkgmeta.Model` values into the `db.Transaction` callback, before `deleteRawModelsForModule` and `persistModelTreeAsRaw` execute. Ensure the mutation occurs only within the transaction flow so failed transactions do not leave the input models assigned.
🤖 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 `@internal/module/meta/dual_store.go`:
- Around line 64-70: Update ensureBaseModelID to treat an invalid b.Id as absent
by generating a new ID when either !b.Id.Valid or the trimmed ID string is
empty; preserve the existing nil guard and assignment behavior.
In `@internal/module/meta/extends_expand_test.go`:
- Around line 29-31: Revert the fixture’s rawModel Name value in the parent
setup to a realistic TypeScript-style declaration name, rather than the Go-style
"pkgmeta.BaseModel" value. Leave the BaseModel field reference and
parent-resolution behavior unchanged.
In `@internal/module/meta/lookup_effective_test.go`:
- Around line 17-24: Replace the named shared-cache SQLite setup in
TestLookupEffectiveModel_FindsLiveRow with the package helper
openDualStoreTestDB(t), preserving the existing table initialization and error
handling. Also update the test using the lookup-eff-closed DSN to use
openDualStoreTestDB(t) so both tests receive isolated temporary databases.
In `@internal/module/meta/raw_helpers.go`:
- Around line 30-35: Update the query construction in the rawModel count helper
to use db.Model(&rawModel{}) so GORM applies the default soft-delete scope, then
retain q = q.Unscoped() only when unscoped is true. Add coverage verifying a
soft-deleted record produces scoped count 0 and unscoped count 1.
In `@internal/testing/e2e/runner.go`:
- Around line 685-687: Update the AutoMigrate error message in the session setup
flow to report the consolidated modmeta.ModuleIndex type instead of the obsolete
metadata.ModuleIndex name, while preserving the existing wrapped error behavior.
---
Outside diff comments:
In `@internal/module/artifact/build/backend/builder.go`:
- Around line 886-905: Make declaration mutation and effective recomputation
atomic by running ReplaceModuleDeclarations and FlushEffective together in one
outer transaction in internal/module/artifact/build/backend/builder.go:886-905,
preserving the existing key collection and error propagation. Apply the same
transaction boundary to RemoveModuleDeclarations and FlushEffective in
internal/module/lifecycle/uninstaller.go:64-70. Add regression coverage that
forces recomputation to fail and verifies both the declaration changes and
effective projections roll back.
---
Nitpick comments:
In `@internal/module/lifecycle/uninstaller_clean_models_test.go`:
- Around line 193-204: Update deleteRawCleanModelsPrefix and deleteRawModelTree
to reuse the raw table-name constants defined in internal/module/meta, such as
rawServiceTable, instead of hardcoded literals. Export the required constants if
necessary, preserving the existing deletion order and behavior.
In `@internal/module/meta/declaration_coverage_test.go`:
- Around line 119-131: Rename TestReplaceModuleDeclarations_NilPrevDeclRow to
reflect that it covers skipping an invalid previous-effective key, such as
TestReplaceModuleDeclarations_SkipsInvalidPrevEffectiveKey. Remove the obsolete
comment stating that ListDeclarations can return a nil element, while retaining
the comment describing the zero-valued pkgmeta.Model coverage.
- Around line 41-52: Extend the test around ReplaceModuleDeclarations to
retrieve the persisted declaration for /a.ts and assert its name is "A2". Keep
the existing non-empty keys assertion and error handling unchanged, using the
test’s available database or lookup mechanism to verify the last duplicate wins.
In `@internal/module/meta/declaration_test.go`:
- Around line 176-179: Remove the redundant ensureDualStoreTables call and its
error handling from the test setup after openDeclarationTestDB in the
remove-decls test, relying on openDeclarationTestDB to initialize the tables.
- Around line 67-97: Extend TestDeleteDeclarationTrees_Cascade’s seeded Model
tree with a decorator containing an argument, and add a parameter and type
parameter to the service, using the existing metadata relationships and raw
models. After DeleteDeclarationTrees, assert that decorator, argument,
parameter, and type-parameter rows for the model’s descendants are all absent,
alongside the existing model, field, and service assertions.
In `@internal/module/meta/declaration.go`:
- Around line 213-244: Deduplicate logical keys in the appendKey closure within
the surrounding declaration-processing function by tracking normalized
(application, name) pairs in a seen map before appending to keys. Preserve the
existing Valid check and ensure repeated keys from prevDecls, prevEff, and
orderedPaths are added only once, consistent with RemoveModuleDeclarations.
- Around line 246-262: Move the `ModuleId` assignment for caller-supplied
`*pkgmeta.Model` values into the `db.Transaction` callback, before
`deleteRawModelsForModule` and `persistModelTreeAsRaw` execute. Ensure the
mutation occurs only within the transaction flow so failed transactions do not
leave the input models assigned.
🪄 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: bbc1d5f2-a78e-4d40-aab6-03328b4e2c4f
📒 Files selected for processing (104)
cmd/cmd_module.gocmd/cmd_module_test.gocmd/cmd_test_cmd_e2e_test.gointernal/bootstrap/service/coordinator.gointernal/bootstrap/service/coordinator_admin_coverage_test.gointernal/bootstrap/service/workspace_freshness.gointernal/module/artifact/build/backend/builder.gointernal/module/artifact/build/backend/builder_test.gointernal/module/artifact/build/backend/inject_test.gointernal/module/artifact/build/injectappmodel/coverage_test.gointernal/module/artifact/build/injectappmodel/helpers.gointernal/module/artifact/build/injectappmodel/injectappmodel_test.gointernal/module/artifact/build/injectappmodel/supersede.gointernal/module/artifact/generate/generator.gointernal/module/evolution/data/loader.gointernal/module/evolution/data/loader_test.gointernal/module/evolution/data/translated_seed.gointernal/module/evolution/schema/foreignkey.gointernal/module/evolution/schema/foreignkey_test.gointernal/module/evolution/schema/helpers_test.gointernal/module/evolution/schema/migrator.gointernal/module/evolution/schema/migrator_test.gointernal/module/evolution/scripts/context_test.gointernal/module/evolution/scripts/history.gointernal/module/evolution/scripts/history_test.gointernal/module/evolution/scripts/runner_decorators_test.gointernal/module/lifecycle/bundles_app_setting_test.gointernal/module/lifecycle/bundles_field_default_test.gointernal/module/lifecycle/bundles_translation_term_test.gointernal/module/lifecycle/install_module_test.gointernal/module/lifecycle/install_prefetch_test.gointernal/module/lifecycle/installer_commit_test.gointernal/module/lifecycle/module_index_sync.gointernal/module/lifecycle/module_index_sync_registry.gointernal/module/lifecycle/module_index_sync_registry_test.gointernal/module/lifecycle/module_index_sync_test.gointernal/module/lifecycle/modulemanager.gointernal/module/lifecycle/modulemanager_coverage_test.gointernal/module/lifecycle/modulemanager_fastfail_test.gointernal/module/lifecycle/uninstaller.gointernal/module/lifecycle/uninstaller_clean_models_test.gointernal/module/lifecycle/uninstaller_model_data_test.gointernal/module/lifecycle/upgrade_uninstall_commit_test.gointernal/module/meta/declaration.gointernal/module/meta/declaration_coverage_test.gointernal/module/meta/declaration_test.gointernal/module/meta/dual_store.gointernal/module/meta/dual_store_coverage_test.gointernal/module/meta/dual_store_test.gointernal/module/meta/effective_merge.gointernal/module/meta/effective_merge_coverage_test.gointernal/module/meta/effective_merge_test.gointernal/module/meta/entities.gointernal/module/meta/entities_test.gointernal/module/meta/extends_expand.gointernal/module/meta/extends_expand_coverage_test.gointernal/module/meta/extends_expand_test.gointernal/module/meta/lookup_effective.gointernal/module/meta/lookup_effective_test.gointernal/module/meta/meta_lock_lease.gointernal/module/meta/meta_model_data.gointernal/module/meta/meta_module_index.gointernal/module/meta/meta_module_management_log.gointernal/module/meta/meta_module_migration_history.gointernal/module/meta/meta_raw_argument.gointernal/module/meta/meta_raw_decorator.gointernal/module/meta/meta_raw_field.gointernal/module/meta/meta_raw_field_test.gointernal/module/meta/meta_raw_model.gointernal/module/meta/meta_raw_parameter.gointernal/module/meta/meta_raw_service.gointernal/module/meta/meta_raw_typeparameter.gointernal/module/meta/meta_setting.gointernal/module/meta/raw_helpers.gointernal/module/meta/raw_helpers_test.gointernal/module/meta/recompute.gointernal/module/meta/recompute_coverage_test.gointernal/module/meta/recompute_test.gointernal/module/meta/selection_merge.gointernal/module/meta/selection_merge_test.gointernal/module/metadata/model.gointernal/module/metadata/model_test.gointernal/state/lease/lease.gointernal/state/lease/lease_test.gointernal/state/lease/model/model.gointernal/state/lease/model/model_test.gointernal/testing/e2e/runner.gopkg/meta/acl_remap.gopkg/meta/acl_remap_coverage_test.gopkg/meta/acl_remap_test.gopkg/meta/declaration.gopkg/meta/declaration_test.gopkg/meta/dual_store_migrate_coverage_test.gopkg/meta/dual_store_migrate_test.gopkg/meta/facade.gopkg/meta/facade_coverage_test.gopkg/meta/facade_test.gopkg/meta/meta_field.gopkg/meta/meta_field_test.gopkg/meta/meta_module.gopkg/meta/model.gopkg/meta/model_test.gopkg/meta/raw_facade.gopkg/meta/utils.go
💤 Files with no reviewable changes (16)
- internal/state/lease/model/model.go
- internal/state/lease/model/model_test.go
- pkg/meta/model_test.go
- pkg/meta/raw_facade.go
- internal/module/metadata/model_test.go
- pkg/meta/facade_coverage_test.go
- pkg/meta/facade_test.go
- pkg/meta/acl_remap.go
- internal/module/metadata/model.go
- pkg/meta/acl_remap_test.go
- pkg/meta/declaration_test.go
- pkg/meta/dual_store_migrate_coverage_test.go
- pkg/meta/declaration.go
- pkg/meta/acl_remap_coverage_test.go
- pkg/meta/facade.go
- pkg/meta/dual_store_migrate_test.go
There was a problem hiding this comment.
All reported issues were addressed across 104 files
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
- Treat invalid NullString IDs as absent and apply soft-delete scope in raw count helpers. - Collect legacy effective keys in RemoveModuleDeclarations and ensure the live unique index after catalog AutoMigrate. - Clean gofmt, obsolete error text, pkgmeta aliases, and brittle shared-memory test DSNs. Co-authored-by: Cursor <cursoragent@cursor.com>
- Update TestSeedModuleIndexForE2EAutoMigrateError after the metadata package rename. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
All reported issues were addressed across 26 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
- Skip drop/recreate when the final live unique index already exists and only clean leftover temp indexes. - Deduplicate Remove/Replace effective-key lookup via appendEffectiveKeysForModule. - Drop the dead meta_ir_ table-name guard from entities tests. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
All reported issues were addressed across 4 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
- Exercise EnsureEffectiveAppNameUniqueIndex success and failure from bootstrap and migrateBaseModule. - Cover ListDeclarations/RemoveModuleDeclarations guards and dual-store ensure edge cases for patch coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
- Drop dead failingDropDialector/Migrator now that MySQL drop-error coverage uses failingDropTempDialector. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
All reported issues were addressed across 4 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
…ests - Extract seedDuplicateLiveModelsForTest so bootstrap and migrateBaseModule failure cases keep the same live-duplicate shape. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
meta_raw_*, declaration APIs, FlushEffective, merge/expand) frompkg/metaintointernal/module/meta, leavingpkg/metaas catalog entities/utils.internal/module/metadataops tables andmeta_lock_leaseintoCatalogEntities/OpsEntities; removemetadataandstate/lease/model.(application, name)unique index.Test plan
go test ./pkg/meta/ ./internal/module/meta/ ./internal/state/lease/ ./internal/bootstrap/service/ ./internal/module/lifecycle/ -count=1go test ./internal/module/artifact/... ./internal/module/evolution/... -count=1meta_raw_*, ops tables, andmeta_lock_leaseMade with Cursor
Summary by cubic
Moved the dual-store meta engine and declaration APIs from
pkg/metatointernal/module/meta(modmeta), consolidating ops tables andmeta_lock_lease. The live(application, name)unique-index ensure is idempotent, and tests now cover index ensure and declaration edge cases with a shared duplicate-model helper.Refactors
internal/module/meta; removedinternal/module/metadataandstate/lease/model.modmeta.CatalogEntities();modmeta.EnsureEffectiveAppNameUniqueIndex(db)is idempotent and only cleans leftover temp indexes.modmetatypes/APIs; added coverage for unique-index ensure and declaration guards; extracted a sharedseedDuplicateLiveModelsForTesthelper for consistent duplicate-live-model scenarios.Migration
meta.EnsureDualStoreTables(db)withdb.AutoMigrate(modmeta.CatalogEntities()...), then callmodmeta.EnsureEffectiveAppNameUniqueIndex(db)(safe to run multiple times).modmeta.ReplaceModuleDeclarations(db, moduleID, []*meta.Model{...})instead of persisting raw trees directly.modmeta.ListDeclarations,modmeta.MergeSameNameModelsByExtensionChain,modmeta.LookupEffectiveModel, andmodmeta.ExpandModelsAlongExtends.modmeta.ModuleIndex,modmeta.ModelData,modmeta.Setting,modmeta.ModuleMigrationHistory, andmodmeta.LockLease.db.Migrator().DropTable("meta_raw_model").Written for commit 9cc6ef6. Summary will update on new commits.
Summary by CodeRabbit
New Features
Refactor