Something went wrong. Try again.
personal memory agent
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151#!/usr/bin/env python3# SPDX-License-Identifier: AGPL-3.0-only# Copyright (c) 2026 sol pbcfrom __future__ import annotations
import hashlibimport reimport subprocessfrom pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]PRE_CUTOVER_COMMIT = "dd04f55c8"SURVIVING_SOL_CLI = "solstone/think/sol_cli.py"
# Pre-cutover deletion-owner manifest. `convey_client.py` intentionally# survives because the OpenAPI contract tests import `resolve_base_url`.EXPECTED_BLOBS = { "solstone/apps/activities/call.py": "b50d44bfee5b1649bc506c39063fb78234966f95", "solstone/apps/awareness/call.py": "c8607807ac37ea9ec596f5a77b9171707ab9b844", "solstone/apps/body/call.py": "14b1ccacec66cda2cc288c3700720ca436fa4308", "solstone/apps/chat/call.py": "bf679409d2463280f2dcb6db7a844302e859c52c", "solstone/apps/entities/call.py": "cde1cfc193f5e6537c6b620c2fc910dc0c584082", "solstone/apps/facets/call.py": "8a0a3a2b4d7086f52af739a87d21294c51bc360d", "solstone/apps/import/call.py": "ec3991bf55ee9c6e157a6aba079c9a1d9f076ae7", "solstone/apps/network/call.py": "763c129a30fe623bc5c9c0a9e44fd9aa2058a5de", "solstone/apps/settings/call.py": "09b9a77917cd756515d8a404efb2acf24f93906a", "solstone/apps/sol/call.py": "486861e6557e2c2758abf321e7b77730d358557e", "solstone/apps/speakers/call.py": "852454b0cfa11d548042ac3e708c879b5eae2a39", "solstone/apps/support/call.py": "5f0802c8a9acde604e70b3a921dbdc36d4579d1f", "solstone/apps/thinking/call.py": "aa2cb8c5985aeab1864479c22326e33a8a45ae2a", "solstone/apps/transcripts/call.py": "c5d9e81b965d8231903632c7f945c4613cf3d2e4", "solstone/think/tools/health.py": "09be4da53688e1d91a10b8bb38c3e9a2a34712b4", "solstone/think/tools/ledger.py": "b6df98b73b13d5d3798305761330db0dd7977985", "solstone/think/tools/profile.py": "96fe4319bdce13029060806aea8523ca680ac2fb", "solstone/think/import_client.py": "31a6c12e341e9144b0f9c1567613abf0aabddc3a", "solstone/think/chat_cli.py": "f41e0523c6e5574f1d920c5304cdaf96643bdeee", SURVIVING_SOL_CLI: "a20570fc0994f6215a013e8c89ce7776ddec7d17",}EXPECTED_SHA256 = hashlib.sha256( "".join( f"{path}\t{blob}\n" for path, blob in sorted(EXPECTED_BLOBS.items()) ).encode("utf-8")).hexdigest()
def manifest_bytes() -> bytes: raw = subprocess.check_output( ["git", "ls-tree", PRE_CUTOVER_COMMIT, "--", *EXPECTED_BLOBS], cwd=REPO_ROOT, ) lines = raw.splitlines() if len(lines) != len(EXPECTED_BLOBS): found_paths = { line.decode("utf-8").split("\t", maxsplit=1)[1] for line in lines if b"\t" in line } missing = sorted(set(EXPECTED_BLOBS) - found_paths) raise RuntimeError( f"git ls-tree returned {len(lines)} entries; missing: {', '.join(missing)}" ) actual: dict[str, str] = {} for line in lines: meta, path = line.decode("utf-8").split("\t", maxsplit=1) parts = meta.split() if len(parts) < 3: raise RuntimeError(f"unexpected git ls-tree row: {line!r}") actual[path] = parts[2] drift = { path: (EXPECTED_BLOBS[path], actual.get(path)) for path in sorted(EXPECTED_BLOBS) if actual.get(path) != EXPECTED_BLOBS[path] } if drift: details = "; ".join( f"{path}: expected {expected}, actual {actual_blob}" for path, (expected, actual_blob) in drift.items() ) raise RuntimeError(f"native sol Python manifest blob drifted: {details}") return "".join(f"{path}\t{blob}\n" for path, blob in sorted(actual.items())).encode( "utf-8" )
def deletion_errors() -> list[str]: errors: list[str] = [] for path in sorted(EXPECTED_BLOBS): current = REPO_ROOT / path if path == SURVIVING_SOL_CLI: if not current.is_file(): errors.append(f"{path} must survive as the journal console script") continue if current.exists(): errors.append(f"{path} still exists after native-sol cutover") errors.extend(sol_cli_survivor_errors(REPO_ROOT / SURVIVING_SOL_CLI)) return errors
def sol_cli_survivor_errors(path: Path) -> list[str]: if not path.is_file(): return [] text = path.read_text(encoding="utf-8") errors: list[str] = [] for required in ("def journal_main", "_dispatch", "COMMANDS", "ALIASES"): if required not in text: errors.append( f"{SURVIVING_SOL_CLI} missing required journal symbol {required}" ) for forbidden in ( "def main", "ACCESS_HELP_GROUPS", "SOL_SERVICE_CMD_REMOVED_ERROR", "def print_help", ): if forbidden in text: errors.append( f"{SURVIVING_SOL_CLI} still contains removed sol surface {forbidden}" ) if re.search(r"Command\([^)]*[\"']access[\"']", text, re.DOTALL): errors.append(f"{SURVIVING_SOL_CLI} still registers an access command") if re.search(r"Alias\([^)]*[\"']access[\"']", text, re.DOTALL): errors.append(f"{SURVIVING_SOL_CLI} still registers an access alias") return errors
def main() -> int: data = manifest_bytes() digest = hashlib.sha256(data).hexdigest() errors = deletion_errors() if digest != EXPECTED_SHA256: errors.insert( 0, "native sol Python manifest digest drifted " f"(expected {EXPECTED_SHA256}, actual {digest})", ) if errors: print("native sol Python deletion manifest failed:") for error in errors: print(f"- {error}") return 1 print( "native sol Python deletion manifest ok: " f"pre_cutover={PRE_CUTOVER_COMMIT} files={len(EXPECTED_BLOBS)} " f"sha256={digest}" ) return 0
if __name__ == "__main__": raise SystemExit(main())