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
8 changes: 6 additions & 2 deletions scripts/orchestration/execution_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -1548,10 +1548,14 @@ def build_review_package(args: argparse.Namespace) -> Path:
handoff, _ = _read_structured(handoff_path)
validated = validate_executor_result_for_task(handoff, task, observe=True, **_observation_kwargs(args))
knowledge_disposition = validated["knowledge_disposition"]
binding = load_task_execution_binding(root, plan_id, task_id)
execution_root = Path(str(binding["execution_path"])).resolve()

base = _resolve_commit(root, str(args.base))
base = _resolve_commit(execution_root, str(args.base))
write_paths = [str(path) for path in _as_list((task.get("files") or {}).get("write"))]
head, diff, name_status, out_of_scope = _review_diff(root, base, str(args.head), write_paths)
head, diff, name_status, out_of_scope = _review_diff(
execution_root, base, str(args.head), write_paths
)
if len(diff.encode("utf-8")) > MAX_DIFF_BYTES or diff.count("\n") > MAX_DIFF_LINES:
oversized = ", ".join(
sorted({path for line in name_status for path in _paths_from_name_status(line)})
Expand Down
123 changes: 119 additions & 4 deletions scripts/orchestration/plans.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,115 @@ def _handoff_recorded_identities(handoff: dict[str, object]) -> list[str]:


def _verified_handoff_tree(root: Path, handoff: dict[str, object]) -> str | None:
repositories = handoff.get("repository") if isinstance(handoff.get("repository"), list) else []
for repository in repositories:
if not isinstance(repository, dict):
continue
recorded_root = str(repository.get("root") or "").strip()
metadata = repository.get("metadata") if isinstance(repository.get("metadata"), dict) else {}
identity = str(metadata.get("actual_commit") or "").strip()
if recorded_root and identity:
tree = _git_tree_id(Path(recorded_root).expanduser().resolve(), identity)
if tree:
return tree
for identity in _handoff_recorded_identities(handoff):
tree = _git_tree_id(root, identity)
if tree:
return tree
return None


def _plan_task_order(args: argparse.Namespace, plan_id: str) -> dict[str, int]:
order: dict[str, int] = {}
for row in index_plans(args):
if row.get("type") != "task" or row.get("plan_id") != plan_id:
continue
front_matter, _body = read_front_matter(artifact_path_from_row(row, args))
task_id = str(front_matter.get("id") or row.get("id") or "")
value = front_matter.get("order")
rank: int | None = None
if isinstance(value, int) and not isinstance(value, bool):
rank = value
elif isinstance(value, str) and re.fullmatch(r"[1-9]\d*", value.strip()):
rank = int(value)
if task_id and rank is not None:
if rank in order.values():
raise SystemExit("acceptance-blocked: final plan task order is ambiguous")
order[task_id] = rank
return order


def _material_repository_root(
args: argparse.Namespace,
plan_id: str,
validated: list[tuple[dict[str, object], dict[str, object]]],
commands: list[str],
) -> Path:
entries: list[tuple[Path, str]] = []
material = [pair for pair in validated if _handoff_has_material_changes(*pair)]
if not material:
return project_root(args)
for handoff, _brief in material:
handoff_has_provenance = False
repositories = handoff.get("repository") if isinstance(handoff.get("repository"), list) else []
for repository in repositories:
if not isinstance(repository, dict):
continue
recorded = str(repository.get("root") or "").strip()
metadata = repository.get("metadata") if isinstance(repository.get("metadata"), dict) else {}
identity = str(metadata.get("actual_commit") or "").strip()
if recorded and identity:
entries.append((Path(recorded).expanduser().resolve(), identity))
handoff_has_provenance = True
if not handoff_has_provenance:
try:
fallback = _resolve_final_plan_workspace(args)
except SystemExit as error:
raise SystemExit(
"acceptance-blocked: material handoff repository provenance is unavailable"
) from error
entries.append((fallback, "HEAD"))
roots = {root for root, _identity in entries}
if len(roots) == 1:
return next(iter(roots))
task_order = _plan_task_order(args, plan_id)
material_ranks: list[int] = []
for handoff, brief in validated:
if not _handoff_has_material_changes(handoff, brief):
continue
task_id = str(brief.get("task_id") or "")
if task_id not in task_order:
raise SystemExit("acceptance-blocked: final plan task order is unavailable")
material_ranks.append(task_order[task_id])
terminal_material_rank = max(material_ranks) if material_ranks else -1
acceptance_entries: list[tuple[Path, str]] = []
for handoff, brief in validated:
if not any(_handoff_command_result(handoff, command) == "passed" for command in commands):
continue
task_id = str(brief.get("task_id") or "")
rank = task_order.get(task_id)
if rank is None or rank < terminal_material_rank:
continue
repositories = handoff.get("repository") if isinstance(handoff.get("repository"), list) else []
for repository in repositories:
if not isinstance(repository, dict):
continue
recorded = str(repository.get("root") or "").strip()
metadata = repository.get("metadata") if isinstance(repository.get("metadata"), dict) else {}
identity = str(metadata.get("actual_commit") or "").strip()
if recorded and identity:
acceptance_entries.append((Path(recorded).expanduser().resolve(), identity))
fresh_acceptance_roots: set[Path] = set()
for root, identity in acceptance_entries:
head_tree = _git_tree_id(root, "HEAD")
recorded_tree = _git_tree_id(root, identity)
if head_tree is not None and recorded_tree is not None and head_tree == recorded_tree:
fresh_acceptance_roots.add(root)
if len(fresh_acceptance_roots) == 1:
return next(iter(fresh_acceptance_roots))
raise SystemExit("acceptance-blocked: final plan repository is ambiguous")


def _acceptance_result_detail(results: set[str]) -> str:
if not results:
return "missing"
Expand Down Expand Up @@ -280,7 +382,7 @@ def _assert_archive_plan_acceptance(
commands = _declared_integration_commands(body)
if not commands:
return
git_root = project_root(args)
git_root = _material_repository_root(args, plan_id, validated, commands)
terminal_tree = _git_tree_id(git_root, "HEAD")
material = [pair for pair in validated if _handoff_has_material_changes(*pair)]
for command in commands:
Expand Down Expand Up @@ -309,7 +411,7 @@ def _assert_archive_plan_acceptance(
raise SystemExit(
f"acceptance-blocked: declared plan-level acceptance {command} is {_acceptance_result_detail(judged)}"
)
workspace = _resolve_final_plan_workspace(args)
workspace = git_root if material else _resolve_final_plan_workspace(args)
for command in commands:
_assert_archive_command_state_neutral(command, workspace)

Expand Down Expand Up @@ -457,9 +559,22 @@ def cmd_archive_plan(args: argparse.Namespace) -> None:
replace_front_matter_value(root_path, "status", "Completed")
moved.append(move_to_archive(root_path, active_root, archived_root))

active_plan_dir = active_root / args.id
sibling_plan_dir = root_path.with_suffix("")
indexed_active_dirs = {
active_root / artifact_path_from_row(row, args).relative_to(active_root).parts[0]
for row in rows
if row.get("type") == "task"
and row.get("plan_id") == args.id
and is_relative_to(artifact_path_from_row(row, args), active_root)
}
if sibling_plan_dir.is_dir() and is_relative_to(sibling_plan_dir, active_root):
active_plan_dir = sibling_plan_dir
elif len(indexed_active_dirs) == 1:
active_plan_dir = next(iter(indexed_active_dirs))
else:
active_plan_dir = active_root / args.id
if active_plan_dir.exists():
archived_plan_dir = archived_root / args.id
archived_plan_dir = archived_root / active_plan_dir.name
if archived_plan_dir.exists():
raise SystemExit(f"Archived plan directory already exists: {archived_plan_dir}")
archived_plan_dir.parent.mkdir(parents=True, exist_ok=True)
Expand Down
36 changes: 36 additions & 0 deletions tests/test_orchestration_execution_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,42 @@ def test_build_review_package_contains_only_bounded_task_diff_and_evidence(tmp_p
assert ".work-bundle/knowledge/notes" not in package


def test_build_review_package_resolves_git_refs_in_bound_execution_repository(
tmp_path: Path,
) -> None:
root, _, task = workspace(tmp_path)
execution_root = tmp_path / "execution-repository"
execution_root.mkdir()
git(execution_root, "init", "-q", "-b", "main")
git(execution_root, "config", "user.email", "test@example.com")
git(execution_root, "config", "user.name", "Test")
source = execution_root / WRITE_SCOPE_FILE
source.parent.mkdir(parents=True)
source.write_text("def compile_task():\n return 'old'\n", encoding="utf-8")
git(execution_root, "add", ".")
git(execution_root, "commit", "-qm", "base")
base = git(execution_root, "rev-parse", "HEAD")

_set_process_validation(task, PASSING_PROCESS)
brief = _compiled_brief(root, task)
_bind_task_execution(root, brief, execution_root=execution_root)
source.write_text("def compile_task():\n return 'new'\n", encoding="utf-8")
git(execution_root, "add", WRITE_SCOPE_FILE)
git(execution_root, "commit", "-qm", "head")
head = git(execution_root, "rev-parse", "HEAD")
handoff = _handoff_for_command(root, PASSING_PROCESS)

target = build_review_package(
args(root, task, handoff=str(handoff), base=base, head=head)
)
package = target.read_text(encoding="utf-8")

assert target == root / ".work-bundle/runtime/execution/plan-001/task-004/review-package.md"
assert f"Base: {base}" in package
assert f"Head: {head}" in package
assert "return 'new'" in package


def test_build_review_package_includes_tracked_and_untracked_worktree_changes(tmp_path: Path) -> None:
root, _, task = workspace(tmp_path)
source = root / WRITE_SCOPE_FILE
Expand Down
Loading
Loading