santifer/career-ops · warning

⚠️ Skipping ${filename}: cannot tell score from status in c

Error message

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

What it means

Same content-based column identification as the pipe path, applied to tab-separated rows (guard #1427): batch TSVs write (status, score), applications.md stores (score, status), so columns 5-6 are resolved by recognizing the score pattern. When neither cell matches a score (X.X/5 or N/A / — / - sentinels), the row could be swapped, and the merge refuses it loudly.

Source

Thrown at merge-tracker.mjs:700

    };
    const extras = parseTsvExtras(parts, filename);
    if (!extras) return null;
    Object.assign(addition, extras);
  } else {
    // Tab-separated
    parts = content.split('\t');
    if (parts.length < 8) {
      console.warn(`⚠️  Skipping malformed TSV ${filename}: ${parts.length} fields`);
      return null;
    }

    // Column order varies: batch TSVs write (status, score), applications.md is
    // (score, status). Identify each by content — the score cell is recognizable
    // by pattern, a status never is — so a reordered TSV merges correctly and an
    // undecidable row is skipped loudly instead of merging swapped data (#1427).
    const resolved = resolveScoreStatus(parts[4].trim(), parts[5].trim());
    if (!resolved) {
      console.warn(`⚠️  Skipping ${filename}: cannot tell score from status in columns 5–6 ("${parts[4].trim()}" | "${parts[5].trim()}") — refusing to merge a possible column swap`);
      return null;
    }

    addition = {
      num: parseInt(parts[0]),
      date: parts[1],
      company: parts[2],
      role: parts[3],
      status: validateStatus(resolved.status),
      // Write-canonical: strip any markdown bold so the stored score stays
      // unbolded (verify-pipeline rejects bold scores).
      score: resolved.score.replace(/\*\*/g, '').trim(),
      pdf: parts[6],
      report: parts[7],
      notes: parts[8] || '',
    };
    const extras = parseTsvExtras(parts, filename);
    if (!extras) return null;

View on GitHub (pinned to 60398d6549)

Solutions

  1. Set the score cell to X.X/5 or an accepted sentinel (N/A, —, or -)
  2. Ensure exactly one of the two cells holds a canonical status
  3. Remove header/comment lines from batch/tracker-additions files before merging

Example fix

# before (fields 5-6)
042	2026-08-20	Acme	SRE	Interview	???	✅	[042](reports/042-acme-2026-08-20.md)	note

# after
042	2026-08-20	Acme	SRE	Interview	4.2/5	✅	[042](reports/042-acme-2026-08-20.md)	note
Defensive patterns

Strategy: validation

Validate before calling

const SCORE_CELL = /^(\d+(\.\d+)?\/5|n\/a|—|-)$/i;
function tsvScoreStatusResolvable(line) {
  const parts = line.split('\t');
  if (parts.length < 8) return false;
  return SCORE_CELL.test(parts[4].trim()) || SCORE_CELL.test(parts[5].trim());
}

Prevention

When it happens

Trigger: A TSV row whose 5th/6th fields are both non-score text — e.g. 'Interview' and 'good' or two empty cells — makes resolveScoreStatus() return null and the warning quotes both cells before the skip.

Common situations: Backfilled rows with blank or ad-hoc score placeholders ('pending', '??'); a header line accidentally left in the additions file; a status duplicated into both columns during hand-editing.

Related errors


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