Files
kunail0804-ComfyUI-Scene-Co…/scripts/validate_knowledge_base.py
T
d6dcabe4f7 feat(knowledge-base): add Knowledge Base validation tooling (#9) (#52)
Implement whole-Knowledge-Base validation per MASTER_SPEC §15.10, §26.4:
compiler/common/kb_validation.py (importable by the loader) plus a CLI at
scripts/validate_knowledge_base.py.

validate_knowledge_base(entries) collects every problem (never stops at the
first); each Message carries a code, the offending id, and a description:
- duplicate canonical id -> SC0005
- invalid category -> SC0008 (drawn from the shared 19-category constant)
- circular expansion (incl. self-expansion) -> SC0003, via DFS cycle detection
- duplicate alias, alias colliding with a canonical id (no chains), missing
  expansion target, and per-entry schema nonconformance (empty tags, unknown
  fields, ...) -> umbrella SC0004
Findings are deterministically ordered.

The CLI loads every *.json (array of entries) from a directory, reports invalid
JSON / non-array files as SC0004, writes JSON findings to stdout, and exits
non-zero when invalid.

Tests: a passing fixture plus a fixture per failure mode, multi-problem
reporting, determinism, and the CLI on valid/invalid/broken-JSON inputs.

Co-authored-by: kunail0804 <kunail0804@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 19:31:16 +02:00

80 lines
2.6 KiB
Python

"""CLI to validate a Knowledge Base directory (issue #9).
Loads every ``*.json`` file under the given directory (each a JSON array of
entries), validates the whole Knowledge Base, and writes the findings as JSON to
stdout. Exit code is 0 when the Knowledge Base is valid, 1 otherwise.
Usage:
python scripts/validate_knowledge_base.py knowledge_base/
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
# Allow running directly (python scripts/validate_knowledge_base.py) without install.
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from compiler.common.kb_validation import validate_knowledge_base # noqa: E402
from compiler.common.result import Message, Severity # noqa: E402
def load_entries(directory: Path) -> tuple[list[dict], list[Message]]:
"""Load all entries from ``*.json`` files; return (entries, load_errors)."""
entries: list[dict] = []
errors: list[Message] = []
for path in sorted(directory.glob("*.json")):
try:
data = json.loads(path.read_text(encoding="utf-8"))
except json.JSONDecodeError as exc:
errors.append(
Message(
code="SC0004",
severity=Severity.FATAL,
title="Invalid JSON",
description=f"{path.name}: {exc}",
context={"id": path.name},
)
)
continue
if isinstance(data, list):
entries.extend(data)
else:
errors.append(
Message(
code="SC0004",
severity=Severity.FATAL,
title="Invalid Knowledge Base file",
description=f"{path.name}: expected a JSON array of entries.",
context={"id": path.name},
)
)
return entries, errors
def main(argv: list[str]) -> int:
if len(argv) != 1:
sys.stderr.write("usage: validate_knowledge_base.py <knowledge_base_dir>\n")
return 2
directory = Path(argv[0])
if not directory.is_dir():
sys.stderr.write(f"not a directory: {directory}\n")
return 2
entries, findings = load_entries(directory)
findings = findings + validate_knowledge_base(entries)
report = {
"valid": not findings,
"entry_count": len(entries),
"findings": [m.to_json() for m in findings],
}
sys.stdout.write(json.dumps(report, indent=2, sort_keys=True) + "\n")
return 0 if not findings else 1
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))