santifer/career-ops · warning

Conflicting status recommendations require manual review:

Error message

Conflicting status recommendations require manual review:

What it means

reply-watch groups per-tracker-row status recommendations derived from classified replies. When replies for the same application number recommend different new statuses (a conflict), no automatic tracker update is performed; main prints this header and then one line per conflicting row so the user resolves manually.

Solutions

  1. Review the conflicting choices printed under this header and set the correct status yourself with `node set-status.mjs <#> <State> --note "..."`.
  2. Re-check the individual replies (paste-reply.mjs / classification) — one is likely misclassified; correct it and re-run.
  3. If both replies are genuinely distinct events, apply them in order manually (earliest first) so the tracker reflects the latest state.
  4. Re-run reply-watch afterwards; the conflict disappears once the row's current status matches one of the recommendations.

Example fix

// before
// Conflicting status recommendations require manual review:
//   #42: Responded (1 reply) vs Rejected (1 replies)
// after
node set-status.mjs 42 Rejected --note "ATS rejection supersedes earlier recruiter email"
Defensive patterns

Strategy: validation

Validate before calling

const recs = groupStatusRecommendations(recommendations);
if (recs.conflicts.length > 0) {
  console.warn('resolve conflicts manually with set-status.mjs before relying on automation');
}

Try / catch

if (groupedRecommendations.conflicts.length > 0) {
  for (const c of groupedRecommendations.conflicts) console.warn(`#${c.num}: needs manual review`);
  return; // never auto-update conflicting rows
}

Prevention

When it happens

Trigger: Running reply-watch over reply-candidates.json where one tracker # received multiple replies classified to different canonical states — e.g. one email reads as Responded and another as Rejected for the same #.

Common situations: An interview invite and a form rejection arriving for the same req; a recruiter email and an automated ATS rejection in the same window; misclassification of an ambiguous reply pushing a minority vote.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at reply-watch.mjs:294

    console.log('');

    if (match.application_num !== null && classification.suggestedTrackerUpdate !== 'none' && classification.suggestedTrackerUpdate !== 'Needs Review') {
      const app = apps.find(a => a.num === match.application_num);
      if (app && app.status !== classification.suggestedTrackerUpdate) {
        recommendations.push({
          num: app.num,
          company: app.company,
          role: app.role,
          oldStatus: app.status,
          newStatus: classification.suggestedTrackerUpdate
        });
      }
    }
  });

  const groupedRecommendations = groupStatusRecommendations(recommendations);
  if (groupedRecommendations.conflicts.length > 0) {
    console.warn('Conflicting status recommendations require manual review:');
    for (const conflict of groupedRecommendations.conflicts) {
      const summary = conflict.choices
        .map(choice => `${choice.newStatus} (${choice.count} ${choice.count === 1 ? 'reply' : 'replies'})`)
        .join(' vs ');
      console.warn(`  #${conflict.num}: ${summary} — no automatic update`);
    }
    console.log('');
  }

  if (groupedRecommendations.updates.length > 0) {
    const updates = groupedRecommendations.updates;
    console.log('Suggested status updates to apply:');
    updates.forEach(r => {
      const count = r.count > 1 ? ` (${r.count} replies)` : '';
      console.log(`  #${r.num} ${r.company} (${r.role}): ${r.oldStatus} → ${r.newStatus}${count}`);
    });
    console.log('');

View on GitHub (pinned to aac998c7ed)