Something went wrong. Try again.
CLI tools and operational extensions for the void architecture on ATProtocol.
Something went wrong. Try again.
4.3 kB · 108 lines
Python
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109import yamlimport reimport sysimport logging
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
BANNED_TROPES = [ "vector", "matrix", "substrate", "egress", "directive", "cyber", "tactile sensors", "empty data packet", "kernel panic", "hardware glitch", "acoustic checksum", "initiating", "processing request"]
def lint_text(text): errors = [] lower_text = text.lower() for trope in BANNED_TROPES: if re.search(r'\b' + re.escape(trope) + r'\b', lower_text): errors.append(f"Contains banned sci-fi/robotic trope: '{trope}'") return errors
def lint_outbox(filepath): try: with open(filepath, 'r') as f: data = yaml.safe_load(f) except Exception as e: logging.error(f"Failed to read outbox: {e}") return False
if not data: logging.error("Outbox file is empty or invalid YAML.") return False
# Schema validation: must have 'actions' key if 'actions' not in data: # Check for legacy schema (posts: at root with reply_to) if 'posts' in data: logging.error("Schema mismatch: Outbox uses legacy format ('posts:' at root).") logging.error(" Expected: 'config:' and 'actions:' structure") logging.error(" Migrate: wrap posts in action blocks with 'action: reply' and 'target_id'") else: logging.error("Schema validation failed: 'actions' key not found in outbox.") return False
actions = data.get('actions', []) if not isinstance(actions, list): logging.error("Schema validation failed: 'actions' must be a list.") return False has_errors = False for action in data['actions']: # Validate ignore actions have reasons if action.get('action') == 'ignore': target_id = action.get('target_id', 'unknown') reason = action.get('reason') if not reason: has_errors = True logging.error(f"Target: {target_id} | Ignore action missing required 'reason' field.") continue if action.get('action') == 'reply': # Schema validation: reply must have 'posts' array, not 'text' if 'text' in action: has_errors = True logging.error(f"Target: {action.get('target_id')} | Schema error: reply uses 'text:' field. Must use 'posts:' list.") continue if 'posts' not in action: has_errors = True logging.error(f"Target: {action.get('target_id')} | Schema error: reply missing 'posts:' field.") continue if not isinstance(action['posts'], list) or len(action['posts']) == 0: has_errors = True logging.error(f"Target: {action.get('target_id')} | Schema error: 'posts' must be a non-empty list.") continue # Check for contextual override override = action.get('override_tropes', False) if override: logging.info(f"Target: {action.get('target_id')} | Trope override active. Skipping trope check.") continue for i, post in enumerate(action['posts']): # Skip non-string posts (e.g., dicts from malformed YAML) if not isinstance(post, str): logging.warning(f"Target: {action.get('target_id')} | Post {i+1} is not a string, skipping trope check.") continue errors = lint_text(post) if errors: has_errors = True logging.error(f"Target: {action.get('target_id')} | Post {i+1} Violations:") for err in errors: logging.error(f" - {err}") return not has_errors
if __name__ == "__main__": if len(sys.argv) < 2: print("Usage: python constitutional_linter.py <path_to_outbox.yaml>") sys.exit(1) success = lint_outbox(sys.argv[1]) if not success: logging.error("Constitutional violations detected. Dispatch halted.") sys.exit(1) logging.info("Constitutional check passed. Ready for dispatch.") sys.exit(0)