Skip to content

Commit 10d502b

Browse files
committed
fix(orchestration): select terminal acceptance repository
1 parent e89333f commit 10d502b

2 files changed

Lines changed: 94 additions & 9 deletions

File tree

scripts/orchestration/plans.py

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,16 @@ def _git_tree_id(root: Path, spec: str) -> str | None:
164164
return result.stdout.strip() or None
165165

166166

167+
def _git_is_ancestor(root: Path, ancestor: str, descendant: str) -> bool:
168+
result = subprocess.run(
169+
["git", "-C", str(root), "merge-base", "--is-ancestor", ancestor, descendant],
170+
capture_output=True,
171+
text=True,
172+
check=False,
173+
)
174+
return result.returncode == 0
175+
176+
167177
def _handoff_recorded_identities(handoff: dict[str, object]) -> list[str]:
168178
identities: list[str] = []
169179
review = handoff.get("acceptance_review") if isinstance(handoff.get("acceptance_review"), dict) else {}
@@ -206,8 +216,9 @@ def _verified_handoff_tree(root: Path, handoff: dict[str, object]) -> str | None
206216
def _material_repository_root(
207217
args: argparse.Namespace,
208218
validated: list[tuple[dict[str, object], dict[str, object]]],
219+
commands: list[str],
209220
) -> Path:
210-
roots: set[Path] = set()
221+
entries: list[tuple[Path, str]] = []
211222
for handoff, brief in validated:
212223
if not _handoff_has_material_changes(handoff, brief):
213224
continue
@@ -216,13 +227,44 @@ def _material_repository_root(
216227
if not isinstance(repository, dict):
217228
continue
218229
recorded = str(repository.get("root") or "").strip()
219-
if recorded:
220-
roots.add(Path(recorded).expanduser().resolve())
221-
if len(roots) > 1:
230+
metadata = repository.get("metadata") if isinstance(repository.get("metadata"), dict) else {}
231+
identity = str(metadata.get("actual_commit") or "").strip()
232+
if recorded and identity:
233+
entries.append((Path(recorded).expanduser().resolve(), identity))
234+
if not entries:
235+
return project_root(args)
236+
acceptance_entries: list[tuple[Path, str]] = []
237+
for handoff, _brief in validated:
238+
if not any(_handoff_command_result(handoff, command) == "passed" for command in commands):
239+
continue
240+
repositories = handoff.get("repository") if isinstance(handoff.get("repository"), list) else []
241+
for repository in repositories:
242+
if not isinstance(repository, dict):
243+
continue
244+
recorded = str(repository.get("root") or "").strip()
245+
metadata = repository.get("metadata") if isinstance(repository.get("metadata"), dict) else {}
246+
identity = str(metadata.get("actual_commit") or "").strip()
247+
if recorded and identity:
248+
acceptance_entries.append((Path(recorded).expanduser().resolve(), identity))
249+
fresh_acceptance_roots = {
250+
root
251+
for root, identity in acceptance_entries
252+
if _git_tree_id(root, "HEAD") == _git_tree_id(root, identity)
253+
}
254+
if len(fresh_acceptance_roots) == 1:
255+
return next(iter(fresh_acceptance_roots))
256+
terminal: list[tuple[Path, str]] = []
257+
identities = {identity for _root, identity in entries}
258+
for root, identity in entries:
259+
if _git_tree_id(root, identity) and all(_git_is_ancestor(root, other, identity) for other in identities):
260+
terminal.append((root, identity))
261+
terminal_identities = {identity for _root, identity in terminal}
262+
if len(terminal_identities) == 1:
263+
identity = next(iter(terminal_identities))
264+
return next(root for root, candidate in terminal if candidate == identity)
265+
if len({root for root, _identity in entries}) > 1:
222266
raise SystemExit("acceptance-blocked: final plan repository is ambiguous")
223-
if roots:
224-
return next(iter(roots))
225-
return project_root(args)
267+
return entries[0][0]
226268

227269

228270
def _acceptance_result_detail(results: set[str]) -> str:
@@ -313,7 +355,7 @@ def _assert_archive_plan_acceptance(
313355
commands = _declared_integration_commands(body)
314356
if not commands:
315357
return
316-
git_root = _material_repository_root(args, validated)
358+
git_root = _material_repository_root(args, validated, commands)
317359
terminal_tree = _git_tree_id(git_root, "HEAD")
318360
material = [pair for pair in validated if _handoff_has_material_changes(*pair)]
319361
for command in commands:

tests/test_orchestration_workflow_contracts.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
validate_executor_result_for_task,
2020
)
2121
from handoffs import cmd_write_handoff, index_handoffs
22-
from plans import _verified_handoff_tree
22+
from plans import _material_repository_root, _verified_handoff_tree
2323

2424

2525
def read(path: str) -> str:
@@ -117,6 +117,49 @@ def test_handoff_tree_resolves_recorded_repository_instead_of_control_root(tmp_p
117117
assert _verified_handoff_tree(control_root, handoff) == tree
118118

119119

120+
def test_material_repository_prefers_fresh_plan_acceptance_root(tmp_path: Path) -> None:
121+
from test_orchestration_execution_context import git
122+
123+
control_root = tmp_path / "control"
124+
earlier_root = tmp_path / "earlier"
125+
accepted_root = tmp_path / "accepted"
126+
control_root.mkdir()
127+
for root, content in ((earlier_root, "old\n"), (accepted_root, "accepted\n")):
128+
root.mkdir()
129+
git(root, "init", "-q")
130+
git(root, "config", "user.email", "test@example.com")
131+
git(root, "config", "user.name", "Test")
132+
(root / "feature.ts").write_text(content, encoding="utf-8")
133+
git(root, "add", ".")
134+
git(root, "commit", "-qm", "feature")
135+
136+
command = "pnpm run ci"
137+
earlier_head = git(earlier_root, "rev-parse", "HEAD").strip()
138+
accepted_head = git(accepted_root, "rev-parse", "HEAD").strip()
139+
validated = [
140+
(
141+
{
142+
"changes": {"files": [{"path": "feature.ts", "action": "modified"}]},
143+
"repository": [{"root": str(earlier_root), "metadata": {"actual_commit": earlier_head}}],
144+
"validation": {"commands": []},
145+
},
146+
{"files": {"write": ["feature.ts"]}},
147+
),
148+
(
149+
{
150+
"changes": {"files": [{"path": "feature.ts", "action": "modified"}]},
151+
"repository": [{"root": str(accepted_root), "metadata": {"actual_commit": accepted_head}}],
152+
"validation": {"commands": [{"command": command, "result": "passed"}]},
153+
},
154+
{"files": {"write": ["feature.ts"]}},
155+
),
156+
]
157+
158+
assert _material_repository_root(
159+
argparse.Namespace(project_root=str(control_root)), validated, [command]
160+
) == accepted_root.resolve()
161+
162+
120163
def test_write_handoff_fills_missing_task_plan_from_authorized_args(tmp_path: Path) -> None:
121164
content = tmp_path / "handoff-content.txt"
122165
content.write_text(

0 commit comments

Comments
 (0)