santifer/career-ops · warning
Possible duplicates: ${group.map(e => `#${e.num}`).join(', '
Error message
Possible duplicates: ${group.map(e => `#${e.num}`).join(', ')} (${group[0].company} — ${group[0].role}) What it means
verify-pipeline.mjs check 2 groups tracker rows by a normalized company::role key (Unicode-aware since #2393 -- an [a-z0-9] strip previously keyed every Japanese company/role to '' and flagged unrelated rows) and warns when a key has more than one row. 'Possible' because the project rule forbids duplicate company+role entries, yet genuinely distinct requisitions can legitimately share a title; the warning lists the row numbers so a human can judge.
Source
Thrown at verify-pipeline.mjs:162
badStatuses++;
}
}
if (badStatuses === 0) ok('All statuses are canonical');
// --- Check 2: Duplicates ---
const companyRoleMap = new Map();
let dupes = 0;
for (const e of entries) {
// Unicode-aware (#2393): an [a-z0-9] strip erases non-Latin scripts outright,
// so every Japanese company and every Japanese role keyed to '' and unrelated
// rows were reported as "possible duplicates".
const key = normalizeTextKey(e.company) + '::' + normalizeTextKey(e.role);
if (!companyRoleMap.has(key)) companyRoleMap.set(key, []);
companyRoleMap.get(key).push(e);
}
for (const [key, group] of companyRoleMap) {
if (group.length > 1) {
warn(`Possible duplicates: ${group.map(e => `#${e.num}`).join(', ')} (${group[0].company} — ${group[0].role})`);
dupes++;
}
}
if (dupes === 0) ok('No exact duplicates found');
// --- Check 3: Report links ---
// Markdown links resolve relative to the file that contains them, so report
// links must resolve against the tracker's own directory (see #760). For the
// transition we also accept legacy root-relative links: try the tracker dir
// first, then fall back to the repo root before flagging a link broken.
const TRACKER_DIR = dirname(APPS_FILE);
let brokenReports = 0;
for (const e of entries) {
const match = e.report.match(/\]\(([^)]+)\)/);
if (!match) continue;
const link = match[1];
if (!existsSync(join(TRACKER_DIR, link)) && !existsSync(join(CAREER_OPS, link))) {
error(`#${e.num}: Report not found: ${link}`);View on GitHub (pinned to 60398d6549)
Solutions
- If both rows are the same opening, collapse them: node dedup-tracker.mjs.
- If they are distinct requisitions, add the req/job/posting ID to both rows' notes (e.g. 'req JR-10423') so merge-tracker treats them as distinct (#1524) and the pairing is explainable.
- Keep each row's report link pointing at its own report file.
Example fix
# before: same title, indistinguishable rows | 64 | ... | Acme | ML Engineer | ... | req JR-10423 | | 71 | ... | Acme | ML Engineer | ... | | # after: both carry their requisition IDs (distinct openings, warning explainable) | 64 | ... | Acme | ML Engineer | ... | req JR-10423 | | 71 | ... | Acme | ML Engineer | ... | req JR-10501 |
Defensive patterns
Strategy: validation
Validate before calling
// Before writing a TSV, check the tracker for an existing company+role row
function normalizeKey(s) { return s.trim().toLowerCase().replace(/[\s–—-]+/g, ' '); }
const existing = entries.some(e =>
normalizeKey(e.company) === normalizeKey(company) && normalizeKey(e.role) === normalizeKey(role));
if (existing && !reqId) throw new Error('company+role already tracked — update the row or add a req ID'); Prevention
- Respect the repo rule: NEVER create a tracker entry if company+role exists — update the existing row instead.
- Put the req/job/posting ID in the notes column when a company runs genuinely distinct requisitions under one title (#1524).
- Run node dedup-tracker.mjs after merges to collapse accidental duplicates.
When it happens
Trigger: Two TSVs merged for the same company+role (violating the never-duplicate rule); a reposted role re-applied and tracked twice; two distinct requisitions with identical titles but no distinguishing req/job ID in the notes column.
Common situations: Parallel workers independently evaluating the same posting; re-applying to a reposted job months later; leveled variants ('Engineer' vs 'Engineer II') that normalize to the same key.
Related errors
- payload must include at least one of: cv, articleDigest
- payload.cv requires { section, entry }
- cv.md not found — cannot add to a CV that does not exist
- payload.articleDigest requires { entry }
- INVALID_DATE
AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20).
Data as JSON: /api/errors/60f501af545e1fe9.
Report an issue: GitHub.