santifer/career-ops · warning
Conflicting status recommendations require manual review:
Error message
Conflicting status recommendations require manual review:
What it means
reply-watch.mjs classifies application replies into one suggested tracker status per reply, then groups them per application row (groupStatusRecommendations). When a single tracker number receives contradictory suggestions -- e.g. two replies say Rejected while one says Interview -- the tool refuses to auto-apply any of them, prints this header, and defers to manual review instead of picking a winner silently.
Source
Thrown at reply-watch.mjs:290
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 60398d6549)
Solutions
- Read the per-conflict detail lines (next warning) to see which statuses and reply counts are fighting over each #num.
- Open the raw replies for that row, decide the true current state, and apply it: node set-status.mjs <num> <State> --note 'context'.
- Re-run reply-watch -- once the tracker reflects reality, the conflicting suggestions collapse.
Example fix
# before Conflicting status recommendations require manual review: #42: Interview (1 reply) vs Rejected (2 replies) — no automatic update # after: resolve by hand from the actual emails, most recent wins $ node set-status.mjs 42 Rejected --note 'rejection email 2026-08-19 confirmed'
Defensive patterns
Strategy: validation
Validate before calling
// Pre-group replies per tracker row and surface disagreements before classification
function findConflicts(repliesByNum) {
return [...repliesByNum].filter(([, rs]) =>
new Set(rs.map(r => r.suggestedStatus)).size > 1
).map(([num]) => num);
} Prevention
- Process reply-watch frequently (daily) so contradictory replies arrive in separate, resolvable runs.
- Keep one tracker row per distinct application; split same-company different-role mail into separate rows.
- Resolve conflicts with node set-status.mjs and a dated note instead of editing the table by hand.
When it happens
Trigger: Batch-classifying a mailbox where a company sent both a rejection and a later interview-reschedule; auto-reply noise mixing with human replies; threading ambiguity mapping two different roles onto one tracker row.
Common situations: Processing a week of accumulated mail in one run; companies that reject one application then invite for another; multiple applications to the same company.
Related errors
- #${conflict.num}: ${summary} — no automatic update
- Skipped #${r.num}: status changed from ${r.oldStatus} to ${r
- Skipped #${r.num}: row no longer exists in the tracker
- payload must include at least one of: cv, articleDigest
- payload.cv requires { section, entry }
AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20).
Data as JSON: /api/errors/5b224f8d8adedf69.
Report an issue: GitHub.