@@ -288,6 +288,8 @@ jobs:
288288 warnings.append("boost clock below base clock")
289289 return [f"{category}: {rel}: {warning}" for warning in warnings]
290290
291+ LOW_VERIFIED_WARNING_PCT = 50.0
292+
291293 stats_lines: list[str] = []
292294 stats_lines.append("## Data summary")
293295 stats_lines.append("")
@@ -296,6 +298,7 @@ jobs:
296298
297299 total_all = verified_all = unverified_all = missing_verified_all = 0
298300 by_category: dict[str, dict[str, int]] = {}
301+ low_verified_categories: list[tuple[str, float, int, int]] = []
299302 for category in CATEGORIES:
300303 paths = rel_jsons(HEAD, category)
301304 verified = unverified = missing_verified = 0
@@ -309,7 +312,10 @@ jobs:
309312 missing_verified += 1
310313 total = len(paths)
311314 tracked = verified + unverified
312- pct = f"{(verified / tracked * 100):.1f}%" if tracked else "n/a"
315+ pct_value = verified / tracked * 100 if tracked else None
316+ pct = f"{pct_value:.1f}%" if pct_value is not None else "n/a"
317+ if pct_value is not None and pct_value < LOW_VERIFIED_WARNING_PCT:
318+ low_verified_categories.append((category, pct_value, verified, tracked))
313319 by_category[category] = {
314320 "total": total,
315321 "verified": verified,
@@ -324,11 +330,32 @@ jobs:
324330 f"| {category} | {total} | {verified} | {unverified} | {missing_verified} | {pct} |"
325331 )
326332 tracked_all = verified_all + unverified_all
327- pct_all = f"{(verified_all / tracked_all * 100):.1f}%" if tracked_all else "n/a"
333+ pct_all_value = verified_all / tracked_all * 100 if tracked_all else None
334+ pct_all = f"{pct_all_value:.1f}%" if pct_all_value is not None else "n/a"
328335 stats_lines.append(
329336 f"| **all** | **{total_all}** | **{verified_all}** | **{unverified_all}** | "
330337 f"**{missing_verified_all}** | **{pct_all}** |"
331338 )
339+ if pct_all_value is not None and pct_all_value < LOW_VERIFIED_WARNING_PCT:
340+ low_verified_categories.append(("all", pct_all_value, verified_all, tracked_all))
341+ if low_verified_categories:
342+ low_verified_categories.sort(key=lambda item: item[1])
343+ coverage_list = ", ".join(
344+ f"{category} {pct:.1f}% ({verified}/{tracked})"
345+ for category, pct, verified, tracked in low_verified_categories[:8]
346+ )
347+ if len(low_verified_categories) > 8:
348+ coverage_list += f", and {len(low_verified_categories) - 8} more"
349+ stats_lines.append("")
350+ stats_lines.append("> [!WARNING]")
351+ stats_lines.append(
352+ f"> Verified coverage is below {LOW_VERIFIED_WARNING_PCT:.0f}% for {coverage_list}."
353+ )
354+ stats_lines.append(
355+ "> This does not fail validation. Keep imported records `verified: false` until "
356+ "manual audit, but treat this as follow-up verification work before relying on "
357+ "the affected categories as curated data."
358+ )
332359
333360 change_lines: list[str] = []
334361 change_lines.append("## Changed data")
0 commit comments