@@ -191,6 +191,8 @@ class GoldenHistoryReport:
191191 fixture_schema : str = FIXTURE_SCHEMA
192192 cases : list [CaseReport ] = field (default_factory = list )
193193 missing_families : list [str ] = field (default_factory = list )
194+ required_families : list [str ] = field (default_factory = list )
195+ covered_families : list [str ] = field (default_factory = list )
194196 summary : dict [str , int ] = field (default_factory = dict )
195197
196198 @property
@@ -212,13 +214,25 @@ def verdict(self) -> str:
212214 def promotion_decision (self ) -> str :
213215 return promotion_decision_for (self .verdict )
214216
217+ @property
218+ def evidence (self ) -> dict [str , Any ]:
219+ return {
220+ "fixture_count" : int (self .summary .get ("fixtures" , 0 )),
221+ "case_count" : int (self .summary .get ("cases" , 0 )),
222+ "required_families" : list (self .required_families ),
223+ "covered_families" : list (self .covered_families ),
224+ "missing_family_count" : len (self .missing_families ),
225+ "missing_families" : list (self .missing_families ),
226+ }
227+
215228 def to_dict (self ) -> dict [str , Any ]:
216229 return {
217230 "schema" : self .schema ,
218231 "schema_version" : self .schema_version ,
219232 "status" : self .status ,
220233 "verdict" : self .verdict ,
221234 "promotion_decision" : self .promotion_decision ,
235+ "evidence" : self .evidence ,
222236 "fixture_schema" : self .fixture_schema ,
223237 "summary" : dict (self .summary ),
224238 "missing_families" : list (self .missing_families ),
@@ -234,6 +248,7 @@ class BundleEntry:
234248 verdict : str
235249 promotion_decision : str
236250 integrity : Mapping [str , Any ] | None = None
251+ evidence : Mapping [str , Any ] | None = None
237252 reason : str | None = None
238253
239254 def to_dict (self ) -> dict [str , Any ]:
@@ -242,6 +257,7 @@ def to_dict(self) -> dict[str, Any]:
242257 "verdict" : self .verdict ,
243258 "promotion_decision" : self .promotion_decision ,
244259 "reason" : self .reason ,
260+ "evidence" : dict (self .evidence ) if self .evidence is not None else None ,
245261 "integrity" : dict (self .integrity ) if self .integrity is not None else None ,
246262 }
247263
@@ -263,12 +279,34 @@ class SimulationReport:
263279 missing_bundles : list [str ] = field (default_factory = list )
264280 error : str | None = None
265281
282+ @property
283+ def evidence (self ) -> dict [str , Any ]:
284+ bundle_count = int (self .summary .get ("total" , len (self .bundles )))
285+ integrity_checked = 0
286+ for entry in self .bundles :
287+ evidence = entry .evidence or {}
288+ if evidence .get ("integrity_checked" ) is True :
289+ integrity_checked += 1
290+
291+ return {
292+ "bundle_count" : bundle_count ,
293+ "missing_bundle_count" : len (self .missing_bundles ),
294+ "integrity_checked_count" : integrity_checked ,
295+ "replay_checked_count" : 0 ,
296+ "replay_skipped" : True ,
297+ "strict_warnings" : any (
298+ (entry .evidence or {}).get ("strict_warnings" ) is True
299+ for entry in self .bundles
300+ ),
301+ }
302+
266303 def to_dict (self ) -> dict [str , Any ]:
267304 payload : dict [str , Any ] = {
268305 "schema" : self .schema ,
269306 "schema_version" : self .schema_version ,
270307 "verdict" : self .verdict ,
271308 "promotion_decision" : self .promotion_decision ,
309+ "evidence" : self .evidence ,
272310 "summary" : dict (self .summary ),
273311 "bundles" : [entry .to_dict () for entry in self .bundles ],
274312 "missing_bundles" : list (self .missing_bundles ),
@@ -325,10 +363,16 @@ def verify_golden_history(
325363 caller decides whether to gate promotion on them.
326364 """
327365
366+ required = sorted (set (required_families ))
328367 fixtures = sorted (Path (fixture_dir ).glob ("*.json" ))
329368
330369 if not fixtures :
331- report = GoldenHistoryReport (status = STATUS_FAILED )
370+ report = GoldenHistoryReport (
371+ status = STATUS_FAILED ,
372+ missing_families = list (required ),
373+ required_families = list (required ),
374+ covered_families = [],
375+ )
332376 report .summary = {
333377 "fixtures" : 0 ,
334378 "cases" : 0 ,
@@ -386,7 +430,7 @@ def verify_golden_history(
386430 )
387431 )
388432
389- missing = sorted (set (required_families ) - covered_families )
433+ missing = sorted (set (required ) - covered_families )
390434 summary = _summarize (cases )
391435 summary ["fixtures" ] = len (fixtures )
392436
@@ -401,6 +445,8 @@ def verify_golden_history(
401445 status = overall ,
402446 cases = cases ,
403447 missing_families = missing ,
448+ required_families = list (required ),
449+ covered_families = sorted (covered_families ),
404450 summary = summary ,
405451 )
406452
@@ -695,6 +741,15 @@ def simulate_bundles(
695741 path = str (path ),
696742 verdict = VERDICT_FAILED ,
697743 promotion_decision = PROMOTION_BLOCK_AND_INVESTIGATE ,
744+ evidence = {
745+ "integrity_checked" : False ,
746+ "integrity_status" : None ,
747+ "integrity_finding_count" : 0 ,
748+ "replay_checked" : False ,
749+ "replay_status" : None ,
750+ "replay_skipped" : True ,
751+ "strict_warnings" : strict_warnings ,
752+ },
698753 reason = f"bundle_unreadable: { exc } " ,
699754 )
700755 bundles .append (entry )
@@ -713,6 +768,19 @@ def simulate_bundles(
713768 verdict = verdict ,
714769 promotion_decision = decision ,
715770 integrity = integrity ,
771+ evidence = {
772+ "integrity_checked" : True ,
773+ "integrity_status" : integrity .get ("status" ),
774+ "integrity_finding_count" : int (
775+ (integrity .get ("summary" ) or {}).get (
776+ "findings" , len (integrity .get ("findings" ) or [])
777+ )
778+ ),
779+ "replay_checked" : False ,
780+ "replay_status" : None ,
781+ "replay_skipped" : True ,
782+ "strict_warnings" : strict_warnings ,
783+ },
716784 )
717785 )
718786 verdicts .append (verdict )
0 commit comments