docs: clarify Claro v1 status and validation

This commit is contained in:
Hermes Agent
2026-06-07 03:03:13 -06:00
parent 0da1095c03
commit 1e0ea06d21
74 changed files with 2351 additions and 336 deletions
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""Claro checker/linter (gentle)."""
import sys, pathlib
OPEN = {"IF":"ENDIF", "DO":"DONE", "REPEAT":"UNTIL", "TEACH":"LEARNED", "TRY":"ENDTRY"}
KNOWN = set(list(OPEN.keys()) + list(OPEN.values()) + ["ELSE","CATCH",
"SAY","ASK","SET","CALL","RETURN","WAIT","HELP",
"IMPORT","EXPORT",
"ADD","GET","PUT","COUNT","HAS","KEYS",
"READ","WRITE","APPEND","EXISTS","DELETE","FILE",
"PARSE","MAKE","JSON",
"WITH","TO","AS","INTO","AT","KEY","TIMES","STOP"
])
def main():
if len(sys.argv) < 2:
print("Usage: python tools/check.py <file.claro>")
return 2
p = pathlib.Path(sys.argv[1])
lines = p.read_text(encoding="utf-8", errors="replace").splitlines()
stack = []
bad = 0
for i, line in enumerate(lines, start=1):
if "\t" in line:
print(f"Line {i}: contains a TAB. Use spaces.")
bad += 1
stripped = line.strip()
if not stripped or stripped.startswith("#"):
continue
head = stripped.split()[0].upper()
if head not in KNOWN:
print(f"Line {i}: warning: unknown keyword '{head}'.")
if head in OPEN:
stack.append((head, i))
elif head in OPEN.values():
if not stack:
print(f"Line {i}: '{head}' without opener.")
bad += 1
else:
op, oi = stack.pop()
exp = OPEN[op]
if head != exp:
print(f"Line {i}: '{head}' closes '{op}' from line {oi}, expected '{exp}'.")
bad += 1
if stack:
for op, oi in stack:
print(f"Line {oi}: '{op}' not closed (expected {OPEN[op]}).")
bad += len(stack)
if bad:
print(f"\nFound {bad} issue(s).")
return 1
print("No problems found.")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""Claro formatter (simple, deterministic)."""
import sys, pathlib
OPEN = {"IF", "DO", "REPEAT", "TEACH", "TRY"}
MID = {"ELSE", "CATCH"}
CLOSE= {"ENDIF", "DONE", "UNTIL", "LEARNED", "ENDTRY"}
def main():
if len(sys.argv) < 2:
print("Usage: python tools/fmt.py <file.claro>")
return 2
p = pathlib.Path(sys.argv[1])
lines = p.read_text(encoding="utf-8").splitlines()
out = []
indent = 0
for raw in lines:
line = raw.rstrip()
stripped = line.strip()
if not stripped:
out.append("")
continue
head = stripped.split()[0].upper()
if head in CLOSE:
indent = max(0, indent-1)
if head in MID:
indent = max(0, indent-1)
out.append((" "*indent) + stripped)
if head in OPEN:
indent += 1
if head in MID:
indent += 1
p.write_text("\n".join(out) + "\n", encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env python3
"""Compatibility wrapper around the built-in Claro RC0 test runner."""
import os
import pathlib
import subprocess
import sys
ROOT = pathlib.Path(__file__).resolve().parent.parent
CLARO = ROOT / ("claro.exe" if os.name == "nt" else "claro")
if not CLARO.exists():
print("Build first: gcc -std=c99 src/claro.c -O0 -o " + ("claro.exe" if os.name == "nt" else "claro") + " -lm")
raise SystemExit(2)
raise SystemExit(subprocess.call([str(CLARO), "test"], cwd=str(ROOT)))
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
import subprocess
import sys
import tempfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
EXE = ROOT / "claro.exe"
DOC = ROOT / "docs" / "CONCURRENCY.md"
def fail(message):
raise SystemExit(message)
def run(cmd):
result = subprocess.run(cmd, cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
return result.returncode, result.stdout
def main():
if not EXE.exists():
fail(f"Missing Claro executable: {EXE}")
if not DOC.exists():
fail("Missing docs/CONCURRENCY.md")
text = DOC.read_text(encoding="utf-8")
required = ["START TASK", "YIELD", "WAIT TASK", "TASK STATUS", "cooperative", "deterministic", "no native threads"]
for phrase in required:
if phrase.lower() not in text.lower():
fail(f"CONCURRENCY.md missing required phrase: {phrase}")
rc, out = run([str(EXE), "check", "tests/40_tasks_status.claro"])
if rc != 0:
fail(out)
rc, out = run([str(EXE), "tests/40_tasks_status.claro"])
if rc != 0:
fail(out)
if "DONE" not in out:
fail(f"TASK STATUS should report DONE after a cooperative task runs. Output was:\n{out}")
with tempfile.TemporaryDirectory() as td:
bad = Path(td) / "missing_endtask.claro"
bad.write_text("START TASK sample\n SAY \"oops\"\n", encoding="utf-8")
rc, out = run([str(EXE), "check", str(bad)])
if rc == 0:
fail("claro check should fail when START TASK is missing ENDTASK/END")
if "START block opened here needs ENDTASK" not in out:
fail(f"Missing ENDTASK diagnostic was not clear enough:\n{out}")
print("Concurrency foundation validation OK")
if __name__ == "__main__":
main()
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
import json
import os
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
def fail(message):
raise SystemExit(message)
def main():
if not EXE.exists():
fail(f"Missing Claro executable: {EXE}")
result = subprocess.run([str(EXE), "ide"], cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if result.returncode != 0:
fail(result.stdout)
try:
data = json.loads(result.stdout)
except json.JSONDecodeError as exc:
fail(f"claro ide did not print valid JSON: {exc}\n{result.stdout}")
required_top_level = ["language", "version", "file_extensions", "keywords", "types", "commands", "snippets", "diagnostics"]
for key in required_top_level:
if key not in data:
fail(f"Missing top-level IDE metadata field: {key}")
if ".claro" not in data["file_extensions"]:
fail("IDE metadata must advertise the .claro file extension")
if "LIST OF TEXT" not in data["types"]:
fail("IDE metadata must include typed container types")
if not any(cmd.get("name") == "typecheck" for cmd in data["commands"]):
fail("IDE metadata must include the typecheck command")
if not any(snip.get("name") == "typed_list" for snip in data["snippets"]):
fail("IDE metadata must include a typed_list snippet")
if not any(diag.get("source") == "claro typecheck" for diag in data["diagnostics"]):
fail("IDE metadata must describe claro typecheck diagnostics")
print("IDE metadata validation OK")
if __name__ == "__main__":
main()
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env python3
import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
HELPER = ROOT / "support" / "lsp" / "claro_lsp_stub.py"
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
def fail(message):
raise SystemExit(message)
def run_helper(*args):
result = subprocess.run([sys.executable, str(HELPER), *args], cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if result.returncode != 0:
fail(result.stdout)
return result.stdout
def main():
if not HELPER.exists():
fail(f"Missing LSP helper: {HELPER}")
if not EXE.exists():
fail(f"Missing Claro executable: {EXE}")
metadata = json.loads(run_helper("metadata"))
if metadata.get("language") != "Claro":
fail("metadata must describe Claro")
if ".claro" not in metadata.get("file_extensions", []):
fail("metadata must include .claro")
if not any(cmd.get("name") == "typecheck" for cmd in metadata.get("commands", [])):
fail("metadata must include typecheck command")
completions = json.loads(run_helper("completions"))
if "SAY" not in completions.get("keywords", []):
fail("completions must include SAY")
if "LIST OF TEXT" not in completions.get("types", []):
fail("completions must include typed containers")
with tempfile.TemporaryDirectory() as td:
bad = Path(td) / "bad_lsp.claro"
bad.write_text("SET score AS NUMBER TO 1\nSET score TO \"oops\"\n", encoding="utf-8")
diagnostics = json.loads(run_helper("diagnostics", str(bad)))
if not diagnostics:
fail("diagnostics must report type errors")
first = diagnostics[0]
for key in ["source", "file", "line", "severity", "message"]:
if key not in first:
fail(f"diagnostic is missing {key}")
if first["source"] != "claro typecheck":
fail("type mismatch should come from claro typecheck")
if first["line"] != 2:
fail(f"expected type diagnostic on line 2, got {first['line']}")
if "NUMBER" not in first["message"] or "TEXT" not in first["message"]:
fail(f"diagnostic should preserve type mismatch message: {first['message']}")
print("LSP helper validation OK")
if __name__ == "__main__":
main()
+80
View File
@@ -0,0 +1,80 @@
#!/usr/bin/env python3
import os
import subprocess
import tempfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
EXPECTED_VERSION = "Claro v1.18.26"
def fail(message):
raise SystemExit(message)
def run(cmd, cwd):
result = subprocess.run(cmd, cwd=cwd, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
return result.returncode, result.stdout
def read(path):
return path.read_text(encoding="utf-8")
def main():
if not EXE.exists():
fail(f"Missing executable: {EXE}")
rc, out = run([str(EXE), "--version"], ROOT)
if rc != 0 or EXPECTED_VERSION not in out:
fail(f"Expected {EXPECTED_VERSION}, got:\n{out}")
with tempfile.TemporaryDirectory() as td:
work = Path(td)
(work / "main.claro").write_text('SAY "Package security demo"\n', encoding="utf-8")
rc, out = run([str(EXE), "package", "init"], work)
if rc != 0:
fail(out)
project = read(work / "claro.project")
for phrase in ["manifest-version: 1", "name: ClaroProject", "main: main.claro", "version: v1.18.26", "packages:"]:
if phrase not in project:
fail(f"claro.project missing {phrase!r}:\n{project}")
rc, out = run([str(EXE), "package", "add", "math-tools"], work)
if rc != 0:
fail(out)
manifest_path = work / "packages" / "math-tools" / "claro.package"
manifest = read(manifest_path)
for phrase in ["manifest-version: 1", "name: math-tools", "version: 1", "source: local", "checksum:"]:
if phrase not in manifest:
fail(f"claro.package missing {phrase!r}:\n{manifest}")
checksum_line = next((line for line in manifest.splitlines() if line.startswith("checksum:")), "")
checksum = checksum_line.split(":", 1)[1].strip()
if len(checksum) < 8 or checksum == "TODO":
fail(f"Package checksum should be a stable non-placeholder value, got {checksum!r}")
lock = read(work / "claro.lock")
for phrase in ["lock-version: 1", "version: v1.18.26", "package: math-tools", "checksum:"]:
if phrase not in lock:
fail(f"claro.lock missing {phrase!r}:\n{lock}")
rc, out = run([str(EXE), "package", "doctor"], work)
if rc != 0:
fail(out)
for phrase in ["OK package manifest: math-tools", "OK package checksum: math-tools", "Package/project files look ready."]:
if phrase not in out:
fail(f"package doctor output missing {phrase!r}:\n{out}")
rc, out = run([str(EXE), "package", "add", "../bad"], work)
if rc == 0:
fail("Unsafe package names with path separators must be rejected")
if "Package names may use only" not in out:
fail(f"Unsafe package diagnostic was unclear:\n{out}")
print("Package security validation OK")
if __name__ == "__main__":
main()
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
import subprocess
import sys
from pathlib import Path
root = Path(__file__).resolve().parents[1]
exe = root / ("claro.exe" if sys.platform.startswith("win") else "claro")
def run(args, expect=0, input_text=None):
p = subprocess.run(args, cwd=root, input=input_text, text=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if p.returncode != expect:
print("FAILED:", " ".join(map(str,args)))
print(p.stdout)
print(p.stderr)
sys.exit(1)
return p.stdout + p.stderr
run([str(exe), "--version"])
run([str(exe), "test"])
run([str(exe), "check", "lessons/01_hello.claro"])
run([str(exe), "check", "lessons/08_functions.claro"])
run([str(exe), "check", "tests/checker_fixtures/bad_unknown_command.claro"], expect=1)
run([str(exe), "check", "tests/checker_fixtures/bad_typo_command.claro"], expect=1)
print("RC3 validation passed")
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
import os
import shutil
import subprocess
import sys
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
EXE = os.path.join(ROOT, 'claro.exe' if os.name == 'nt' else 'claro')
SRC = os.path.join(ROOT, 'src', 'claro.c')
cmd = ['gcc', '-std=c99', SRC, '-O0', '-o', EXE, '-lm']
print('+', ' '.join(cmd))
subprocess.check_call(cmd, cwd=ROOT)
checks = [
[EXE, '--version'],
[EXE, 'test'],
[EXE, 'check', 'tests/25_simple_syntax.claro'],
[EXE, 'check', 'tests/26_end_alias.claro'],
[EXE, 'check', 'tests/28_text_list_helpers.claro'],
[EXE, 'tests/29_arguments_demo.claro', 'apple', 'banana'],
]
for c in checks:
print('+', ' '.join(c))
subprocess.check_call(c, cwd=ROOT)
print('RC6 validation completed.')
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""Validate Claro v1.09.26 RC9 from the package root."""
import os
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
def run(cmd):
print("+", " ".join(str(c) for c in cmd))
return subprocess.run(cmd, cwd=ROOT).returncode
def main():
if run(["gcc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"]):
return 1
checks = [
[str(EXE), "--version"],
[str(EXE), "test"],
[str(EXE), "doctor"],
[str(EXE), "validate"],
[str(EXE), "examples/simple_functions.claro"],
[str(EXE), "examples/text_polish.claro"],
]
for cmd in checks:
if run(cmd):
return 1
print("Claro v1.09.26 RC9 validation passed.")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env python3
import os
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
EXPECTED = {
"tests/typecheck_bad.claro": [
"tests/typecheck_bad.claro:2: Type mismatch for score: it was first set as NUMBER, but this value looks like TEXT.",
"tests/typecheck_bad.claro:3: Type mismatch for name: expected TEXT, but this value looks like NUMBER.",
"tests/typecheck_bad.claro:4: Type check failed: expected TEXT, but score looks like NUMBER.",
],
"tests/typecheck_container_bad.claro": [
"tests/typecheck_container_bad.claro:3: Type mismatch for list names: expected TEXT item, but this value looks like NUMBER.",
"tests/typecheck_container_bad.claro:6: Type mismatch for map scores: expected NUMBER value, but this value looks like TEXT.",
],
}
STALE_PHRASES = [
"was first set as NUMBER, but this looks like TEXT",
"name needs TEXT, but this looks like NUMBER",
"Type check says this should be TEXT",
"ADD to names needs TEXT items",
"PUT into scores needs NUMBER values",
]
def run_typecheck(path):
result = subprocess.run(
[str(EXE), "typecheck", path],
cwd=ROOT,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
print("$", EXE.name, "typecheck", path)
print(result.stdout, end="")
if result.returncode == 0:
raise SystemExit(f"Expected {path} to fail typecheck")
lines = [line.strip() for line in result.stdout.splitlines() if line.strip()]
expected = EXPECTED[path]
if lines != expected:
raise SystemExit(
"Unexpected diagnostics for " + path + "\nExpected:\n"
+ "\n".join(expected) + "\nGot:\n" + "\n".join(lines)
)
for stale in STALE_PHRASES:
if stale in result.stdout:
raise SystemExit(f"Found stale diagnostic wording: {stale}")
return result.stdout
def main():
if not EXE.exists():
raise SystemExit(f"Missing Claro executable: {EXE}")
for path in EXPECTED:
run_typecheck(path)
print("Typecheck diagnostics validation complete")
if __name__ == "__main__":
main()
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""Validate Claro v1.10.26 from the package root."""
import os
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
def run(cmd):
print("+", " ".join(str(c) for c in cmd))
return subprocess.run(cmd, cwd=ROOT).returncode
def main():
if run(["gcc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"]):
return 1
checks = [
[str(EXE), "--version"],
[str(EXE), "test"],
[str(EXE), "doctor"],
[str(EXE), "validate"],
[str(EXE), "examples/simple_functions.claro"],
[str(EXE), "examples/text_polish.claro"],
[str(EXE), "examples/types_and_objects.claro"],
[str(EXE), "examples/tasks_and_graphics.claro"],
]
for cmd in checks:
if run(cmd):
return 1
print("Claro v1.10.26 validation passed.")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
"""Validate Claro v1.14.26 from the package root."""
import os
import subprocess
import sys
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
EXE = os.path.join(ROOT, 'claro_validate')
SRC = os.path.join(ROOT, 'src', 'claro.c')
def run(cmd, **kwargs):
print('+', ' '.join(cmd))
return subprocess.run(cmd, cwd=ROOT, text=True, **kwargs)
if run(['gcc', '-std=c99', SRC, '-O0', '-o', EXE, '-lm']).returncode:
sys.exit(1)
if run([EXE, '--version']).returncode:
sys.exit(1)
if run([EXE, 'test']).returncode:
sys.exit(1)
if run([EXE, 'typecheck', 'tests/typecheck_good.claro']).returncode:
sys.exit(1)
bad = run([EXE, 'typecheck', 'tests/typecheck_bad.claro'])
if bad.returncode == 0:
print('Expected typecheck_bad.claro to fail')
sys.exit(1)
if run([EXE, 'validate']).returncode:
sys.exit(1)
print('Claro v1.14.26 validation passed.')
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env python3
import os, subprocess, sys
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
os.chdir(ROOT)
exe = './claro.exe' if os.name == 'nt' else './claro'
cmds = [
['gcc', '-std=c99', 'src/claro.c', '-O0', '-o', exe, '-lm'],
[exe, '--version'],
[exe, 'test'],
[exe, 'validate'],
[exe, 'check', 'examples/objects_classes.claro'],
]
for cmd in cmds:
print('+', ' '.join(cmd))
rc = subprocess.call(cmd)
if rc != 0:
sys.exit(rc)
print('Claro v1.15.26 validation passed.')
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env python3
import os
import shutil
import subprocess
import tempfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
def run(cmd, cwd=ROOT, input_text=None):
result = subprocess.run(cmd, cwd=cwd, input=input_text, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print("$", " ".join(map(str, cmd)))
print(result.stdout, end="")
if result.returncode != 0:
raise SystemExit(result.returncode)
return result.stdout
if not EXE.exists():
run(["gcc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"])
run([str(EXE), "--version"])
run([str(EXE), "test"])
run([str(EXE), "validate"])
with tempfile.TemporaryDirectory() as td:
work = Path(td)
shutil.copy2(EXE, work / EXE.name)
local = work / EXE.name
run([str(local), "new", "DemoProject"], cwd=work)
proj = work / "DemoProject"
run([str(local), "package", "doctor"], cwd=proj)
run([str(local), "package", "add", "text"], cwd=proj)
run([str(local), "package", "add", "text"], cwd=proj)
listing = run([str(local), "package", "list"], cwd=proj)
if listing.count("text") != 1:
raise SystemExit("Package was duplicated in listing")
bad = subprocess.run([str(local), "package", "add", "../bad"], cwd=proj, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print("$", local, "package add ../bad")
print(bad.stdout, end="")
if bad.returncode == 0:
raise SystemExit("Unsafe package name was accepted")
run([str(local), "package", "remove", "text"], cwd=proj)
run([str(local), "run"], cwd=proj)
print("Validation passed. Claro v1.18.26 networking reliability is ready for use.")
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
SRC = ROOT / "src" / "claro.c"
ZIG_CC = Path.home() / "AppData" / "Local" / "hermes" / "tools" / "zig-x86_64-windows-0.14.1" / "zig.exe"
def run(cmd):
print("$", " ".join(str(c) for c in cmd))
result = subprocess.run(cmd, cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print(result.stdout, end="")
if result.returncode != 0:
raise SystemExit(result.returncode)
return result.stdout
def compiler_cmd():
env_cc = os.environ.get("CLARO_CC")
if env_cc:
parts = env_cc.split()
return parts + ["-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"]
if ZIG_CC.exists():
return [str(ZIG_CC), "cc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"]
return ["gcc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"]
def build():
if not SRC.exists():
raise SystemExit("Missing src/claro.c")
run(compiler_cmd())
def main():
build()
out = run([str(EXE), "--version"])
if "Claro v1.18.26" not in out:
raise SystemExit("Wrong version string")
run([str(EXE), "test"])
run([str(EXE), "validate"])
run([sys.executable, "tools/validate_ide_metadata.py"])
run([sys.executable, "tools/validate_lsp_helper.py"])
run([sys.executable, "tools/validate_concurrency.py"])
run([sys.executable, "tools/validate_package_security.py"])
run([sys.executable, "tools/validate_typecheck_diagnostics.py"])
run([sys.executable, "tools/validate_version_convention.py"])
run([str(EXE), "examples/networking.claro"])
print("v1.18.26 validation complete")
if __name__ == "__main__":
main()
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env python3
import json
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
EXE = ROOT / "claro.exe"
EXPECTED = "Claro v1.18.26"
def fail(message):
raise SystemExit(message)
def run(cmd):
return subprocess.run(cmd, cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
def main():
if not EXE.exists():
fail(f"Missing executable: {EXE}")
out = run([str(EXE), "--version"]).stdout.strip()
if out != EXPECTED:
fail(f"Expected version {EXPECTED!r}, got {out!r}")
ide = run([str(EXE), "ide"])
if ide.returncode != 0:
fail(ide.stdout)
metadata = json.loads(ide.stdout)
if metadata.get("version") != "v1.18.26":
fail(f"Expected IDE metadata version 'v1.18.26', got {metadata.get('version')!r}")
validator = ROOT / "tools" / "validate_v1_17.py"
text = validator.read_text(encoding="utf-8")
if "Claro v1.18.26" not in text:
fail("tools/validate_v1_17.py must validate Claro v1.18.26")
print("Version convention validation OK")
if __name__ == "__main__":
main()