// Owned desktop-only integration, applied AFTER native artifact verification. // Native architecture JSON has no per-edge attachment offset. Keep the central // governance ports native; shift only these lifecycle channels on the same sides. export const targetLifecycleOffsets = [ {id:'stage-2', from:'architecture', to:'ui-ux', x:870, shiftedX:850, y1:262.5, y2:457.5}, {id:'stage-3', from:'ui-ux', to:'planning', x:870, shiftedX:850, y1:532.5, y2:727.5}, {id:'stage-6', from:'uat', to:'security', x:330, shiftedX:350, y1:727.5, y2:532.5}, {id:'stage-7', from:'security', to:'releases', x:330, shiftedX:350, y1:457.5, y2:262.5}, ]; export function targetRouting(viewer) { for (const {id, from, to, x, shiftedX, y1, y2} of targetLifecycleOffsets) { const oldPath = `M ${x} ${y1} L ${x} ${y2}`; const newPath = `M ${shiftedX} ${y1} L ${shiftedX} ${y2}`; const oldPoints = `${x},${y1};${x},${y2}`; const newPoints = `${shiftedX},${y1};${shiftedX},${y2}`; // Match the whole native crossover group: its mask must move with the edge. // Trace/export use this actual path; composition checks use its point list. const groups = [...viewer.matchAll(/]*>\s*]*\/>\s*]*\/>\s*<\/g>/g)] .filter(([group]) => group.includes(`data-edge-id="${id}"`)); if (groups.length !== 1) throw new Error(`Target routing drift: ${id} group`); const group = groups[0][0]; for (const attribute of [`data-edge-from="${from}"`, `data-edge-to="${to}"`, `data-composition-points="${oldPoints}"`, 'data-graph-role="automatic-crossover-underlay"']) { if (!group.includes(attribute)) throw new Error(`Target routing drift: ${id} ${attribute}`); } if (group.split(`d="${oldPath}"`).length !== 3) throw new Error(`Target routing drift: ${id} paths`); const shifted = group.replaceAll(`d="${oldPath}"`, `d="${newPath}"`) .replace(`data-composition-points="${oldPoints}"`, `data-composition-points="${newPoints}"`); viewer = viewer.replace(group, shifted); } return viewer; }