@@ -83,6 +83,100 @@ def _read_allowlist(root: Path) -> Set[str]:
8383 }
8484
8585
86+ def _load_json_object (path : Path ) -> dict [str , Any ] | None :
87+ if not path .is_file ():
88+ return None
89+ try :
90+ payload = json .loads (path .read_text (encoding = "utf-8" ))
91+ except (OSError , UnicodeDecodeError , json .JSONDecodeError ):
92+ return None
93+ return payload if isinstance (payload , dict ) else None
94+
95+
96+ def _publication_status_contract (root : Path ) -> dict [str , Any ]:
97+ """Compare redundant public publication flags with the authoritative manifest."""
98+
99+ manifest_path = root / "data/latest/run_manifest.json"
100+ verdict_path = root / "data/latest/system_verdict.json"
101+ manifest = _load_json_object (manifest_path )
102+ verdict = _load_json_object (verdict_path )
103+ if manifest is None or verdict is None :
104+ return {"checked" : False , "reason" : "status contracts are not both present" }
105+
106+ top_status = verdict .get ("pipeline_status" )
107+ run = verdict .get ("run" )
108+ run_status = run .get ("pipeline_status" ) if isinstance (run , dict ) else None
109+ status_objects = {
110+ "system_verdict.pipeline_status" : top_status ,
111+ "system_verdict.run.pipeline_status" : run_status ,
112+ }
113+ status_objects = {
114+ key : value for key , value in status_objects .items () if isinstance (value , dict )
115+ }
116+ if not status_objects :
117+ return {"checked" : False , "reason" : "system verdict has no public pipeline status" }
118+
119+ expected = bool (
120+ manifest .get ("validation_ok" )
121+ and manifest .get ("publish_ready" )
122+ and manifest .get ("published" )
123+ )
124+ source_lineage = verdict .get ("source_lineage" )
125+ readiness = (
126+ source_lineage .get ("ai_publish_readiness" )
127+ if isinstance (source_lineage , dict )
128+ else None
129+ )
130+ if isinstance (readiness , dict ):
131+ if "ok" in readiness :
132+ expected = expected and bool (readiness .get ("ok" ))
133+ if "published" in readiness :
134+ expected = expected and bool (readiness .get ("published" ))
135+
136+ actual = {
137+ key : value .get ("publish_ok" ) for key , value in status_objects .items ()
138+ }
139+ return {
140+ "checked" : True ,
141+ "expected_publish_ok" : expected ,
142+ "actual_publish_ok" : actual ,
143+ "manifest_path" : str (manifest_path ),
144+ "verdict_path" : str (verdict_path ),
145+ "verdict" : verdict ,
146+ "status_objects" : status_objects ,
147+ }
148+
149+
150+ def reconcile_public_status_contract (root : Path ) -> dict [str , Any ]:
151+ """Synchronize public publish flags without changing strategy-effectiveness flags."""
152+
153+ root = root .resolve ()
154+ contract = _publication_status_contract (root )
155+ if not contract .get ("checked" ):
156+ return contract
157+
158+ expected = bool (contract ["expected_publish_ok" ])
159+ changed_fields : list [str ] = []
160+ for location , status in contract ["status_objects" ].items ():
161+ if status .get ("publish_ok" ) is not expected :
162+ status ["publish_ok" ] = expected
163+ changed_fields .append (f"{ location } .publish_ok" )
164+
165+ if changed_fields :
166+ verdict_path = Path (contract ["verdict_path" ])
167+ verdict_path .write_text (
168+ json .dumps (contract ["verdict" ], ensure_ascii = False , indent = 2 ) + "\n " ,
169+ encoding = "utf-8" ,
170+ )
171+
172+ return {
173+ "checked" : True ,
174+ "expected_publish_ok" : expected ,
175+ "changed" : bool (changed_fields ),
176+ "changed_fields" : changed_fields ,
177+ }
178+
179+
86180def _walk_json (value : Any , location : str , violations : list [str ]) -> None :
87181 if isinstance (value , dict ):
88182 for key , child in value .items ():
@@ -122,7 +216,13 @@ def prepare_public_tree(root: Path) -> dict[str, Any]:
122216 elif target .exists () or target .is_symlink ():
123217 removed .append (relative )
124218 target .unlink ()
125- return {"ok" : True , "removed_count" : len (removed ), "removed" : sorted (removed )}
219+ status_reconciliation = reconcile_public_status_contract (root )
220+ return {
221+ "ok" : True ,
222+ "removed_count" : len (removed ),
223+ "removed" : sorted (removed ),
224+ "status_reconciliation" : status_reconciliation ,
225+ }
126226
127227
128228def audit_public_tree (
@@ -175,6 +275,16 @@ def audit_public_tree(
175275 f"{ _repo_path (root , path )} : literal credential is not publishable"
176276 )
177277
278+ status_contract = _publication_status_contract (root )
279+ if status_contract .get ("checked" ):
280+ expected = bool (status_contract ["expected_publish_ok" ])
281+ for location , actual in status_contract ["actual_publish_ok" ].items ():
282+ if actual is not expected :
283+ violations .append (
284+ f"{ location } .publish_ok={ actual !r} : expected { expected !r} "
285+ "from run_manifest and ai_publish_readiness"
286+ )
287+
178288 unique = sorted (set (violations ))
179289 return {
180290 "ok" : not unique ,
0 commit comments