Download mindxtrain/research/guard.py from PYTHAI/mindXtrain: direct link, hf CLI and curl.
- Browser
- Download file 5.54 kB
-
https://huggingface.co/PYTHAI/mindXtrain/resolve/main/mindxtrain/research/guard.py
- Command line
-
hf download hf://PYTHAI/mindXtrain/mindxtrain/research/guard.py
-
curl -L -o guard.py https://huggingface.co/PYTHAI/mindXtrain/resolve/main/mindxtrain/research/guard.py
5.54 kB
| """Git editable-allowlist fence + dedup (disciplines 1 and 3). | |
| The fence is what converts the search trail into a sequence of re-checkable git | |
| commits — exactly what `receipt_emit._git_sha` already reads into the manifest. Three | |
| rules, all authoritative (a git failure aborts the attempt, never silently passes): | |
| 1. **Commit-before-measure** — the declared editable must be committed; no | |
| staged/unstaged edits left at measure time. | |
| 2. **Editable fence** — nothing in the working tree may be dirty *outside* the | |
| editable allowlist. The agent touches one file; nothing else moves. | |
| 3. **Dedup-by-commit** — refuse to re-measure a commit already scored for this | |
| researcher (looked up against the ledger). | |
| Pure stdlib; base-install importable. The status parser uses | |
| ``--porcelain=v1 -z --untracked-files=all`` and handles rename/copy two-field records | |
| and exotic filenames — the naive ``line[3:].strip()`` approach is wrong on renames, | |
| quoting, untracked-dir collapse, and spaced names. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import subprocess | |
| from pathlib import Path, PurePosixPath | |
| from typing import TYPE_CHECKING | |
| if TYPE_CHECKING: | |
| from mindxtrain.research.ledger import Ledger | |
| class ResearchAbort(RuntimeError): | |
| """Raised when an attempt violates the git fence or dedup rule.""" | |
| def _git(*args: str, cwd: Path) -> str: | |
| """Authoritative git wrapper — RAISES on nonzero/OSError. | |
| Same subprocess shape as `receipt_emit._git_sha` (capture, text, 5s timeout) but | |
| raises `ResearchAbort` instead of returning "" — the fence must be trustworthy. | |
| """ | |
| try: | |
| out = subprocess.run( | |
| ["git", *args], | |
| cwd=str(cwd), | |
| capture_output=True, | |
| text=True, | |
| timeout=5.0, | |
| check=False, | |
| ) | |
| except (OSError, subprocess.SubprocessError) as exc: | |
| raise ResearchAbort(f"git {' '.join(args)} failed: {exc}") from exc | |
| if out.returncode != 0: | |
| raise ResearchAbort( | |
| f"git {' '.join(args)} failed (exit {out.returncode}): {out.stderr.strip()}" | |
| ) | |
| return out.stdout | |
| def _rel_posix(path: str | Path, root: Path) -> str: | |
| """Normalise any path to a repo-root-relative POSIX string for comparison. | |
| Porcelain emits root-relative POSIX already; `editable` may be cwd-relative or | |
| absolute. Resolving both to the same frame is what stops rule 2 false-positiving | |
| on every attempt. | |
| """ | |
| p = Path(path) | |
| if p.is_absolute(): | |
| p = Path(os.path.relpath(p, root)) | |
| return PurePosixPath(os.path.normpath(p.as_posix())).as_posix() | |
| def _status_entries(root: Path) -> list[tuple[str, str]]: | |
| """Parse `git status --porcelain=v1 -z --untracked-files=all` into (XY, path). | |
| Records are NUL-separated. A rename/copy (X or Y in {R, C}) is two NUL fields — | |
| destination first, then source (the `-z` field order is reversed vs the human | |
| format); both are yielded so a rename *out of* the allowlist is still caught. | |
| `--untracked-files=all` expands untracked directories to individual files so the | |
| fence can match them. | |
| """ | |
| raw = _git("status", "--porcelain=v1", "-z", "--untracked-files=all", cwd=root) | |
| tokens = raw.split("\0") | |
| entries: list[tuple[str, str]] = [] | |
| i, n = 0, len(tokens) | |
| while i < n: | |
| tok = tokens[i] | |
| if len(tok) < 4: # empty trailing token or malformed; skip defensively | |
| i += 1 | |
| continue | |
| xy, path = tok[:2], tok[3:] | |
| entries.append((xy, path)) | |
| if xy[0] in ("R", "C") or xy[1] in ("R", "C"): | |
| i += 1 | |
| if i < n and tokens[i]: | |
| entries.append((xy, tokens[i])) # rename/copy source path | |
| i += 1 | |
| return entries | |
| def assert_clean_and_committed(editable: list[Path], root: Path) -> str: | |
| """Enforce rules 1 + 2; return the short-12 commit hash of HEAD. | |
| Raises `ResearchAbort` if the working tree is dirty outside the editable | |
| allowlist (rule 2) or if the editable itself still has uncommitted changes | |
| (rule 1). On success the tree is clean and the editable's edit lives in HEAD. | |
| """ | |
| root = Path(root) | |
| allow = {_rel_posix(p, root) for p in editable} | |
| stray = sorted({_rel_posix(path, root) for _, path in _status_entries(root)} - allow) | |
| if stray: | |
| raise ResearchAbort(f"working tree dirty outside editable allowlist: {stray}") | |
| editable_dirty = _git( | |
| "status", "--porcelain", "-z", "--", *[str(p) for p in editable], cwd=root | |
| ) | |
| if editable_dirty.strip("\0").strip(): | |
| raise ResearchAbort("commit the editable file(s) before measuring") | |
| return _git("rev-parse", "--short=12", "HEAD", cwd=root).strip() | |
| def assert_new_commit(commit: str, researcher: str, ledger: Ledger) -> None: | |
| """Dedup gate (rule 3): abort if this researcher already scored `commit`.""" | |
| if ledger.has_commit(researcher, commit): | |
| raise ResearchAbort(f"commit {commit} already scored for researcher {researcher!r}") | |
| def repo_root(cwd: Path | None = None) -> Path: | |
| """Absolute path of the enclosing git work tree; raises if not in a repo.""" | |
| out = _git("rev-parse", "--show-toplevel", cwd=cwd or Path.cwd()) | |
| return Path(out.strip()) | |
| def git(*args: str, cwd: Path) -> str: | |
| """Public authoritative git call (raises on failure). Used by the search loop.""" | |
| return _git(*args, cwd=cwd) | |
| __all__ = [ | |
| "ResearchAbort", | |
| "assert_clean_and_committed", | |
| "assert_new_commit", | |
| "git", | |
| "repo_root", | |
| ] | |