Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions nanobot/runtime/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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():
Expand Down
18 changes: 18 additions & 0 deletions tests/test_runtime_coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading