Skip to content

chore(i18n): ImportPackaged facade, CRUD invalidate, sync design - #256

Merged
buke merged 4 commits into
mainfrom
chore/i18n-p6-facade-ci-design
Aug 7, 2026
Merged

chore(i18n): ImportPackaged facade, CRUD invalidate, sync design#256
buke merged 4 commits into
mainfrom
chore/i18n-p6-facade-ci-design

Conversation

@buke

@buke buke commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

User description

Summary

  • Extend $choysum.i18n with sync invalidateModule and async upsertPackagedTerms (ScopeProvider), wired through the default engine plugin.
  • Add TranslationTerm.ImportPackaged plus CRUD success-path TermStore invalidation; update bridge types and unit coverage.
  • Clean up modules/core/i18n/README.md and auth_forward comments for the TranslationTerm topology (local .dev design rewrite is intentionally not in this PR).

Test plan

  • go test ./internal/i18n/bridge/ ./internal/i18n/import/ ./internal/defaultengine/... -count=1
  • ./choysum test unit core --be (incl. ImportPackaged / invalidate helpers)
  • Manual: Editor Update → same-process _t sees new value after invalidate
  • Manual: ImportPackaged matches UpsertPackagedTerms (skip override, purge, invalidate)

Made with Cursor


Summary by cubic

Add runtime i18n writes and cache invalidation with a provider-based bridge. Exposes $choysum.i18n.invalidateModule and async $choysum.i18n.upsertPackagedTerms, auto‑invalidates after TranslationTerm CRUD, and adds the TranslationTerm.ImportPackaged facade.

  • New Features

    • Install provider-based i18n bridge: WithTerminologyProvider via quickjsengine.NewRuntimePluginWithProvider; exposes t, invalidateModule, and async upsertPackagedTerms.
    • Add TranslationTerm.ImportPackaged facade that forwards to $choysum.i18n.upsertPackagedTerms and returns import stats; TypeScript $choysum.i18n and model types updated.
  • Bug Fixes

    • Harden bridge: invalidateModule uses Registry.ExistingStore and rejects empty/core apps or nullish args; upsertPackagedTerms validates required args and supports string/Uint8Array/ArrayBuffer PO text, rejecting with JS errors.
    • Internal refactor: shared installer for $choysum.i18n; inject upsert/marshal deps into performUpsertPackagedTerms (removed mutable test hooks) and drop a dead ExecContext guard; tests cover error branches, poText variants, ExistingStore, and CRUD invalidation (patch coverage 100%).

Written for commit d0c6731. Summary will update on new commits.

Review in cubic


PR Type

Enhancement


Description

  • Core Go runtime i18n bridge enhancements

    • Register terminology provider in QuickJS runtime setup
    • Expose invalidateModule and upsertPackagedTerms via $choysum.i18n
  • TypeScript ORM model cache invalidation

    • Invalidate Go TermStore automatically on TranslationTerm CRUD
    • Add ImportPackaged facade method for PO translation imports
  • License compliance and test coverage

    • Add two TS source files with Apache-2.0 SPDX headers
    • Include Go bridge and TS ORM unit test suites

File Walkthrough

Relevant files
Enhancement
6 files
init.go
Wire i18n runtime plugin using scope provider                       
+2/-4     
terminology.go
Expose invalidateModule and upsertPackagedTerms bridge functions
+145/-16
translate.ts
Update ChoysumI18n bridge type interface definitions         
+14/-0   
_translation_term_cache.ts
Add terminology cache invalidation helpers with SPDX header
+70/-0   
translation_term_base_model.ts
Add ImportPackaged facade and post-CRUD cache invalidation
+162/-1 
$choysum.d.ts
Update global $choysum.i18n TypeScript declaration             
+21/-0   
Tests
2 files
terminology_test.go
Test runtime i18n invalidation and upsert bridge                 
+136/-0 
translation_term_cache.test.ts
Add unit tests for TranslationTerm cache invalidation       
+180/-0 
Documentation
2 files
auth_forward.go
Update RPC auth context forward comment references             
+2/-2     
README.md
Update doc references to TranslationTerm model topology   
+4/-3     

Summary by CodeRabbit

  • New Features

    • Added terminology lookup, module invalidation, and packaged translation import support.
    • Added support for importing PO translation files as text or byte arrays.
    • Added detailed import results, including inserted, skipped, rejected, and purged terms.
    • Translation-term changes now automatically refresh related terminology caches.
  • Documentation

    • Updated internationalization integration and terminology registration guidance.
  • Tests

    • Added coverage for imports, validation, persistence, lookups, and cache invalidation.

- Extend $choysum.i18n with invalidateModule and upsertPackagedTerms via ScopeProvider.

- Invalidate TermStore after TranslationTerm CRUD and add ImportPackaged facade.

- Refresh core i18n README and auth_forward comments for TranslationTerm topology.

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

cubic-dev-ai Bot commented Aug 7, 2026

Copy link
Copy Markdown

We've triggered an ultrareview automatically — This PR rewires the i18n bridge with new cache invalidation and async packaged-term upserts spanning Go and TS; a missed invalidation or scope/lifecycle bug could leave stale translations or corrupt term rows in production, so it merits a deep multi-pass review.. I'll post findings when complete.

An ultrareview is cubic's deepest review, catching hard-to-find bugs in the most critical PRs. It runs a longer, multi-pass analysis using cubic's most capable review models, and typically takes around 30 minutes. It consumes your team's reviewed-lines allowance at 3× the standard rate.

Automated ultrareviews are disabled by default. We triggered this run as part of your trial. Want cubic to do this for every high-risk PR? Enable auto-ultrareview in your settings.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The PR connects the runtime i18n bridge to scope providers, adds module invalidation and packaged PO-term upserts, and integrates these operations with translation-term CRUD flows and cache handling.

Terminology bridge and model integration

Layer / File(s) Summary
Runtime bridge wiring and packaged-term processing
internal/defaultengine/init.go, internal/i18n/bridge/terminology.go, internal/i18n/gateway/auth_forward.go, modules/core/i18n/README.md
The runtime plugin now passes a scope provider. The bridge validates invalidation and packaged-term requests, imports PO input, and returns import statistics.
Translation-term contracts and cache invalidation
modules/core/service/i18n/translate.ts, modules/core/service/orm/model/_translation_term_cache.ts, modules/core/service/orm/model/translation_term_base_model.ts, modules/core/types/$choysum.d.ts
The TypeScript bridge exposes invalidation and packaged-term upsert operations. Translation-term CRUD methods invalidate affected modules, and ImportPackaged forwards validated requests.
Bridge and model integration tests
internal/i18n/bridge/terminology_test.go, modules/core/service/orm/model/translation_term_cache.test.ts
Tests cover provider-backed persistence and lookup, validation errors, bridge forwarding, module extraction, and CRUD invalidation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TranslationTermModel
  participant ChoysumI18nBridge
  participant TerminologyProvider
  TranslationTermModel->>ChoysumI18nBridge: upsertPackagedTerms(application, module, lang, poText)
  ChoysumI18nBridge->>TerminologyProvider: import packaged PO terms
  TerminologyProvider-->>ChoysumI18nBridge: import statistics and language
  ChoysumI18nBridge-->>TranslationTermModel: resolved upsert result
Loading

Possibly related PRs

  • choysum-dev/choysum#250: Defines the terminology bridge and packaged-term import mechanisms extended by this PR.
  • choysum-dev/choysum#252: Introduces the translation-term model that this PR extends with import and invalidation behavior.
  • choysum-dev/choysum#255: Refactors terminology around TranslationTerm and packaged-term upserts used by this PR.

Suggested labels: Review effort 3/5

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly identifies the main ImportPackaged and CRUD invalidation changes, although it omits some bridge details.
Description check ✅ Passed The description provides a detailed summary, file-level scope, test plan, pending manual checks, and agreement context for the changes.
✨ 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 chore/i18n-p6-facade-ci-design

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

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

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

Potential Bug

In the Update and Delete static overrides, Search is called with limit: 0. In most SQL/ORM query builders, a limit of 0 is translated literally to LIMIT 0, which returns zero rows. If this is the case, before will always be empty, and the cache invalidation for the affected modules will be silently skipped. If the intention is to query without any limit, the limit option should be omitted or set to undefined.

static override async Update<T extends BaseModel>(
  this: { new (...args: any[]): T } & typeof BaseModel,
  condition: QueryCondition<T>,
  values: Partial<Updateable<T & BaseModel>>,
  returnFields?: FieldSelection<T>,
  options?: UpdateOptions
): Promise<Partial<T>[]> {
  const application = hostApplication(this);
  const before = await (this as any).Search(condition as any, {
    fields: ['Module'] as any,
    limit: 0,
  });
  const out = await super.Update(condition as any, values as any, returnFields as any, options as any);
  invalidateTerminologyModules(application, [
    ...modulesFromPayloads(values),
    ...modulesFromRows(before),
    ...modulesFromRows(out),
  ]);
  return out as unknown as Partial<T>[];
}

static override async UpdateById<T extends BaseModel>(
  this: { new (...args: any[]): T } & typeof BaseModel,
  id: string,
  values: Partial<Updateable<T & BaseModel>>,
  returnFields?: FieldSelection<T>,
  options?: UpdateOptions
): Promise<Partial<T>> {
  const application = hostApplication(this);
  let module = String((values as any)?.Module ?? '').trim();
  if (!module) {
    try {
      const existing = await (this as any).Browse(id, ['Module'] as any);
      module = String(existing?.Module ?? '').trim();
    } catch {
      /* Browse may fail if row gone; still attempt update */
    }
  }
  const out = await super.UpdateById(id as any, values as any, returnFields as any, options as any);
  invalidateTerminologyModules(application, [module, ...modulesFromRows(out)]);
  return out as unknown as Partial<T>;
}

static override async Delete<T extends BaseModel>(
  this: { new (...args: any[]): T } & typeof BaseModel,
  condition: QueryCondition<T>,
  options?: DeleteOptions
): Promise<number> {
  const application = hostApplication(this);
  const before = await (this as any).Search(condition as any, {
    fields: ['Module'] as any,
    limit: 0,
    ...(options || {}),
  });
  const count = await super.Delete(condition as any, options as any);
  invalidateTerminologyModules(application, modulesFromRows(before));
  return count;
}

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Failed to generate code suggestions for PR

@codecov

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

🧹 Nitpick comments (10)
modules/core/service/orm/model/_translation_term_cache.ts (3)

34-38: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Record the swallowed invalidation failure.

A failure here leaves the Go TermStore with stale terms and produces no signal. The write already succeeded, so throwing is not correct. Log the failure instead, so a stale cache can be diagnosed.

♻️ Proposed logging of the failure
   try {
     bridge.invalidateModule(app, mod);
-  } catch {
-    /* best-effort: write already succeeded */
+  } catch (err) {
+    // best-effort: the write already succeeded; the cache stays stale until the next warm.
+    console.warn('invalidateTerminologyModule failed', app, mod, 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_cache.ts` around lines 34 -
38, Update the invalidation catch block around bridge.invalidateModule(app, mod)
to log the caught failure while preserving best-effort behavior and not
rethrowing after the successful write. Include enough context to diagnose stale
TermStore terms.

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

modulesFromRows and modulesFromPayloads have identical bodies.

Both functions normalize the input to an array, read Module, trim it, and keep non-empty values. Keep both export names for call-site intent, but share one implementation.

♻️ Proposed consolidation
-export function modulesFromRows(rows: unknown): string[] {
-  const list = Array.isArray(rows) ? rows : rows != null ? [rows] : [];
-  const out: string[] = [];
-  for (const row of list) {
-    const mod = String((row as any)?.Module ?? '').trim();
-    if (mod) out.push(mod);
-  }
-  return out;
-}
-
-export function modulesFromPayloads(values: unknown): string[] {
-  const list = Array.isArray(values) ? values : values != null ? [values] : [];
-  const out: string[] = [];
-  for (const row of list) {
-    const mod = String((row as any)?.Module ?? '').trim();
-    if (mod) out.push(mod);
-  }
-  return out;
-}
+function moduleNames(input: unknown): string[] {
+  const list = Array.isArray(input) ? input : input != null ? [input] : [];
+  const out: string[] = [];
+  for (const item of list) {
+    const mod = String((item as any)?.Module ?? '').trim();
+    if (mod) out.push(mod);
+  }
+  return out;
+}
+
+/** Module names from persisted rows returned by a repository call. */
+export const modulesFromRows = moduleNames;
+
+/** Module names from caller-supplied write payloads. */
+export const modulesFromPayloads = moduleNames;
🤖 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_cache.ts` around lines 52 -
70, Consolidate the duplicated normalization logic in modulesFromRows and
modulesFromPayloads into one shared helper, while retaining both exported
functions as intent-specific wrappers that delegate to it. Preserve the current
handling of nullish, scalar, array, trimmed, and empty Module values.

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

Consolidate the $choysum.i18n bridge contract.

The bridge contract is declared in three places: _translation_term_cache.ts, translate.ts, and $choysum.d.ts. The local types also mark bridge methods optional, while $choysum.d.ts marks them required.

Place the shared type and accessor in a module under service/i18n, then import them from both consumers. This preserves the existing dependency direction.

🤖 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_cache.ts` around lines 4 -
25, The $choysum.i18n bridge contract is duplicated and inconsistent across the
listed files. Create a shared bridge type and getChoysumI18nBridge accessor
under service/i18n, make the bridge methods consistently required as defined by
$choysum.d.ts, and import/reuse them from
modules/core/service/orm/model/_translation_term_cache.ts and
modules/core/service/i18n/translate.ts; update $choysum.d.ts to consume the
shared contract if needed while preserving the existing dependency direction.
modules/core/service/orm/model/translation_term_base_model.ts (2)

461-464: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Update does not forward options to the pre-write Search.

Delete at lines 502-506 spreads ...(options || {}) into its pre-write Search. Update omits it. The two pre-reads then apply different soft-delete visibility for the same kind of condition. Forward options here so both paths select the same rows.

♻️ Proposed alignment
     const before = await (this as any).Search(condition as any, {
       fields: ['Module'] as any,
       limit: 0,
+      ...(options || {}),
     });
🤖 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
461 - 464, Update the pre-write Search call in Update to spread ...(options ||
{}) into its query options, matching Delete’s pre-read behavior so both paths
apply the same soft-delete visibility for identical conditions.

460-470: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Replace the pre-write row scan with a grouped module read.

limit: 0 does not limit Search, so each write loads every matching row. Use ReadGroup(['Module'], condition) and extract keys.Module or labels.Module before invalidation. Apply the same change to Delete.

🤖 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
460 - 470, In the update flow around the pre-write Search call, replace the full
row scan with ReadGroup(['Module'], condition), extracting module names from
each group's keys.Module or labels.Module for invalidateTerminologyModules.
Apply the same grouped-module lookup before Delete, preserving invalidation of
modules from the payload and affected rows where applicable.
internal/i18n/bridge/terminology.go (1)

141-184: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Guard the packaged-term import against oversized PO input.

performUpsertPackagedTerms runs the full parse, table migration, transaction, and cache warm inline on the QuickJS execution thread. i18nimport.UpsertPackagedTerms also warms every affected language after the write. A large PO payload blocks the engine for the whole operation, and the returned promise resolves only after the blocking work completes.

Add an upper bound on len(poText) and reject payloads above it. This keeps a single import from stalling the runtime.

♻️ Proposed size guard
 	poText, err := poTextBytes(args[3])
 	if err != nil {
 		return ctx.ThrowError(err)
 	}
+	const maxPoBytes = 8 << 20
+	if len(poText) > maxPoBytes {
+		return ctx.ThrowError(fmt.Errorf("upsertPackagedTerms: poText exceeds %d bytes", maxPoBytes))
+	}
🤖 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/i18n/bridge/terminology.go` around lines 141 - 184, Update
performUpsertPackagedTerms to enforce an upper bound on len(poText) immediately
after poTextBytes succeeds and before resolving the runtime scope or calling
i18nimport.UpsertPackagedTerms. Reject oversized payloads with a QuickJS error,
using the repository’s established maximum PO input size constant if available;
preserve normal processing for payloads at or below the limit.
modules/core/service/orm/model/translation_term_cache.test.ts (3)

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

Import BaseModel directly instead of walking the prototype chain.

Object.getPrototypeOf(TranslationTermBaseModel.prototype).constructor assumes that TranslationTermBaseModel extends BaseModel with no intermediate class. If an intermediate class is added later, the patch targets that class instead, the override still calls the real super.Create, and the test gives a false result with no error.

Import BaseModel from ./model and patch it directly.

Also applies to: 169-171

🤖 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_cache.test.ts` around lines
142 - 144, Import BaseModel directly from ./model and update the test’s Create
override to target BaseModel rather than deriving it via
Object.getPrototypeOf(TranslationTermBaseModel.prototype).constructor. Apply the
same change to the additional override referenced in the comment, while
preserving the existing originalCreate capture and restoration behavior.

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

Four of the six CRUD overrides have no invalidation test.

The PR adds Create, CreateMany, Update, UpdateById, Delete, and DeleteById overrides in modules/core/service/orm/model/translation_term_base_model.ts. This file tests Create and UpdateById only.

The untested paths carry the more complex logic. Update and Delete both run a pre-write Search and merge module names from several sources. CreateMany merges payload modules with row modules. A regression in any of them leaves the Go TermStore stale with no test signal.

Add invalidation tests for CreateMany, Update, Delete, and DeleteById.

🤖 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_cache.test.ts` around lines
129 - 180, Add focused invalidation tests alongside the existing Create and
UpdateById tests for the TtInvTerm.CreateMany, TtInvTerm.Update,
TtInvTerm.Delete, and TtInvTerm.DeleteById overrides. Mock the relevant
BaseModel methods and Browse/Search results to exercise each method’s
module-merging logic, then assert global i18n.invalidateModule receives the
expected app/module pairs; restore all mocks and global state in finally blocks.

73-81: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a case for an empty lang.

The guard in ImportPackaged is if (!module || !lang). Only the empty module half is tested. Add one call with lang: '' so both halves of the condition are covered.

🤖 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_cache.test.ts` around lines
73 - 81, Add a rejection test for TtInvTerm.ImportPackaged using a valid module
and an empty lang value, and assert the TRANSLATION_TERM_IMPORT_ARGS error. Keep
the existing empty-module and null-poText cases unchanged.
internal/i18n/bridge/terminology_test.go (1)

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

Add a case for the Uint8Array PO input.

The test covers only the string form of poText. poTextBytes has three additional branches for Uint8Array, Uint8ClampedArray, and byte arrays, and none of them are exercised. The declared public type in modules/core/types/$choysum.d.ts accepts string | Uint8Array, so the binary path is part of the supported contract.

Add one upsert call that passes a Uint8Array built in JavaScript.

🤖 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/i18n/bridge/terminology_test.go` around lines 166 - 184, Extend the
test around the existing upsertPackagedTerms evaluation to add a second upsert
call whose PO input is a JavaScript Uint8Array. Use the same valid PO content
and assertions as the string case, verifying the binary input is accepted and
reports one upserted term; keep the existing string-path coverage intact.
🤖 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/i18n/bridge/terminology.go`:
- Around line 111-124: Update invalidateModuleFunc to use a read-only
existing-store lookup instead of reg.StoreFor, returning false when the
application has no registered store and avoiding cache mutation; add the lookup
to the registry’s existing-store API and update the test expectation for
invalidating before an auth store exists from true to false.
- Around line 128-139: Update performUpsertPackagedTerms and its NewPromise
callback so every error path returns ctx.NewError(err) rather than the
JS_EXCEPTION sentinel, allowing ret.IsError() to detect failures and
reject(ret). Preserve freeing ret after resolve or reject.

In `@modules/core/service/orm/model/translation_term_base_model.ts`:
- Around line 428-437: Update the static Create method to combine modules
derived from the input value with modules derived from the returned row before
calling invalidateTerminologyModules, matching the existing CreateMany behavior
so invalidation still occurs when returnFields omits Module.

In `@modules/core/service/orm/model/translation_term_cache.test.ts`:
- Around line 26-32: Update the test around invalidateTerminologyModule to
restore root.$choysum in a finally block, ensuring restoration occurs whether
the no-op assertion passes or throws; preserve the existing saved-value and
deletion setup.
- Around line 57-65: Update expectRejects so the assertion for a resolved
promise is not executed inside the try/catch: track whether awaiting promise
completes without rejection, validate that flag after the catch, and keep the
existing ChoysumError code checks only for actual rejections.

In `@modules/core/types/`$choysum.d.ts:
- Around line 249-265: Mark the i18n.invalidateModule and
i18n.upsertPackagedTerms members in the declaration optional, matching their
conditional installation in installI18nObject and the existing internal type
declarations. Preserve their current function signatures and return types while
allowing callers to guard against their absence.

---

Nitpick comments:
In `@internal/i18n/bridge/terminology_test.go`:
- Around line 166-184: Extend the test around the existing upsertPackagedTerms
evaluation to add a second upsert call whose PO input is a JavaScript
Uint8Array. Use the same valid PO content and assertions as the string case,
verifying the binary input is accepted and reports one upserted term; keep the
existing string-path coverage intact.

In `@internal/i18n/bridge/terminology.go`:
- Around line 141-184: Update performUpsertPackagedTerms to enforce an upper
bound on len(poText) immediately after poTextBytes succeeds and before resolving
the runtime scope or calling i18nimport.UpsertPackagedTerms. Reject oversized
payloads with a QuickJS error, using the repository’s established maximum PO
input size constant if available; preserve normal processing for payloads at or
below the limit.

In `@modules/core/service/orm/model/_translation_term_cache.ts`:
- Around line 34-38: Update the invalidation catch block around
bridge.invalidateModule(app, mod) to log the caught failure while preserving
best-effort behavior and not rethrowing after the successful write. Include
enough context to diagnose stale TermStore terms.
- Around line 52-70: Consolidate the duplicated normalization logic in
modulesFromRows and modulesFromPayloads into one shared helper, while retaining
both exported functions as intent-specific wrappers that delegate to it.
Preserve the current handling of nullish, scalar, array, trimmed, and empty
Module values.
- Around line 4-25: The $choysum.i18n bridge contract is duplicated and
inconsistent across the listed files. Create a shared bridge type and
getChoysumI18nBridge accessor under service/i18n, make the bridge methods
consistently required as defined by $choysum.d.ts, and import/reuse them from
modules/core/service/orm/model/_translation_term_cache.ts and
modules/core/service/i18n/translate.ts; update $choysum.d.ts to consume the
shared contract if needed while preserving the existing dependency direction.

In `@modules/core/service/orm/model/translation_term_base_model.ts`:
- Around line 461-464: Update the pre-write Search call in Update to spread
...(options || {}) into its query options, matching Delete’s pre-read behavior
so both paths apply the same soft-delete visibility for identical conditions.
- Around line 460-470: In the update flow around the pre-write Search call,
replace the full row scan with ReadGroup(['Module'], condition), extracting
module names from each group's keys.Module or labels.Module for
invalidateTerminologyModules. Apply the same grouped-module lookup before
Delete, preserving invalidation of modules from the payload and affected rows
where applicable.

In `@modules/core/service/orm/model/translation_term_cache.test.ts`:
- Around line 142-144: Import BaseModel directly from ./model and update the
test’s Create override to target BaseModel rather than deriving it via
Object.getPrototypeOf(TranslationTermBaseModel.prototype).constructor. Apply the
same change to the additional override referenced in the comment, while
preserving the existing originalCreate capture and restoration behavior.
- Around line 129-180: Add focused invalidation tests alongside the existing
Create and UpdateById tests for the TtInvTerm.CreateMany, TtInvTerm.Update,
TtInvTerm.Delete, and TtInvTerm.DeleteById overrides. Mock the relevant
BaseModel methods and Browse/Search results to exercise each method’s
module-merging logic, then assert global i18n.invalidateModule receives the
expected app/module pairs; restore all mocks and global state in finally blocks.
- Around line 73-81: Add a rejection test for TtInvTerm.ImportPackaged using a
valid module and an empty lang value, and assert the
TRANSLATION_TERM_IMPORT_ARGS error. Keep the existing empty-module and
null-poText cases unchanged.
🪄 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: c9df261b-344f-4bb3-a62d-b857ce3b0080

📥 Commits

Reviewing files that changed from the base of the PR and between d382bf3 and 4acdb96.

📒 Files selected for processing (10)
  • internal/defaultengine/init.go
  • internal/i18n/bridge/terminology.go
  • internal/i18n/bridge/terminology_test.go
  • internal/i18n/gateway/auth_forward.go
  • modules/core/i18n/README.md
  • modules/core/service/i18n/translate.ts
  • modules/core/service/orm/model/_translation_term_cache.ts
  • modules/core/service/orm/model/translation_term_base_model.ts
  • modules/core/service/orm/model/translation_term_cache.test.ts
  • modules/core/types/$choysum.d.ts

Comment thread internal/i18n/bridge/terminology.go
Comment thread internal/i18n/bridge/terminology.go
Comment thread modules/core/service/orm/model/translation_term_base_model.ts
Comment thread modules/core/service/orm/model/translation_term_cache.test.ts
Comment thread modules/core/service/orm/model/translation_term_cache.test.ts
Comment thread modules/core/types/$choysum.d.ts

@cubic-dev-ai cubic-dev-ai 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.

Ultrareview completed in 15m 41s

All reported issues were addressed across 10 files

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread internal/i18n/bridge/terminology.go Outdated
Comment thread internal/defaultengine/init.go
Comment thread internal/i18n/bridge/terminology.go
Comment thread modules/core/service/orm/model/_translation_term_cache.ts
Comment thread internal/i18n/bridge/terminology.go Outdated
Comment thread modules/core/types/$choysum.d.ts Outdated
Comment thread internal/i18n/bridge/terminology.go
Comment thread modules/core/service/orm/model/translation_term_cache.test.ts Outdated
Comment thread modules/core/service/orm/model/_translation_term_cache.ts
Comment thread modules/core/service/orm/model/translation_term_cache.test.ts
buke and others added 2 commits August 7, 2026 11:54
- Use ExistingStore for invalidateModule and NewError for upsert rejections.

- Guard nullish bridge args; invalidate Create from payload when Module is projected away.

- Align $choysum.i18n types and facade unit-test teardown/assertions with runtime.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Cover bridge error branches, poText variants, ExistingStore, and CRUD invalidate paths.

- Drop unreachable ExecContext nil guard; add package-level hooks for upsert/marshal failures.

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

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 5 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread internal/i18n/bridge/terminology.go Outdated
Comment thread modules/core/service/orm/model/translation_term_cache.test.ts Outdated
- Pass upsert/marshal deps into performUpsertPackagedTerms instead of package globals.

- Drop no-op void BaseModel and redundant restore from withInvalidateSpy.

Co-authored-by: Cursor <cursoragent@cursor.com>
@buke
buke merged commit 61a4a1b into main Aug 7, 2026
46 checks passed
@buke
buke deleted the chore/i18n-p6-facade-ci-design branch August 7, 2026 04:32
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