santifer/career-ops · warning

⚠️ Tracker #${addition.num} already used; assigning #${entr

Error message

⚠️  Tracker #${addition.num} already used; assigning #${entryNum} to ${addition.company} — ${addition.role}. Report link remains ${addition.report}.

What it means

merge-tracker.mjs consumes per-evaluation TSVs from batch/tracker-additions/, each carrying a reserved entry number. When that number is already occupied in applications.md (usedNumbers), the merge refuses to reuse it, assigns the next free ID above the current maximum, and warns loudly because the report file still carries the old number -- a deliberate report/tracker drift signal (#1704, #1733). Out-of-order finishes from parallel workers are the expected cause; the renumber only happens on a real collision.

Source

Thrown at merge-tracker.mjs:1265

      console.error(
        `❌ ${file}: could not locate tracker row #${duplicate.num} ` +
        `(${duplicate.company} — ${duplicate.role}) to update; this evaluation was NOT merged.`,
      );
      failedAdditions.push(file);
    }
  } else {
    // New entry - preserve the TSV's reserved ID whenever it is actually
    // free. Parallel workers can finish out of order, so a valid reservation
    // may be lower than the current tracker maximum (#1733). Renumber only on
    // a real collision, using the next free ID above the current maximum and
    // warning loudly so report/tracker drift is visible (#1704).
    let entryNum;
    if (!usedNumbers.has(addition.num)) {
      entryNum = addition.num;
    } else {
      entryNum = maxNum + 1;
      while (usedNumbers.has(entryNum)) entryNum++;
      console.warn(
        `⚠️  Tracker #${addition.num} already used; assigning #${entryNum} to ` +
        `${addition.company} — ${addition.role}. Report link remains ${addition.report}.`,
      );
    }
    usedNumbers.add(entryNum);
    if (entryNum > maxNum) maxNum = entryNum;

    const pdf = reportNum && pdfIndex.has(String(reportNum)) ? '✅' : addition.pdf;
    const newLine = buildRow({
      num: entryNum, date: addition.date, company: addition.company, role: addition.role,
      via: addition.via || '—',
      location: addition.location || '—',
      score: addition.score, status: addition.status, pdf,
      report: addition.report, notes: addition.notes,
      // Write the key on the way in. Backfill is the one-time EXPAND phase for
      // rows that predate the column; a row added today must carry its own URL
      // or Pass 0 can never match it and dedup stays fuzzy-only for new work.
      url: addition.url || '',

View on GitHub (pinned to 60398d6549)

Solutions

  1. Run node reserve-report-num.mjs --count N immediately before spawning workers and give each worker its own slot; merge soon after so sentinels are still alive.
  2. If the warning already fired, trust the renumbered tracker row and fix the stale reference: correct the report link or note on the row, or rename the report file to match the new number if nothing else references it.
  3. Inspect batch/tracker-additions/ for duplicate num columns and correct the stale TSV before re-running node merge-tracker.mjs.
  4. Release ranges you did not use with node reserve-report-num.mjs --release NNN-MMM so numbers return to the pool.

Example fix

# before: two TSVs both claim #064 (second one triggers the warning)
064  2026-08-20  Acme  Senior ML Engineer  Evaluated  4.2/5  ...  [064](reports/064-acme-2026-08-20.md)  ...
# after: reserve first, then write each TSV with its own slot
$ node reserve-report-num.mjs --count 2   # -> 064-065
064  2026-08-20  Acme   Senior ML Engineer  Evaluated  4.2/5  ...
065  2026-08-20  Globex  Data Platform Lead  Evaluated  4.0/5  ...
Defensive patterns

Strategy: validation

Validate before calling

// Before merging, check every TSV's num against the tracker's used numbers
import { readFileSync, readdirSync } from 'node:fs';
const used = new Set(
  [...readFileSync('data/applications.md', 'utf-8').matchAll(/^\|\s*(\d+)\s*\|/gm)].map(m => Number(m[1]))
);
for (const f of readdirSync('batch/tracker-additions').filter(f => f.endsWith('.tsv'))) {
  const num = Number(readFileSync(`batch/tracker-additions/${f}`, 'utf-8').split('\t')[0]);
  if (used.has(num)) console.error(`${f}: num #${num} already used — reserve a free slot first`);
}

Prevention

When it happens

Trigger: Parallel batch evaluators whose reservations expired or were reused before merge; two TSVs in tracker-additions/ claiming the same num column; merging TSVs from an older session after the tracker advanced past their numbers; hand-written TSVs with a guessed num.

Common situations: Fan-out workers reserving via reserve-report-num.mjs more than 4h before merging (sentinels GC'd, slots freed and re-claimed); copy-pasting a previous TSV as a template and forgetting to bump the number; merging a stale batch directory alongside a fresh one.

Related errors


AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20). Data as JSON: /api/errors/24db861e84833be0. Report an issue: GitHub.