santifer/career-ops · warning
Duplicate reports for same company+role
Error message
Duplicate reports for same company+role: ${group.join(', ')} What it means
A warning from verify-pipeline.mjs (Check 9) that two or more report files in reports/ normalize to the same company+role key (slugified company + role). Unlike #593 this checks report filenames, not tracker rows; duplicates suggest the same offer was evaluated multiple times, wasting report numbers and possibly creating divergent evaluations.
Solutions
- Decide which report is canonical; update its tracker row via set-status.mjs and mark the duplicate row Discarded.
- Before re-evaluating a known company+role, check the tracker/report first and only refresh the existing report's content.
- Run `node detect-reposts.mjs` to distinguish a genuine repost from a duplicate evaluation before merging.
- Rely on merge-tracker.mjs's URL-first dedup so a re-evaluation updates the existing row rather than adding a new one.
Example fix
// before: full re-evaluation of a known offer paste JD → new report 051-acme-senior-backend.md // after: refresh the existing report, keep one per company+role node set-status.mjs 42 Evaluated --note "re-verified posting 2026-09-10"
Defensive patterns
Strategy: validation
Validate before calling
const key = normalizeKey(companySlug) + '::' + normalizeKey(role); if (reportsByRole.has(key)) refreshExistingReport(reportsByRole.get(key)[0]) else writeNewReport();
Prevention
- Check the tracker for an existing company+role report before evaluating a JD.
- Run detect-reposts.mjs to decide repost vs duplicate.
- Let merge-tracker.mjs's URL-first dedup keep one row per posting.
When it happens
Trigger: Running `node verify-pipeline.mjs` when reports/ holds e.g. `042-acme-senior-backend-2026-08-01.md` and `051-acme-senior-backend-2026-09-10.md` — same slugified company::role key — typically from re-evaluating an offer instead of updating the original report, or from slug variations that normalize identically.
Common situations: Re-pasting the same JD weeks later and generating a fresh report instead of reusing #42; different batch workers evaluating the same URL concurrently without URL-based dedup; reposts detected by detect-reposts.mjs that were then re-evaluated wholesale.
Related errors
- Possible duplicates: `).join(', ')} ( — )
- Could not release report reservation
- ⚠️ Keep # and # : exact-title match but advanced status…
- ⚠️
- Orphan report — no tracker row references #
AI-assisted analysis of santifer/career-ops@e7abd431fc (2026-09-16).
Data as JSON: /api/errors/6030c195b76c1a3b.
Report an issue: GitHub.
Appendix: source
Thrown at verify-pipeline.mjs:317
let dupReports = 0;
const reportsByRole = new Map();
for (const name of reportFiles) {
const companySlug = name.match(REPORT_FILE_RE)[2];
let role = null;
try {
role = extractRole(readFileSync(join(REPORTS_DIR, name), 'utf-8'));
} catch {
// Unreadable report — the orphan check below still sees it.
}
if (!role) continue;
const key = normalizeKey(companySlug) + '::' + normalizeKey(role);
if (!reportsByRole.has(key)) reportsByRole.set(key, []);
reportsByRole.get(key).push(name);
}
for (const group of reportsByRole.values()) {
if (group.length > 1) {
warn(`Duplicate reports for same company+role: ${group.join(', ')}`);
dupReports++;
}
}
if (dupReports === 0) ok('No duplicate reports for the same company+role');
// --- Check 10: Orphan reports with no tracker row (#1425) ---
// Every reports/NNN-*.md should be referenced by a tracker row — by the
// [NNN] link text(s), the NNN- prefix of the linked filename(s), or (only when
// the cell carries no markdown link at all) the row's own number.
//
// The row's own number is a LAST RESORT, not a standing signal. Tracker row
// numbers and report numbers are independent counters that diverge in normal
// operation — #1733 established that a reserved report number is discarded
// when it is <= the tracker max, permanently desynchronising the two. Treating
// a row's number as a reference whenever it merely coexists with an unrelated
// link therefore masks real orphans: a row numbered 950 that legitimately
// links to report 955 also silently "references" an unrelated orphaned
// report 950. Only when the cell has no link is the row number the only signalView on GitHub (pinned to e7abd431fc)