Skip to content

fix(dedup): distinct non-Latin companies merged into one, deleting a row - #2587

Merged
santifer merged 1 commit into
career-ops-hq:mainfrom
darkpandawarrior:fix/dedup-unicode-company
Aug 7, 2026
Merged

santifer merged 1 commit into
career-ops-hq:mainfrom
darkpandawarrior:fix/dedup-unicode-company

Conversation

@darkpandawarrior

@darkpandawarrior darkpandawarrior commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

The bug

dedup-tracker.mjs kept its own local normalizeCompany() using the pre-#2429 ASCII fold:

.replace(/[^a-z0-9 ]/g, '')

Any company name written entirely in a non-Latin script folds to '' under that. Two different employers therefore share a grouping key, pass roleMatch() when their titles agree — common, since "Engineer" / "Backend Engineer" recur everywhere — and the lower-scored row is deleted.

| 1 | アクメ株式会社       | Engineer | 4.2/5 |
| 2 | グロベックス合同会社 | Engineer | 3.0/5 |
before: 🗑️  Remove #2 (グロベックス合同会社 — Engineer, 3.0/5) → kept #1 (4.2/5)
after:  📊 0 duplicates removed

The run prints its normal success output. Nothing warns. Only the .bak preserves the deleted application.

This is the third instance of the same class here

That's the part I think matters more than the fix:

So the fix is to delete the local copy and import the shared one, rather than patch the regex — that's what stops a fourth instance.

Why this is safe

The value is only ever an in-memory grouping key (groups Map, dedup-tracker.mjs:328). It is never persisted, never written to a row, and never compared against a key produced by another script. Adopting the shared normalization changes no stored data.

I checked the Latin path explicitly rather than assuming: Acme (Inc.) and Acme Inc still merge, and Globex / Initech still stay apart.

Tests

A sibling of the existing #2393 blind-via block, placed next to it, on the ordinary company path:

  • two distinct non-Latin employers stay apart (fails before this change)
  • punctuation variants of one Latin employer still merge
  • two distinct Latin employers still stay apart

The last two are the regression guards for the normalization swap.

node test-all.mjs → 0 failed.

Filed directly per CONTRIBUTING.md (bug fix, no issue needed).

Summary by CodeRabbit

  • Bug Fixes

    • Improved duplicate detection for company names using Unicode-aware normalization.
    • Distinct non-Latin company names, including Japanese names, are now kept separate.
    • Equivalent Latin company names with punctuation or spacing differences continue to be combined while retaining the higher-scoring entry.
    • Distinct Latin companies are no longer incorrectly grouped together.
  • Tests

    • Added regression coverage for multilingual names and company-name variations.

dedup-tracker.mjs kept its own local normalizeCompany() using the pre-career-ops-hq#2429
ASCII fold (`[^a-z0-9 ]`). Every company name written entirely in a non-Latin
script folds to the empty string under that, so two DIFFERENT employers share a
grouping key, pass the role match when their titles agree (common — "Engineer",
"Backend Engineer"), and the lower-scored row is deleted.

Silent: the run prints its normal success output. Only the .bak preserves the
lost application.

  | 1 | アクメ株式会社     | Engineer | 4.2/5 |
  | 2 | グロベックス合同会社 | Engineer | 3.0/5 |
  -> before: "Remove career-ops-hq#2 (グロベックス合同会社) -> kept career-ops-hq#1"; row 2 gone
  -> after:  0 duplicates removed

This is the third instance of one defect class in this area. f589ce8 (career-ops-hq#2429)
made tracker-utils.mjs Unicode-aware, and merge-tracker.mjs and set-status.mjs
inherited it because they import the shared helper; dedup-tracker.mjs had forked
its own copy and was missed. fe4561b then fixed this same file's blind-via key
(career-ops-hq#2397) while the ordinary company path twelve lines below kept the old fold —
the comment above that code even describes the bug for the Via key.

Fix: delete the local copy and import the shared normalizeCompany, so the two
cannot drift again. The key is only ever an in-memory grouping key, never
persisted or compared across scripts, so adopting the shared normalization
changes no stored data.

The comment describing the old behaviour is corrected in the same change.

Tests: a sibling of the existing career-ops-hq#2393 blind-via block, on the ordinary company
path. Two distinct non-Latin employers stay apart; punctuation variants of one
Latin employer ("Acme (Inc.)" / "Acme Inc") still merge; two distinct Latin
employers still stay apart. Verified the first fails before this change.

test-all: 0 failed.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a47a6864-28ac-40e6-bdc1-93d419b2ecd9

📥 Commits

Reviewing files that changed from the base of the PR and between ccb7b0d and 63c554f.

📒 Files selected for processing (2)
  • dedup-tracker.mjs
  • test-all.mjs

📝 Walkthrough

Walkthrough

dedup-tracker.mjs now uses shared Unicode-aware company normalization. Its grouping documentation reflects this behavior. test-all.mjs adds end-to-end coverage for non-Latin company separation and Latin company variant deduplication.

Changes

Unicode company deduplication

Layer / File(s) Summary
Shared normalization and grouping contract
dedup-tracker.mjs
The tracker imports normalizeCompany from tracker-utils.mjs and documents Unicode-aware normalization for company and Via grouping.
Deduplication regression coverage
test-all.mjs
The end-to-end test verifies separate non-Latin and distinct Latin companies, and merges equivalent Latin variants while retaining the higher-scored row.

Estimated code review effort: 3 (Moderate) | ~15–30 minutes

Possibly related PRs

Suggested labels: p1

Suggested reviewers: santifer, scott-emberson, luochen211

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main fix: preventing distinct non-Latin companies from merging and deleting a row.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@santifer
santifer merged commit 130e3fa into career-ops-hq:main Aug 7, 2026
14 checks passed
@santifer

santifer commented Aug 7, 2026

Copy link
Copy Markdown
Member

Merged, @darkpandawarrior, and this is the right shape for it. #2429 made tracker-utils's normalizeCompany Unicode-aware and merge-tracker/set-status inherited it; dedup-tracker kept a private copy, so it stayed on the old [^a-z0-9] fold. Deleting the copy rather than patching it is what stops the two from drifting apart a third time.

Reproduced here before merging, with both controls: the local function on main folds アクメ株式会社 and グロベックス合同会社 to the same empty key (so one of two genuinely separate applications got deleted), while the shared one keeps them apart and still merges Acme (Inc.) with Acme Inc and still keeps Acme and Globex separate. Main 3070/0 after the merge.

Worth naming: the blind-via channel path right above it was fixed for this exact class in #2393, and the ordinary path underneath it stayed broken. A fix that lands next to its sibling and doesn't get applied to it is a pattern I want to catch earlier 🚀

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.

2 participants