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
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def test_compaction_accepts_schema_and_git_valid_checkpoint(self) -> None:
)
message = recovery["hookSpecificOutput"]["additionalContext"]
self.assertIn("Validated checkpoint", message)
self.assertIn(str(checkpoint_path), message)
self.assertIn(str(checkpoint_path.resolve()), message)

def test_detached_head_with_multiple_containing_branches_is_ambiguous(self) -> None:
with tempfile.TemporaryDirectory() as directory:
Expand Down
6 changes: 5 additions & 1 deletion .agents/skills/plan-anvil/tests/test_preflight_and_git.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,11 @@ def test_probe_failure_cleans_resources_and_reports_hook_block(self) -> None:
hooks = repo / hooks
hooks.mkdir(parents=True, exist_ok=True)
hook = hooks / "pre-commit"
hook.write_text("#!/bin/sh\nexit 1\n", encoding="utf-8", newline="\n")
hook.write_text(
"#!/bin/sh\necho 'pre-commit hook failed' >&2\nexit 1\n",
encoding="utf-8",
newline="\n",
)
hook.chmod(0o755)
result = probe_git_capabilities(repo, "probe-hook-block", root / "external")
self.assertFalse(result["ok"], result)
Expand Down
6 changes: 3 additions & 3 deletions .agents/skills/plan-anvil/tests/test_safety_regressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ def test_seal_artifacts_holds_lock_through_validation(self) -> None:
author_valid_plan(context)

observed: dict[str, str] = {}
real_validate_plan = seal_module.validate_plan
real_validate_plan_contract = seal_module.validate_plan_contract

def validate_while_locked(*args, **kwargs):
lock_path = context["run_root"] / ".generation-lock"
payload = json.loads(lock_path.read_text(encoding="utf-8"))
observed["command"] = payload["command"]
return real_validate_plan(*args, **kwargs)
return real_validate_plan_contract(*args, **kwargs)

with patch.object(seal_module, "validate_plan", side_effect=validate_while_locked):
with patch.object(seal_module, "validate_plan_contract", side_effect=validate_while_locked):
result = seal_module.seal_artifacts(context["planning"], context["run_root"])

self.assertTrue(result["ok"])
Expand Down
16 changes: 13 additions & 3 deletions .codex/hooks/plan_anvil_hooklib.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,24 @@ def _source_worktree(active: ActiveRun) -> Path | None:
return Path(raw).resolve() if isinstance(raw, str) and raw else None


def _path_identity(path: Path) -> str:
return os.path.normcase(os.path.normpath(str(path.resolve())))


def active_run_candidates_for_event(event: dict[str, Any]) -> list[ActiveRun]:
raw_cwd = event.get("cwd") if isinstance(event.get("cwd"), str) else None
repo = git_root(raw_cwd)
if repo is None:
return []
repo_identity = _path_identity(repo)
all_runs = active_runs(repo)
exact = [item for item in all_runs if item.worktree == repo]
source = [item for item in all_runs if _source_worktree(item) == repo]
exact = [item for item in all_runs if _path_identity(item.worktree) == repo_identity]
source = [
item
for item in all_runs
if (source_worktree := _source_worktree(item)) is not None
and _path_identity(source_worktree) == repo_identity
]
if exact:
candidates = exact
elif source:
Expand All @@ -127,7 +137,7 @@ def active_run_candidates_for_event(event: dict[str, Any]) -> list[ActiveRun]:
explicit = _explicit_run_id(event)
if explicit is not None:
candidates = [item for item in candidates if item.run_root.name == explicit]
unique: dict[Path, ActiveRun] = {item.run_root: item for item in candidates}
unique: dict[str, ActiveRun] = {_path_identity(item.run_root): item for item in candidates}
return sorted(unique.values(), key=lambda item: item.run_root.name)


Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to PlanAnvil are documented here.

## [Unreleased]

### Fixed

- canonicalize event repository paths before active-run routing so source-worktree matching is stable across macOS symlink aliases and Windows path aliases;
- keep checkpoint recovery assertions platform-neutral by comparing canonical paths;
- update the artifact-sealing lock regression test to observe the current `validate_plan_contract` gate;
- make the Git-hook probe fixture emit explicit hook diagnostics while preserving fail-closed classification for unrelated commit failures;
- synchronize golden blind-review fixtures and dependent comparison hashes with the required independent `plan-anvil-reviewer` author role.

## [0.1.0] - 2026-07-12

### Added
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"author_role": "plan-anvil-reviewer",
"created_at": "2026-07-12T18:00:00Z",
"findings": [],
"inputs": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"blind_review_hash": "sha256:0b278c608ef8973584e451cb036f19566af028be8bd5b6c0a4fdac484f30341f",
"blind_review_sidecar_hash": "sha256:bfd4b664dea9860ec56d60e87628006165410f59004e3e68250aadcec213a2d9",
"blind_review_sidecar_hash": "sha256:389a86eb5fb0d2f8d652c12a0612f21a9ce7073931dabd7146984233ec56f8d5",
"created_at": "2026-07-12T18:00:00Z",
"deterministic_validation": "PASS",
"reasons": [],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"author_role": "plan-anvil-reviewer",
"created_at": "2026-07-12T18:00:00Z",
"findings": [],
"inputs": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"blind_review_hash": "sha256:0b278c608ef8973584e451cb036f19566af028be8bd5b6c0a4fdac484f30341f",
"blind_review_sidecar_hash": "sha256:f1a0f88f3221571225909d189b4f77553447c30b322686afd52f39e87e74f8fb",
"blind_review_sidecar_hash": "sha256:97963f012ba8eee2d8f16a4aa7a3f803b100935d0c43d5f591a5ff37e9e8d08b",
"created_at": "2026-07-12T18:00:00Z",
"deterministic_validation": "PASS",
"reasons": [],
Expand Down
Loading