@@ -282,6 +282,41 @@ class IssueSyncer extends Base {
282282 } ;
283283 }
284284
285+ // Phase 2: Reconcile locations for all known issues, including those not in the delta.
286+ // This is critical for archiving stale issues that haven't been updated but whose
287+ // milestone now requires them to be moved.
288+ logger . info ( 'Reconciling local file locations for all known issues...' ) ;
289+ for ( const issueNumber in newMetadata . issues ) {
290+ const issueData = newMetadata . issues [ issueNumber ] ;
291+
292+ // Re-determine the correct path based on the issue's known state.
293+ const correctPath = this . #getIssuePath( {
294+ number : issueNumber ,
295+ state : issueData . state ,
296+ labels : [ ] , // Labels for dropping are handled on pull, not needed here.
297+ milestone : issueData . milestone ? { title : issueData . milestone } : null ,
298+ closedAt : issueData . closedAt
299+ } ) ;
300+
301+ if ( correctPath && issueData . path !== correctPath ) {
302+ logger . info ( `Mismatched path for #${ issueNumber } . Expected: ${ correctPath } , Found: ${ issueData . path } ` ) ;
303+ try {
304+ await fs . mkdir ( path . dirname ( correctPath ) , { recursive : true } ) ;
305+ await fs . rename ( issueData . path , correctPath ) ;
306+ logger . info ( `📦 Moved #${ issueNumber } : ${ issueData . path } → ${ correctPath } ` ) ;
307+
308+ // Update the metadata with the new path
309+ newMetadata . issues [ issueNumber ] . path = correctPath ;
310+
311+ // This is a local-only operation, so only update the 'moved' statistic.
312+ stats . pulled . moved = ( stats . pulled . moved || 0 ) + 1 ;
313+
314+ } catch ( e ) {
315+ logger . warn ( `Could not move #${ issueNumber } during reconciliation. Error: ${ e . message } ` ) ;
316+ }
317+ }
318+ }
319+
285320 return { newMetadata, stats } ;
286321 }
287322
0 commit comments