""" PERMANENCE — Pre-submission validation script. Run this before every git push to catch issues early. Usage: python validate_submission.py All checks must pass before the repo is submitted. """ import sys passed = [] failed = [] def OK(msg): passed.append(msg) print(f" ✓ {msg}") def FAIL(msg, detail=""): failed.append(msg) print(f" ✗ {msg}" + (f": {detail}" if detail else "")) print("=" * 65) print("PERMANENCE SUBMISSION VALIDATION") print("=" * 65) # ── 1. Required files exist ────────────────────────────────────── print("\n[1] Required files") import pathlib required_files = [ "openenv.yaml", "pyproject.toml", "README.md", "models.py", "client.py", "server/__init__.py", "server/permanence_server.py", "server/app.py", "server/Dockerfile", "server/requirements.txt", "training/train.py", "training/train_trl.py", "training/reward_functions.py", "training/config.yaml", "permanence/env.py", "permanence/openenv_env.py", "interactive_eval.py", "export_ghost_demo.py", "app.py", ] for f in required_files: if pathlib.Path(f).exists(): OK(f) else: FAIL(f"MISSING: {f}") # ── 2. openenv.yaml fields ─────────────────────────────────────── print("\n[2] openenv.yaml") try: import yaml spec = yaml.safe_load(pathlib.Path("openenv.yaml").read_text()) OK("openenv.yaml parses") if spec else FAIL("openenv.yaml empty") OK("author: chanikya") if spec.get("author") == "chanikya" else FAIL( f"author is '{spec.get('author')}' not 'chanikya'" ) OK("spec_version present") if "spec_version" in spec else FAIL("spec_version missing") OK("entry_point present") if "entry_point" in spec else FAIL("entry_point missing") OK("app block present") if "app" in spec else FAIL("app block missing") OK("5 tasks defined") if len(spec.get("tasks", [])) == 5 else FAIL( f"Expected 5 tasks, got {len(spec.get('tasks', []))}" ) OK("tags include openenv") if "openenv" in spec.get("tags", []) else FAIL( "openenv tag missing" ) except Exception as e: FAIL(f"openenv.yaml error: {e}") # ── 3. pyproject.toml ──────────────────────────────────────────── print("\n[3] pyproject.toml") try: import tomllib d = tomllib.load(open("pyproject.toml", "rb")) author = d["project"]["authors"][0].get("name", "") OK("author: Chanikya") if author == "Chanikya" else FAIL( f"author is '{author}' not 'Chanikya'" ) OK("license: MIT") if d["project"]["license"]["text"] == "MIT" else FAIL("license not MIT") except Exception as e: FAIL(f"pyproject.toml error: {e}") # ── 4. README has HF frontmatter ───────────────────────────────── print("\n[4] README.md HuggingFace frontmatter") try: readme = pathlib.Path("README.md").read_text() OK("Starts with ---") if readme.startswith("---") else FAIL( "README must start with --- (HF frontmatter)" ) OK("sdk: docker") if "sdk: docker" in readme else FAIL( "sdk: docker missing from frontmatter" ) OK("openenv tag") if "openenv" in readme[:500] else FAIL( "openenv tag missing from frontmatter" ) except Exception as e: FAIL(f"README error: {e}") # ── 5. OpenEnv compliance ──────────────────────────────────────── print("\n[5] OpenEnv compliance") try: from openenv.core import Environment, Observation, Action, State from permanence.openenv_env import PermanenceOpenEnv from models import PermanenceAction, PermanenceObservation, PermanenceState OK("PermanenceOpenEnv inherits Environment") if issubclass( PermanenceOpenEnv, Environment ) else FAIL("PermanenceOpenEnv does not inherit from openenv.core.Environment") OK("PermanenceAction inherits Action") if issubclass( PermanenceAction, Action ) else FAIL("PermanenceAction does not inherit from openenv.core.Action") OK("PermanenceObservation inherits Observation") if issubclass( PermanenceObservation, Observation ) else FAIL("PermanenceObservation does not inherit from openenv.core.Observation") OK("PermanenceState inherits State") if issubclass( PermanenceState, State ) else FAIL("PermanenceState does not inherit from openenv.core.State") # Test reset/step/state env = PermanenceOpenEnv() obs = env.reset(seed=42) OK("reset() returns PermanenceObservation") if isinstance( obs, PermanenceObservation ) else FAIL(f"reset() returns {type(obs)}") action = PermanenceAction( text='' ) obs2 = env.step(action) OK("step() returns PermanenceObservation") if isinstance( obs2, PermanenceObservation ) else FAIL(f"step() returns {type(obs2)}") st = env.state OK("state property returns PermanenceState") if isinstance( st, PermanenceState ) else FAIL(f"state returns {type(st)}") meta = env.get_metadata() OK(f"get_metadata().name = {meta.name}") env.close() OK("close() works") except Exception as e: FAIL(f"OpenEnv compliance error: {e}") # ── 6. Server app endpoints ────────────────────────────────────── print("\n[6] server/app.py endpoints") try: from fastapi.testclient import TestClient from server.app import app client = TestClient(app) r = client.get("/health") OK("/health returns 200") if r.status_code == 200 else FAIL( f"/health returns {r.status_code}" ) r = client.post("/reset", json={}) OK("/reset with empty body returns 200") if r.status_code == 200 else FAIL( f"/reset{{}} returns {r.status_code}: {r.text[:200]}" ) r = client.get("/state") OK("/state returns 200") if r.status_code == 200 else FAIL( f"/state returns {r.status_code}" ) r = client.get("/schema") OK("/schema returns 200") if r.status_code == 200 else FAIL( f"/schema returns {r.status_code}" ) r = client.get("/metadata") OK("/metadata returns 200") if r.status_code == 200 else FAIL( f"/metadata returns {r.status_code}" ) except Exception as e: FAIL(f"server/app.py error: {e}") # ── 7. Dockerfile has required fields ──────────────────────────── print("\n[7] server/Dockerfile") try: df = pathlib.Path("server/Dockerfile").read_text() OK("FROM python") if "FROM python" in df else FAIL("Missing FROM python") OK("EXPOSE 7860") if "7860" in df else FAIL("Missing EXPOSE 7860") OK("ENV PYTHONPATH=/app") if "PYTHONPATH=/app" in df else FAIL( "Missing ENV PYTHONPATH=/app" ) OK("HEALTHCHECK") if "HEALTHCHECK" in df else FAIL("Missing HEALTHCHECK") OK("uvicorn CMD") if "uvicorn" in df and "CMD" in df else FAIL("Missing uvicorn CMD") except Exception as e: FAIL(f"Dockerfile error: {e}") # ── 8. Core env imports ────────────────────────────────────────── print("\n[8] permanence package") try: from permanence.env import PermanenceEnv env = PermanenceEnv() obs, info = env.reset() OK("PermanenceEnv.reset() works") assert "text" in obs, f"obs missing 'text': {obs}" OK("reset() returns obs with text field") _, reward, terminated, truncated, info = env.step( "" ) OK("PermanenceEnv.step() works") except Exception as e: FAIL(f"permanence env error: {e}") # ── 9. Training imports ────────────────────────────────────────── print("\n[9] training modules") try: from training.reward_functions import ( reward_format, reward_prediction_accuracy, reward_no_catastrophe, ) scores = reward_format( [""] ) assert scores[0] == 1.0, f"Expected 1.0, got {scores[0]}" OK("reward_format works") OK("reward_functions.py imports") except Exception as e: FAIL(f"training error: {e}") try: import training.train_trl OK("train_trl.py imports") except ImportError as e: if "torch" in str(e) or "unsloth" in str(e) or "trl" in str(e): OK(f"train_trl.py skipped (GPU dependency: {e})") else: FAIL(f"train_trl.py import error: {e}") except Exception as e: FAIL(f"train_trl.py error: {e}") # ── FINAL RESULT ───────────────────────────────────────────────── print() print("=" * 65) n_ok, n_fail = len(passed), len(failed) print(f"RESULTS: {n_ok} PASSED | {n_fail} FAILED") print("=" * 65) if n_fail > 0: print("\nFAILED CHECKS:") for f in failed: print(f" ✗ {f}") print("\nFix all failures before pushing.") sys.exit(1) else: print("\n✓ ALL CHECKS PASSED — REPO IS SUBMISSION-READY") sys.exit(0)