santifer/career-ops · warning

⚠️ Skipping : cannot tell score from status in columns 5–6…

Error message

⚠️  Skipping ${filename}: cannot tell score from status in columns 5–6 ("${parts[4]}" | "${parts[5]}") — refusing to merge a possible column swap

What it means

In the headerless positional forms, columns 5 and 6 hold score and status — but the two orders in circulation disagree (batch TSVs write status/score, applications.md writes score/status), so resolveScoreStatus identifies each cell by content: a score matches the X.X/5 (or sentinel) pattern, a status never does. When neither cell is recognizably a score (e.g. both look like statuses, or neither does), the pair is undecidable and the row is refused rather than merged with swapped columns.

Solutions

  1. Switch this row to the headed TSV form (labels row + one data row) — the header disambiguates score/status by name, so a '—'/'—' discarded row merges correctly.
  2. If staying headerless, write a recognizable score ('4.2/5') or score sentinel 'N/A' in one of columns 5-6 so the pair becomes decidable.
  3. Verify both cells actually belong in columns 5-6 — an earlier missing cell shifts everything left and can produce this warning.
  4. Check the status values against templates/states.yml; a typo'd status that matches no pattern contributes to the ambiguity.

Example fix

// before (headerless: '—' in both cells — undecidable)
42	2026-09-16	Acme	Backend	—	—	❌	[042](reports/042.md)

// after (headed form resolves by name)
score	status
—	Discarded
Defensive patterns

Strategy: validation

Validate before calling

const scoreLike = (v) => /^\d\.\d\/5$/.test(String(v ?? '').replace(/\*\*/g, '').trim()) || ['N/A','—','-'].includes(String(v ?? '').trim());
const isStatusLike = (v) => /^[A-Za-z][A-Za-z ]*$/.test(String(v ?? '').trim());
if (scoreLike(a) === scoreLike(b)) {
  throw new Error(`${file}: cannot tell score from status in columns 5-6 — use the headed form`);
}

Prevention

When it happens

Trigger: parseTsvContent (merge-tracker.mjs:953-956 for pipe rows): resolveScoreStatus(parts[4], parts[5]) returns null — neither cell matches the score pattern nor a sentinel. Classic case: a discarded, never-scored row carrying '—' in both cells, or two status-looking strings ('Applied' | 'Evaluated').

Common situations: A discarded row used the '—' sentinel for both score and status (the known undecidable case — use the headed form, which has no such case); the writer emitted two status values; a non-standard score format ('four point two', '85%') that matches neither pattern; columns 5-6 were shifted by an earlier dropped cell.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/1d0adca61a467541. Report an issue: GitHub.

Appendix: source

Thrown at merge-tracker.mjs:955

  let parts;
  let addition;

  // Detect pipe-delimited (markdown table row)
  if (content.startsWith('|')) {
    parts = content.split('|').map(s => s.trim());
    if (parts[0] === '') parts.shift();
    if (parts[parts.length - 1] === '') parts.pop();
    if (parts.length < 8) {
      console.warn(`⚠️  Skipping malformed pipe-delimited ${filename}: ${parts.length} fields`);
      return null;
    }
    // Format: num | date | company | role | score | status | pdf | report | notes [| location]
    // Identify score vs status by content, not position, so a swapped row can't
    // merge silently (#1427).
    const resolved = resolveScoreStatus(parts[4], parts[5]);
    if (!resolved) {
      console.warn(`⚠️  Skipping ${filename}: cannot tell score from status in columns 5–6 ("${parts[4]}" | "${parts[5]}") — refusing to merge a possible column swap`);
      return null;
    }
    addition = {
      num: parseTrackerNum(parts[0]),
      date: parts[1],
      company: parts[2],
      role: parts[3],
      // Write-canonical: the tracker stores scores unbolded (verify-pipeline
      // rejects bold scores), so strip any markdown bold from the incoming cell.
      score: resolved.score.replace(/\*\*/g, '').trim(),
      status: validateStatus(resolved.status),
      pdf: parts[6],
      report: parts[7],
      notes: parts[8] || '',
    };
    const extras = parseTsvExtras(parts, filename);
    if (!extras) return null;
    Object.assign(addition, extras);

View on GitHub (pinned to aac998c7ed)