Skip to content

Commit 5bd2ea6

Browse files
authored
Merge pull request #80 from PyAutoLabs/feature/review-inplace-tasks
fix: review faculty --task resolves in-place (no-worktree) tasks
2 parents 274282f + e9808e2 commit 5bd2ea6

3 files changed

Lines changed: 100 additions & 3 deletions

File tree

agents/faculties/review/AGENTS.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ produced by the reviewing agent (the session or subagent consulting this
4040
faculty) following the procedure below — mirroring how vitals' script reads
4141
Heart and the agent reasons over the verdict.
4242

43-
1. `review.sh` (→ `_review.py`, stdlib-only) resolves the task worktree or
44-
repo paths and emits, per repo: merge-base against `origin/main`, commits
43+
1. `review.sh` (→ `_review.py`, stdlib-only) resolves the task worktree —
44+
or, for an in-place task with no worktree, the checkouts its `active.md`
45+
`- repos:` block claims — or explicit repo paths and emits, per repo: merge-base against `origin/main`, commits
4546
ahead, diff stat, changed files, and risk flags (public-API-shaped paths,
4647
config/schema files, tests changed or not, generated files).
4748
2. The reviewing agent runs, over that surface: a **code review at high

agents/faculties/review/_review.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import argparse
2020
import json
21+
import os
22+
import re
2123
import subprocess
2224
import sys
2325
from pathlib import Path
@@ -75,11 +77,46 @@ def repo_surface(repo: Path) -> dict | None:
7577
}
7678

7779

80+
def in_place_repos(task: str) -> list[Path]:
81+
"""Checkouts claimed by an in-place task (worktree: none) in active.md.
82+
83+
Parses only the named task's `- repos:` block (` - <Repo>: <branch>`
84+
lines), never the bare 2-space claims other tooling reads — an in-place
85+
entry lists its repos there and the checkouts live at the workspace root.
86+
"""
87+
pyauto_root = Path(os.environ.get(
88+
"PYAUTO_ROOT", Path.home() / "Code" / "PyAutoLabs"
89+
))
90+
active = pyauto_root / "PyAutoMind" / "active.md"
91+
if not active.exists():
92+
return []
93+
repos: list[Path] = []
94+
in_entry = in_block = False
95+
for line in active.read_text(errors="replace").splitlines():
96+
if line.startswith("## "):
97+
in_entry = line[3:].strip() == task
98+
in_block = False
99+
continue
100+
if not in_entry:
101+
continue
102+
if line.strip() == "- repos:":
103+
in_block = True
104+
continue
105+
if in_block:
106+
m = re.match(r"^ - ([A-Za-z0-9_-]+):", line)
107+
if m:
108+
repos.append(pyauto_root / m.group(1))
109+
else:
110+
in_block = False
111+
return [r for r in repos if (r / ".git").exists()]
112+
113+
78114
def resolve_repos(task: str | None, repos: list[str]) -> list[Path]:
79115
if task:
80116
root = WT_BASE / task
81117
if not root.is_dir():
82-
return []
118+
# No worktree — an in-place task; fall back to its active.md claims.
119+
return in_place_repos(task)
83120
# Claimed repos are real directories (not symlinks) holding a .git
84121
# file/dir — worktree_create symlinks everything unclaimed.
85122
return sorted(

tests/test_review_inplace.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""In-place (no-worktree) task resolution in the review faculty."""
2+
3+
import subprocess
4+
import sys
5+
from pathlib import Path
6+
7+
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "agents" / "faculties" / "review"))
8+
9+
import _review # noqa: E402
10+
11+
ACTIVE = """\
12+
# Active Tasks
13+
14+
## other-task
15+
- worktree: ~/Code/PyAutoLabs-wt/other-task
16+
- repos:
17+
- PyAutoArray: feature/other-task
18+
19+
## my-inplace-task
20+
- issue: https://example/1
21+
- status: workspace-dev
22+
- worktree: none (in-place)
23+
- repos:
24+
- repo_a: feature/my-inplace-task
25+
- repo_b: feature/my-inplace-task
26+
27+
## no-repos-task
28+
- repos:
29+
"""
30+
31+
32+
def make_root(tmp_path, monkeypatch):
33+
(tmp_path / "PyAutoMind").mkdir()
34+
(tmp_path / "PyAutoMind" / "active.md").write_text(ACTIVE)
35+
for name in ("repo_a", "repo_b"):
36+
subprocess.run(["git", "init", "-q", str(tmp_path / name)], check=True)
37+
monkeypatch.setenv("PYAUTO_ROOT", str(tmp_path))
38+
return tmp_path
39+
40+
41+
def test_in_place_repos_reads_only_the_named_tasks_block(tmp_path, monkeypatch):
42+
root = make_root(tmp_path, monkeypatch)
43+
repos = _review.in_place_repos("my-inplace-task")
44+
assert repos == [root / "repo_a", root / "repo_b"]
45+
46+
47+
def test_in_place_repos_skips_missing_checkouts_and_unknown_tasks(tmp_path, monkeypatch):
48+
make_root(tmp_path, monkeypatch)
49+
# other-task claims PyAutoArray, which has no checkout under this root.
50+
assert _review.in_place_repos("other-task") == []
51+
assert _review.in_place_repos("nonexistent") == []
52+
assert _review.in_place_repos("no-repos-task") == []
53+
54+
55+
def test_resolve_repos_falls_back_when_no_worktree(tmp_path, monkeypatch):
56+
root = make_root(tmp_path, monkeypatch)
57+
monkeypatch.setattr(_review, "WT_BASE", tmp_path / "no-such-wt-base")
58+
repos = _review.resolve_repos("my-inplace-task", [])
59+
assert repos == [root / "repo_a", root / "repo_b"]

0 commit comments

Comments
 (0)