From 908a312ca73240c56d361a3b0782f62621c61761 Mon Sep 17 00:00:00 2001 From: ozand Date: Sat, 2 May 2026 17:03:38 +0300 Subject: [PATCH] fix: read release metadata for promotion source commits --- nanobot/runtime/coordinator.py | 36 +++++++++++++++++++++++++++++++ tests/test_runtime_coordinator.py | 18 ++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/nanobot/runtime/coordinator.py b/nanobot/runtime/coordinator.py index 4facd86..0c88bc4 100644 --- a/nanobot/runtime/coordinator.py +++ b/nanobot/runtime/coordinator.py @@ -430,6 +430,39 @@ def _observed_product_head_source_fingerprint(workspace: Path) -> dict[str, Any] } +def _release_metadata_source_fingerprint(search_roots: list[Path]) -> dict[str, Any] | None: + """Read source provenance from archive/release metadata when .git is absent. + + eeepc deploys the system emitter from git archives, so the runtime tree has no + `.git` directory. A release-side `SOURCE_COMMIT` file is the auditable source + of truth for those pinned trees and must win over unrelated cwd git repos. + """ + + def _read_first(root: Path, names: tuple[str, ...]) -> str | None: + for name in names: + path = root / name + try: + value = path.read_text(encoding="utf-8").strip() + except OSError: + continue + if value: + return value + return None + + for root in search_roots: + commit = _read_first(root, ("SOURCE_COMMIT", "REVISION", "COMMIT")) + if not commit: + continue + return { + "source_repo_root": str(root), + "source_commit": commit, + "source_branch": _read_first(root, ("SOURCE_BRANCH", "BRANCH")), + "source_tree": _read_first(root, ("SOURCE_TREE", "TREE")), + "source_authority": "release_metadata", + } + return None + + def _runtime_source_fingerprint(workspace: Path) -> dict[str, Any]: env_commit = os.environ.get('NANOBOT_SOURCE_COMMIT') or os.environ.get('SOURCE_COMMIT') if env_commit: @@ -441,6 +474,9 @@ def _runtime_source_fingerprint(workspace: Path) -> dict[str, Any]: 'source_authority': 'environment', } search_roots = [workspace, Path(__file__).resolve().parents[2], Path.cwd()] + release_metadata = _release_metadata_source_fingerprint(search_roots) + if release_metadata: + return release_metadata for candidate_root in search_roots: repo_root = candidate_root while repo_root != repo_root.parent and not (repo_root / '.git').exists(): diff --git a/tests/test_runtime_coordinator.py b/tests/test_runtime_coordinator.py index b8a6b15..bda1411 100644 --- a/tests/test_runtime_coordinator.py +++ b/tests/test_runtime_coordinator.py @@ -35,6 +35,24 @@ def test_runtime_source_fingerprint_prefers_explicit_release_env_for_archived_ru } +def test_runtime_source_fingerprint_reads_release_metadata_before_git_for_archived_runtime(tmp_path, monkeypatch): + (tmp_path / "SOURCE_COMMIT").write_text("release-file-commit-789\n", encoding="utf-8") + (tmp_path / "SOURCE_BRANCH").write_text("main\n", encoding="utf-8") + (tmp_path / "SOURCE_TREE").write_text("tree-file-123\n", encoding="utf-8") + monkeypatch.delenv("NANOBOT_SOURCE_COMMIT", raising=False) + monkeypatch.delenv("SOURCE_COMMIT", raising=False) + + fingerprint = _runtime_source_fingerprint(tmp_path) + + assert fingerprint == { + "source_repo_root": str(tmp_path), + "source_commit": "release-file-commit-789", + "source_branch": "main", + "source_tree": "tree-file-123", + "source_authority": "release_metadata", + } + + def test_runtime_source_fingerprint_falls_back_to_observed_product_head_when_git_is_unavailable(tmp_path, monkeypatch): state_dir = tmp_path / "state" / "self_evolution" state_dir.mkdir(parents=True)