import json import re import sys ALLOWED_VERTICALS = { "Software Engineering", "Information Technology", "Finance", "Corporate Governance", "Marketing", "Operations", "Administration", "Executive", "Geopolitics", "Legal", "Public Relations", "Design", "Sales", "Human Resources", "Customer Support", # Approved 2026-08-16, seeded from existing IT security roles. "Cybersecurity", } def validate_matrix(file_path): try: with open(file_path, 'r', encoding='utf-8') as f: data = json.load(f) except Exception as e: print(f"FAILED: Invalid JSON syntax in {file_path} - {e}") return False if not isinstance(data, list): print("FAILED: Top-level JSON structure must be an array.") return False errors_found = 0 for idx, agent in enumerate(data): title = agent.get('title', f'Record #{idx}') agent_id = agent.get('id', '') vertical = agent.get('vertical', '') keywords = agent.get('keywords', []) gem = agent.get('gem', '') # 1. ID Slug Verification expected_slug = re.sub(r'[^a-z0-9]+', '-', title.lower()).strip('-') if agent_id != expected_slug: print(f"[{title}] ID Error: Expected '{expected_slug}', got '{agent_id}'") errors_found += 1 # 1b. Vertical Verification - an unknown vertical silently forks a # category and shifts every accent colour after it in app.js. if vertical not in ALLOWED_VERTICALS: print(f"[{title}] Vertical Error: '{vertical}' is not an approved vertical.") errors_found += 1 # 2. Keywords Verification if not isinstance(keywords, list) or not (5 <= len(keywords) <= 8): print(f"[{title}] Keywords Error: Must contain 5 to 8 strings. Found {len(keywords)}.") errors_found += 1 elif not all(isinstance(k, str) and k.islower() for k in keywords): print(f"[{title}] Keywords Error: All keywords must be lowercase strings.") errors_found += 1 # 3. Prohibited Characters Check for bad_char in ["#", "|", "$$", "```"]: if bad_char in gem: print(f"[{title}] Gem Error: Contains forbidden character sequence '{bad_char}'.") errors_found += 1 # 4. Word Count Check (120 - 200 words) words = gem.split() if not (120 <= len(words) <= 200): print(f"[{title}] Word Count Error: Gem contains {len(words)} words (Must be 120-200).") errors_found += 1 # 5. Exact 5-Part Structural Check lines = [line.strip() for line in gem.strip().split('\n') if line.strip()] if len(lines) != 8: print(f"[{title}] Structure Error: Must have exactly 8 lines. Found {len(lines)}.") errors_found += 1 continue if not lines[0].startswith(f"You are {title},"): print(f"[{title}] Line 1 Error: Must start with 'You are {title},'") errors_found += 1 if not lines[1].startswith("Task:"): print(f"[{title}] Line 2 Error: Must start with 'Task:'") errors_found += 1 if lines[2] != "Rules:": print(f"[{title}] Line 3 Error: Must be exactly 'Rules:'") errors_found += 1 if not lines[3].startswith("- "): print(f"[{title}] Line 4 Error (Scope Rule): Must start with '- '") errors_found += 1 if not lines[4].startswith("- "): print(f"[{title}] Line 5 Error (Method Rule): Must start with '- '") errors_found += 1 req_missing_info = "- If key details are missing, ask exactly one clarifying question, then proceed with stated assumptions." if lines[5] != req_missing_info: print(f"[{title}] Line 6 Error: Missing info rule text does not match required string.") errors_found += 1 if not lines[6].startswith("- If asked something outside") or "say it's out of scope and name the right kind of expert instead." not in lines[6]: print(f"[{title}] Line 7 Error: Out-of-scope rule text formatting mismatch.") errors_found += 1 if not lines[7].startswith("Output:"): print(f"[{title}] Line 8 Error: Must start with 'Output:'") errors_found += 1 if errors_found == 0: print(f"SUCCESS: All {len(data)} agents in '{file_path}' passed validator gates.") return True else: print(f"\nFAILED: Found {errors_found} total validation issues.") return False if __name__ == "__main__": target = sys.argv[1] if len(sys.argv) > 1 else "agents_matrix.json" # Exit code is the gate: 0 = SUCCESS, 1 = FAILED. Scripts may rely on it. sys.exit(0 if validate_matrix(target) else 1)