// WHY: single source of truth for group -> section, cancelled is reported not placed function groupToSection(group) { switch (group) { case 'completed': return 'completed'; case 'started': return 'inProgress'; case 'unstarted': return 'todo'; case 'backlog': return 'backlog'; default: return null; } } function buildDiscrepancies(planeIssues, todoRows, identifier) { const todoById = new Map(todoRows.map(row => [row.id, row])); const planeById = new Map(); const cancelled = []; for (const issue of planeIssues) { const id = `${identifier}-${issue.sequence_id}`; const group = (issue.state && issue.state.group) || 'backlog'; const section = groupToSection(group); if (!section) { cancelled.push({ id, name: issue.name }); continue; } planeById.set(id, { id, name: issue.name, section, group }); } const mismatches = []; const newIssues = []; const orphans = []; for (const [id, p] of planeById) { const t = todoById.get(id); if (t && t.section !== p.section) mismatches.push({ id, fromSection: t.section, toSection: p.section, name: p.name }); if (!t) newIssues.push({ id, section: p.section, name: p.name }); } for (const [id, t] of todoById) { if (!planeById.has(id)) orphans.push({ id, section: t.section, name: t.name }); } return { mismatches, newIssues, orphans, cancelled, planeById, todoById }; } // WHY: Plane is authoritative, so plane rows always win; decisions only move/add local rows function buildFinalSections(buildData, decisions) { const final = { completed: [], todo: [], inProgress: [], backlog: [] }; const { planeById, todoById, mismatches } = buildData; for (const [id, p] of planeById) { const t = todoById.get(id); const note = t ? t.note : ''; const isMismatch = mismatches.some(m => m.id === id); if (isMismatch && !decisions.moveToPlane.has(id)) { final[t.section].push({ id, name: p.name, note }); } else { final[p.section].push({ id, name: p.name, note }); } } for (const orphan of buildData.orphans) { if (decisions.keepOrphan.has(orphan.id)) { final[orphan.section].push({ id: orphan.id, name: orphan.name, note: '' }); } } return final; } module.exports = { groupToSection, buildDiscrepancies, buildFinalSections };