{"record":{"id":"08c4fe3e1853f06d","repo":"santifer/career-ops","slug":"skipped-r-num-status-changed-from-r-oldstat","errorCode":null,"errorMessage":"Skipped #${r.num}: status changed from ${r.oldStatus} to ${result.conflicts.get(r.num)} during review","messagePattern":"Skipped #(.+?): status changed from (.+?) to (.+?) during review","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"reply-watch.mjs","lineNumber":319,"sourceCode":"    const updates = groupedRecommendations.updates;\n    console.log('Suggested status updates to apply:');\n    updates.forEach(r => {\n      const count = r.count > 1 ? ` (${r.count} replies)` : '';\n      console.log(`  #${r.num} ${r.company} (${r.role}): ${r.oldStatus} → ${r.newStatus}${count}`);\n    });\n    console.log('');\n\n    const answer = await askQuestion(`Apply recommended status updates to ${APPS_FILE}? (y/N): `);\n    if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {\n      const result = await updateTrackerStatuses(updates);\n      for (const r of updates) {\n        const count = r.count > 1 ? ` (${r.count} replies)` : '';\n        if (result.applied.has(r.num)) {\n          console.log(`Updated #${r.num} to ${r.newStatus}${count}`);\n        } else if (result.alreadyCurrent.has(r.num)) {\n          console.log(`No change for #${r.num}: already ${r.newStatus}${count}`);\n        } else if (result.conflicts.has(r.num)) {\n          console.warn(`Skipped #${r.num}: status changed from ${r.oldStatus} to ${result.conflicts.get(r.num)} during review`);\n        } else if (result.missing.has(r.num)) {\n          console.warn(`Skipped #${r.num}: row no longer exists in the tracker`);\n        }\n      }\n      console.log('\\n✅ Tracker review complete');\n\n      // Sync tracker DB if tracker.mjs exists\n      try {\n        const { execSync } = await import('child_process');\n        execSync('node tracker.mjs sync', { stdio: 'ignore' });\n        console.log('Synced database index (applications.db).');\n      } catch (e) {\n        // ignore\n      }\n    } else {\n      console.log('Updates skipped.');\n    }\n  }","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/reply-watch.mjs#L301-L337","documentation":"Optimistic-concurrency guard in reply-watch's apply step: after the user answers 'y', updateTrackerStatuses() re-reads the tracker and only writes rows whose current status still equals the oldStatus captured during classification. A row that changed between review and apply (another terminal, a set-status.mjs or merge-tracker run) lands in result.conflicts and is skipped with this warning instead of clobbering the newer state.","triggerScenarios":"Editing applications.md or running set-status.mjs / merge-tracker.mjs in a second terminal while reply-watch's confirmation prompt is open; long pauses between classification and the y/N answer; scheduled automation firing mid-review.","commonSituations":"Two sessions open on the same repo; cron-scheduled merges overlapping an interactive reply-watch; review prompts left overnight.","solutions":["Re-run node reply-watch.mjs -- it re-reads current statuses and re-recommends against the fresh state.","Or set the intended state directly: node set-status.mjs <num> <State> --note '...'.","Avoid concurrent writers: finish reply-watch before running other tracker-mutating commands."],"exampleFix":"# before\nSkipped #42: status changed from Responded to Interview during review\n# after: re-classify against the fresh tracker\n$ node reply-watch.mjs   # now recommends from Interview onward","handlingStrategy":"retry","validationCode":"// Minimize the review window: capture tracker state and apply promptly\nconst snapshot = readTrackerStatuses(); // classify + prompt user quickly\n// re-validate right before applying\nconst current = readTrackerStatuses();\nconst safe = updates.filter(u => current.get(u.num) === u.oldStatus);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not leave reply-watch's y/N prompt open while running other tracker writes (set-status, merge-tracker, dedup).","When the skip fires, simply re-run reply-watch — it re-recommends from the fresh state.","Use one tracker-mutating session at a time; the file lock serializes writes but not your review time."],"tags":["reply-watch","optimistic-locking","write-conflict","tracker","concurrency"],"backgroundTag":"optimistic-lock-conflict","analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}