Skip to content

Allow coderouter legacy cleanup after new accounts - #9689

Merged
lawrencecchen merged 2 commits into
mainfrom
fix/coderouter-source-cleanup
Aug 6, 2026
Merged

lawrencecchen merged 2 commits into
mainfrom
fix/coderouter-source-cleanup

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

The rollback source only needs to be a subset of encrypted RDS. Requiring equal counts would permanently block cleanup if a new account were added after cutover. Cleanup still refuses deletion if any legacy source account lacks an encrypted destination.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Allow legacy credential cleanup when new encrypted accounts exist, and ensure migration only advances credential revisions.

  • Bug Fixes
    • Replaced count equality check with an ID-based subset check of encrypted accounts; cleanup blocks only when source accounts lack encrypted counterparts (error shows missing count).
    • Made migration monotonic: insert-or-ignore, and update credentials and vault only when the incoming credentialRevision is higher.

Written for commit 6e85fba. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved validation when deleting source accounts.
    • Deletion is now prevented if any source account lacks a matching encrypted credential.
    • Prevented older imported credentials and account details from overwriting newer information.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The migration validates source accounts against encrypted credentials by account ID. Credential imports avoid overwriting newer records and update existing credentials or account metadata only when the incoming revision is newer.

Changes

Credential migration

Layer / File(s) Summary
Account ID deletion validation
web/scripts/coderouter/migrate-encrypted-credentials.ts
The migration compares source account IDs with encrypted credential account IDs before deleting source records. It refuses deletion when a source account lacks an encrypted credential.
Revision-aware credential import
web/services/coderouter/repository.ts
importEncryptedCredential inserts credentials without overwriting conflicts. It updates credentials and account metadata only when the incoming revision is newer. The Drizzle lt operator supports these checks.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • manaflow-ai/cmux#9688 — The issue concerns cleanup of encrypted credential migration records and uses the changed migration script.

Possibly related PRs

  • manaflow-ai/cmux#9119 — Both PRs modify encrypted credential migration and revision-aware repository handling.
  • manaflow-ai/cmux#9633 — This PR refines credential import conflict and revision handling in the same repository.
  • manaflow-ai/cmux#9686 — Both PRs modify the migration script and importEncryptedCredential.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux User-Facing Error Privacy ❌ Error The changed migration command error prints the internal team ID and migration details (source accounts and encryption status), which the rule forbids in user-facing command output. Replace the error with a generic product-term message and safe next action; keep the team ID and migration diagnostics in sanitized logs or telemetry.
Description check ⚠️ Warning The description explains the change and rationale but omits the required Testing, Demo Video, Review Trigger, and Checklist sections. Add the required Testing, Demo Video, Review Trigger, and Checklist sections, and provide the requested testing and review details.
✅ Passed checks (23 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: allowing legacy cleanup after new encrypted accounts are added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Cmux Swift Actor Isolation ✅ Passed The pull request changes only two TypeScript files; it introduces no production Swift code or Swift actor-isolation changes.
Cmux Swift Blocking Runtime ✅ Passed The pull request changes only two TypeScript files; the combined diff contains no Swift files and adds no Swift blocking or timing synchronization.
Cmux Browser Automation Off-Main ✅ Passed The diff changes only web/services/coderouter/repository.ts; it does not touch the Swift browser automation files named by the rule or any browser socket command routing.
Cmux Expensive Synchronous Load ✅ Passed The PR changes only two TypeScript files: migration validation and coderouter repository logic. It adds no Swift changes or expensive synchronous agent-history loads.
Cmux Cache Substitution Correctness ✅ Passed The diff does not substitute a cache for an authoritative read: migration reads the vault and RDS directly, while importEncryptedCredential persists its fresh input with revision guards.
Cmux No Hacky Sleeps ✅ Passed The two changed TypeScript files add only ID/revision checks and transactional database updates; the diff introduces no sleep, timer, polling, fixed delay, or wall-clock wait.
Cmux Algorithmic Complexity ✅ Passed The migration uses a Set plus linear passes over source and encrypted accounts, and repository updates use account-ID predicates; no nested or per-target collection rescans were introduced.
Cmux Swift Concurrency ✅ Passed The patch changes only two TypeScript files; it introduces no cmux-owned Swift code or Swift concurrency patterns.
Cmux Swift @Concurrent ✅ Passed The pull request changes only TypeScript; no Swift files or Swift concurrency annotations are in the diff.
Cmux Swift Package Boundaries ✅ Passed The PR diff contains only TypeScript changes in web/services/coderouter/repository.ts and no Swift or SwiftPM production changes.
Cmux Swiftpm Lockfiles ✅ Passed The PR diff contains only two TypeScript files; it has no SwiftPM, Xcode project, .gitignore, workflow, or dependency changes covered by this rule.
Cmux Swift Logging ✅ Passed The diff changes only TypeScript in web/services/coderouter/repository.ts and adds no Swift logging or diagnostic output.
Cmux Full Internationalization ✅ Passed The only changed file is repository.ts; the diff adds database conflict/revision logic and no new or changed user-facing text, locale keys, catalogs, or metadata.
Cmux Swiftui State Layout ✅ Passed The PR range changes only two TypeScript files; it contains no SwiftUI, Swift, or AppKit changes, so the SwiftUI state-layout check is not applicable.
Cmux Architecture Rethink ✅ Passed The PR changes only two TypeScript coderouter files. The combined patch has no Swift paths or prohibited Swift architectural mechanisms.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR diff contains only web/services/coderouter/repository.ts. It adds no Swift or window code, so the auxiliary-window shortcut rule does not apply.
Cmux Source Artifacts ✅ Passed The PR diff changes only web/services/coderouter/repository.ts, a hand-written TypeScript source file; no local, generated, cache, temp, or build artifacts appear.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The PR changes only web/services/coderouter/repository.ts; it contains no Swift diff under a production Sources/ path, so this check is not applicable.
Cmux No Ambient Global State ✅ Passed The PR diff changes only TypeScript files; it contains no production Swift changes, so the ambient global state check is not applicable.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/coderouter-source-cleanup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@lawrencecchen
lawrencecchen merged commit 4665c6e into main Aug 6, 2026
5 of 6 checks passed
@lawrencecchen
lawrencecchen deleted the fix/coderouter-source-cleanup branch August 6, 2026 03:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/services/coderouter/repository.ts`:
- Around line 240-261: Update importEncryptedCredential so both the credential
upsert and coderouterAccounts metadata update are gated by the same monotonic
revision from the imported credential source. Use that revision for the account
vaultRevision comparison as well as coderouterCredentials.credentialRevision,
preventing stale metadata updates when stored revisions diverge. Add coverage
for missing credentials and divergent stored revisions.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4dcd322c-0004-4d10-acce-c4fb07b8a9a8

📥 Commits

Reviewing files that changed from the base of the PR and between 80d24ff and 6e85fba.

📒 Files selected for processing (1)
  • web/services/coderouter/repository.ts

Comment on lines +240 to +261
const [inserted] = await tx
.insert(coderouterCredentials)
.values(encryptedValues(input.encrypted))
.onConflictDoUpdate({
.onConflictDoNothing({
target: coderouterCredentials.accountId,
set: {
})
.returning({ accountId: coderouterCredentials.accountId });
if (!inserted) {
await tx
.update(coderouterCredentials)
.set({
...encryptedValues(input.encrypted),
updatedAt: new Date(),
},
});
})
.where(and(
eq(coderouterCredentials.accountId, input.encrypted.accountId),
lt(
coderouterCredentials.credentialRevision,
input.encrypted.credentialRevision,
),
));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 8 \
  'export const coderouterAccounts|vaultRevision|credentialRevision|importEncryptedCredential' \
  web/db/schema.ts \
  web/services/coderouter \
  web/scripts/coderouter

rg -n -C 8 \
  'importEncryptedCredential|vaultRevision|credentialRevision' \
  web --glob '*test*' --glob '*spec*' || true

Repository: manaflow-ai/cmux

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repository.ts around importEncryptedCredential =="
sed -n '150,285p' web/services/coderouter/repository.ts

echo
echo "== migration callers and importEncryptedCredential call sites =="
rg -n -C 5 'importEncryptedCredential|encryptCredential\(\{[\s\S]*?credentialRevision: input\.encrypted\.credentialRevision|credentialRevision: account\.revision|credentialRevision: expectedRevision \+ 1' web/services/coderouter web/scripts/coderouter

echo
echo "== importEncryptedCredential call sites =="
rg -n -C 4 'importEncryptedCredential' web

Repository: manaflow-ai/cmux

Length of output: 11046


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== vault.ts relevant contents =="
ast-grep outline web/services/coderouter/vault.ts --view expanded || true
sed -n '1,260p' web/services/coderouter/vault.ts

echo
echo "== coderouter schema relevant insert/update migration/statements context =="
rg -n -C 6 'coderouter_accounts|coderouter_credentials|vault_revision|credential_revision' web/db web/migrations web --glob '*.ts' --glob '*.tsx' --glob '*.sql' || true

echo
echo "== deterministic SQL-like behavior for current import predicate cases =="
python3 - <<'PY'
def update_result(input_revision, stored_credential_revision=None, stored_account_revision=None):
    if stored_credential_revision is None:
        credential_inserted = True
        credential_updated = False
    elif stored_credential_revision < input_revision:
        credential_inserted = False
        credential_updated = True
    else:
        credential_inserted = False
        credential_updated = False
    if stored_account_revision is not None and stored_account_revision < input_revision:
        account_updated = True
    else:
        account_updated = False
    return {
        "credential_inserted": credential_inserted,
        "credential_updated": credential_updated,
        "account_updated": account_updated,
        "stored_credential": stored_credential_revision,
        "stored_account": stored_account_revision,
        "input": input_revision,
    }

cases = [
    {"label": "missing credential, stored account newer", "stored_credential": None, "stored_account": 10, "input": 5},
    {"label": "older credential, stored account newer", "stored_credential": 1, "stored_account": 10, "input": 5},
    {"label": "credential newer, stored account newer", "stored_credential": 20, "stored_account": 10, "input": 5},
    {"label": "both newer than import", "stored_credential": 20, "stored_account": 15, "input": 5},
]
for c in cases:
    print(c["label"], json := update_result(c["input"], c["stored_credential"], c["stored_account"]))
PY

Repository: manaflow-ai/cmux

Length of output: 31655


Gate both credential and account updates with one monotonic revision.

importEncryptedCredential reads only coderouterCredentials.credentialRevision for the credential write and coderouterAccounts.vaultRevision for the metadata write. If the imported revision is newer than the stored credential revision but older than the account revision, account metadata such as label and credentialExpiresAt is overwritten with stale data while the credential row remains newer. Make both statements accept one revision from the credential source, and add coverage for missing credentials plus divergent stored revisions.

🤖 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 `@web/services/coderouter/repository.ts` around lines 240 - 261, Update
importEncryptedCredential so both the credential upsert and coderouterAccounts
metadata update are gated by the same monotonic revision from the imported
credential source. Use that revision for the account vaultRevision comparison as
well as coderouterCredentials.credentialRevision, preventing stale metadata
updates when stored revisions diverge. Add coverage for missing credentials and
divergent stored revisions.

Source: Path instructions

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant