{"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":338,"sourceCode":"          const u = updatesByNum.get(num);\n          if (u) {\n            const line = `${num}\\t${todayStr}\\t${u.oldStatus}\\t${u.newStatus}\\treply-watch\\t\\n`;\n            try {\n              fs.appendFileSync(statusLogFile, line, 'utf-8');\n            } catch (err) {\n              console.warn(`Warning: failed to append to status-log.tsv for #${num}: ${err.message}`);\n            }\n          }\n        }\n      });\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":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/reply-watch.mjs#L320-L356","documentation":"When applying the agreed status updates, reply-watch re-reads the tracker under a shared lock and detects rows whose status changed since the recommendations were computed. If row #N's current status no longer equals r.oldStatus, applying would overwrite a concurrent edit, so the update is skipped and this warning reports the observed intermediate change.","triggerScenarios":"Another process (set-status.mjs, tracker merge, a parallel session) modified the row between the recommendation phase and the apply phase; user hand-edited applications.md mid-run; a stale tracker file cached at read time.","commonSituations":"Running reply-watch while set-status.mjs runs from another terminal; reviewing an old reply-candidates.json against a tracker that has since advanced; two reply-watch runs overlapping.","solutions":["Re-run reply-watch: it will recompute recommendations against the current tracker state and either apply or surface a fresh conflict.","If the concurrent change is the correct final state, do nothing — the skip prevented a clobbering overwrite.","If the concurrent change was accidental, restore the intended status with `node set-status.mjs <#> <State>` and re-run.","Avoid editing the tracker (manually or via other tools) while reply-watch is applying updates."],"exampleFix":"// before\n// Skipped #42: status changed from Responded to Interview during review\n// after (recompute against current tracker)\nnode reply-watch.mjs   # fresh run applies against Interview-era state","handlingStrategy":"retry","validationCode":"const current = getRowStatus(tracker, r.num);\nif (current !== r.oldStatus) console.warn(`#${r.num} changed concurrently (${current}) — skipping`);","typeGuard":null,"tryCatchPattern":"} 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  // re-run reply-watch to recompute against the new state\n}","preventionTips":["Don't run set-status.mjs or edit the tracker while reply-watch is applying updates.","Re-run reply-watch after any skip-during-review warning; it recomputes fresh recommendations.","Keep the tracker as the single writer via set-status.mjs to minimize concurrent mutations."],"tags":["tracker","concurrency","race-condition","reply-watch"],"backgroundTag":"invalid-state-transition","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}