← Files Investment BankingARCHIVED FILE
skills/company-tearsheet/scripts/validate_tearsheet_json.py
4.86 KB · Oct 5, 2026 · 18:28 UTC
#!/usr/bin/env python3
"""Validate a structured company tearsheet JSON file.
Expected top-level shape:
{
"entity": "ExampleCo",
"profile_type": "company",
"as_of_date": "2026-05-07",
"sources": [{"source_id": "S1", "source_name": "...", ...}],
"metrics": [{"metric": "Revenue", "period": "FY2025", "value": 123, ...}],
"sections": {"one_line_view": "...", "business_snapshot": [...], ...}
}
This script performs lightweight deterministic checks. It does not verify whether
source claims are true; the assistant must still apply the skill's evidence rules.
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
ALLOWED_EVIDENCE = {
"fact_source_reported",
"fact_provider_standardized",
"derived_calculation",
"management_claim",
"estimate_consensus",
"analyst_interpretation",
"assumption_user_provided",
"assumption_inferred",
"missing_required_source",
}
ALLOWED_CONFIDENCE = {"high", "medium", "low"}
REQUIRED_TOP_LEVEL = ["entity", "profile_type", "as_of_date"]
REQUIRED_SOURCE_FIELDS = ["source_id", "source_name", "source_type"]
REQUIRED_METRIC_FIELDS = ["metric", "period", "value", "units", "source", "evidence", "confidence"]
def load_json(path: Path) -> dict[str, Any]:
try:
data = json.loads(path.read_text(encoding="utf-8"))
except json.JSONDecodeError as exc:
raise SystemExit(f"invalid json: {exc}") from exc
if not isinstance(data, dict):
raise SystemExit("top-level json must be an object")
return data
def validate(data: dict[str, Any]) -> list[str]:
errors: list[str] = []
warnings: list[str] = []
for field in REQUIRED_TOP_LEVEL:
if not data.get(field):
errors.append(f"missing top-level field: {field}")
sources = data.get("sources", [])
if not isinstance(sources, list):
errors.append("sources must be a list")
sources = []
source_ids = set()
for idx, source in enumerate(sources, start=1):
if not isinstance(source, dict):
errors.append(f"source {idx} must be an object")
continue
for field in REQUIRED_SOURCE_FIELDS:
if not source.get(field):
errors.append(f"source {idx} missing field: {field}")
if source.get("source_id"):
source_ids.add(str(source["source_id"]))
if not source.get("as_of_date"):
warnings.append(f"source {idx} missing as_of_date")
metrics = data.get("metrics", [])
if metrics and not isinstance(metrics, list):
errors.append("metrics must be a list")
metrics = []
for idx, metric in enumerate(metrics, start=1):
if not isinstance(metric, dict):
errors.append(f"metric {idx} must be an object")
continue
for field in REQUIRED_METRIC_FIELDS:
if field not in metric or metric.get(field) in (None, ""):
errors.append(f"metric {idx} missing field: {field}")
evidence = metric.get("evidence")
if evidence and evidence not in ALLOWED_EVIDENCE:
errors.append(f"metric {idx} has invalid evidence label: {evidence}")
confidence = metric.get("confidence")
if confidence and confidence not in ALLOWED_CONFIDENCE:
errors.append(f"metric {idx} has invalid confidence label: {confidence}")
source_ref = str(metric.get("source", ""))
source_id = source_ref.split()[0] if source_ref else ""
if source_id and source_ids and source_id not in source_ids:
warnings.append(f"metric {idx} source id not found in sources: {source_id}")
if not metric.get("period"):
errors.append(f"metric {idx} missing period")
if metric.get("evidence") == "assumption_inferred" and metric.get("confidence") != "low":
warnings.append(f"metric {idx} inferred assumptions should usually be low confidence")
data_quality_flags = data.get("data_quality_flags", [])
if data_quality_flags and not isinstance(data_quality_flags, list):
errors.append("data_quality_flags must be a list")
return [f"ERROR: {e}" for e in errors] + [f"WARNING: {w}" for w in warnings]
def main() -> int:
parser = argparse.ArgumentParser(description="Validate a company tearsheet JSON file")
parser.add_argument("input_json", type=Path, help="Path to tearsheet JSON")
parser.add_argument("--strict", action="store_true", help="Treat warnings as failures")
args = parser.parse_args()
data = load_json(args.input_json)
messages = validate(data)
for message in messages:
print(message)
has_error = any(m.startswith("ERROR:") for m in messages)
has_warning = any(m.startswith("WARNING:") for m in messages)
if has_error or (args.strict and has_warning):
return 1
print("validation passed")
return 0
if __name__ == "__main__":
sys.exit(main())
SHA-256: 93ad620f5be84d82f3c87ba38280b963055899dd259aa7ca39b326c60095c992