@@ -51,7 +51,8 @@ public async Task ConvertPendingAsync_PreservesSuccessfulOutputAndLeavesSharedTe
5151 Assert . Equal ( 1 , result . Converted ) ;
5252 Assert . Equal ( 1 , result . Failures ) ;
5353 Assert . Equal ( "newer.txt" , Path . GetFileName ( attemptedInputs [ 0 ] ) ) ;
54- Assert . Equal ( "converted" , File . ReadAllText ( Path . ChangeExtension ( newerInput , ".md" ) ) ) ;
54+ // Generated content keeps the converter payload before the durable source marker appended during publication.
55+ Assert . StartsWith ( "converted" , File . ReadAllText ( Path . ChangeExtension ( newerInput , ".md" ) ) ) ;
5556 Assert . False ( File . Exists ( Path . ChangeExtension ( olderInput , ".md" ) ) ) ;
5657 Assert . True ( File . Exists ( existingTemporaryOutput ) ) ;
5758 // Shared temporary names do not establish ownership, so both existing and newly appearing files survive.
@@ -83,9 +84,11 @@ public async Task ConvertPendingAsync_AppendsOriginalPdfEmbedAfterExtractedText(
8384 new DocumentConversionLog ( context . ConfigurationDirectory ) ,
8485 new DocumentConversionOptions ( ) ) . ConvertPendingAsync ( context ) ;
8586
86- // The embed persists the PDF-to-Markdown relationship for NoteManager's media viewer.
87+ // The marker suppresses duplicate rescans after rename while the embed preserves the established PDF viewer relationship .
8788 Assert . Equal (
88- $ "Extracted PDF text{ Environment . NewLine } { Environment . NewLine } ![[report.pdf]]",
89+ $ "Extracted PDF text{ Environment . NewLine } { Environment . NewLine } "
90+ + "<!-- notemanager-conversion-source: report.pdf -->"
91+ + $ "{ Environment . NewLine } { Environment . NewLine } ![[report.pdf]]",
8992 File . ReadAllText ( Path . ChangeExtension ( sourcePath , ".md" ) ) ) ;
9093 Assert . True ( File . Exists ( sourcePath ) ) ;
9194 Assert . Equal ( 1 , result . Converted ) ;
@@ -217,6 +220,49 @@ public void FindPendingDocuments_ExcludesTildePrefixedWordTemporaryFiles()
217220 Assert . Equal ( document , pending [ 0 ] . InputPath ) ;
218221 }
219222
223+ [ Theory ]
224+ [ InlineData ( ".docx" ) ]
225+ [ InlineData ( ".pdf" ) ]
226+ public async Task ConvertPendingAsync_ConvertedNoteRenamed_DoesNotRegenerateOriginalOutput (
227+ string extension )
228+ {
229+ using var folder = new TemporaryFolder ( ) ;
230+ var sourcePath = Path . Combine ( folder . Path , $ "invoice{ extension } ") ;
231+ var originalOutputPath = Path . ChangeExtension ( sourcePath , ".md" ) ;
232+ var renamedOutputPath = Path . Combine ( folder . Path , "paid.md" ) ;
233+ File . WriteAllText ( sourcePath , "source" ) ;
234+ var conversionCount = 0 ;
235+ var runner = new StubRunner ( ( _ , output ) =>
236+ {
237+ conversionCount ++ ;
238+ File . WriteAllText ( output , "converted" ) ;
239+ return SuccessResult ( ) ;
240+ } ) ;
241+ var context = CreateContext ( folder . Path ) ;
242+ var service = new DocumentConversionService (
243+ runner ,
244+ new DocumentConversionLog ( context . ConfigurationDirectory ) ,
245+ new DocumentConversionOptions ( ) ) ;
246+
247+ await service . ConvertPendingAsync ( context ) ;
248+ File . Move ( originalOutputPath , renamedOutputPath ) ;
249+ var rescan = await service . ConvertPendingAsync ( context ) ;
250+
251+ // The marker moves with paid.md, so the surviving source no longer looks pending at invoice.md.
252+ Assert . Equal ( 1 , conversionCount ) ;
253+ Assert . Equal ( 0 , rescan . Total ) ;
254+ Assert . False ( File . Exists ( originalOutputPath ) ) ;
255+ Assert . True ( File . Exists ( sourcePath ) ) ;
256+ Assert . Contains (
257+ $ "notemanager-conversion-source: { Uri . EscapeDataString ( Path . GetFileName ( sourcePath ) ) } ",
258+ File . ReadAllText ( renamedOutputPath ) ) ;
259+ if ( extension . Equals ( ".pdf" , StringComparison . OrdinalIgnoreCase ) )
260+ {
261+ // PDF output keeps its authored embed while the marker provides the scan-stable identity.
262+ Assert . Contains ( "![[invoice.pdf]]" , File . ReadAllText ( renamedOutputPath ) ) ;
263+ }
264+ }
265+
220266 [ Fact ]
221267 public async Task ConvertPendingAsync_StagesUtf8TextWithByteOrderMark ( )
222268 {
@@ -392,7 +438,8 @@ public async Task ConvertPendingAsync_InvalidJsonResult_FailsOnlyItsDocumentAndC
392438 Assert . Equal ( 1 , result . Converted ) ;
393439 Assert . Equal ( 1 , result . Failures ) ;
394440 Assert . False ( File . Exists ( Path . ChangeExtension ( invalidInput , ".md" ) ) ) ;
395- Assert . Equal ( "converted" , File . ReadAllText ( Path . ChangeExtension ( validInput , ".md" ) ) ) ;
441+ // A later valid output retains its conversion result before the source marker is added.
442+ Assert . StartsWith ( "converted" , File . ReadAllText ( Path . ChangeExtension ( validInput , ".md" ) ) ) ;
396443 Assert . Empty ( Directory . EnumerateDirectories ( folder . Path , ".notemanager-doc2md-*" ) ) ;
397444 Assert . Equal ( 1 , refreshCount ) ;
398445 Assert . Contains ( statuses , status => status . Contains (
0 commit comments