Skip to content

Migrate conversation language field default to 'en' - #11349

Open
undivisible wants to merge 3 commits into
mainfrom
fix/migrate-db-language-field-10040660832344661720
Open

Migrate conversation language field default to 'en'#11349
undivisible wants to merge 3 commits into
mainfrom
fix/migrate-db-language-field-10040660832344661720

Conversation

@undivisible

@undivisible undivisible commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Failure-Class: none

Sets the default value of the conversation language field to 'en' and provides a Firebase migration script to update existing conversations.


PR created automatically by Jules for task 10040660832344661720 started by @undivisible

Review in cubic


Note

Medium Risk
Touches persisted conversation data via a production Firestore migration and changes creation defaults across core conversation models. Sync path correctly preserves non-English languages, but rollout still needs careful staged execution.

Overview
Conversation language now defaults to en on Conversation, CreateConversation, and ExternalIntegrationCreateConversation, with matching OpenAPI and Dart wire-model updates.

Sync creation in process_segment now passes the resolved detected/preferred language into CreateConversation, so non-English sync conversations are not overwritten by the new default.

Adds a resumable Firestore backfill (008_migrate_language_to_en.py) that sets missing/empty language to en while preserving existing values, plus unit coverage for the default, migration safety, and sync language persistence.

Reviewed by Cursor Bugbot for commit 02fb397. Configure here.


Verification

  • Backend unit tests on touched files pass: test_conversation_language_default.py (5), test_migrate_language_to_en.py (9), test_sync_transcription_prefs.py (65).
  • scan_import_time_side_effects.py on the migration: 0 violations; scan_async_blockers.py --dirs migrations utils/sync: 0 findings.
  • Rebased onto origin/main; pr_preflight.py --lane local green.
  • Migration backfill is bounded and resumable: paged user iteration, language-projected conversation streams, default 8 workers, --max-users / --max-writes / --start-after, non-zero exit on any worker failure, --dry-run reports prospective updates. Usage and rollout commands in the migration docstring.

@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_5bc4e1a2-b781-4698-89cf-a72835462ac6)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3204027c2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# Initialize Firebase Admin SDK
try:
cred = credentials.ApplicationDefault()
firebase_admin.initialize_app(cred)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Defer Firebase initialization until migration execution

Move Firebase initialization and firestore.client() into main() (and inject the client into the helpers). The required python backend/scripts/scan_import_time_side_effects.py check reports this new top-level firebase_admin.initialize_app call, so the commit cannot pass preflight; it also prevents importing the migration helpers without initializing credentialed infrastructure.

AGENTS.md reference: backend/AGENTS.md:L235-L235

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8a6c13d: Firebase init and client construction are fully deferred; the migration calls database._client.get_firestore_client() only inside execution paths, and scan_import_time_side_effects.py reports 0 violations.

Comment on lines +93 to +94
except Exception as e:
logger.error(f"Error processing {uid}: {e}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fail the migration when any user update fails

If a Firestore read or batch commit fails for even one user, this handler only logs the exception and the process still prints Done and exits successfully. A transient failure or concurrent document deletion can therefore leave part of the database unmigrated while deployment automation or an operator records the migration as successful; track failures and return a nonzero exit status after all futures finish.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8a6c13d: worker failures are counted and the process exits non-zero (sys.exit(1)), so automation cannot record a partial run as successful. Covered by test_worker_failure_exits_nonzero.


source: Optional[ConversationSource] = ConversationSource.omi
language: Optional[str] = None # applies only to Friend # TODO: once released migrate db to default 'en'
language: Optional[str] = 'en' # applies only to Friend

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add regression coverage for the new language defaults

Add tests proving that omitted language values default to en for all three changed conversation models, and that the migration updates missing/empty values while preserving existing languages and exposing write failures. This commit changes production defaults and adds a data migration without modifying any test, contrary to the repository's required behavior-change coverage.

AGENTS.md reference: AGENTS.md:L28-L28

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: test_conversation_language_default.py proves omitted language defaults to 'en' on all three models with explicit values preserved; test_migrate_language_to_en.py covers missing/empty backfill, existing-language preservation, dry-run, and failure exit.


source: ConversationSource = ConversationSource.omi
language: Optional[str] = None
language: Optional[str] = 'en'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the detected language when creating sync conversations

The sync pipeline derives language from the user's explicit preference or STT detection, but constructs CreateConversation without passing it and supplies it only as the separate processing argument (utils/sync/pipeline.py:1075,1149-1162). With this new default, _get_conversation_obj serializes such Spanish or other non-English conversations as language='en'; subsequent reprocessing uses the persisted conversation.language, so summaries and derived data can be regenerated in English. Pass the resolved language into the model rather than allowing the default to replace a known value.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 02fb397: process_segment now passes the resolved language into CreateConversation, with regression test test_detected_language_is_stored_on_new_conversation (French-detected sync persists 'fr').

# so we get all conversations and filter locally, or just check 'language' field.
# To be efficient, we'll stream conversations and update those needing it.

conversations = list(conversations_ref.stream())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stream projected conversation fields during the backfill

For users with long histories or large encrypted transcript documents, list(conversations_ref.stream()) downloads every field and retains the user's entire conversation history in memory; with 64 users processed concurrently, the production-wide migration can exhaust memory and transfer far more Firestore data than needed before it reaches the updates. Iterate the stream directly and project only language so memory remains bounded and transcript blobs are not fetched.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8a6c13d: conversations are streamed with .select(['language']) only, no list() materialization; users are paged by document id so memory stays bounded.

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

1 issue found across 2 files

Confidence score: 3/5

  • In backend/models/conversation.py, defaulting CreateConversation.language to 'en' creates a concrete regression risk for callers that omit the field (such as the sync pipeline), which can silently store conversations with the wrong language and affect downstream language-dependent behavior—ensure detected/preferred language is always passed explicitly or remove the default and fail fast when missing.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/models/conversation.py">

<violation number="1" location="backend/models/conversation.py:289">
P1: With `CreateConversation.language` now defaulting to `'en'`, any caller that constructs `CreateConversation` without explicitly passing the detected/preferred language (e.g. the sync pipeline, which resolves language separately) will silently persist `language='en'` even for non-English conversations. Since `conversation.language` is used for later reprocessing, this can cause summaries and derived data to be regenerated in English for non-English conversations. Ensure all callers pass the resolved language into the model instead of relying on the new default.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic


source: ConversationSource = ConversationSource.omi
language: Optional[str] = None
language: Optional[str] = 'en'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: With CreateConversation.language now defaulting to 'en', any caller that constructs CreateConversation without explicitly passing the detected/preferred language (e.g. the sync pipeline, which resolves language separately) will silently persist language='en' even for non-English conversations. Since conversation.language is used for later reprocessing, this can cause summaries and derived data to be regenerated in English for non-English conversations. Ensure all callers pass the resolved language into the model instead of relying on the new default.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/models/conversation.py, line 289:

<comment>With `CreateConversation.language` now defaulting to `'en'`, any caller that constructs `CreateConversation` without explicitly passing the detected/preferred language (e.g. the sync pipeline, which resolves language separately) will silently persist `language='en'` even for non-English conversations. Since `conversation.language` is used for later reprocessing, this can cause summaries and derived data to be regenerated in English for non-English conversations. Ensure all callers pass the resolved language into the model instead of relying on the new default.</comment>

<file context>
@@ -286,7 +286,7 @@ class CreateConversation(BaseModel):
 
     source: ConversationSource = ConversationSource.omi
-    language: Optional[str] = None
+    language: Optional[str] = 'en'
 
     processing_conversation_id: Optional[str] = None
</file context>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Covered by the fix for 3746820357 (02fb397): the sync pipeline threads the detected/preferred language into CreateConversation and a non-English sync regression test asserts it.

Comment thread backend/migrations/008_migrate_language_to_en.py Outdated
Comment thread backend/migrations/008_migrate_language_to_en.py Outdated
Comment thread backend/migrations/008_migrate_language_to_en.py Outdated
@mintlify

mintlify Bot commented Aug 10, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
omi 🟢 Ready View Preview Aug 10, 2026, 6:04 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@undivisible undivisible added human Human-authored pull request backend Backend Task (python) privacy-review Touches user-data persistence, permissions, or privacy-sensitive surfaces AI and removed human Human-authored pull request labels Aug 10, 2026
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_065eca9c-02b5-4d79-8b24-9ecf232bdd3d)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c3f793ddc9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +21 to +22
cred = credentials.ApplicationDefault()
firebase_admin.initialize_app(cred)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use the shared Firestore client for migration credentials

When an operator uses the backend-supported SERVICE_ACCOUNT_JSON credential mode, credentials.ApplicationDefault() ignores that variable and the migration exits because it only accepts ADC via GOOGLE_APPLICATION_CREDENTIALS. Use database._client.get_firestore_client() so the migration honors the repository's credential preparation and configured Firestore target instead of requiring operators to translate supported credentials manually.

AGENTS.md reference: backend/AGENTS.md:L191-L193

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8a6c13d: the migration uses database._client.get_firestore_client(), which routes through prepare_google_credentials and honors SERVICE_ACCOUNT_JSON.

@undivisible undivisible added needs-maintainer-review Needs a human maintainer to sign off before merge needs-tests PR introduces logic that should be covered by tests labels Aug 10, 2026

@undivisible undivisible left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the default language to en can silently rewrite non-English synced conversations because affected creation paths do not propagate detected language. Please preserve detected language for synced conversations, constrain the default to the intended Friend migration, and add regression coverage for a non-English sync.

@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_6603c84c-ab69-4c78-a529-414bf1e453fd)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1794dae771

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


for doc in conversations:
data = doc.to_dict()
if data.get('source') not in ('friend', 'friend_com'):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Backfill Omi-source conversations too

Historical sync-created conversations are stored with source omi: process_segment defaults its source to ConversationSource.omi, and before this commit its CreateConversation call omitted the language. This filter therefore skips exactly those existing records that can lack the field, leaving their persisted language unset even though the migration reports success. Include omi in the backfill scope rather than limiting it to the legacy Friend source names.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: the migration applies to all conversation sources (no source filter), so omi-source sync conversations with missing language are backfilled too.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cf7b0f620a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"backend/utils/retrieval/tools/calendar_tools.py": 1513,
"backend/utils/stt/streaming.py": 1523,
"backend/utils/sync/pipeline.py": 2499
"backend/utils/sync/pipeline.py": 2500

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore the ratchet to the unchanged source count

Reset this entry to 2499 and remove the unrelated baseline raise. backend/utils/sync/pipeline.py is not modified by this commit and remains 2499 lines, so running check_product_file_line_count_ratchet.py against the commit reports both that the source file is absent from the diff and that the raised limit does not match its line count; this makes the required preflight contract fail.

AGENTS.md reference: AGENTS.md:L34-L35

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The baseline was already restored to 2499 at eff8ac8; it is now raised to 2500 with a justification only because 02fb397 adds the language=language line to pipeline.py, and the ratchet check passes.

@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_207e2a77-843a-4f14-8794-b66e7da92bf6)

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pushing this through — the backend/model default itself looks consistent, but I’m going to block until the migration/CI pieces are made safe and green.

Blocking items:

  1. backend/migrations/008_migrate_language_to_en.py is a production Firestore data migration, but it eagerly loads every user (get_all_users() returns list(users_ref.stream())) and then eagerly loads every conversation for each user (conversations = list(conversations_ref.stream())) while running 64 users in parallel. For Omi-scale user data this has a high blast radius: unbounded memory/read load, no paging/checkpoint/resume, and no operator throttle beyond --dry-run. Please make this migration bounded and operationally safe before merge (stream or page users/conversations, lower/configure concurrency, include progress/checkpoint/resume or at least an explicit max-users/max-writes guard, and document the dry-run/rollout command).

  2. CI is currently red on this head. Dart Analyze & Tests fails the analyzer ratchet with override_on_non_overriding_member: 5 found, baseline 0, while Hygiene/PR Metadata Preflight fail failure-class-protocol because the fix: commits do not declare Failure-Class: FC-<slug> | new | none. Please fix the ratchet/generated-file state and the required failure-class declaration so the PR is mergeable.

File-specific notes from this review:

  • backend/models/conversation.py: the three Pydantic defaults now make omitted conversation language resolve to en; that matches the stated backend/API intent, but it makes the migration/rollout correctness more important because omitted values will no longer remain distinguishable from English.
  • app/lib/backend/schema/gen/conversation_wire.g.dart: the generated client parser now also defaults missing language to "en", which is consistent with the backend but should be regenerated/validated with the app analyzer green.
  • backend/migrations/008_migrate_language_to_en.py: needs the production-safety changes above before it should be run against user conversation data.
  • docs/api-reference/app-client-openapi.json: the two added OpenAPI default: "en" entries match the backend request/response schema change.
  • docs/api-reference/integration-public-openapi.json: the public integration schema also reflects the new default; this is worth keeping because external clients may rely on the documented default.
  • app/analysis_baseline.json: the baseline edit does not match the current analyzer result, so it needs to be corrected together with the generated app changes.

Leaving this as changes requested because it touches user-data persistence and the current migration path is too broad to run safely as-is; maintainer sign-off is also needed for the data backfill rollout once the script is bounded.


Reviewed by Omi's automated maintainer review (glm-5.2).


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@undivisible
undivisible force-pushed the fix/migrate-db-language-field-10040660832344661720 branch from 926de2a to 83a8a7c Compare August 10, 2026 21:11
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_55b3e555-ceeb-40ae-962e-95b17460df2d)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 83a8a7cfe6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


elapsed = time.time() - start
logger.info(f"Done in {elapsed:.1f}s")
logger.info(f"Results: {total_updates} conversations updated across {users_updated} users.")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Report dry-run results as prospective updates

When the migration is invoked with --dry-run, process_user_conversations increments both counters without committing any writes, but this final message still reports that the conversations were "updated." An operator relying on the preview output as rollout evidence could therefore mistake a dry run for a completed migration; label these as conversations that would be updated when args.dry_run is true.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8a6c13d: dry-run output reports 'would be updated' instead of 'updated'; asserted in test_dry_run_reports_without_committing.

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. I rechecked the new head and the generated/model default changes are aligned, but I still need to keep this blocked because the production backfill script is not safe enough to run against user conversation data yet, and the branch is still red.

Blocking items:

  1. backend/migrations/008_migrate_language_to_en.py still loads the full users collection into memory (get_all_users() returns list(users_ref.stream()) at lines 36-39), then loads every conversation for each user into memory (conversations = list(conversations_ref.stream()) at line 52), and runs 64 users in parallel (line 92). That addresses the intended backfill, but it still has unbounded read/memory/write blast radius and no checkpoint/resume, paging, max-user/max-write guard, or operator throttle beyond --dry-run. Please make the migration bounded and resumable/throttled before this can be run on production user data.

  2. CI is still failing on this head (Hygiene, and the current check rollup also shows a failing PR Metadata Preflight). Please get the required hygiene/preflight state green before merge.

File-specific review notes:

  • backend/models/conversation.py: the Conversation, CreateConversation, and ExternalIntegrationCreateConversation defaults now all resolve omitted language to en. That is consistent with the PR goal, but it changes the meaning of omitted language, so the data rollout and any non-English import/sync paths need maintainer confidence.
  • app/lib/backend/schema/gen/conversation_wire.g.dart: the generated Dart constructor and fromJson reader now default missing language to "en", matching the backend schema; this looks mechanically consistent with the model change.
  • backend/migrations/008_migrate_language_to_en.py: the migration still filters missing/empty language values correctly, but it needs bounded streaming/paging and operational controls before it is safe for a production Firestore backfill.
  • docs/api-reference/app-client-openapi.json: the two app-client OpenAPI schema entries now document default: "en", matching the backend request/response defaults.
  • docs/api-reference/integration-public-openapi.json: the public integration schema now documents the same default: "en"; because external clients may rely on this contract, it should stay in sync with the backend behavior.
  • app/analysis_baseline.json: the prefer_final_fields ratchet is reduced from 4 to 3, which is a positive cleanup, but CI still needs to be green before merge.

I also checked the prior automated blocking review: the Dart analyzer portion appears addressed, but the Firestore migration safety concern is still present on the current head, so I’m not dismissing the earlier changes-requested review. Leaving this for human maintainer sign-off because this is a user-data backfill/default-semantics change, not just a mechanical generated-schema update.


Reviewed by Omi's automated maintainer review (glm-5.2).


by AI on behalf of David — for maintainer follow-up, please @Git-on-my-level with the specific rollout or data-safety question.

@undivisible
undivisible force-pushed the fix/migrate-db-language-field-10040660832344661720 branch from 83a8a7c to eff8ac8 Compare August 10, 2026 23:11
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_5326ca3b-ca26-435b-a913-f65851403c37)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eff8ac8feb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"non_constant_identifier_names": 1,
"overridden_fields": 1,
"prefer_final_fields": 4,
"prefer_final_fields": 3,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore the unchanged analyzer baseline

The only Dart source edit in this commit is conversation_wire.g.dart, while app/scripts/analyze_ratchet.sh explicitly excludes .g.dart diagnostics from its counts, so none of the four existing non-generated prefer_final_fields occurrences was removed. Lowering this baseline to 3 therefore makes the analyzer ratchet reject the unchanged app sources in CI; keep the count at 4 unless a non-generated occurrence is fixed.

AGENTS.md reference: app/AGENTS.md:L90-L90

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept at 3: analyze_ratchet.sh is a cap (fails only when current > baseline), and Dart Analyze & Tests is green on this head with baseline 3, so the non-generated count is genuinely <=3. Restoring to 4 would leave a stale cap.

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. I rechecked this head, and the generated/model default changes are still aligned with the PR goal, but I need to keep this blocked because the production backfill script remains too broad for user conversation data and the branch is still red.

Blocking items:

  1. backend/migrations/008_migrate_language_to_en.py still has an unbounded Firestore rollout shape: get_all_users() materializes every user with list(users_ref.stream()), process_user_conversations() materializes every conversation for each user with list(conversations_ref.stream()), and main() runs 64 users concurrently. It also logs worker exceptions but does not fail the process, so a partial migration can still exit successfully. Please make the migration bounded and operator-safe before it can be run on production data: stream/page users and conversations, use lower/configurable concurrency, add max-users/max-writes or checkpoint/resume controls, and exit non-zero if any worker/write fails.

  2. CI is still not green on this head: the check summary shows Hygiene failing. Please get the required hygiene/preflight state green before merge.

File-specific review notes:

  • backend/models/conversation.py: Conversation, CreateConversation, and ExternalIntegrationCreateConversation now default omitted language to en. That is consistent with the stated backend/API goal, but it changes omitted-language semantics for user data, so the rollout/backfill path needs maintainer confidence.
  • app/lib/backend/schema/gen/conversation_wire.g.dart: the generated Dart constructor and JSON reader now default missing language to "en", matching the backend schema change; this looks mechanically consistent with the model update.
  • backend/migrations/008_migrate_language_to_en.py: the missing/empty-language filter is scoped to the intended values, but the current implementation still needs bounded streaming/paging, throttling/checkpointing, and failure accounting before it is safe for production Firestore.
  • docs/api-reference/app-client-openapi.json: both app-client schema entries now document default: "en", matching the backend request/response default.
  • docs/api-reference/integration-public-openapi.json: the public integration schema now documents the same default, which is important because external clients may rely on that contract.
  • app/analysis_baseline.json: the analyzer baseline improves prefer_final_fields from 4 to 3, but the remaining hygiene failure still needs to be resolved before merge.

I also checked the stale automation change requests: the earlier analyzer/generated-file concern appears resolved, but the Firestore migration safety concern is still present on the current head, so I am not dismissing the prior blocking reviews.

Leaving this for human maintainer review because this changes user-data backfill/default semantics and needs explicit data-rollout sign-off once the migration script is bounded.


Reviewed by Omi's automated maintainer review (glm-5.2).


by AI on behalf of David — for maintainer follow-up on this data-backfill/default-semantics change, please @Git-on-my-level with the rollout or data-safety question.

Conversation, CreateConversation, and ExternalIntegrationCreateConversation
now resolve omitted language to 'en' instead of null, matching the Friend
migration intent. OpenAPI specs and the generated Dart wire model are
regenerated to document the default. Regression tests cover the three
models (omitted -> en, explicit -> preserved).
Rewrites the one-off backfill for operator safety on production Firestore:
users are paged by document id, conversations are streamed with a language
projection only, concurrency is configurable (default 8), --max-users and
--max-writes bound staged rollouts, --start-after resumes, any per-user
failure exits non-zero, and --dry-run reports prospective updates. Uses the
shared get_firestore_client so SERVICE_ACCOUNT_JSON is honored.
process_segment resolves the sync language from user prefs or STT detection
but did not pass it into CreateConversation; with the new 'en' default,
non-English sync conversations were persisted as 'en' and reprocessed in
English. Thread the resolved language through, add a regression test for a
French-detected sync, and bump the pipeline line-count ratchet.

Failure-Class: none
@undivisible
undivisible force-pushed the fix/migrate-db-language-field-10040660832344661720 branch from eff8ac8 to 02fb397 Compare August 12, 2026 08:36
@cursor

cursor Bot commented Aug 12, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_207ef93e-82ba-42c8-9607-6bb32740ccf2)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 02fb397539

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

while not max_users or collected < max_users:
query = db.collection('users').order_by('__name__').limit(page_size)
if last_id:
query = query.start_after([last_id])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pass a document reference to the pagination cursor

When the full migration reaches its second page, or whenever --start-after is supplied, this passes a raw string as the cursor value for order_by('__name__'). Firestore document-ID cursors require a document snapshot/reference (the repository's other paginated queries pass the last snapshot or collection.document(id)), so the production query rejects this cursor even though the fake test accepts it; the migration therefore cannot progress beyond the first page. Use the last snapshot or a document reference in the cursor.

Useful? React with 👍 / 👎.

Comment on lines +135 to +139
for uid in iter_user_ids(db, start_after=args.start_after, page_size=args.page_size, max_users=args.max_users):
if args.max_writes and total_writes >= args.max_writes:
logger.info('Reached --max-writes %d, stopping', args.max_writes)
break
futures[executor.submit(process_user_conversations, db, uid, args.dry_run)] = uid

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Enforce --max-writes while work is running

For every invocation with --max-writes, total_writes remains zero throughout this entire scheduling loop because results are not collected until the subsequent as_completed loop. Consequently the limit never stops scheduling, and a staged run such as --max-writes 1000 can update the whole database. Results need to be consumed while scheduling, with the remaining write budget enforced before additional user work is allowed.

Useful? React with 👍 / 👎.

Comment on lines +158 to +161
'Finished in %.1fs with %d failed user(s); rerun with --start-after %s to resume',
elapsed,
failures,
last_uid,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not resume after an out-of-order completion

When any worker fails, last_uid is merely the most recently completed successful future, not the greatest contiguous user ID processed before the failure. For example, if u1 fails and u2 completes afterward, the suggested --start-after u2 retry permanently skips u1 while the idempotent migration appears complete. Track a contiguous ordered checkpoint, explicitly retry failed IDs, or advise rerunning without this cursor.

Useful? React with 👍 / 👎.

if args.max_writes and total_writes >= args.max_writes:
logger.info('Reached --max-writes %d, stopping', args.max_writes)
break
futures[executor.submit(process_user_conversations, db, uid, args.dry_run)] = uid

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound the number of submitted user futures

On the default unlimited full run, this submits one future for every user before collecting any result, and both the executor's unbounded queue and futures retain all of them. The user iterator's paged reads and eight-worker setting therefore do not bound memory; on the repository's 100K-plus user collection, the migration can consume substantial memory or be killed before processing finishes. Keep only a small bounded window of futures in flight and replenish it as each completes.

Useful? React with 👍 / 👎.

@Git-on-my-level Git-on-my-level removed the needs-tests PR introduces logic that should be covered by tests label Aug 12, 2026

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update — this resolves the blockers I had on the earlier heads. I’m leaving this as a positive maintainer signal rather than a formal approval because it still changes a production user-data migration path and should get maintainer sign-off on rollout before merge.

What I checked:

  • backend/migrations/008_migrate_language_to_en.py now pages users with iter_user_ids(), lowers/configures concurrency through --workers, supports --max-users, --max-writes, --start-after, and --dry-run, and exits non-zero on per-user failures. That addresses the earlier unbounded Firestore sweep / partial-success concern.
  • backend/models/conversation.py consistently defaults Conversation, CreateConversation, and ExternalIntegrationCreateConversation language to 'en' while still preserving explicitly provided non-English values.
  • backend/utils/sync/pipeline.py now passes the detected/preferred language into CreateConversation, so the new default should not silently overwrite non-English sync conversations.
  • backend/tests/unit/test_sync_transcription_prefs.py adds the important regression case for detected 'fr' being persisted on newly synced conversations.
  • backend/tests/unit/test_migrate_language_to_en.py covers missing/empty backfill behavior, existing-language preservation, dry-run behavior, pagination/resume, max-user bounds, and failure propagation.
  • backend/tests/unit/test_conversation_language_default.py covers the model default and explicit-language preservation paths.
  • app/lib/backend/schema/gen/conversation_wire.g.dart and the two OpenAPI JSON files mirror the new 'en' default for clients/docs.
  • The ratchet/baseline files (.github/scripts/product_file_line_count_ratchet_baseline/backend-utils.json, app/analysis_baseline.json) are consistent with the implementation/test churn.

CI is green in the context I reviewed. I’m also dismissing the stale automation change-request reviews from older heads because their specific blockers — unbounded migration shape, failure handling, and red checks — are resolved on this head.

Remaining maintainer note: because this backfills persisted conversation data, please run the migration in a staged rollout (--dry-run, then bounded --max-users / --max-writes, resume from logged user IDs) and confirm that defaulting missing/empty historical language to English is the intended product/data policy before full execution.

Automated maintainer review by glm-5.2 for Omi.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@Git-on-my-level
Git-on-my-level dismissed stale reviews from themself August 12, 2026 09:51

Resolved on current head: migration is now bounded/resumable, failures exit non-zero, sync language is preserved, and checks are green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI backend Backend Task (python) needs-maintainer-review Needs a human maintainer to sign off before merge privacy-review Touches user-data persistence, permissions, or privacy-sensitive surfaces

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants