Skip to content

feat(injectappmodel): inject TranslationTerm MetaModel per app - #252

Merged
buke merged 9 commits into
mainfrom
feat/injectappmodel-translation-term-p2
Aug 6, 2026
Merged

feat(injectappmodel): inject TranslationTerm MetaModel per app#252
buke merged 9 commits into
mainfrom
feat/injectappmodel-translation-term-p2

Conversation

@buke

@buke buke commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

User description

Summary

  • Registers TranslationTerm as the first C2 Spec (EnsureServiceEntry: true, softDelete: false) and materializes a virtual service/index.ts plus __generated__/translation_term.ts without touching package.json or the source tree.
  • Adds TranslationTermBaseModel with ORM fields, unique-index ensure, and GetTranslations (Gateway still dials Go I18n until P3).
  • Applies Effects.ServiceEntryPath to the builder/plugin entry when empty; reverts Ensure'd Module.ServiceEntryPoint after build so DB stays package.json-sourced; app bundles can still host TranslationTerm for empty-entry apps (e.g. web).

Test plan

  • go test ./internal/module/artifact/build/injectappmodel/ ./internal/module/artifact/build/backend/ ./internal/esbplugins/backendplugin/ ./internal/module/lifecycle/ -count=1
  • ./choysum test typecheck core
  • Install web (no entryPoints.service) and confirm TranslationTerm injects; disk package.json unchanged
  • Confirm sibling modules in one app still get a single TranslationTerm claim

Made with Cursor


PR Type

Enhancement


Description

  • Go Core: Register TranslationTerm C2 model with EnsureServiceEntry and virtual entry generation

  • Go Core: Revert in-memory ServiceEntryPoint mutations prior to database persistence

  • TS Modules: Add TranslationTermBaseModel in modules/core with GetTranslations and index ensure

  • Licensing & Tests: Include Apache-2.0 SPDX header on new model; expand Go unit tests across build packages


File Walkthrough

Relevant files
Enhancement
12 files
plugin.go
Expose SetEntryPoint and expand esbuild virtual resolve filter
+9/-1     
builder.go
Revert ensured service entry on persist and register missing virtual
sources
+46/-1   
inject.go
Adopt virtual ServiceEntryPath in builder and plugin when entry point
is empty
+12/-0   
bundle.go
Materialize virtual service entries during multi-module bundling
+28/-6   
effects.go
Add ServiceEntryPath field to Effects and preserve on merge
+17/-5   
inject.go
Generate virtual service/index.ts for EnsureServiceEntry specs
+24/-10 
registry.go
Register TranslationTerm spec as first builtin C2 model   
+10/-0   
session.go
Track and revert temporary ServiceEntryPoint mutations in session
+39/-0   
source.go
Provide virtual service entry path and stub source generators
+17/-0   
bundles.go
Include empty-entry modules when resolving TranslationTerm bundle
owners
+35/-4   
index.ts
Re-export TranslationTermBaseModel and TranslationTermModelCtor
+4/-1     
translation_term_base_model.ts
Add TranslationTermBaseModel with schema, hash computation, and
GetTranslations
+322/-0 
Tests
4 files
builder_test.go
Initialize empty inject registry in backend builder unit tests
+4/-0     
coverage_test.go
Update coverage tests for new builtin spec count and merged effects
+13/-7   
injectappmodel_test.go
Add unit tests for TranslationTerm injection and entry point revert
+132/-27
bundles_app_setting_test.go
Update bundle C2 virtual import test assertions                   
+4/-4     
Documentation
1 files
spec.go
Update EnsureServiceEntry documentation comment                   
+2/-2     

Summary by CodeRabbit

  • New Features

    • Added translation-term support, including generated models, imports, and translation catalog retrieval.
    • Translation terms can now be included in backend bundles and application modules.
    • Builds can automatically create temporary service entry points when required.
  • Bug Fixes

    • Prevented temporary service entry files from being persisted or reused after builds.
    • Improved cleanup and rollback when injection or validation fails.
    • Enhanced handling of missing service entry files, module paths, and relative imports.
    • Preserved correct import resolution for virtual and on-disk service files.

- Register TranslationTerm Spec first with EnsureServiceEntry and softDelete false.

- Materialize a virtual service/index.ts, mutate in-memory entry for sibling Specs, and adopt builder entryPoint via Effects without writing package.json.

- Add TranslationTermBaseModel with fields, unique-index ensure, and GetTranslations; widen backend OnResolve for virtual sources.

- Revert Ensure'd ServiceEntryPoint after build/Persist so DB stays disk-sourced; include empty-entry hosts in app bundles for TranslationTerm.

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

coderabbitai Bot commented Aug 6, 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: 3a5637f0-bfee-42bc-9793-e3dac9dfb537

📥 Commits

Reviewing files that changed from the base of the PR and between abd4613 and 45e33bb.

📒 Files selected for processing (7)
  • internal/module/artifact/build/backend/inject_test.go
  • internal/module/artifact/build/injectappmodel/effects.go
  • internal/module/artifact/build/injectappmodel/inject.go
  • internal/module/artifact/build/injectappmodel/injectappmodel_test.go
  • internal/module/lifecycle/bundles.go
  • internal/module/lifecycle/bundles_translation_term_test.go
  • modules/core/service/orm/model/translation_term_base_model_coverage.test.ts
📝 Walkthrough

Walkthrough

The change adds TranslationTermBaseModel and registers it as a built-in specification. Injection can create virtual service entries, propagate entry paths through backend builds and bundles, and restore temporary module state before persistence.

Changes

TranslationTerm injection and service entries

Layer / File(s) Summary
TranslationTerm model and registration
modules/core/service/...
Adds TranslationTermBaseModel, translation catalog retrieval, deterministic hashing, uniqueness handling, and core registration.
Virtual service-entry injection
internal/module/artifact/build/injectappmodel/...
Adds TranslationTerm as a built-in specification. Injection can materialize virtual service entries, merge effects, and restore temporary module state.
Backend and bundle integration
internal/esbplugins/backendplugin/..., internal/module/artifact/build/backend/..., internal/module/lifecycle/..., internal/testing/backend/backend.go
Propagates service-entry paths to plugins and bundles, resolves translation-term virtual sources, selects owner modules, and reverts temporary paths before persistence.

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

Sequence Diagram(s)

sequenceDiagram
  participant ModuleBuilder
  participant InjectAppModels
  participant BackendPlugin
  participant TranslationTermBaseModel
  ModuleBuilder->>InjectAppModels: apply TranslationTerm injection
  InjectAppModels->>TranslationTermBaseModel: generate translation-term model source
  InjectAppModels-->>ModuleBuilder: return virtual files and ServiceEntryPath
  ModuleBuilder->>BackendPlugin: SetEntryPoint(ServiceEntryPath)
  BackendPlugin->>BackendPlugin: resolve virtual sources and load imports
  ModuleBuilder->>ModuleBuilder: revert temporary service entry before persistence
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% 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 identifies the main change: per-app TranslationTerm injection.
Description check ✅ Passed The description provides a detailed summary, test plan, implementation details, and known unchecked validation items.
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/injectappmodel-translation-term-p2

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

@github-actions

github-actions Bot commented Aug 6, 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

Database Schema Issue

In ensureTermUniqueIndex, the index DDL uses lowercased column names (module, lang, scope, src, kind) instead of matching the ORM model's field name casing (Module, Lang, Scope, Src, Kind). In PostgreSQL, columns created from model fields are case-sensitive quoted identifiers, causing CREATE UNIQUE INDEX to fail with a "column module does not exist" error. Because the statement execution is wrapped in a silent try...catch block, index creation fails silently on PostgreSQL databases.

let ddl = '';
if (dialect === 'postgres' || dialect === 'postgresql') {
  ddl = `CREATE UNIQUE INDEX IF NOT EXISTS "${indexName}" ON "${table}" (module, lang, scope, src, kind)`;
} else if (dialect === 'mysql') {
  ddl = `CREATE UNIQUE INDEX \`${indexName}\` ON \`${table}\` (module(64), lang, scope(255), src(255), kind)`;
} else {
  ddl = `CREATE UNIQUE INDEX IF NOT EXISTS \`${indexName}\` ON \`${table}\` (module, lang, scope, src, kind)`;
}

try {
  const exec = ($choysum as any)?.db?.execute;
  if (typeof exec === 'function') {
    await exec.call(($choysum as any).db, ddl, '[]');
    ensuredUniqueIndexTables.add(table);
  }
} catch {
  // Best-effort: uniqueness still enforced when writers collide.

@codecov

codecov Bot commented Aug 6, 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!

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

No code suggestions found for the PR.

Comment thread modules/core/service/orm/model/translation_term_base_model.ts
Comment thread internal/module/artifact/build/backend/builder.go
- Drop registerMissingEntryVirtualSource so missing on-disk entries still fail as before (hooks tests).

- Gate Ensure on Spec base model availability when ModulesPath exists, and avoid mutating ServiceEntryPoint during BundleInject.

- Keep TranslationTermBaseModel side-effect-only (no core/service export) to preserve the export-surface unit test.

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.

Actionable comments posted: 6

🧹 Nitpick comments (4)
internal/module/artifact/build/injectappmodel/inject.go (1)

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

Use append for out.Imports to match the file handling on Line 215.

Line 215 appends to out.Files to preserve the virtual service entry. Line 216 replaces out.Imports. The two lines are only equivalent because nothing writes out.Imports earlier in this function. If a future ensure step adds an import, this line discards it.

♻️ Proposed change
-	out.Imports = []string{path}
+	out.Imports = append(out.Imports, path)
🤖 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/injectappmodel/inject.go` at line 216, Update
the import assignment in the relevant injection function to append path to
out.Imports rather than replacing the slice, matching the out.Files handling and
preserving any imports added earlier.
modules/core/service/orm/model/translation_term_base_model.ts (2)

176-201: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Key the DDL cache by dialect and table, and log the swallowed error.

ensuredUniqueIndexTables stores only the table name. If two applications in the same process use the same table name against different databases, the second application skips the DDL. The empty catch also hides real failures, so a permanently missing unique index is invisible.

♻️ Proposed change
-  if (!table || ensuredUniqueIndexTables.has(table)) return;
-
-  const dialect = String(($choysum as any)?.db?.dialectName || 'sqlite').toLowerCase();
+  if (!table) return;
+  const dialect = String(($choysum as any)?.db?.dialectName || 'sqlite').toLowerCase();
+  const cacheKey = `${dialect}\u0000${table}`;
+  if (ensuredUniqueIndexTables.has(cacheKey)) return;
   const indexName = `uq_${table}_key`;
@@
-      await exec.call(($choysum as any).db, ddl, '[]');
-      ensuredUniqueIndexTables.add(table);
+      await exec.call(($choysum as any).db, ddl, '[]');
+      ensuredUniqueIndexTables.add(cacheKey);
     }
-  } catch {
+  } catch (err) {
     // Best-effort: uniqueness still enforced when writers collide.
+    console.warn(`ensureTermUniqueIndex failed for ${table}:`, err);
   }
🤖 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/core/service/orm/model/translation_term_base_model.ts` around lines
176 - 201, Update ensureTermUniqueIndex to key ensuredUniqueIndexTables by both
the normalized dialect and table name, so identical tables on different database
dialects each execute their DDL. In the catch block, log the DDL execution error
with sufficient context instead of silently swallowing it, while preserving the
existing best-effort behavior.

282-285: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift

Avoid reading and hashing the full language catalog on every request.

Search ignores limit: 0 because it applies limits only when the value is truthy, so this call returns all matching rows. Cache the catalog/hash or update it when terms change to avoid repeated language-scoped full reads and in-memory hashing for large catalogs.

🤖 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/core/service/orm/model/translation_term_base_model.ts` around lines
282 - 285, Update the catalog lookup around TranslationTermBaseModel.Search so
requests do not repeatedly read and hash every language-scoped row. Reuse a
cached catalog/hash and invalidate or refresh it when translation terms change,
or otherwise maintain the hash incrementally through term mutations. Ensure
normal request handling avoids full-catalog reads while preserving correct
hashes after updates.
internal/module/artifact/build/injectappmodel/session.go (1)

169-174: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Start the exported doc comment with the exported name.

The comment documents revertEnsuredServiceEntry, but it is attached to RevertEnsuredServiceEntry. Go doc convention and the revive exported rule require the comment to begin with the exported identifier.

♻️ Proposed change
-// revertEnsuredServiceEntry restores Module.ServiceEntryPoint when Ensure mutated
-// it for this build. Exported for Persist so DB stays aligned with package.json
-// (cold builds always re-Ensure from an empty disk entry).
+// RevertEnsuredServiceEntry restores Module.ServiceEntryPoint when Ensure mutated
+// it for this build. Persist calls it so the DB stays aligned with package.json
+// (cold builds always re-Ensure from an empty disk entry).
🤖 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/injectappmodel/session.go` around lines 169 -
174, Update the doc comment above Session.RevertEnsuredServiceEntry to begin
with the exact exported identifier “RevertEnsuredServiceEntry”, while preserving
the existing explanation of its behavior.
🤖 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/artifact/build/injectappmodel/bundle.go`:
- Around line 82-89: Update the EnsureServiceEntry branch around
spec.EnsureServiceEntry to resolve relative ServiceEntryPoint paths against
mod.Path before calling os.Stat, while preserving absolute paths. Create the
VirtualFile only when os.Stat returns os.IsNotExist(err); propagate permission
and all other stat errors instead of treating them as missing files.

In `@internal/module/artifact/build/injectappmodel/helpers.go`:
- Around line 54-55: Update the os.Stat error handling in Ensure to return true
only when the error indicates os.ErrNotExist; return false for permission or
other filesystem errors so inaccessible module roots are rejected.

In `@internal/module/artifact/build/injectappmodel/inject.go`:
- Around line 192-205: Keep the ensured service entry build-local: update
materializeInject and the Session/Effects flow in
internal/module/artifact/build/injectappmodel/inject.go:192-205 and
session.go:155-167 to store the effective entry path in Session/Effects, then
pass that value to the builder and plugins instead of mutating shared
meta.Module.ServiceEntryPoint. Ensure BuildWithoutPersist failure paths,
including updatePrebuildResult, build, and validate, do not leave shared module
state changed.

In `@internal/module/lifecycle/bundles.go`:
- Around line 102-104: Update the TranslationTerm bundle-selection flow around
pickTranslationTermOwnerModule to derive candidate modules and ordered
applications from eligible installedMods rather than backendMods filtered by
non-empty ServiceEntryPoint, so empty-entry modules reach the supported
selection path and TranslationTerm is injected. Add a regression test covering
an application with no pre-existing service entry.

In `@modules/core/service/orm/model/translation_term_base_model.ts`:
- Around line 186-187: The MySQL path in the unique-index creation logic must
avoid retrying an already-existing index. Before executing the DDL built in the
mysql branch, query information_schema.statistics for the current database,
table, and indexName and skip creation when it exists; ensure the existing
ensuredUniqueIndexTables tracking is updated for both existing and newly created
indexes.
- Around line 288-313: Update the method’s query and processing flow so
computeTermHash receives every row for the requested language, without applying
module_names filtering first. Preserve module_names filtering and the
KIND_LITERAL check only when building termsByModule, while leaving the
unchanged-hash response behavior intact.

---

Nitpick comments:
In `@internal/module/artifact/build/injectappmodel/inject.go`:
- Line 216: Update the import assignment in the relevant injection function to
append path to out.Imports rather than replacing the slice, matching the
out.Files handling and preserving any imports added earlier.

In `@internal/module/artifact/build/injectappmodel/session.go`:
- Around line 169-174: Update the doc comment above
Session.RevertEnsuredServiceEntry to begin with the exact exported identifier
“RevertEnsuredServiceEntry”, while preserving the existing explanation of its
behavior.

In `@modules/core/service/orm/model/translation_term_base_model.ts`:
- Around line 176-201: Update ensureTermUniqueIndex to key
ensuredUniqueIndexTables by both the normalized dialect and table name, so
identical tables on different database dialects each execute their DDL. In the
catch block, log the DDL execution error with sufficient context instead of
silently swallowing it, while preserving the existing best-effort behavior.
- Around line 282-285: Update the catalog lookup around
TranslationTermBaseModel.Search so requests do not repeatedly read and hash
every language-scoped row. Reuse a cached catalog/hash and invalidate or refresh
it when translation terms change, or otherwise maintain the hash incrementally
through term mutations. Ensure normal request handling avoids full-catalog reads
while preserving correct hashes after updates.
🪄 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: da3d615e-ca16-44a1-8d7c-6438ddacc003

📥 Commits

Reviewing files that changed from the base of the PR and between a711242 and 10ff5fc.

📒 Files selected for processing (18)
  • internal/esbplugins/backendplugin/plugin.go
  • internal/module/artifact/build/backend/builder.go
  • internal/module/artifact/build/backend/builder_test.go
  • internal/module/artifact/build/backend/inject.go
  • internal/module/artifact/build/injectappmodel/bundle.go
  • internal/module/artifact/build/injectappmodel/coverage_test.go
  • internal/module/artifact/build/injectappmodel/effects.go
  • internal/module/artifact/build/injectappmodel/helpers.go
  • internal/module/artifact/build/injectappmodel/inject.go
  • internal/module/artifact/build/injectappmodel/injectappmodel_test.go
  • internal/module/artifact/build/injectappmodel/registry.go
  • internal/module/artifact/build/injectappmodel/session.go
  • internal/module/artifact/build/injectappmodel/source.go
  • internal/module/artifact/build/injectappmodel/spec.go
  • internal/module/lifecycle/bundles.go
  • internal/module/lifecycle/bundles_app_setting_test.go
  • modules/core/service/index.ts
  • modules/core/service/orm/model/translation_term_base_model.ts

Comment thread internal/module/artifact/build/injectappmodel/bundle.go
Comment thread internal/module/artifact/build/injectappmodel/helpers.go Outdated
Comment thread internal/module/artifact/build/injectappmodel/inject.go
Comment thread internal/module/lifecycle/bundles.go
Comment thread modules/core/service/orm/model/translation_term_base_model.ts
Comment thread modules/core/service/orm/model/translation_term_base_model.ts
buke and others added 3 commits August 6, 2026 17:10
- Adopt an on-disk service/index.ts instead of registering a virtual stub when Module.ServiceEntryPoint is empty.

- Point unit-test Module.ServiceEntryPoint at the real entryPoint so Ensure does not run over live apps.

- Keep ResolveDir at dirname(path) when a virtual source path also exists on disk so ./models imports resolve.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Align GetTranslations with Go: language-wide hash and empty module_names yields empty terms_by_module.

- Revert Ensure'd ServiceEntryPoint from releaseInjectSchedules so build failures cannot leak a virtual entry.

- Resolve relative service entries before Stat, treat only ErrNotExist as missing, and collect empty-entry apps for TranslationTerm bundle inject.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Cover GetTranslations, dialect DDL ensure, and hash/filter edge cases in translation_term_base_model.

- Exercise Ensure/ServiceEntryPath adopt, Bundle empty-entry emit, helpers guards, and disk ResolveDir shadowing.

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

buke commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@cursor review

Comment thread internal/module/artifact/build/injectappmodel/inject.go
buke and others added 2 commits August 6, 2026 18:16
- Cover nullish/falsy normalize, default GetTranslations req, empty application, and DDL catch paths.

- Extract compareTermHashKeys and pass raw Value into hashing so every branch is exercisable from unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Always set Effects.ServiceEntryPath when Ensure runs, including on-disk adopt, so applyInjectEffects can fill an empty builder entryPoint.

- Add a regression covering inject with empty builder entry and an existing service/index.ts.

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.

Actionable comments posted: 2

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

264-269: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Preserve application order when appending owners.

Go map iteration does not preserve order. This loop appends translationTermOwners in a nondeterministic order. That order affects virtual-import registration and bundle output.

Track application order from installed, or derive it with the existing stable application ordering logic before appending owners.

🤖 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/bundles.go` around lines 264 - 269, Update the
owner-collection flow around pickTranslationTermOwnerModule to avoid iterating
directly over the byApp map when appending owners. Iterate applications using
the stable order from installed or the existing application-ordering logic, then
look up each app’s modules in byApp and append owners in that deterministic
order while preserving seenApp behavior.
🤖 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/lifecycle/bundles.go`:
- Around line 106-111: The bundle target selection around orderedApps and
appendTranslationTermOwnersFromInstalled must include eligible applications that
own TranslationTerms, even when all non-core modules have empty
ServiceEntryPoint values. Add those applications to orderedApps, selecting a
valid representative module when no backend module exists, so bundle
construction and ensureBundleC2VirtualImports receive the owners; add an
integration test covering an installation containing only empty service entries.

In `@modules/core/service/orm/model/translation_term_base_model_coverage.test.ts`:
- Around line 192-218: Restore meta.tableName in the finally block using the
originalTable value captured before mutation, and remove reliance on the
success-path assignment after the assertions. Ensure cleanup occurs even when
either assertion or GetTranslations call fails, while preserving the existing
global cleanup.

---

Nitpick comments:
In `@internal/module/lifecycle/bundles.go`:
- Around line 264-269: Update the owner-collection flow around
pickTranslationTermOwnerModule to avoid iterating directly over the byApp map
when appending owners. Iterate applications using the stable order from
installed or the existing application-ordering logic, then look up each app’s
modules in byApp and append owners in that deterministic order while preserving
seenApp behavior.
🪄 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: c56ab916-0a55-4ab6-a870-530a8c41b081

📥 Commits

Reviewing files that changed from the base of the PR and between b78f638 and abd4613.

📒 Files selected for processing (12)
  • internal/esbplugins/backendplugin/plugin_test.go
  • internal/module/artifact/build/backend/builder_test.go
  • internal/module/artifact/build/backend/inject.go
  • internal/module/artifact/build/backend/inject_test.go
  • internal/module/artifact/build/injectappmodel/bundle.go
  • internal/module/artifact/build/injectappmodel/helpers.go
  • internal/module/artifact/build/injectappmodel/inject.go
  • internal/module/artifact/build/injectappmodel/injectappmodel_test.go
  • internal/module/lifecycle/bundles.go
  • internal/module/lifecycle/bundles_translation_term_test.go
  • modules/core/service/orm/model/translation_term_base_model.ts
  • modules/core/service/orm/model/translation_term_base_model_coverage.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • internal/module/artifact/build/injectappmodel/helpers.go
  • internal/module/artifact/build/backend/inject.go
  • internal/module/artifact/build/injectappmodel/bundle.go
  • internal/module/artifact/build/backend/builder_test.go
  • internal/module/artifact/build/injectappmodel/inject.go

Comment thread internal/module/lifecycle/bundles.go
- Fall back to a TranslationTerm owner as the multi-app bundle representative when orderedApps is empty.

- Append Ensure-only owners in installed order, and restore mutated tableName in coverage tests on failure.

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

buke commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@cursor review

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

- Extract writeBackendBundleToDir and unit-test !ok, error wrap, and success.

- Cover early no-representative return and successful buildBackendBundlesToDir via a test hook.

Co-authored-by: Cursor <cursoragent@cursor.com>
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