"""Shared logging helpers. Per CLAUDE.md: - Always log config + git commit ID before running anything. - Use timestamps in folder names. - Save more rather than less. """ from __future__ import annotations import json import os import platform import subprocess import sys from datetime import datetime, timezone from pathlib import Path from typing import Any def _git_commit(repo_dir: Path) -> str: try: out = subprocess.run( ["git", "-C", str(repo_dir), "rev-parse", "HEAD"], capture_output=True, text=True, check=True, ) sha = out.stdout.strip() except Exception as e: # noqa: BLE001 sha = f"" try: dirty = subprocess.run( ["git", "-C", str(repo_dir), "status", "--porcelain"], capture_output=True, text=True, check=True, ) if dirty.stdout.strip(): sha = f"{sha}-dirty" except Exception: # noqa: BLE001 pass return sha def make_run_dir(name: str, config: dict[str, Any], root: Path | None = None) -> Path: """Create logs/_/ with git_commit.txt, config.json, env.json. Returns the directory. """ repo = Path(__file__).resolve().parent.parent root = root or (repo / "logs") ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H%M%SZ") run_dir = root / f"{ts}_{name}" run_dir.mkdir(parents=True, exist_ok=True) (run_dir / "git_commit.txt").write_text(_git_commit(repo) + "\n") (run_dir / "config.json").write_text(json.dumps(config, indent=2, default=str)) (run_dir / "env.json").write_text( json.dumps( { "python": sys.version, "platform": platform.platform(), "argv": sys.argv, "cwd": os.getcwd(), "started_utc": ts, }, indent=2, ) ) return run_dir