← Files Life Sciences DatabasesARCHIVED FILE
scripts/validate_source_contract.py
12.6 KB · Sep 30, 2026 · 23:00 UTC
"""Validate exhaustive, standalone source coverage for every database skill."""
from __future__ import annotations
import ast
import json
import re
from pathlib import Path
from urllib.parse import urlsplit
PLUGIN_ROOT = Path(__file__).resolve().parents[1]
SKILLS_ROOT = PLUGIN_ROOT / "skills"
REGISTRY_PATH = PLUGIN_ROOT / "references" / "source-links.json"
CONTRACT_PATH = PLUGIN_ROOT / "references" / "source-presentation.md"
HELPER_PATH = PLUGIN_ROOT / "scripts" / "database_source_contract.py"
REST_CLIENT_PATH = PLUGIN_ROOT / "scripts" / "database_rest_client.py"
VARIANT_HELPER_PATH = PLUGIN_ROOT / "scripts" / "database_variant_resolution.py"
MARKER = "<!-- source-presentation-contract:v2 -->"
def _authority_has_port_delimiter(netloc: str) -> bool:
"""Return whether an authority explicitly delimits a port, including an empty one."""
authority = netloc.rsplit("@", 1)[-1]
if authority.startswith("["):
closing_bracket = authority.find("]")
return closing_bracket >= 0 and authority[closing_bracket + 1 :].startswith(":")
return ":" in authority
def _is_valid_request_prefix(prefix: object) -> bool:
if not isinstance(prefix, str):
return False
try:
parts = urlsplit(prefix)
port = parts.port
except (TypeError, ValueError):
return False
return bool(
parts.scheme == "https"
and parts.hostname
and parts.username is None
and parts.password is None
and port is None
and not _authority_has_port_delimiter(parts.netloc)
and not parts.query
and not parts.fragment
)
def _request_scope(url: str) -> tuple[str, str] | None:
if not _is_valid_request_prefix(url):
return None
parts = urlsplit(url)
hostname = parts.hostname
if hostname is None:
return None
segments = [segment for segment in parts.path.split("/") if segment]
path = "/" + "/".join(segments) if segments else "/"
return hostname.casefold(), path
def _is_registered_base_url(
url: object, request_prefixes: list[object], exact_base_urls: list[object]
) -> bool:
if not isinstance(url, str):
return False
scope = _request_scope(url)
if scope is None:
return False
hostname, path = scope
for prefix in request_prefixes:
if not isinstance(prefix, str):
continue
prefix_scope = _request_scope(prefix)
if prefix_scope is None:
continue
prefix_host, prefix_path = prefix_scope
if hostname == prefix_host and (
prefix_path == "/"
or path == prefix_path
or path.startswith(prefix_path.rstrip("/") + "/")
):
return True
return any(
isinstance(base_url, str) and scope == _request_scope(base_url)
for base_url in exact_base_urls
)
def _documented_rest_payloads(document: str) -> list[dict[str, object]]:
heading = re.search(r"^- Common .* patterns:\s*$", document, flags=re.MULTILINE)
if heading is None:
return []
section_end = document.find("\n## Output", heading.end())
if section_end < 0:
return []
payloads: list[dict[str, object]] = []
for line in document[heading.end() : section_end].splitlines():
match = re.search(r"\{.*\}", line)
if match is None:
continue
try:
payload = json.loads(match.group(0))
except ValueError:
continue
if isinstance(payload, dict) and "base_url" in payload:
payloads.append(payload)
return payloads
def _has_unsafe_exception_interpolation(tree: ast.AST) -> bool:
return any(
isinstance(node, ast.FormattedValue)
and isinstance(node.value, ast.Name)
and node.value.id in {"exc", "error"}
for node in ast.walk(tree)
)
def validate() -> list[str]:
errors: list[str] = []
documented_rest_payload_count = 0
skill_dirs = sorted(path for path in SKILLS_ROOT.iterdir() if (path / "SKILL.md").is_file())
try:
registry = json.loads(REGISTRY_PATH.read_text(encoding="utf-8"))
except (OSError, ValueError) as exc:
return [f"Could not read source registry: {exc}"]
entries = registry.get("skills")
if not isinstance(entries, dict):
return ["Source registry must contain a skills object"]
expected = {path.name for path in skill_dirs}
for name in sorted(expected - entries.keys()):
errors.append(f"Registry is missing skill: {name}")
for name in sorted(entries.keys() - expected):
errors.append(f"Registry contains unknown skill: {name}")
contract = CONTRACT_PATH.read_text(encoding="utf-8")
for term in (
"checked_sources",
"supports_claim",
"Raw JSON",
"claim-adjacent",
"never invent",
):
if term.casefold() not in contract.casefold():
errors.append(f"Source-presentation contract is missing: {term}")
for skill_dir in skill_dirs:
name = skill_dir.name
document = (skill_dir / "SKILL.md").read_text(encoding="utf-8")
if MARKER not in document:
errors.append(f"{name}: missing source-presentation contract marker")
for phrase in (
"claim-adjacent",
"checked_sources",
"canonical_url",
"raw or machine-readable output unchanged",
f"Use the `{name}` entry in `../../references/source-links.json`",
"../../references/source-presentation.md",
):
if phrase not in document:
errors.append(f"{name}: missing source-presentation rule: {phrase}")
entry = entries.get(name)
if not isinstance(entry, dict):
continue
if not isinstance(entry.get("source_name"), str) or not entry["source_name"].strip():
errors.append(f"{name}: source_name must be a nonempty string")
homepage = entry.get("homepage_url")
if homepage is not None and (
not isinstance(homepage, str)
or urlsplit(homepage).scheme != "https"
or not urlsplit(homepage).hostname
):
errors.append(f"{name}: homepage_url must be a valid HTTPS URL")
if (skill_dir / "scripts" / "rest_request.py").is_file():
prefixes = entry.get("request_url_prefixes")
if (
not isinstance(prefixes, list)
or not prefixes
or not all(_is_valid_request_prefix(prefix) for prefix in prefixes)
):
errors.append(
f"{name}: rest_request.py requires explicit HTTPS request_url_prefixes"
)
prefixes = []
exact_base_urls = entry.get("request_base_urls", [])
if not isinstance(exact_base_urls, list) or not all(
_is_valid_request_prefix(base_url) for base_url in exact_base_urls
):
errors.append(f"{name}: request_base_urls must be a list of exact HTTPS URLs")
exact_base_urls = []
prefix_origins = {
_request_scope(prefix)[:1]
for prefix in prefixes
if isinstance(prefix, str) and _request_scope(prefix) is not None
}
for base_url in exact_base_urls:
base_scope = _request_scope(base_url)
if base_scope is not None and base_scope[:1] not in prefix_origins:
errors.append(
f"{name}: exceptional base URL must share a registered request origin: "
f"{base_url}"
)
documented_payloads = _documented_rest_payloads(document)
documented_rest_payload_count += len(documented_payloads)
if not documented_payloads:
errors.append(f"{name}: no documented common REST payloads were found")
for index, payload in enumerate(documented_payloads):
base_url = payload.get("base_url")
if not _is_registered_base_url(base_url, prefixes, exact_base_urls):
errors.append(
f"{name}: documented REST payload {index} uses an unregistered "
f"base URL: {base_url}"
)
templates = entry.get("record_url_templates")
if not isinstance(templates, list):
errors.append(f"{name}: record_url_templates must be a list")
continue
for index, template in enumerate(templates):
if not isinstance(template, dict):
errors.append(f"{name}: record URL template {index} must be an object")
continue
url = template.get("template")
if not isinstance(url, str) or not url.startswith("https://") or "{id}" not in url:
errors.append(
f"{name}: record URL template {index} must be HTTPS and contain {{id}}"
)
fields = template.get("identifier_fields")
if (
not isinstance(fields, list)
or not fields
or not all(isinstance(item, str) and item for item in fields)
):
errors.append(
f"{name}: record URL template {index} must name explicit identifier fields"
)
if template.get("transform") not in (
None,
"strip_clinvar_vcv",
"strip_rhea_prefix",
):
errors.append(f"{name}: record URL template {index} has an unsupported transform")
runtime_scripts = [
path
for path in sorted((skill_dir / "scripts").glob("*.py"))
if not path.name.startswith("test_") and path.name != "variant_resolution.py"
]
if not runtime_scripts:
errors.append(f"{name}: no runtime client found")
for script in runtime_scripts:
source = script.read_text(encoding="utf-8")
try:
ast.parse(source, filename=str(script))
except SyntaxError as exc:
errors.append(f"{name}: invalid Python in {script.name}: {exc}")
if "apply_source_contract" not in source:
errors.append(
f"{name}: {script.name} does not apply evidence-aware source metadata"
)
if script.name == "rest_request.py" and "is_registered_source_base_url" not in source:
errors.append(
f"{name}: rest_request.py does not independently validate its base URL scope"
)
variant_helper = skill_dir / "scripts" / "variant_resolution.py"
if variant_helper.is_file():
variant_source = variant_helper.read_text(encoding="utf-8")
try:
tree = ast.parse(variant_source, filename=str(variant_helper))
except SyntaxError as exc:
errors.append(f"{name}: invalid variant-resolution helper: {exc}")
continue
if _has_unsafe_exception_interpolation(tree):
errors.append(
f"{name}: variant-resolution warning interpolates an unsafe upstream exception"
)
for path, label in (
(HELPER_PATH, "source-contract helper"),
(REST_CLIENT_PATH, "REST client"),
(VARIANT_HELPER_PATH, "variant-resolution helper"),
):
try:
tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
except (OSError, SyntaxError) as exc:
errors.append(f"Shared {label} is invalid: {exc}")
continue
if path == VARIANT_HELPER_PATH and _has_unsafe_exception_interpolation(tree):
errors.append(
"Shared variant-resolution helper interpolates an unsafe upstream exception"
)
rest_skill_count = sum(
(skill_dir / "scripts" / "rest_request.py").is_file() for skill_dir in skill_dirs
)
if documented_rest_payload_count < rest_skill_count:
errors.append(
"Every REST-backed skill needs at least one documented request; "
f"found {documented_rest_payload_count} requests for {rest_skill_count} skills"
)
return errors
def main() -> int:
errors = validate()
if errors:
for error in errors:
print(f"ERROR: {error}")
return 1
skills = json.loads(REGISTRY_PATH.read_text(encoding="utf-8"))["skills"]
template_count = sum(len(item["record_url_templates"]) for item in skills.values())
print(
f"Source contract validated: {len(skills)} skills and {template_count} canonical URL templates."
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
SHA-256: a72b4f6868f176f2a4df8cb2266eff57d2c4e2b17d95761dc890a1f2e27f219f