#!/usr/bin/env python3 """ Validate a wizard's mempalace.yaml against the fleet taxonomy standard. Usage: python validate_mempalace_taxonomy.py /path/to/mempalace.yaml python validate_mempalace_taxonomy.py --ci /path/to/mempalace.yaml Exit codes: 0 = valid 1 = missing required rooms or other violations """ import sys from pathlib import Path try: import yaml except ImportError: print("ERROR: PyYAML not installed. Run: pip install pyyaml") sys.exit(1) REQUIRED_ROOMS = { "forge", "hermes", "nexus", "issues", "experiments", } def load_standard(): # Try to find the fleet standard in the-nexus clone or local path candidates = [ Path(__file__).parent.parent / "mempalace_taxonomy.yaml", Path("/tmp/nexus_clone/docs/mempalace_taxonomy.yaml"), Path(__file__).parent.parent.parent / "the-nexus" / "docs" / "mempalace_taxonomy.yaml", ] for c in candidates: if c.exists(): with open(c) as f: return yaml.safe_load(f) return None def validate(path: Path): errors = [] warnings = [] if not path.exists(): errors.append(f"File not found: {path}") return errors, warnings with open(path) as f: data = yaml.safe_load(f) if not data: errors.append("Empty or invalid YAML") return errors, warnings rooms = data.get("rooms", data.get("wings", {}).get("bezalel", {}).get("rooms", [])) if isinstance(rooms, list) and rooms and isinstance(rooms[0], dict): room_names = {r.get("name") for r in rooms if isinstance(r, dict)} elif isinstance(rooms, dict): room_names = set(rooms.keys()) else: room_names = set() missing = REQUIRED_ROOMS - room_names if missing: errors.append(f"Missing required rooms: {', '.join(sorted(missing))}") # Check for duplicate room names if len(room_names) < len(list(rooms) if isinstance(rooms, list) else rooms): errors.append("Duplicate room names detected") # Check for empty keywords if isinstance(rooms, list): for r in rooms: if isinstance(r, dict): kw = r.get("keywords", []) if not kw: warnings.append(f"Room '{r.get('name')}' has no keywords") standard = load_standard() if standard: std_optional = set(standard.get("optional_rooms", {}).keys()) unknown = room_names - REQUIRED_ROOMS - std_optional if unknown: warnings.append(f"Non-standard rooms (OK but not in fleet spec): {', '.join(sorted(unknown))}") return errors, warnings def main(): import argparse parser = argparse.ArgumentParser(description="Validate MemPalace taxonomy") parser.add_argument("config", help="Path to mempalace.yaml") parser.add_argument("--ci", action="store_true", help="CI mode: fail on warnings too") args = parser.parse_args() errors, warnings = validate(Path(args.config)) if warnings: for w in warnings: print(f"WARNING: {w}") if errors: for e in errors: print(f"ERROR: {e}") sys.exit(1) if args.ci and warnings: print("Validation failed in CI mode (warnings treated as errors)") sys.exit(1) print("OK: Taxonomy validation passed") sys.exit(0) if __name__ == "__main__": main()