mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-08-01 19:18:35 -04:00
593 lines
15 KiB
Python
Executable File
593 lines
15 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
import sys
|
|
from collections import Counter, defaultdict
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
CATALOG_PATH = ROOT / "feature-catalog.json"
|
|
CATALOG_MD_PATH = ROOT / "feature-catalog.md"
|
|
DOMAINS_DIR = ROOT / "domains"
|
|
REVIEWS_DIR = ROOT / "reviews"
|
|
|
|
EXPECTED_COMMIT = "d8a2059df8e53bc7275c45339849d14c8651e73c"
|
|
EXPECTED_FEATURES = 79
|
|
EXPECTED_DOMAINS = 16
|
|
|
|
VALID_STATUSES = {
|
|
"verified",
|
|
"partial",
|
|
"disabled",
|
|
"experimental",
|
|
"legacy",
|
|
"dead-code-candidate",
|
|
"unverified",
|
|
}
|
|
|
|
VALID_MATURITY = {"E0", "E1", "E2", "E3", "E4"}
|
|
|
|
VALID_RUNTIME = {
|
|
"not-required",
|
|
"pending",
|
|
"blocked",
|
|
"completed",
|
|
}
|
|
|
|
REQUIRED_FIELDS = {
|
|
"id",
|
|
"domain",
|
|
"name",
|
|
"purpose",
|
|
"status",
|
|
"evidence_maturity",
|
|
"verified_at_commit",
|
|
"evidence",
|
|
"runtime_validation",
|
|
}
|
|
|
|
FEATURE_ID_RE = re.compile(r"^[A-Z][A-Z0-9]*-\d{3}$")
|
|
|
|
DOMAIN_HEADING_RE = re.compile(
|
|
r"^##\s+`?([A-Z][A-Z0-9]*-\d{3})`?"
|
|
r"\s+(?:—|-)\s+(.+?)\s*$"
|
|
)
|
|
|
|
DOMAIN_FIELD_RE = re.compile(
|
|
r"^-\s+\*\*"
|
|
r"(Domain|Status|Evidence Maturity|Commit Verified)"
|
|
r"\*\*:\s*(.*?)\s*$"
|
|
)
|
|
|
|
DOMAIN_RUNTIME_RE = re.compile(
|
|
r"^-\s+\*\*Runtime Validation\*\*:\s*(.*?)\s*$"
|
|
)
|
|
|
|
REVIEW_HEADING_RE = re.compile(
|
|
r"^###\s+(?:\d+\.\s+)?"
|
|
r"([A-Z][A-Z0-9]*-\d{3})"
|
|
r"\s+(?:—|-)\s+.+$"
|
|
)
|
|
|
|
REVIEW_RESULT_RE = re.compile(
|
|
r"^-\s+\*\*Resulting Status(?:\s+and|/)\s+Maturity\*\*:"
|
|
r"\s*`([^`]+)`\s*/\s*`([^`]+)`\s*$"
|
|
)
|
|
|
|
|
|
def clean(value: str) -> str:
|
|
value = value.strip()
|
|
|
|
if (
|
|
len(value) >= 2
|
|
and value.startswith("`")
|
|
and value.endswith("`")
|
|
):
|
|
return value[1:-1].strip()
|
|
|
|
return value
|
|
|
|
|
|
def load_catalog(errors: list[str]) -> list[dict[str, Any]]:
|
|
try:
|
|
data = json.loads(
|
|
CATALOG_PATH.read_text(encoding="utf-8")
|
|
)
|
|
except Exception as exc:
|
|
errors.append(f"Unable to read catalog: {exc}")
|
|
return []
|
|
|
|
if isinstance(data, list):
|
|
features = data
|
|
elif isinstance(data, dict) and isinstance(data.get("features"), list):
|
|
features = data["features"]
|
|
else:
|
|
errors.append(
|
|
"Catalog must be an array or contain a features array"
|
|
)
|
|
return []
|
|
|
|
if not all(isinstance(feature, dict) for feature in features):
|
|
errors.append("Every catalog feature must be an object")
|
|
return []
|
|
|
|
return features
|
|
|
|
|
|
def validate_catalog(
|
|
features: list[dict[str, Any]],
|
|
errors: list[str],
|
|
) -> None:
|
|
if len(features) != EXPECTED_FEATURES:
|
|
errors.append(
|
|
f"Expected {EXPECTED_FEATURES} features, "
|
|
f"found {len(features)}"
|
|
)
|
|
|
|
ids = [feature.get("id") for feature in features]
|
|
|
|
duplicates = sorted(
|
|
feature_id
|
|
for feature_id, count in Counter(ids).items()
|
|
if feature_id and count > 1
|
|
)
|
|
|
|
if duplicates:
|
|
errors.append(
|
|
"Duplicate feature IDs: " + ", ".join(duplicates)
|
|
)
|
|
|
|
for index, feature in enumerate(features):
|
|
feature_id = feature.get("id")
|
|
label = (
|
|
feature_id
|
|
if isinstance(feature_id, str)
|
|
else f"<index:{index}>"
|
|
)
|
|
|
|
missing = sorted(
|
|
field
|
|
for field in REQUIRED_FIELDS
|
|
if feature.get(field) in (None, "", [])
|
|
)
|
|
|
|
if missing:
|
|
errors.append(
|
|
f"{label}: missing fields: {', '.join(missing)}"
|
|
)
|
|
|
|
if (
|
|
not isinstance(feature_id, str)
|
|
or not FEATURE_ID_RE.fullmatch(feature_id)
|
|
):
|
|
errors.append(f"{label}: invalid feature ID")
|
|
|
|
if feature.get("status") not in VALID_STATUSES:
|
|
errors.append(
|
|
f"{label}: invalid status "
|
|
f"{feature.get('status')!r}"
|
|
)
|
|
|
|
if feature.get("evidence_maturity") not in VALID_MATURITY:
|
|
errors.append(
|
|
f"{label}: invalid maturity "
|
|
f"{feature.get('evidence_maturity')!r}"
|
|
)
|
|
|
|
if feature.get("verified_at_commit") != EXPECTED_COMMIT:
|
|
errors.append(
|
|
f"{label}: incorrect verified_at_commit"
|
|
)
|
|
|
|
runtime = feature.get("runtime_validation")
|
|
|
|
if not isinstance(runtime, dict):
|
|
errors.append(
|
|
f"{label}: runtime_validation must be an object"
|
|
)
|
|
continue
|
|
|
|
required = runtime.get("required")
|
|
runtime_status = runtime.get("status")
|
|
reason = runtime.get("reason")
|
|
|
|
if not isinstance(required, bool):
|
|
errors.append(
|
|
f"{label}: runtime required must be boolean"
|
|
)
|
|
|
|
if runtime_status not in VALID_RUNTIME:
|
|
errors.append(
|
|
f"{label}: invalid runtime status "
|
|
f"{runtime_status!r}"
|
|
)
|
|
|
|
if not isinstance(reason, str) or not reason.strip():
|
|
errors.append(
|
|
f"{label}: runtime reason is blank"
|
|
)
|
|
|
|
if required is False and runtime_status != "not-required":
|
|
errors.append(
|
|
f"{label}: required=false requires not-required"
|
|
)
|
|
|
|
if required is True and runtime_status == "not-required":
|
|
errors.append(
|
|
f"{label}: required=true cannot be not-required"
|
|
)
|
|
|
|
|
|
def parse_domain(
|
|
path: Path,
|
|
errors: list[str],
|
|
) -> dict[str, dict[str, str]]:
|
|
records: dict[str, dict[str, str]] = {}
|
|
current_id: str | None = None
|
|
|
|
for line_number, line in enumerate(
|
|
path.read_text(encoding="utf-8").splitlines(),
|
|
start=1,
|
|
):
|
|
heading = DOMAIN_HEADING_RE.match(line)
|
|
|
|
if heading:
|
|
current_id = heading.group(1)
|
|
|
|
if current_id in records:
|
|
errors.append(
|
|
f"{path.relative_to(ROOT)}:{line_number}: "
|
|
f"duplicate heading {current_id}"
|
|
)
|
|
|
|
records[current_id] = {
|
|
"Name": heading.group(2).strip(),
|
|
}
|
|
continue
|
|
|
|
field = DOMAIN_FIELD_RE.match(line)
|
|
|
|
if field and current_id:
|
|
value = clean(field.group(2))
|
|
|
|
if not value:
|
|
errors.append(
|
|
f"{path.relative_to(ROOT)}:{line_number}: "
|
|
f"blank {field.group(1)}"
|
|
)
|
|
|
|
records[current_id][field.group(1)] = value
|
|
continue
|
|
|
|
runtime = DOMAIN_RUNTIME_RE.match(line)
|
|
|
|
if runtime and current_id:
|
|
value = clean(runtime.group(1))
|
|
runtime_status = re.split(
|
|
r"\s+(?:—|-)\s+",
|
|
value,
|
|
maxsplit=1,
|
|
)[0]
|
|
records[current_id]["Runtime Validation"] = (
|
|
runtime_status.strip("` ")
|
|
)
|
|
|
|
return records
|
|
|
|
|
|
def validate_domains(
|
|
features: list[dict[str, Any]],
|
|
errors: list[str],
|
|
) -> None:
|
|
catalog = {
|
|
feature["id"]: feature
|
|
for feature in features
|
|
if feature.get("id")
|
|
}
|
|
|
|
expected_by_domain: defaultdict[str, set[str]] = defaultdict(set)
|
|
|
|
for feature in features:
|
|
expected_by_domain[feature["domain"]].add(feature["id"])
|
|
|
|
paths = sorted(DOMAINS_DIR.glob("*.md"))
|
|
|
|
if len(paths) != EXPECTED_DOMAINS:
|
|
errors.append(
|
|
f"Expected {EXPECTED_DOMAINS} domain files, "
|
|
f"found {len(paths)}"
|
|
)
|
|
|
|
all_found: set[str] = set()
|
|
|
|
for path in paths:
|
|
domain = path.stem
|
|
records = parse_domain(path, errors)
|
|
found = set(records)
|
|
expected = expected_by_domain.get(domain, set())
|
|
all_found.update(found)
|
|
|
|
if found != expected:
|
|
missing = sorted(expected - found)
|
|
extra = sorted(found - expected)
|
|
|
|
errors.append(
|
|
f"{domain}: missing={missing}, unexpected={extra}"
|
|
)
|
|
|
|
for feature_id, record in records.items():
|
|
feature = catalog.get(feature_id)
|
|
|
|
if feature is None:
|
|
continue
|
|
|
|
expected_values = {
|
|
"Name": feature["name"],
|
|
"Domain": feature["domain"],
|
|
"Status": feature["status"],
|
|
"Evidence Maturity": feature["evidence_maturity"],
|
|
"Commit Verified": feature["verified_at_commit"],
|
|
"Runtime Validation": (
|
|
feature["runtime_validation"]["status"]
|
|
),
|
|
}
|
|
|
|
for field, expected_value in expected_values.items():
|
|
actual = record.get(field)
|
|
|
|
if actual != expected_value:
|
|
errors.append(
|
|
f"{path.relative_to(ROOT)}: "
|
|
f"{feature_id} {field}: "
|
|
f"{actual!r} != {expected_value!r}"
|
|
)
|
|
|
|
if all_found != set(catalog):
|
|
errors.append(
|
|
"Domain Markdown IDs do not match catalog JSON"
|
|
)
|
|
|
|
|
|
def validate_catalog_markdown(
|
|
features: list[dict[str, Any]],
|
|
errors: list[str],
|
|
) -> None:
|
|
expected = {
|
|
feature["id"]: feature
|
|
for feature in features
|
|
}
|
|
found: dict[str, list[str]] = {}
|
|
|
|
for line in CATALOG_MD_PATH.read_text(
|
|
encoding="utf-8"
|
|
).splitlines():
|
|
if not line.startswith("|"):
|
|
continue
|
|
|
|
cells = [
|
|
cell.strip()
|
|
for cell in line.strip().strip("|").split("|")
|
|
]
|
|
|
|
if len(cells) < 6:
|
|
continue
|
|
|
|
feature_id = clean(cells[0])
|
|
|
|
if FEATURE_ID_RE.fullmatch(feature_id):
|
|
found[feature_id] = cells
|
|
|
|
if set(found) != set(expected):
|
|
errors.append(
|
|
"feature-catalog.md IDs do not match JSON"
|
|
)
|
|
|
|
for feature_id, cells in found.items():
|
|
feature = expected[feature_id]
|
|
runtime = feature["runtime_validation"]
|
|
runtime_display = (
|
|
runtime["status"]
|
|
if runtime["required"]
|
|
else "not required"
|
|
)
|
|
|
|
actual = {
|
|
"name": cells[1].replace("\\|", "|"),
|
|
"domain": clean(cells[2]),
|
|
"status": clean(cells[3]),
|
|
"maturity": clean(cells[4]),
|
|
"runtime": clean(cells[5]),
|
|
}
|
|
|
|
wanted = {
|
|
"name": feature["name"],
|
|
"domain": feature["domain"],
|
|
"status": feature["status"],
|
|
"maturity": feature["evidence_maturity"],
|
|
"runtime": runtime_display,
|
|
}
|
|
|
|
for field, expected_value in wanted.items():
|
|
if actual[field] != expected_value:
|
|
errors.append(
|
|
f"feature-catalog.md: {feature_id} "
|
|
f"{field}: {actual[field]!r} "
|
|
f"!= {expected_value!r}"
|
|
)
|
|
|
|
|
|
def validate_reviews(
|
|
features: list[dict[str, Any]],
|
|
errors: list[str],
|
|
) -> int:
|
|
catalog = {
|
|
feature["id"]: feature
|
|
for feature in features
|
|
}
|
|
checked = 0
|
|
|
|
for path in sorted(
|
|
REVIEWS_DIR.glob("evidence-sample-*.md")
|
|
):
|
|
current_id: str | None = None
|
|
results: set[str] = set()
|
|
|
|
for line_number, line in enumerate(
|
|
path.read_text(encoding="utf-8").splitlines(),
|
|
start=1,
|
|
):
|
|
heading = REVIEW_HEADING_RE.match(line)
|
|
|
|
if heading:
|
|
current_id = heading.group(1)
|
|
continue
|
|
|
|
result = REVIEW_RESULT_RE.match(line)
|
|
|
|
if not result or current_id is None:
|
|
continue
|
|
|
|
status, maturity = result.groups()
|
|
results.add(current_id)
|
|
checked += 1
|
|
|
|
feature = catalog.get(current_id)
|
|
|
|
if feature is None:
|
|
errors.append(
|
|
f"{path.relative_to(ROOT)}:{line_number}: "
|
|
f"unknown feature {current_id}"
|
|
)
|
|
continue
|
|
|
|
if feature["status"] != status:
|
|
errors.append(
|
|
f"{current_id}: review status {status!r} "
|
|
f"!= catalog {feature['status']!r}"
|
|
)
|
|
|
|
if feature["evidence_maturity"] != maturity:
|
|
errors.append(
|
|
f"{current_id}: review maturity {maturity!r} "
|
|
f"!= catalog "
|
|
f"{feature['evidence_maturity']!r}"
|
|
)
|
|
|
|
if path.name == "evidence-sample-01.md" and len(results) != 12:
|
|
errors.append(
|
|
f"{path.relative_to(ROOT)}: expected 12 "
|
|
f"review results, found {len(results)}"
|
|
)
|
|
|
|
return checked
|
|
|
|
|
|
def validate_whitespace(errors: list[str]) -> None:
|
|
for path in sorted(ROOT.rglob("*")):
|
|
if not path.is_file():
|
|
continue
|
|
|
|
if path.suffix not in {".md", ".json", ".py", ".txt"}:
|
|
continue
|
|
|
|
for line_number, line in enumerate(
|
|
path.read_text(
|
|
encoding="utf-8",
|
|
errors="replace",
|
|
).splitlines(),
|
|
start=1,
|
|
):
|
|
if line != line.rstrip(" \t"):
|
|
errors.append(
|
|
f"{path.relative_to(ROOT)}:{line_number}: "
|
|
"trailing whitespace"
|
|
)
|
|
|
|
if any(ROOT.rglob("*.pyc")):
|
|
errors.append("Generated .pyc files exist")
|
|
|
|
if any(
|
|
path.is_dir()
|
|
for path in ROOT.rglob("__pycache__")
|
|
):
|
|
errors.append("__pycache__ exists")
|
|
|
|
|
|
def main() -> int:
|
|
errors: list[str] = []
|
|
features = load_catalog(errors)
|
|
reviewed = 0
|
|
|
|
if features:
|
|
validate_catalog(features, errors)
|
|
validate_domains(features, errors)
|
|
validate_catalog_markdown(features, errors)
|
|
reviewed = validate_reviews(features, errors)
|
|
|
|
validate_whitespace(errors)
|
|
|
|
print(f"Catalog Features: {len(features)}")
|
|
print(
|
|
"Unique Feature IDs:",
|
|
len({feature.get("id") for feature in features}),
|
|
)
|
|
print(
|
|
"Domain Files:",
|
|
len(list(DOMAINS_DIR.glob("*.md"))),
|
|
)
|
|
|
|
if features:
|
|
print(
|
|
"Statuses:",
|
|
dict(
|
|
Counter(
|
|
feature.get("status")
|
|
for feature in features
|
|
)
|
|
),
|
|
)
|
|
print(
|
|
"Evidence Maturity:",
|
|
dict(
|
|
Counter(
|
|
feature.get("evidence_maturity")
|
|
for feature in features
|
|
)
|
|
),
|
|
)
|
|
print(
|
|
"Runtime Validation:",
|
|
dict(
|
|
Counter(
|
|
(
|
|
feature.get(
|
|
"runtime_validation",
|
|
{},
|
|
).get("required"),
|
|
feature.get(
|
|
"runtime_validation",
|
|
{},
|
|
).get("status"),
|
|
)
|
|
for feature in features
|
|
)
|
|
),
|
|
)
|
|
|
|
print(f"Review Results Checked: {reviewed}")
|
|
print(f"Consistency Errors: {len(errors)}")
|
|
|
|
for error in errors:
|
|
print(f"ERROR: {error}")
|
|
|
|
return 1 if errors else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|