@@ -794,12 +794,16 @@ def write_source_url_if_unchanged(path: Path, record: dict[str, Any], url: str)
794794 current = json .loads (raw )
795795 except (OSError , UnicodeDecodeError , json .JSONDecodeError ):
796796 return False
797- if not isinstance (current , dict ) or content_hash (current ) != content_hash (record ):
797+ if not isinstance (current , dict ):
798+ return False
799+ source_urls = current .get ("source_urls" )
800+ if isinstance (source_urls , list ) and url in source_urls :
801+ return True
802+ if content_hash (current ) != content_hash (record ):
798803 return False
799804 updated = add_source_url_text (raw , url )
800805 if updated is None :
801- source_urls = current .get ("source_urls" )
802- return isinstance (source_urls , list ) and url in source_urls
806+ return False
803807 tmp = path .with_suffix (path .suffix + ".tmp" )
804808 try :
805809 tmp .write_bytes (updated .encode ("utf-8" ))
@@ -1165,7 +1169,15 @@ def fetch(url: str) -> tuple[int | None, str, str]:
11651169 digest = content_hash (record )
11661170 cached = cache .get (rel )
11671171 if cached and cached .get ("hash" ) == digest and cached .get ("decision" ) in DECISIONS :
1168- result .rows .append (_row_from_cache (cached ))
1172+ row = _row_from_cache (cached )
1173+ if not dry_run and cached ["decision" ] == CONFIRM :
1174+ url = cached .get ("proposed_url" )
1175+ if not isinstance (url , str ) or not write_source_url_if_unchanged (
1176+ repo / rel , record , url
1177+ ):
1178+ row ["decision" ] = "write-failed"
1179+ row ["reason" ] = "source-urls-write-failed-or-record-changed"
1180+ result .rows .append (row )
11691181 result .cached += 1
11701182 continue
11711183 outcome = evaluate_record (record , fetcher , fetch )
@@ -1190,8 +1202,8 @@ def fetch(url: str) -> tuple[int | None, str, str]:
11901202 "source-urls-write-failed-or-record-changed" ,
11911203 outcome .suffix_only ,
11921204 )
1193- # Transient blocks stay uncached so a later run can retry them. A real
1194- # CONFIRM is cached only after the guarded source_urls write succeeded .
1205+ # Transient blocks stay uncached so a later run can retry them. Cached
1206+ # dry-run CONFIRMs are written through the same guard on an apply run .
11951207 if outcome .liveness not in RETRY_LIVENESS and outcome .reason != "network-error" :
11961208 append_cache (
11971209 cache_entry (rel_path = rel , record = record , result = outcome , ts = _now_iso ()),
0 commit comments