chore: adopt learning-first charter and UTC run versions

This commit is contained in:
Hermes Agent
2026-10-20 15:57:54 +00:00
parent 9dfd777c41
commit dd7e8b8b20
20 changed files with 138 additions and 47 deletions
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""Stamp a development/release run once, using its current UTC date and time.
Run before building: python3 tools/stamp_version.py && make
Ordinary rebuilds and validation retain the stamp for reproducibility.
Historical release notes and changelog entries are deliberately not rewritten.
"""
from datetime import datetime, timezone
from pathlib import Path
import re
ROOT = Path(__file__).resolve().parents[1]
CURRENT_DOCS = (
"CURRENT_STATUS.md", "CLASSROOM_READINESS.md", "CLI.md", "SPEC.md",
"FINAL_RELEASE_CHECKLIST.md", "WEB_SERVER_PLAN.md", "GRAPHICS.md",
"PERFORMANCE.md", "COMPLETE_PLATFORM_ROADMAP.md", "SDL12.md",
"V1_16_PACKAGES_PROJECTS.md",
)
def main():
source = ROOT / "src/claro.c"
match = re.search(r'#define CLARO_VERSION "Claro ([^"\n]+)"', source.read_text())
if not match:
raise SystemExit("Cannot find CLARO_VERSION; no files changed")
previous = match.group(1)
version = datetime.now(timezone.utc).strftime("v%Y.%m.%d.%H%M%S")
paths = [source, ROOT / "README.md"]
paths += sorted((ROOT / "tools").glob("*.py"))
paths += [ROOT / "docs" / name for name in CURRENT_DOCS]
updates = []
for path in paths:
if path == Path(__file__).resolve() or not path.exists():
continue
old = path.read_text(encoding="utf-8")
new = old.replace(previous, version)
if new != old:
updates.append((path, new))
for path, content in updates:
path.write_text(content, encoding="utf-8")
print(f"Claro {version} (UTC); updated {len(updates)} current source/docs/validator files")
if __name__ == "__main__":
main()
+9 -9
View File
@@ -6,7 +6,7 @@ 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"
EXPECTED_VERSION = "Claro v2026.10.20.155641"
def fail(message):
@@ -57,11 +57,11 @@ def main():
fail(out)
starter = work / "StarterApp"
starter_project = read(starter / "claro.project")
for phrase in ["manifest-version: 1", "name: StarterApp", "main: main.claro", "version: v1.18.26", "packages:"]:
for phrase in ["manifest-version: 1", "name: StarterApp", "main: main.claro", "version: v2026.10.20.155641", "packages:"]:
if phrase not in starter_project:
fail(f"claro new project file missing {phrase!r}:\n{starter_project}")
starter_lock = read(starter / "claro.lock")
for phrase in ["lock-version: 1", "version: v1.18.26"]:
for phrase in ["lock-version: 1", "version: v2026.10.20.155641"]:
if phrase not in starter_lock:
fail(f"claro new lockfile missing {phrase!r}:\n{starter_lock}")
@@ -71,7 +71,7 @@ def main():
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:"]:
for phrase in ["manifest-version: 1", "name: ClaroProject", "main: main.claro", "version: v2026.10.20.155641", "packages:"]:
if phrase not in project:
fail(f"claro.project missing {phrase!r}:\n{project}")
@@ -100,7 +100,7 @@ def main():
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:"]:
for phrase in ["lock-version: 1", "version: v2026.10.20.155641", "package: math-tools", "checksum:"]:
if phrase not in lock:
fail(f"claro.lock missing {phrase!r}:\n{lock}")
@@ -127,12 +127,12 @@ def main():
fail(f"Lock format-version diagnostic was unclear:\n{out}")
if read(work / "claro.lock") != invalid_lock:
fail("package doctor must not rewrite an invalid lock format version")
invalid_release_lock = lock.replace("version: v1.18.26", "version: v1.18.25", 1)
invalid_release_lock = lock.replace("version: v2026.10.20.155641", "version: v1.18.25", 1)
(work / "claro.lock").write_text(invalid_release_lock, encoding="utf-8")
rc, out = run([str(EXE), "package", "doctor"], work)
if rc == 0:
fail("package doctor must reject a lockfile for a different Claro release")
if "BAD lock release version: expected exactly one version: v1.18.26 line" not in out:
if "BAD lock release version: expected exactly one version: v2026.10.20.155641 line" not in out:
fail(f"Lock release-version diagnostic was unclear:\n{out}")
if read(work / "claro.lock") != invalid_release_lock:
fail("package doctor must not rewrite an invalid lock release version")
@@ -343,7 +343,7 @@ def main():
fail("Long package name should not create a package folder")
(work / "claro.project").write_text(
"manifest-version: 1\nname: ClaroProject\nmain: main.claro\nversion: v1.18.26\npackages:\npackage: ../bad\n",
"manifest-version: 1\nname: ClaroProject\nmain: main.claro\nversion: v2026.10.20.155641\npackages:\npackage: ../bad\n",
encoding="utf-8",
)
rc, out = run([str(EXE), "package", "doctor"], work)
@@ -382,7 +382,7 @@ def main():
fail("package add should not create a package folder while unsafe names remain in claro.project")
(work / "claro.project").write_text(
"manifest-version: 1\nname: ClaroProject\nmain: main.claro\nversion: v1.18.26\npackages:\npackage: math-tools\npackage: ../bad\n",
"manifest-version: 1\nname: ClaroProject\nmain: main.claro\nversion: v2026.10.20.155641\npackages:\npackage: math-tools\npackage: ../bad\n",
encoding="utf-8",
)
rc, out = run([str(EXE), "package", "remove", "math-tools"], work)
+1 -1
View File
@@ -43,4 +43,4 @@ with tempfile.TemporaryDirectory() as td:
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.")
print("Validation passed. Claro v2026.10.20.155641 networking reliability is ready for use.")
+2 -2
View File
@@ -38,7 +38,7 @@ def build():
def main():
build()
out = run([str(EXE), "--version"])
if "Claro v1.18.26" not in out:
if "Claro v2026.10.20.155641" not in out:
raise SystemExit("Wrong version string")
run([str(EXE), "test"])
run([str(EXE), "validate"])
@@ -55,7 +55,7 @@ def main():
run([sys.executable, "tools/validate_write_file_expression_cleanup.py"])
run([sys.executable, "tools/validate_version_convention.py"])
run([str(EXE), "examples/networking.claro"])
print("v1.18.26 validation complete")
print("v2026.10.20.155641 validation complete")
if __name__ == "__main__":
main()
+10 -5
View File
@@ -1,11 +1,13 @@
#!/usr/bin/env python3
import json
import re
from datetime import datetime
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
EXE = ROOT / "claro.exe"
EXPECTED = "Claro v1.18.26"
EXPECTED = "Claro v2026.10.20.155641"
def fail(message):
@@ -20,18 +22,21 @@ def main():
if not EXE.exists():
fail(f"Missing executable: {EXE}")
out = run([str(EXE), "--version"]).stdout.strip()
if not re.fullmatch(r"Claro v\d{4}\.\d{2}\.\d{2}\.\d{6}", out):
fail(f"Expected UTC run timestamp Claro vYYYY.MM.DD.HHMMSS, got {out!r}")
datetime.strptime(out.removeprefix("Claro v"), "%Y.%m.%d.%H%M%S")
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}")
if metadata.get("version") != "v2026.10.20.155641":
fail(f"Expected IDE metadata version 'v2026.10.20.155641', 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")
if "Claro v2026.10.20.155641" not in text:
fail("tools/validate_v1_17.py must validate Claro v2026.10.20.155641")
print("Version convention validation OK")