santifer/career-ops · warning
Possible duplicates: `).join(', ')} ( — )
Error message
Possible duplicates: ${group.map(e => `#${e.num}`).join(', ')} (${group[0].company} — ${group[0].role}) What it means
A warning from verify-pipeline.mjs's duplicate check (Check 2): two or more tracker entries in applications.md share the same normalized company+role key (normalizeTextKey on both fields). Exact-string duplicates usually mean the same opening was added twice — the AGENTS.md rule forbids new entries when company+role already exists; update the existing row instead.
Solutions
- Run `node dedup-tracker.mjs` to remove exact duplicates automatically.
- Decide which row is canonical and update it via `node set-status.mjs <report#> <State> --note ...`; delete the redundant row.
- For genuinely distinct openings with near-identical titles, add the req/posting ID to the notes of both rows (`req JR-10423`) so merge-tracker.mjs treats them as distinct.
- Prevent recurrence: never hand-edit applications.md; write batch/tracker-additions/*.tsv and run `node merge-tracker.mjs` after each batch.
Example fix
// before: second hand-added row duplicates row 42 | 43 | 2026-09-16 | Acme | Senior Backend Eng | ... | // after: update the existing row instead node set-status.mjs 42 Applied --note "applied 2026-09-16"
Defensive patterns
Strategy: validation
Validate before calling
const key = normalizeTextKey(company) + '::' + normalizeTextKey(role); if (existingKeys.has(key)) updateExistingRow(existingRowNum) else appendRow();
Prevention
- Never hand-edit applications.md; always merge via merge-tracker.mjs.
- Add req/posting IDs to notes for same-title sibling openings.
- Run verify-pipeline.mjs at the end of every batch to catch dupes early.
When it happens
Trigger: Running `node verify-pipeline.mjs` when the tracker contains two rows whose lowercased/normalized company and role strings are identical, e.g. after hand-editing applications.md or merging without merge-tracker.mjs dedup.
Common situations: Manually appending rows instead of writing TSVs to batch/tracker-additions/; two parallel batch workers both adding the same offer; same role posted at different URLs not distinguished by req ID in notes; copy-pasting an existing row to change its status.
Related errors
- Duplicate reports for same company+role
- # : Score has markdown bold
- INVALID_DATE
- ⚠️ Keep # and # : exact-title match but advanced status…
- pending TSVs in tracker-additions/ (not merged)
AI-assisted analysis of santifer/career-ops@e7abd431fc (2026-09-16).
Data as JSON: /api/errors/60f501af545e1fe9.
Report an issue: GitHub.
Appendix: source
Thrown at verify-pipeline.mjs:167
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 e7abd431fc)