← Files Public Equity InvestingARCHIVED FILE
skills/earnings-deep-dive/scripts/verify_tearsheet.py
3.97 KB · Oct 2, 2026 · 00:03 UTC
#!/usr/bin/env python3
"""Lightweight tear sheet QA.
Checks:
- Required section headers exist
- No unrendered {PLACEHOLDER} tokens remain
- Beat/Miss and Guidance tables exist and include a SourceTag column
- Table rows do not have empty SourceTag cells
- Warn if tear sheet is likely >1 page (heuristic by line count)
Exit codes:
0 = pass (warnings may exist)
1 = fail
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
def _find_table_block(lines: list[str], header_pattern: str) -> list[str]:
# Find the first markdown table after a header
for i, line in enumerate(lines):
if re.match(header_pattern, line.strip()):
# scan forward for the first table header line
for j in range(i + 1, min(i + 50, len(lines))):
if lines[j].strip().startswith("|") and "SourceTag" in lines[j]:
# take until blank line
block = []
for k in range(j, len(lines)):
if not lines[k].strip():
break
block.append(lines[k])
return block
return []
def main() -> int:
if any(arg in {"-h", "--help"} for arg in sys.argv[1:]):
print("Usage: python scripts/verify_tearsheet.py output/TearSheet.md")
return 0
if len(sys.argv) != 2:
print("Usage: python scripts/verify_tearsheet.py output/TearSheet.md")
return 1
path = Path(sys.argv[1])
if not path.exists():
print(f"ERROR: Tear sheet not found: {path}")
return 1
txt = path.read_text(encoding="utf-8")
lines = txt.splitlines()
errors: list[str] = []
warnings: list[str] = []
# Required sections
required_headers = [
"## Beat/Miss vs expectations",
"## Guidance delta",
"## Drivers",
"## Key quotes",
"## Model impact",
"## Watch list",
]
for h in required_headers:
if h not in txt:
errors.append(f"Missing required section header: {h}")
# Unrendered placeholders
if re.search(r"\{[A-Z0-9_]+\}", txt):
errors.append("Found unrendered {PLACEHOLDER} token(s)")
if re.search(r"\bTODO\b", txt):
errors.append("Found TODO placeholder token(s)")
unresolved_missing = re.findall(
r"MISSING:\s*(add|explain|map|summarize|write|list|populate|decompose)[^\n]*",
txt,
flags=re.IGNORECASE,
)
if unresolved_missing:
errors.append("Found unresolved authoring placeholder(s) beginning with MISSING:")
# Tables
beat_tbl = _find_table_block(lines, r"^## Beat/Miss vs expectations$")
guid_tbl = _find_table_block(lines, r"^## Guidance delta$")
if not beat_tbl:
errors.append("Beat/Miss table not found or missing SourceTag column")
if not guid_tbl:
errors.append("Guidance table not found or missing SourceTag column")
def _check_src_cells(tbl: list[str], label: str):
# Skip first two lines (header + separator)
for row in tbl[2:]:
if not row.strip().startswith("|"):
continue
cells = [c.strip() for c in row.strip().strip("|").split("|")]
if len(cells) < 1:
continue
src = cells[-1]
if src == "":
errors.append(f"{label}: empty SourceTag cell")
if beat_tbl:
_check_src_cells(beat_tbl, "Beat/Miss")
if guid_tbl:
_check_src_cells(guid_tbl, "Guidance")
# 1-page heuristic
if len(lines) > 140:
warnings.append(
f"Tear sheet is {len(lines)} lines; may exceed one page depending on formatting"
)
if warnings:
print("WARNINGS:")
for w in warnings:
print(f" - {w}")
if errors:
print("ERRORS:")
for e in errors:
print(f" - {e}")
return 1
print("Tear sheet QA passed (warnings may exist).")
return 0
if __name__ == "__main__":
raise SystemExit(main())
SHA-256: 7de13faca6fd537282bac213347d94435c5faf8dc4cc6970f0132d6e7ca15d9c