"""Logic for the `check` domain. Transport-agnostic (D-263). Nothing here prints, calls `sys.exit`, or imports typer. A service must not know it was called from a CLI — that is what lets a test call it directly, lets one domain's service call another's, and leaves a second front end possible without a rewrite. """ from __future__ import annotations import re from pathlib import Path from tooling.core import config from tooling.domains.check.schemas import VersionCheck # Anchored to line start so the commentary above `version:` (which mentions # earlier versions by number) can never be mistaken for the field itself. _YAML_VERSION = re.compile(r"^version:\s*(\S+)\s*$", re.MULTILINE) _GODOT_VERSION = re.compile(r'^config/version\s*=\s*"([^"]*)"\s*$', re.MULTILINE) def client_version() -> VersionCheck: """Compare the version in project.yaml with the one baked into the client. project.yaml is the version source of truth (CLAUDE.md). The client cannot read it at runtime — an exported build has no repo root — so the value is mirrored into `application/config/version` in client/project.godot, which Godot bakes into the PCK (T-1241). A mirror nobody checks is worse than the bug it replaced: the old code failed LOUDLY in an export ("?.?.?" everywhere), whereas a stale mirror fails SILENTLY — the Atlas disk cache keeps serving canvases under a version that stopped matching the build. That is the T-1239 failure, which cost eight days of a map drawn from a canvas whose generating code no longer existed. """ root = config.repo_root() yaml_version, problem = _read(root / "project.yaml", _YAML_VERSION, "`version:` line") if problem: return VersionCheck(ok=False, problem=problem) godot_path = root / "client" / "project.godot" godot_version, problem = _read(godot_path, _GODOT_VERSION, "`config/version=` line") if problem: return VersionCheck(ok=False, yaml_version=yaml_version, problem=problem) return VersionCheck( ok=yaml_version == godot_version, yaml_version=yaml_version, godot_version=godot_version, ) def _read(path: Path, pattern: re.Pattern[str], label: str) -> tuple[str | None, str | None]: """Return (value, problem). Exactly one of the two is ever set.""" if not path.exists(): return None, f"{path} not found" match = pattern.search(path.read_text(encoding="utf-8")) if not match: return None, f"no {label} in {path}" return match.group(1), None